From dna-commits at lists.jboss.org  Fri Oct  2 11:18:24 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 2 Oct 2009 11:18:24 -0400
Subject: [dna-commits] DNA SVN:  r1279 - branches.
Message-ID: <200910021518.n92FIOfo003397@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-10-02 11:18:24 -0400 (Fri, 02 Oct 2009)
New Revision: 1279

Added:
   branches/ddl_sequencer/
Log:


Copied: branches/ddl_sequencer (from rev 1278, trunk)


From dna-commits at lists.jboss.org  Fri Oct  2 12:13:43 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 2 Oct 2009 12:13:43 -0400
Subject: [dna-commits] DNA SVN: r1280 - in branches/ddl_sequencer:
	extensions and 42 other directories.
Message-ID: <200910021613.n92GDhuv014915@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-10-02 12:13:40 -0400 (Fri, 02 Oct 2009)
New Revision: 1280

Added:
   branches/ddl_sequencer/.project
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/.classpath
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/.project
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/.settings/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/pom.xml
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataType.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParser.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/Db2DdlParser.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/PostgresDdlParser.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SqlServerDdlParser.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SybaseDdlParser.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/derby/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/derby/DerbyDdlConstants.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlConstants.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlParser.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/AlterTable.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Column.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/ForeignKeyReference.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Index.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Statement.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Table.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableConstraint.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableElement.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TypedStatement.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/datatype/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParserTest.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParserTest.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/OracleDdlParserTest.java
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/resources/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/resources/ddl/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/resources/sql2bnf.aug92.txt
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/ddl/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlConstants$DataTypes.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlConstants$StatementStartPhrases.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlConstants.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlTokenStream$DdlTokenizer.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlTokenStream.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/OldDdlTokenizer.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/StandardDdlParser.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/datatype/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/Db2DdlParser.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser$DerbyDataTypeParser.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser$MySqlDataTypeParser.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/PostgresDdlParser.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/SqlServerDdlParser.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/SybaseDdlParser.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/derby/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/oracle/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/statement/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/sql2bnf.aug92.txt
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/logs/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/logs/test/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.BasicDdlParserTest.xml
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.xml
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.xml
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.xml
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.BasicDdlParserTest.txt
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.txt
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.txt
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.txt
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/ddl/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/datatype/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/datatype/DataTypeParserTest.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/dialect/
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParserTest.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/dialect/OracleDdlParserTest.class
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/sql2bnf.aug92.txt
Log:
Initial check-in of ddl sequencer and parsing framework

Added: branches/ddl_sequencer/.project
===================================================================
--- branches/ddl_sequencer/.project	                        (rev 0)
+++ branches/ddl_sequencer/.project	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>dna</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+	</buildSpec>
+	<natures>
+	</natures>
+</projectDescription>


Property changes on: branches/ddl_sequencer/.project
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/.classpath
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/.classpath	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/.classpath	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+	<classpathentry kind="src" path="src/test/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/.classpath
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/.project
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/.project	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/.project	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>dna-sequencer-ddl</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+	</natures>
+</projectDescription>


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/.project
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/pom.xml
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/pom.xml	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/pom.xml	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,88 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.jboss.dna</groupId>
+    <artifactId>dna</artifactId>
+    <version>0.7-SNAPSHOT</version>
+    <relativePath>../..</relativePath>
+  </parent>
+  <!-- The groupId and version values are inherited from parent -->
+  <artifactId>dna-sequencer-ddl</artifactId>
+  <packaging>jar</packaging>
+  <name>JBoss DNA DDL Sequencer</name>
+  <description>JBoss DNA Sequencer that processes DDL files</description>
+  <url>http://labs.jboss.org/dna</url>
+  <dependencies>
+    <!-- 
+    Common
+    -->
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-graph</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-common</artifactId>
+      <version>${pom.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-graph</artifactId>
+      <version>${pom.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <!-- 
+    Testing (note the scope)
+    -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-library</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+    </dependency>
+    <!-- 
+    Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) 
+    -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+    </dependency>
+    <!-- 
+    Java Concurrency in Practice annotations
+    -->
+    <dependency>
+      <groupId>net.jcip</groupId>
+      <artifactId>jcip-annotations</artifactId>
+    </dependency>
+  </dependencies>
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/pom.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataType.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataType.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataType.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,117 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.ddl.datatype;
+
+/**
+ * 
+ * @author blafond
+ *
+ */
+public class DataType {
+	private String name;
+	private int length = 0;
+	private int precision = 0;
+	private int scale = 0;
+	private boolean isKMGLength = false;
+	private String kmgValue = null;
+	
+	public DataType (String theName) {
+		super();
+		this.name = theName;
+	}
+	
+	public DataType (String name, int length) {
+		super();
+		this.name = name;
+		this.length = length;
+	}
+	
+	
+	public DataType (String name, int precision, int scale) {
+		super();
+		this.name = name;
+		this.precision = precision;
+		this.scale = scale;
+	}
+	
+	public String getName() {
+		return this.name;
+	}
+	
+	public void setName(String value) {
+		this.name = value;
+	}
+	
+	public void setLength(int value) {
+		this.length = value;
+	}
+	
+	public int getLength() {
+		return this.length;
+	}
+	
+	public void setPrecision(int value) {
+		this.precision = value;
+	}
+	
+	public int getPrecision() {
+		return this.precision;
+	}
+	
+	public int getScale() {
+		return this.scale;
+	}
+	
+	public void setScale(int value) {
+		this.scale = value;
+	}
+
+	@Override
+	public String toString() {
+		StringBuffer result = new StringBuffer(100);
+		result.append("DataType()").append(" ").append(name);
+				
+		return result.toString();
+	}
+
+	public boolean isKMGLength() {
+		return isKMGLength;
+	}
+
+	public void setKMGLength(boolean isKMGLength) {
+		this.isKMGLength = isKMGLength;
+	}
+
+	public String getKMGValue() {
+		return kmgValue;
+	}
+
+	public void setKMGValue(String kmgValue) {
+		this.kmgValue = kmgValue;
+	}
+	
+	
+
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataType.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParser.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParser.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParser.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,593 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.ddl.datatype;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.sequencer.ddl.DdlConstants;
+import org.jboss.dna.sequencer.ddl.DdlTokenStream;
+
+public class DataTypeParser implements DdlConstants {
+	private static List<String[]> basicCharStringTypes 			= new ArrayList<String[]>();
+	private static List<String[]> basicNationalCharStringTypes 	= new ArrayList<String[]>();
+	private static List<String[]> basicBitStringTypes 			= new ArrayList<String[]>();
+	private static List<String[]> basicExactNumericTypes 		= new ArrayList<String[]>();
+	private static List<String[]> basicApproxNumericStringTypes = new ArrayList<String[]>();
+	private static List<String[]> basicDateTimeTypes 			= new ArrayList<String[]>();
+	
+    private int defaultLength = 255;
+    private int defaultPrecision = 0;
+    private int defaultScale = 0;
+
+    public DataTypeParser () {
+    	super();
+    	
+    	initialize();
+    }
+    
+    private void initialize() {
+    	
+    	basicCharStringTypes.add(DataTypes.DTYPE_CHARACTER);
+    	basicCharStringTypes.add(DataTypes.DTYPE_CHAR);
+    	basicCharStringTypes.add(DataTypes.DTYPE_CHARACTER_VARYING);
+    	basicCharStringTypes.add(DataTypes.DTYPE_CHAR_VARYING);
+    	basicCharStringTypes.add(DataTypes.DTYPE_VARCHAR);
+    	
+    	basicNationalCharStringTypes.add(DataTypes.DTYPE_NATIONAL_CHARACTER);
+    	basicNationalCharStringTypes.add(DataTypes.DTYPE_NATIONAL_CHARACTER_VARYING);
+    	basicNationalCharStringTypes.add(DataTypes.DTYPE_NATIONAL_CHAR);
+    	basicNationalCharStringTypes.add(DataTypes.DTYPE_NATIONAL_CHAR_VARYING);
+    	basicNationalCharStringTypes.add(DataTypes.DTYPE_NCHAR_VARYING);
+    	
+    	basicBitStringTypes.add(DataTypes.DTYPE_BIT);
+    	basicBitStringTypes.add(DataTypes.DTYPE_BIT_VARYING);
+    	
+    	basicExactNumericTypes.add(DataTypes.DTYPE_NUMERIC);
+    	basicExactNumericTypes.add(DataTypes.DTYPE_DEC);
+    	basicExactNumericTypes.add(DataTypes.DTYPE_DECIMAL);
+    	basicExactNumericTypes.add(DataTypes.DTYPE_INTEGER);
+    	basicExactNumericTypes.add(DataTypes.DTYPE_INT);
+    	basicExactNumericTypes.add(DataTypes.DTYPE_SMALLINT);
+    	
+    	basicApproxNumericStringTypes.add(DataTypes.DTYPE_FLOAT);
+    	basicApproxNumericStringTypes.add(DataTypes.DTYPE_REAL);
+    	basicApproxNumericStringTypes.add(DataTypes.DTYPE_DOUBLE_PRECISION);
+    	
+    	basicDateTimeTypes.add(DataTypes.DTYPE_DATE);
+    	basicDateTimeTypes.add(DataTypes.DTYPE_TIME);
+    	basicDateTimeTypes.add(DataTypes.DTYPE_TIMESTAMP);
+
+    }
+    
+    /**
+     * Method determines if the next set of tokens matches one of the registered data type token sets.
+     * 
+     * @param tokens
+     * @return is registered data type
+     * @throws ParsingException
+     */
+    public final boolean isDatatype( DdlTokenStream tokens)  throws ParsingException {
+        	// Loop through the registered statement start string arrays and look for exact matches.
+        	
+        	for( String[] stmts : basicCharStringTypes ) {
+        		if( tokens.matches(stmts) ) return true;
+        	}
+
+        	for( String[] stmts : basicNationalCharStringTypes ) {
+        		if( tokens.matches(stmts) ) return true;
+        	}
+
+
+        	for( String[] stmts : basicBitStringTypes ) {
+        		if( tokens.matches(stmts) ) return true;
+        	}
+
+
+        	for( String[] stmts : basicExactNumericTypes) {
+        		if( tokens.matches(stmts) ) return true;
+        	}
+
+
+        	for( String[] stmts : basicApproxNumericStringTypes ) {
+        		if( tokens.matches(stmts) ) return true;
+        	}
+
+
+        	for( String[] stmts : basicDateTimeTypes ) {
+        		if( tokens.matches(stmts) ) return true;
+        	}
+
+	        // If no type is found, assume it's a custom type
+        	return isCustomDataType(tokens);
+    }
+    
+
+    /**
+     * Method determines if the next set of tokens matches one of the registered data type token sets.
+     * @param tokens
+     * @param type
+     * @return is registered data type
+     * @throws ParsingException
+     */
+    private boolean isDatatype( DdlTokenStream tokens, int type)  throws ParsingException {
+    	// Loop through the registered statement start string arrays and look for exact matches.
+    	
+    	switch( type ) {
+	    	case DataTypes.DTYPE_CODE_CHAR_STRING: {
+	        	for( String[] stmts : basicCharStringTypes ) {
+	        		if( tokens.matches(stmts) ) return true;
+	        	}
+	    	} break;
+	    	case DataTypes.DTYPE_CODE_NCHAR_STRING: {
+	        	for( String[] stmts : basicNationalCharStringTypes ) {
+	        		if( tokens.matches(stmts) ) return true;
+	        	}
+	    	} break;
+	    	case DataTypes.DTYPE_CODE_BIT_STRING: {
+	        	for( String[] stmts : basicBitStringTypes ) {
+	        		if( tokens.matches(stmts) ) return true;
+	        	}
+	    	} break;
+	    	case DataTypes.DTYPE_CODE_EXACT_NUMERIC: {
+	        	for( String[] stmts : basicExactNumericTypes ) {
+	        		if( tokens.matches(stmts) ) return true;
+	        	}
+	    	} break;
+	    	case DataTypes.DTYPE_CODE_APROX_NUMERIC: {
+	        	for( String[] stmts : basicApproxNumericStringTypes ) {
+	        		if( tokens.matches(stmts) ) return true;
+	        	}
+	    	} break;
+	    	case DataTypes.DTYPE_CODE_DATE_TIME: {
+	        	for( String[] stmts : basicDateTimeTypes ) {
+	        		if( tokens.matches(stmts) ) return true;
+	        	}
+	    	} break;
+    	}
+    	
+    	return false;
+    }
+    
+    /**
+     * Method to determine of next tokens represent a custom data type. Subclasses should override this method and perform
+     * token checks for any non-SQL92 spec'd data types.
+     * 
+     * @param tokens
+     * @return is custom data type
+     * @throws ParsingException
+     */
+    protected boolean isCustomDataType( DdlTokenStream tokens )  throws ParsingException {
+    	return false;
+    }
+    
+    /**
+     * Method which performs the actual parsing of the data type name and applicable values (i.e. VARCHAR(20)) if data type is
+     * found.
+     * 
+     * @param tokens
+     * @return the {@link DataType}
+     * @throws ParsingException
+     */
+    public DataType parse( DdlTokenStream tokens )  throws ParsingException {
+    	DataType result = null;
+    	
+    	if ( isDatatype(tokens, DataTypes.DTYPE_CODE_CHAR_STRING) ) {
+    		result = parseCharStringType(tokens);
+    	} else if( isDatatype(tokens, DataTypes.DTYPE_CODE_NCHAR_STRING) ) {
+    		result = parseNationalCharStringType(tokens);
+    	} else if( isDatatype(tokens, DataTypes.DTYPE_CODE_BIT_STRING) ) {
+    		result = parseBitStringType(tokens);
+    	} else if( isDatatype(tokens, DataTypes.DTYPE_CODE_EXACT_NUMERIC) ) {
+    		result = parseExactNumericType(tokens);
+    	} else if( isDatatype(tokens, DataTypes.DTYPE_CODE_APROX_NUMERIC) ) {
+    		result = parseApproxNumericType(tokens);
+    	} else if( isDatatype(tokens, DataTypes.DTYPE_CODE_DATE_TIME) ) {
+    		result = parseDateTimeType(tokens);
+    	} else {
+    		result = parseCustomType(tokens);
+    	}
+    	
+    	return result;
+    }
+    
+    /**
+     * Parses SQL-92 Character string data types.
+     * 
+     * <character string type> ::=
+     *	 	  CHARACTER [ <left paren> <length> <right paren> ]
+     *	 	| CHAR [ <left paren> <length> <right paren> ]
+     *	 	| CHARACTER VARYING <left paren> <length> <right paren>
+     *	 	| CHAR VARYING <left paren> <length> <right paren>
+     *	 	| VARCHAR <left paren> <length> <right paren>
+     * 
+     * @param tokens
+     * @return the {@link DataType}
+     * @throws ParsingException
+     */
+    protected DataType parseCharStringType( DdlTokenStream tokens ) throws ParsingException {
+    	DataType result = null;
+    	String typeName = null;
+    	
+    	
+    	if( tokens.matches(DataTypes.DTYPE_VARCHAR)) {
+			typeName = tokens.consume(); // "VARCHAR"
+			int length = parseBracketedInteger(tokens);
+	        
+	        result = new DataType(typeName, length);
+    	} else {
+	    	
+			typeName = tokens.consume(); // "CHARACTER", "CHAR", 
+			
+			if( tokens.matches("VARYING")) {
+				typeName = typeName + SPACE + tokens.consume();
+				int length = parseBracketedInteger(tokens);
+		        
+		        result = new DataType(typeName, length);
+			} else {
+				int length = getDefaultLength();
+				if( tokens.matches('(')) {
+					length = parseBracketedInteger(tokens);
+				}
+				result = new DataType(typeName, length);
+			}
+    	}
+        
+    	return result;
+    }
+    
+    /**
+     * Parses SQL-92 National Character string data types.
+     * 
+     * <national character string type> ::=
+     *	      NATIONAL CHARACTER [ <left paren> <length> <right paren> ]
+     *	 	| NATIONAL CHAR [ <left paren> <length> <right paren> ]
+     *	 	| NCHAR [ <left paren> <length> <right paren> ]
+     *	 	| NATIONAL CHARACTER VARYING <left paren> <length> <right paren>
+     *	 	| NATIONAL CHAR VARYING <left paren> <length> <right paren>
+     *	 	| NCHAR VARYING <left paren> <length> <right paren>
+     * 
+     * @param tokens
+     * @return the {@link DataType}
+     * @throws ParsingException
+     */
+    protected DataType parseNationalCharStringType( DdlTokenStream tokens ) throws ParsingException {
+    	DataType result = null;
+    	String typeName = null;
+    	
+    	if( tokens.matches(DataTypes.DTYPE_NCHAR_VARYING) ) {
+			typeName = tokens.consume() + SPACE + tokens.consume();
+			int length = parseBracketedInteger(tokens);
+            
+            result = new DataType(typeName, length);
+    	} else if( tokens.matches("NATIONAL") ) {
+    		typeName = tokens.consume();
+    		if( tokens.matchesAnyOf("CHAR", "CHARACTER") ) {
+    			typeName = typeName + SPACE + tokens.consume();
+        		if( tokens.matches("VARYING") ) {
+        			typeName = typeName + SPACE + tokens.consume();
+        			int length = parseBracketedInteger(tokens);
+                    
+                    result = new DataType(typeName, length);
+        		} else {
+        			result = new DataType(typeName, getDefaultLength());
+        		}
+    		}
+    	}
+    	
+    	return result;
+    }
+    
+    /**
+     * Parses SQL-92 Bit string data types.
+     * 
+     * <bit string type> ::=
+	 *     BIT [ <left paren> <length> <right paren> ]
+	 *   | BIT VARYING <left paren> <length> <right paren>
+     * 
+     * 
+     * @param tokens
+     * @return the {@link DataType}
+     * @throws ParsingException
+     */
+    protected DataType parseBitStringType( DdlTokenStream tokens ) throws ParsingException {
+    	DataType result = null;
+    	
+		String typeName = tokens.consume(); // "BIT"
+		
+		if( tokens.matches("VARYING") ) {
+			typeName = typeName + SPACE + tokens.consume();
+			int length = parseBracketedInteger(tokens);
+	        
+	        result = new DataType(typeName, length);
+		} else {
+			result = new DataType(typeName, getDefaultLength());
+		}
+
+    	return result;
+    }
+    
+    /**
+     * Parses SQL-92 Exact numeric data types.
+     * 
+     * <exact numeric type> ::=
+	 *     NUMERIC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+	 *   | DECIMAL [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+	 *   | DEC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+	 *   | INTEGER
+	 *   | INT
+     *	 | SMALLINT
+     *
+     * @param tokens
+     * @return the {@link DataType}
+     * @throws ParsingException
+     */
+    protected DataType parseExactNumericType( DdlTokenStream tokens ) throws ParsingException {
+    	DataType result = null;
+    	String typeName = null;
+    	
+    	if( tokens.matchesAnyOf("INTEGER", "INT", "SMALLINT") ) {
+    		typeName = tokens.consume();
+    		result = new DataType(typeName);
+    	} else if( tokens.matchesAnyOf("NUMERIC", "DECIMAL", "DEC")) {
+    		typeName = tokens.consume();
+    		int precision = 0;
+    		int scale = 0;
+        	if( tokens.matches('(')) {
+	            tokens.consume('(');
+	            precision = parseInteger(tokens);
+	            if( tokens.canConsume(',') ) {
+	                scale =  parseInteger(tokens);
+	            } else {
+	            	scale = getDefaultScale();
+	            }
+	            tokens.consume(')');
+        	} else {
+        		precision = getDefaultPrecision();
+        		scale = getDefaultScale();
+        	}
+        	result = new DataType(typeName, precision, scale);
+    	}
+
+    	return result;
+    }
+    
+    /**
+     * Parses SQL-92 Approximate numeric data types.
+     * 
+     * <approximate numeric type> ::=
+	 *     FLOAT [ <left paren> <precision> <right paren> ]
+	 *   | REAL
+	 *   | DOUBLE PRECISION
+     * 
+     * @param tokens
+     * @return the {@link DataType}
+     * @throws ParsingException
+     */
+    protected DataType parseApproxNumericType( DdlTokenStream tokens ) throws ParsingException {
+    	DataType result = null;
+    	String typeName = null;
+    	
+    	if( tokens.matches(DataTypes.DTYPE_REAL) ) {
+    		typeName = tokens.consume();
+    		result = new DataType(typeName);
+    	} else if( tokens.matches(DataTypes.DTYPE_DOUBLE_PRECISION) ) {
+    		typeName = tokens.consume() + SPACE + tokens.consume();
+    		result = new DataType(typeName);
+    	} else if( tokens.matches(DataTypes.DTYPE_FLOAT)) {
+    		typeName = tokens.consume();
+    		int precision = 0;
+        	if( tokens.matches('(')) {
+	            precision = parseBracketedInteger(tokens);
+        	}
+        	result = new DataType(typeName, precision, 0);
+    	}
+
+    	return result;
+    }
+    
+    
+    /**
+     * Parses SQL-92 Date and Time data types.
+     * 
+	 *	<datetime type> ::=
+	 *	      DATE
+	 *	    | TIME [ <left paren> <time precision> <right paren> ]
+	 *	          [ WITH TIME ZONE ]
+	 *	    | TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+	 *	          [ WITH TIME ZONE ]
+     * 
+     * @param tokens
+     * @return the {@link DataType}
+     * @throws ParsingException
+     */
+    protected DataType parseDateTimeType( DdlTokenStream tokens ) throws ParsingException {
+    	DataType result = null;
+    	String typeName = null;
+    	
+    	if( tokens.matches(DataTypes.DTYPE_DATE) ) {
+    		typeName = tokens.consume();
+    		result = new DataType(typeName);
+    	} else if( tokens.matches(DataTypes.DTYPE_TIME)) {
+    		typeName = tokens.consume();
+    		int precision = 0;
+        	if( tokens.matches('(')) {
+	            precision = parseBracketedInteger(tokens);
+        	}
+        	result = new DataType(typeName, precision, 0);
+    	} else if( tokens.matches(DataTypes.DTYPE_TIMESTAMP)) {
+    		typeName = tokens.consume();
+    		int precision = 0;
+        	if( tokens.matches('(')) {
+	            precision = parseBracketedInteger(tokens);
+        	}
+        	result = new DataType(typeName, precision, 0);
+    	}
+
+    	return result;
+    }
+    
+    /**
+     * General catch-all data type parsing method that sub-classes can override to parse database-specific data types.
+     * 
+     * @param tokens
+     * @return the {@link DataType}
+     * @throws ParsingException
+     */
+    protected DataType parseCustomType( DdlTokenStream tokens ) throws ParsingException {
+    	return null;
+    }
+
+    /**
+     * 
+     * @return integer default value for length
+     */
+	public int getDefaultLength() {
+		return defaultLength;
+	}
+
+	/**
+	 * 
+	 * @param defaultLength
+	 */
+	public void setDefaultLength(int defaultLength) {
+		this.defaultLength = defaultLength;
+	}
+
+	/**
+	 * 
+	 * @return integer default value for precision
+	 */
+	public int getDefaultPrecision() {
+		return defaultPrecision;
+	}
+
+	/**
+	 * 
+	 * @param defaultPrecision
+	 */
+	public void setDefaultPrecision(int defaultPrecision) {
+		this.defaultPrecision = defaultPrecision;
+	}
+
+	/**
+	 * 
+	 * @return integer default value for scale
+	 */
+	public int getDefaultScale() {
+		return defaultScale;
+	}
+
+	/**
+	 * 
+	 * @param defaultScale
+	 */
+	public void setDefaultScale(int defaultScale) {
+		this.defaultScale = defaultScale;
+	}
+    
+	/**
+	 * Returns an integer value from the input token stream assuming the integer is not bracketed with parenthesis.
+	 * 
+	 * 
+	 * @param tokens
+	 * @return integer value
+	 */
+    protected int parseInteger( DdlTokenStream tokens ) {
+    	String value = tokens.consume();
+    	
+    	if( isKMGInteger(value) ) {
+    		value = value.substring(0, value.length()-1);
+    	}
+    	
+    	return new BigInteger(value).intValue();
+    }
+    
+    /**
+     * Returns an integer value from the input token stream assuming the integer is bracketed with parenthesis.
+     * 
+     * Example:  (255)
+     * 
+     * @param tokens
+     * @return integer value
+     */
+    protected int parseBracketedInteger( DdlTokenStream tokens ) {
+        tokens.consume('(');
+        int length = parseInteger(tokens);
+        tokens.consume(')');
+        
+        return length;
+    }
+
+    /**
+     * Returns the whether or not a tokenized string is a complex BLOB or CLOB length value ending in K, M or G
+     * 
+     * Example:  BLOB (100M) or CLOB(64K)
+     * 
+     * @param value
+     * @return true if value ends with M, K or G
+     */
+    protected boolean isKMGInteger( String value ) {
+    	return ( value.endsWith("M") || value.endsWith("K") || value.endsWith("G"));
+    }
+    
+    /**
+     * 
+     * Returns the integer value of the input string.
+     * 
+     * Handles both straight integer string or complex KMG (CLOB or BLOB) value.
+     * 
+     * Throws {@link NumberFormatException} if a valid integer is not found.
+     * 
+     * @param value
+     * @return integer value
+     */
+    protected int parseInteger( String value ) {
+    	if( isKMGInteger(value) ) {
+    		value = value.substring(0, value.length()-1);
+    	}
+    	
+    	return new BigInteger(value).intValue();
+    }
+    
+    /**
+     * Returns the K, M or G string value of a tokenized complex BLOB or CLOB length value
+     * 
+     * Example:  BLOB (100M) or CLOB(64K)
+     * 
+     * @param value
+     * @return K, M or G or NULL if not found.
+     */
+    protected String getKMG( String value ) {
+    	if( value.endsWith("M")) return "M";
+    	if( value.endsWith("K")) return "K";
+    	if( value.endsWith("G")) return "G";
+    	
+    	return null;
+    }
+
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParser.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/Db2DdlParser.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/Db2DdlParser.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/Db2DdlParser.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,100 @@
+package org.jboss.dna.sequencer.ddl.dialect;
+
+import org.jboss.dna.sequencer.ddl.StandardDdlParser;
+
+
+
+public class Db2DdlParser extends StandardDdlParser {
+	private static final String[] COMMENT_ON = {"COMMENT", "ON"};
+
+	
+	/*
+	
+	 * ===========================================================================================================================
+	 * Data Types
+
+		|--+-SMALLINT---------------------------------------------------------------+--|
+		   +-+-INTEGER-+------------------------------------------------------------+
+		   | '-INT-----'                                                            |
+		   +-BIGINT-----------------------------------------------------------------+
+		   +-+-FLOAT--+---------------+-+-------------------------------------------+
+		   | |        '-(--integer--)-' |                                           |
+		   | +-REAL---------------------+                                           |
+		   | |         .-PRECISION-.    |                                           |
+		   | '-DOUBLE--+-----------+----'                                           |
+		   +-+-DECIMAL-+--+-----------------------------+---------------------------+
+		   | +-DEC-----+  '-(--integer--+----------+--)-'                           |
+		   | +-NUMERIC-+                '-,integer-'                                |
+		   | '-NUM-----'                                                            |
+		   +-+-+-CHARACTER-+--+-----------+--------------+--+---------------------+-+
+		   | | '-CHAR------'  '-(integer)-'              |  |  (1)                | |
+		   | +-+-VARCHAR----------------+--(--integer--)-+  '--------FOR BIT DATA-' |
+		   | | '-+-CHARACTER-+--VARYING-'                |                          |
+		   | |   '-CHAR------'                           |                          |
+		   | '-LONG VARCHAR------------------------------'                          |
+		   +-+-+-BLOB----------------+---------+--+----------------------+----------+
+		   | | '-BINARY LARGE OBJECT-'         |  '-2 (--2 integer--+---+--2 )-'          |
+		   | +-+-CLOB------------------------+-+                +-2 K-+               |
+		   | | '-+-CHARACTER-+--LARGE OBJECT-' |                +-2 M-+               |
+		   | |   '-CHAR------'                 |                '-2 G-'               |
+		   | '-DBCLOB--------------------------'                                    |
+		   +-GRAPHIC--+-----------+-------------------------------------------------+
+		   |          '-(integer)-'                                                 |
+		   +-VARGRAPHIC--(integer)--------------------------------------------------+
+		   +-LONG VARGRAPHIC--------------------------------------------------------+
+		   +-DATE-------------------------------------------------------------------+
+		   +-TIME-------------------------------------------------------------------+
+		   +-TIMESTAMP--------------------------------------------------------------+
+		   +-DATALINK--+---------------+--------------------------------------------+
+		   |           '-(--integer--)-'                                            |
+		   +-distinct-type-name-----------------------------------------------------+
+		   +-structured-type-name---------------------------------------------------+
+		   '-REF--(type-name2)------------------------------------------------------'
+
+
+	
+	 * ===========================================================================================================================
+	 * Data Definition Statements
+	 ALLOCATE CURSOR
+	 ALTER [BUFFERPON | (DATABASE PARTITION GROUP) | DATABASE | FUNCTION | METHOD | NICKNAME | PROCEDURE | SEQUENCE | SERVER 
+	      | TABLE | TABLESPACE | TYPE | (USER MAPPING) | VIEW | WRAPPER]
+	 ASSOCIATE LOCATORS
+	 CASE
+	 COMMENT ON
+	 COMMIT [WORK]
+	 CREATE [ALIAS | BUFFERPOOL | (DATABASE PARTITION GROUP) | (DISTINCT TYPE) | (EVENT MONITOR) | FUNCTION | (FUNCTION MAPPING) 
+	      | INDEX | METHOD | NICKNAME | PROCEDURE | SCHEMA | SEQUENCE | SERVER | TABLE | TABLESPACE | TRANSFORM | TRIGGER | TYPE
+	      | (TYPE MAPPING) | (USER MAPPING) | VIEW | WRAPPER]
+	 DECLARE GLOBAL TEMPORARY TABLE
+	 DELETE FROM
+	 DROP [ALIAS | BUFFERPOOL | (DATABASE PARTITION GROUP) | (EVENT MONITOR) | FUNCTION | (SPECIFIC FUNCTION) | (FUNCTION MAPPING) 
+	      | INDEX | (INDEX EXTENSION) | METHOD | (SPECIFIC METHOD) | NICKNAME | PACKAGE | PROCEDURE | (SPECIFIC PROCEDURE) | SCHEMA 
+	      | SEQUENCE | SERVER | TABLE | (TABLE HIERARCHY) | TABLESPACE[S] | TRANSFORM[S] | TRIGGER | TYPE | (TYPE MAPPING) 
+	      | (USER MAPPING) | (USER MAPPING FOR) | VIEW | (VIEW HIERARCHY) | WRAPPER]
+	
+	*/
+	
+	private static final String TERMINATOR = "%"; 
+	
+	public Db2DdlParser() {
+		super();
+		initialize();
+	}
+	
+	
+	private void initialize() {
+		registerStatementStartPhrase(COMMENT_ON);
+		setTerminator(TERMINATOR);
+	}
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/Db2DdlParser.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,630 @@
+package org.jboss.dna.sequencer.ddl.dialect;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.sequencer.ddl.DdlTokenStream;
+import org.jboss.dna.sequencer.ddl.StandardDdlParser;
+import org.jboss.dna.sequencer.ddl.DdlTokenStream.DdlTokenizer;
+import org.jboss.dna.sequencer.ddl.datatype.DataType;
+import org.jboss.dna.sequencer.ddl.datatype.DataTypeParser;
+import org.jboss.dna.sequencer.ddl.dialect.derby.DerbyDdlConstants;
+import org.jboss.dna.sequencer.ddl.statement.AlterTable;
+import org.jboss.dna.sequencer.ddl.statement.Column;
+import org.jboss.dna.sequencer.ddl.statement.Index;
+import org.jboss.dna.sequencer.ddl.statement.Statement;
+import org.jboss.dna.sequencer.ddl.statement.TableConstraint;
+import org.jboss.dna.sequencer.ddl.statement.TypedStatement;
+
+public class DerbyDdlParser extends StandardDdlParser implements DerbyDdlConstants {
+	
+	static List<String[]> derbyDataTypeStrings = new ArrayList<String[]>();
+	
+	private static final String TERMINATOR = DEFAULT_TERMINATOR; 
+	
+	public DerbyDdlParser() {
+		setDatatypeParser(new DerbyDataTypeParser());
+		
+		initialize();
+	}
+	
+	
+	private void initialize() {
+
+		
+		setDoUseTerminator(true);
+		
+		setTerminator(TERMINATOR);
+		
+		derbyDataTypeStrings.addAll(DerbyDataTypes.CUSTOM_DATATYPE_START_PHRASES);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#registerWords(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected void registerWords(DdlTokenStream tokens) {
+		tokens.registerKeyWords(CUSTOM_KEYWORDS);
+		
+		tokens.registerStatementStartPhrase(DerbyStatementStartPhrases.ALTER_PHRASES);
+		tokens.registerStatementStartPhrase(DerbyStatementStartPhrases.CREATE_PHRASES);
+		tokens.registerStatementStartPhrase(DerbyStatementStartPhrases.DROP_PHRASES);
+		tokens.registerStatementStartPhrase(DerbyStatementStartPhrases.SET_PHRASES);
+		tokens.registerStatementStartPhrase(DerbyStatementStartPhrases.MISC_PHRASES);
+		super.registerWords(tokens);
+	}
+
+
+	/**
+	 * 
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseCustomStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected Statement parseCustomStatement(DdlTokenStream tokens)
+			throws ParsingException {
+		Statement result = super.parseCustomStatement(tokens);
+		if( result == null ) {
+			if( tokens.canConsume(DerbyStatementStartPhrases.STMT_LOCK_TABLE) ) {
+				result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_LOCK_TABLE));
+			} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_RENAME_TABLE) ) {
+				result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_RENAME_TABLE));
+			} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_RENAME_INDEX) ) {
+				result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_RENAME_INDEX));
+			} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_DECLARE_GLOBAL_TEMP_TABLE) ) {
+	    		result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_DECLARE_GLOBAL_TEMP_TABLE));
+	    	} 
+		}
+		return result;
+	}
+
+	/**
+	 * 
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseCreateStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream, boolean)
+	 */
+	@Override
+	protected Statement parseCreateStatement(DdlTokenStream tokens, boolean inSchema)
+			throws ParsingException {
+		Statement result = null;
+
+		if( tokens.matches(DerbyStatementStartPhrases.STMT_CREATE_INDEX) 
+	    		|| tokens.matches(DerbyStatementStartPhrases.STMT_CREATE_UNIQUE_INDEX) ) {
+	    		result = parseCreateIndex(tokens);
+	    } else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_CREATE_FUNCTION) ) {
+    		result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_CREATE_FUNCTION));
+    	} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_CREATE_PROCEDURE) ) {
+    		result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_CREATE_PROCEDURE));
+    	} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_CREATE_ROLE) ) {
+    		result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_CREATE_ROLE));
+    	} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_CREATE_SYNONYM) ) {
+    		result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_CREATE_SYNONYM));
+    	} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_CREATE_TRIGGER) ) {
+    		result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_CREATE_TRIGGER));
+    	}
+		
+		if( result == null ) {
+			result = super.parseCreateStatement(tokens, inSchema);
+		}
+		
+		return result;
+	}
+	
+	/**
+	 * Parses DDL CREATE INDEX {@link Statement}
+	 * 
+	 * @param tokens
+	 * @return the parsed CREATE INDEX {@link Statement}
+	 * @throws ParsingException
+	 */
+    protected Statement parseCreateIndex( DdlTokenStream tokens ) throws ParsingException {
+		// CREATE [UNIQUE] INDEX index-Name
+		//    	ON table-Name ( Simple-column-Name [ ASC | DESC ] [ , Simple-column-Name [ ASC | DESC ]] * )
+    	tokens.consume(); // CREATE
+    	boolean isUnique = tokens.canConsume("UNIQUE");
+    	tokens.canConsume("BITMAP");
+    	tokens.consume("INDEX");
+    	String indexName = consumeIdentifier(tokens);
+    	tokens.consume("ON");
+    	tokens.canConsume("CLUSTER");
+    	String tableName = parseName(tokens);
+    	
+    	//System.out.println("  >> PARSING Create INDEX >>  Name = " + indexName + "  For TABLE = " + tableName);
+    	
+    	List<String> columnNameList = new ArrayList<String>();
+		try {
+			if( tokens.matches('(')) {
+				tokens.consume();
+				columnNameList = parseColumnNameList(tokens);
+				tokens.consume(')');
+			}
+		} catch (ParsingException e) {
+			// Give up on parsing, probably a nested function (i.e.   banned (lower(banned_name)); 
+			parseUntilTerminator(tokens);
+		}
+		Index newIndex = new Index(indexName, tableName, columnNameList);
+		newIndex.setUnique(isUnique);
+		
+        consumeTerminator(tokens);
+        
+		return newIndex;
+    }	
+	
+    
+    
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseDropStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected Statement parseDropStatement(DdlTokenStream tokens)
+			throws ParsingException {
+		Statement result = null;
+		
+		if( tokens.canConsume(DerbyStatementStartPhrases.STMT_DROP_FUNCTION) ) {
+			result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_DROP_FUNCTION));
+		} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_DROP_INDEX) ) {
+			result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_DROP_INDEX));
+		} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_DROP_PROCEDURE) ) {
+			result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_DROP_PROCEDURE));
+		} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_DROP_ROLE) ) {
+			result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_DROP_ROLE));
+		} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_DROP_SYNONYM) ) {
+			result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_DROP_SYNONYM));
+		} else if( tokens.canConsume(DerbyStatementStartPhrases.STMT_DROP_TRIGGER) ) {
+			result = parseIgnorableStatement(tokens, getStatementTypeName(DerbyStatementStartPhrases.STMT_DROP_TRIGGER));
+		} 
+		
+		if( result == null ) {
+			result = super.parseDropStatement(tokens);
+		}
+
+		return result;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseGrantStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected Statement parseGrantStatement(DdlTokenStream tokens)
+			throws ParsingException {
+		if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "TO")) {
+			tokens.consume(GRANT);
+			String privilege = tokens.consume();
+			tokens.consume("TO");
+			String toValue = tokens.consume();
+			Statement stmt = new TypedStatement();
+			String value = parseUntilTerminator(tokens);
+			stmt.setValue(value);
+			stmt.setType("GRANT" + SPACE + privilege + SPACE + "TO" + SPACE + toValue);
+			consumeTerminator(tokens);
+			return stmt;
+		}
+		if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "ON")) {
+			tokens.consume(GRANT);
+			String privilege = tokens.consume();
+			tokens.consume("ON");
+			String toValue = tokens.consume();
+			Statement stmt = new TypedStatement();
+			String value = parseUntilTerminator(tokens);
+			stmt.setValue(value);
+			stmt.setType("GRANT" + SPACE + privilege + SPACE + "ON" + SPACE + toValue);
+			consumeTerminator(tokens);
+			return stmt;
+		}
+		if( 	tokens.matches(GRANT, CREATE) ||
+				tokens.matches(GRANT, ALTER) || 
+				tokens.matches(GRANT, DROP) || 
+				tokens.matches(GRANT, "EXECUTE") || 
+				tokens.matches(GRANT, "MANAGE") || 
+				tokens.matches(GRANT, "QUERY") || 
+				tokens.matches(GRANT, "ON", "COMMIT") || 
+				tokens.matches(GRANT, "ANY") || 
+				tokens.matches(GRANT, "SELECT") || 
+				tokens.matches(GRANT, "RESTRICTED") || 
+				tokens.matches(GRANT, "FLASHBACK") || 
+				tokens.matches(GRANT, "GLOBAL") || 
+				tokens.matches(GRANT, "DEBUG") ||
+				tokens.matches(GRANT, "GLOBAL") ||
+				tokens.matches(GRANT, "ADVISOR") ||
+				tokens.matches(GRANT, "ADMINISTER") ||
+				tokens.matches(GRANT, "BACKUP") ||
+				tokens.matches(GRANT, "LOCK") ||
+				tokens.matches(GRANT, "UPDATE") || 
+				tokens.matches(GRANT, "DELETE") ||
+				tokens.matches(GRANT, "INSERT") ||
+				tokens.matches(GRANT, "UNLIMITED") ||
+				tokens.matches(GRANT, "UNDER") ||
+				tokens.matches(GRANT, "ANALYZE") ||
+				tokens.matches(GRANT, "AUDIT") ||
+				tokens.matches(GRANT, "COMMENT") ||
+				tokens.matches(GRANT, "EXEMPT") ||
+				tokens.matches(GRANT, "FORCE") ||
+				tokens.matches(GRANT, "RESUMABLE") ||
+				tokens.matches(GRANT, "SYSDBA") ||
+				tokens.matches(GRANT, "REFERENCES") ||
+				tokens.matches(GRANT, "SYSOPER") ||
+				tokens.matches(GRANT, "WRITE") ||
+				tokens.matches(GRANT, "EXECUTE") ) {
+			tokens.consume(GRANT);
+			String nextTok = tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume();
+			Statement stmt = new TypedStatement();
+			String value = parseUntilTerminator(tokens);
+			stmt.setValue(value);
+			stmt.setType("GRANT" + " " + nextTok);
+			consumeTerminator(tokens);
+			return stmt;
+		}
+		
+		
+		return null;
+	}
+
+	
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseAlterTableStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected Statement parseAlterTableStatement(DdlTokenStream tokens)
+			throws ParsingException {
+		
+		//		ALTER TABLE table-Name
+		//		{
+		//		    ADD COLUMN column-definition |
+		//		    ADD CONSTRAINT clause |
+		//		    DROP [ COLUMN ] column-name [ CASCADE | RESTRICT ] |
+		//		    DROP { PRIMARY KEY | FOREIGN KEY constraint-name | UNIQUE constraint-name | CHECK constraint-name | CONSTRAINT constraint-name } |
+		//		    ALTER [ COLUMN ] column-alteration |
+		//		    LOCKSIZE { ROW | TABLE }
+		//		}
+		
+    	tokens.consume(); // consumes 'ALTER'
+    	tokens.consume("TABLE");
+    	String tableName = parseName(tokens);
+    	
+    	//System.out.println("  >> PARSING ALTER STATEMENT >>  TABLE Name = " + tableName);
+    	
+    	AlterTable alterTableStatement = new AlterTable(tableName);
+    	
+    	if( tokens.canConsume("ADD") ) {
+    		TableConstraint constraint = parseTableConstraint(tokens);
+    		if( constraint != null ) {
+    			alterTableStatement.addConstraint(constraint);
+    		} else {
+    			// This segment can also be enclosed in "()" brackets to handle multiple Column ADDs
+    			if( tokens.matches('(')) {
+    				List<Column> columns = parseColumns(tokens);
+		    		for( Column newColumn : columns ) {
+		    			alterTableStatement.addColumn(newColumn);
+		    		}
+    			} else {
+    				// Assume single ADD COLUMN
+	    			Column newColumn = parseSingleTerminatedColumnDefinition(tokens);
+	    			
+	    			alterTableStatement.addColumn(newColumn);
+    			}
+    		}
+
+        } else if( tokens.canConsume("DROP")) {
+        	// DROP { PRIMARY KEY | FOREIGN KEY constraint-name | UNIQUE constraint-name | CHECK constraint-name | CONSTRAINT constraint-name }
+        	if( tokens.canConsume("PRIMARY", "KEY") ||
+        		tokens.canConsume("FOREIGN", "KEY") ||
+        		tokens.canConsume("UNIQUE") ||
+        		tokens.canConsume("CHECK") ||
+        		tokens.canConsume("CONSTRAINT") ) {
+        		parseName(tokens); // constraint name
+        	} else {
+				// DROP [ COLUMN ] column-name [ CASCADE | RESTRICT ]
+	        	tokens.canConsume("COLUMN");   // "COLUMN" is optional
+	        	
+	        	String columnName = parseName(tokens);
+	    		alterTableStatement.addDroppedColumnName(columnName);
+	        		
+	        	tokens.canConsume("CASCADE");
+	        	tokens.canConsume("RESTRICT");
+        	}
+    	} else if( tokens.canConsume("ALTER")) {
+    		//		column-alteration
+    		//
+    		//		column-Name SET DATA TYPE VARCHAR(integer) |
+    		//		column-Name SET DATA TYPE VARCHAR FOR BIT DATA(integer) |
+    		//		column-name SET INCREMENT BY integer-constant |
+    		//		column-name RESTART WITH integer-constant |
+    		//		column-name [ NOT ] NULL |
+    		//		column-name [ WITH | SET ] DEFAULT default-value |
+    		//		column-name DROP DEFAULT 
+    		
+    		tokens.canConsume("COLUMN");
+    		parseName(tokens);
+    		if( tokens.matches("DEFAULT") ) {
+    			parseDefaultClause(tokens);
+    		} else if( tokens.canConsume("SET")) {
+    			if( tokens.canConsume("DATA", "TYPE" ) ) {
+    				tokens.consume("VARCHAR");
+    				tokens.canConsume("FOR", "BIT", "DATA");
+    				tokens.consume("(", DdlTokenStream.ANY_VALUE, ")");
+    			} else if( tokens.canConsume("INCREMENT") ) {
+    				tokens.consume("BY", DdlTokenStream.ANY_VALUE); 
+    			}
+    			parseDefaultClause(tokens);
+    		} else if( tokens.canConsume("WITH") ) {
+    			parseDefaultClause(tokens);
+    		} else {
+    			tokens.canConsume("RESTART", "WITH", DdlTokenStream.ANY_VALUE);
+    			tokens.canConsume("DROP", "DEFAULT");
+    			tokens.canConsume("NOT", "NULL");
+    			tokens.canConsume("NULL");
+    		}
+    		
+    	} else if( tokens.canConsume("LOCKSIZE")) {
+    		tokens.canConsume("ROWS");
+    		tokens.canConsume("TABLE");
+    	}
+    	
+    	consumeTerminator(tokens);
+    	
+        return alterTableStatement;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseColumnDefinition(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected Column parseColumnDefinition(DdlTokenStream tokens)  throws ParsingException {
+		// column-definition
+		//
+		// Simple-column-Name DataType
+		//	    [ Column-level-constraint ]*
+		//	    [ [ WITH ] DEFAULT { ConstantExpression | NULL } |generated-column-spec ]
+		//	    [ Column-level-constraint ]*
+		
+		// generated-column-spec
+		//
+		//		[ GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( START WITH IntegerConstant [ ,INCREMENT BY IntegerConstant] ) ]  ]  ]
+		
+		// EXAMPLE COLUMNS
+		// (i INT GENERATED BY DEFAULT AS IDENTITY (START WITH 2, INCREMENT BY 1), 
+		//  ch CHAR(50));
+
+    	tokens.canConsume("COLUMN"); // FOR ALTER TABLE ADD [COLUMN] case
+        String columnName = parseName(tokens);
+        DataType datatype = getDatatypeParser().parse(tokens);
+        
+        Column newColumn = new Column(columnName, datatype);
+        
+        // Now clauses and constraints can be defined in any order, so we need to keep parsing until we get to a comma
+        
+        while( tokens.hasNext() && !tokens.matches(',')) {
+        		String defaultValue = parseDefaultClause(tokens);
+        		if( defaultValue != null ) {
+        			newColumn.setDefaultValue(defaultValue);
+        		} else {
+        			parseGeneratedColumnSpecClause(tokens);
+		        	parseCollateClause(tokens, newColumn);
+		        	parseColumnConstraint(tokens, newColumn);
+        		}
+	        	tokens.canConsume(DdlTokenizer.COMMENT);
+	    }
+
+        return newColumn;
+	}
+
+	private void parseGeneratedColumnSpecClause( DdlTokenStream tokens )  throws ParsingException {
+		// generated-column-spec
+		//
+		//		[ GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( START WITH IntegerConstant [ ,INCREMENT BY IntegerConstant] ) ]  ]  ]
+		if( tokens.canConsume("GENERATED") ) { 
+			tokens.canConsume("ALWAYS");
+			tokens.canConsume("BY", "DEFAULT");
+			tokens.canConsume("AS", "IDENTITY");
+			
+			if( tokens.canConsume("(", "START", "WITH") ) {
+				tokens.consume(); // integer constant
+				tokens.consume(',');
+				if( tokens.canConsume("INCREMENT", "BY")) {
+					tokens.consume(); // integer constant
+				}
+				tokens.consume(")");
+			}
+		}
+		
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#getDataTypeStartWords()
+	 */
+	@Override
+	protected List<String> getCustomDataTypeStartWords() {
+		return DerbyDataTypes.CUSTOM_DATATYPE_START_WORDS;
+	}
+
+	class DerbyDataTypeParser extends DataTypeParser {
+
+    	/**
+    	 * 
+    	 * {@inheritDoc}
+    	 *
+    	 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#isCustomDataType(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+    	 */
+    	@Override
+		protected boolean isCustomDataType(DdlTokenStream tokens)
+				throws ParsingException {
+        	// Loop through the registered statement start string arrays and look for exact matches.
+        	
+        	for( String[] stmts : derbyDataTypeStrings ) {
+        		if( tokens.matches(stmts) ) return true;
+        	}
+			return super.isCustomDataType(tokens);
+		}
+		
+		/**
+		 * 
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#parseApproxNumericType(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+		 */
+		@Override
+		protected DataType parseApproxNumericType(DdlTokenStream tokens)
+				throws ParsingException {
+			return super.parseApproxNumericType(tokens);
+		}
+
+
+		/**
+		 * 
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#parseBitStringType(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+		 */
+		@Override
+		protected DataType parseBitStringType(DdlTokenStream tokens)
+				throws ParsingException {
+			return super.parseBitStringType(tokens);
+		}
+
+		/**
+		 * 
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#parseBracketedInteger(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+		 */
+		@Override
+		protected int parseBracketedInteger(DdlTokenStream tokens) {
+			return super.parseBracketedInteger(tokens);
+		}
+
+		/**
+		 * 
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#parseCharStringType(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+		 */
+		@Override
+		protected DataType parseCharStringType(DdlTokenStream tokens)
+				throws ParsingException {
+	    	DataType result = super.parseCharStringType(tokens);
+	    	
+			tokens.canConsume("FOR", "BIT", "DATA");
+	        
+	    	return result;
+		}
+
+		/**
+		 * 
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#parseCustomType(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+		 */
+		@Override
+		protected DataType parseCustomType(DdlTokenStream tokens)
+				throws ParsingException {
+	    	DataType result = null;
+	    	String typeName = null;
+	    	int length = 0;
+	    	
+			if( tokens.matches(DerbyDataTypes.DTYPE_BINARY_LARGE_OBJECT) || tokens.matches(DerbyDataTypes.DTYPE_CHARACTER_LARGE_OBJECT)) {
+				typeName = tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume();
+				boolean isKMGLength = false;
+				String kmgValue = null;
+				if( tokens.canConsume('(')) {
+					String lengthValue = tokens.consume();
+					kmgValue = getKMG(lengthValue);
+					
+					isKMGLength = isKMGInteger(lengthValue);
+					
+					length = parseInteger(lengthValue);
+					
+		            tokens.canConsume(')');
+				}
+				
+				result = new DataType(typeName, length);
+				
+				result.setKMGLength(isKMGLength);
+				result.setKMGValue(kmgValue);
+			} else if( tokens.matches(DerbyDataTypes.DTYPE_CLOB) || tokens.matches(DerbyDataTypes.DTYPE_BLOB) ){
+				typeName = tokens.consume();
+				boolean isKMGLength = false;
+				String kmgValue = null;
+				if( tokens.canConsume('(')) {
+					String lengthValue = tokens.consume();
+					kmgValue = getKMG(lengthValue);
+					
+					isKMGLength = isKMGInteger(lengthValue);
+					
+					length = parseInteger(lengthValue);
+					
+		            tokens.canConsume(')');
+				}
+				
+				result = new DataType(typeName, length);
+				
+				result.setKMGLength(isKMGLength);
+				result.setKMGValue(kmgValue);
+			} else if( tokens.matches(DerbyDataTypes.DTYPE_BIGINT) ){
+				typeName = tokens.consume();
+			} else if( tokens.matches(DerbyDataTypes.DTYPE_LONG_VARCHAR_FBD) ){
+				typeName = tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume();
+			} else if( tokens.matches(DerbyDataTypes.DTYPE_LONG_VARCHAR) ){
+				typeName = tokens.consume() + SPACE + tokens.consume();
+			} else if( tokens.matches(DerbyDataTypes.DTYPE_BIGINT) ){
+				typeName = tokens.consume();
+			} else if( tokens.matches(DerbyDataTypes.DTYPE_DOUBLE) ){
+				typeName = tokens.consume();
+			} else if( tokens.matches(DerbyDataTypes.DTYPE_XML) ){
+				typeName = tokens.consume();
+			} 
+			
+			if( result == null ) {
+				super.parseCustomType(tokens);
+			}
+			return result;
+		}
+
+		/**
+		 * 
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#parseDateTimeType(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+		 */
+		@Override
+		protected DataType parseDateTimeType(DdlTokenStream tokens)
+				throws ParsingException {
+			return super.parseDateTimeType(tokens);
+		}
+
+		/**
+		 * 
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#parseExactNumericType(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+		 */
+		@Override
+		protected DataType parseExactNumericType(DdlTokenStream tokens)
+				throws ParsingException {
+			return super.parseExactNumericType(tokens);
+		}
+    	
+    }
+
+
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,490 @@
+package org.jboss.dna.sequencer.ddl.dialect;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.sequencer.ddl.DdlTokenStream;
+import org.jboss.dna.sequencer.ddl.StandardDdlParser;
+import org.jboss.dna.sequencer.ddl.datatype.DataType;
+import org.jboss.dna.sequencer.ddl.datatype.DataTypeParser;
+import org.jboss.dna.sequencer.ddl.statement.Index;
+import org.jboss.dna.sequencer.ddl.statement.Statement;
+
+public class MySqlDdlParser extends StandardDdlParser {
+	/*
+	* ===========================================================================================================================
+		* Data Definition Statements
+		ALTER [DATABASE | EVENT | FUNCTION | SERVER | TABLE | VIEW]
+		CREATE [DATABASE | EVENT | FUNCTION | INDEX | PROCEDURE | SERVER | TABLE | TRIGGER | VIEW]
+		DROP [DATABASE | EVENT | FUNCTION | INDEX | PROCEDURE | SERVER | TABLE | TRIGGER | VIEW]
+		RENAME TABLE
+	*/
+	private static final String[] STMT_CREATE_DATABASE = {"CREATE", "DATABASE"};
+	private static final String[] STMT_CREATE_EVENT = {"CREATE", "EVENT"};
+	private static final String[] STMT_CREATE_SERVER = {"CREATE", "SERVER"};
+	private static final String[] STMT_CREATE_FUNCTION = {"CREATE", "FUNCTION"};
+	private static final String[] STMT_CREATE_INDEX = {"CREATE", "INDEX"};
+	private static final String[] STMT_CREATE_UNIQUE_INDEX = {"CREATE", "UNIQUE", "INDEX"};
+	private static final String[] STMT_CREATE_PROCEDURE = {"CREATE", "PROCEDURE"};
+	private static final String[] STMT_CREATE_TRIGGER = {"CREATE", "TRIGGER"};
+	
+	protected static final String[] DTYPE_TINYINT = {"TINYINT"}; // TINYINT[(M)] [UNSIGNED] [ZEROFILL]
+	protected static final String[] DTYPE_MEDIUMINT = {"MEDIUMINT"}; // MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL]
+	protected static final String[] DTYPE_BIGINT = {"BIGINT"}; // BIGINT[(M)] [UNSIGNED] [ZEROFILL]
+	protected static final String[] DTYPE_FIXED = {"FIXED"};// FIXED[(M[,D])] [UNSIGNED] [ZEROFILL]
+	protected static final String[] DTYPE_BOOL = {"BOOL"};// BOOL
+	protected static final String[] DTYPE_BOOLEAN = {"BOOLEAN"};// BOOLEAN
+	
+	protected static final String[] DTYPE_DOUBLE = {"DOUBLE"}; // DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]
+	
+
+	/* 
+	 * ===========================================================================================================================
+	 * DATYPES
+	 * Numeric Types
+	    -----------------------------------------------------------
+	    NEW  TYPE
+	    -----------------------------------------------------------
+		**	BIT[(M)]
+		**	TINYINT[(M)] [UNSIGNED] [ZEROFILL]
+		**	BOOL, BOOLEAN
+			SMALLINT[(M)] [UNSIGNED] [ZEROFILL]
+		**	MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL]
+			INT[(M)] [UNSIGNED] [ZEROFILL]
+			INTEGER[(M)] [UNSIGNED] [ZEROFILL]
+		**	BIGINT[(M)] [UNSIGNED] [ZEROFILL]
+		**	SERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE. 
+			FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]
+			FLOAT(p) [UNSIGNED] [ZEROFILL]
+		**	DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]
+			DOUBLE PRECISION[(M,D)] [UNSIGNED] [ZEROFILL], 
+			REAL[(M,D)] [UNSIGNED] [ZEROFILL]
+			DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL]
+			DEC[(M[,D])] [UNSIGNED] [ZEROFILL], 
+			NUMERIC[(M[,D])] [UNSIGNED] [ZEROFILL], 
+		**	FIXED[(M[,D])] [UNSIGNED] [ZEROFILL]
+	
+	* Date and Time Types
+	* 
+	DATE
+	DATETIME
+	TIMESTAMP
+	TIME
+	YEAR[2|4]
+	
+	* String Types
+	*
+	
+	[NATIONAL] CHAR[(M)] [CHARACTER SET charset_name] [COLLATE collation_name]
+		- CHAR is shorthand for CHARACTER. NATIONAL CHAR (or its equivalent short form, NCHAR) i
+		- The CHAR BYTE data type is an alias for the BINARY data type.
+	[NATIONAL] VARCHAR(M) [CHARACTER SET charset_name] [COLLATE collation_name]
+		- VARCHAR is shorthand for CHARACTER VARYING
+	BINARY(M)
+	VARBINARY(M)
+	TINYBLOB
+	TINYTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
+		- A TEXT column with a maximum length of 255
+	BLOB[(M)]
+		- A BLOB column with a maximum length of 65,535
+	TEXT[(M)] [CHARACTER SET charset_name] [COLLATE collation_name]
+		- A TEXT column with a maximum length of 65,535
+	MEDIUMBLOB - maximum length of 16,777,215
+	MEDIUMTEXT [CHARACTER SET charset_name] [COLLATE collation_name]	
+		- maximum length of 16,777,215
+	LONGBLOB - maximum length of 4,294,967,295
+	LONGTEXT [CHARACTER SET charset_name] [COLLATE collation_name]	
+		- maximum length of 4,294,967,295
+	ENUM('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name]
+		- NULL or the special ''
+		- maximum of 65,535 distinct values
+	SET('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name]
+		- zero or more values
+		- maximum of 64 members
+	
+	* ===========================================================================================================================
+	* CREATE TABLE
+	
+	CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
+    (create_definition,...)
+    [table_options]
+    [partition_options]
+
+	Or:
+	
+	CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
+	    [(create_definition,...)]
+	    [table_options]
+	    [partition_options]
+	    select_statement
+	
+	Or:
+	
+	CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
+	    { LIKE old_tbl_name | (LIKE old_tbl_name) }
+	
+	create_definition:
+	    col_name column_definition
+	  | [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...)
+	      [index_option] ...
+	  | {INDEX|KEY} [index_name] [index_type] (index_col_name,...)
+	      [index_option] ...
+	  | [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY]
+	      [index_name] [index_type] (index_col_name,...)
+	      [index_option] ...
+	  | {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (index_col_name,...)
+	      [index_option] ...
+	  | [CONSTRAINT [symbol]] FOREIGN KEY
+	      [index_name] (index_col_name,...) reference_definition
+	  | CHECK (expr)
+	
+	column_definition:
+	    data_type [NOT NULL | NULL] [DEFAULT default_value]
+	      [AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY]
+	      [COMMENT 'string']
+	      [COLUMN_FORMAT {FIXED|DYNAMIC|DEFAULT}]
+	      [STORAGE {DISK|MEMORY|DEFAULT}]
+	      [reference_definition]
+	
+	data_type:
+	    BIT[(length)]
+	  | TINYINT[(length)] [UNSIGNED] [ZEROFILL]
+	  | SMALLINT[(length)] [UNSIGNED] [ZEROFILL]
+	  | MEDIUMINT[(length)] [UNSIGNED] [ZEROFILL]
+	  | INT[(length)] [UNSIGNED] [ZEROFILL]
+	  | INTEGER[(length)] [UNSIGNED] [ZEROFILL]
+	  | BIGINT[(length)] [UNSIGNED] [ZEROFILL]
+	  | REAL[(length,decimals)] [UNSIGNED] [ZEROFILL]
+	  | DOUBLE[(length,decimals)] [UNSIGNED] [ZEROFILL]
+	  | FLOAT[(length,decimals)] [UNSIGNED] [ZEROFILL]
+	  | DECIMAL[(length[,decimals])] [UNSIGNED] [ZEROFILL]
+	  | NUMERIC[(length[,decimals])] [UNSIGNED] [ZEROFILL]
+	  | DATE
+	  | TIME
+	  | TIMESTAMP
+	  | DATETIME
+	  | YEAR
+	  | CHAR[(length)]
+	      [CHARACTER SET charset_name] [COLLATE collation_name]
+	  | VARCHAR(length)
+	      [CHARACTER SET charset_name] [COLLATE collation_name]
+	  | BINARY[(length)]
+	  | VARBINARY(length)
+	  | TINYBLOB
+	  | BLOB
+	  | MEDIUMBLOB
+	  | LONGBLOB
+	  | TINYTEXT [BINARY]
+	      [CHARACTER SET charset_name] [COLLATE collation_name]
+	  | TEXT [BINARY]
+	      [CHARACTER SET charset_name] [COLLATE collation_name]
+	  | MEDIUMTEXT [BINARY]
+	      [CHARACTER SET charset_name] [COLLATE collation_name]
+	  | LONGTEXT [BINARY]
+	      [CHARACTER SET charset_name] [COLLATE collation_name]
+	  | ENUM(value1,value2,value3,...)
+	      [CHARACTER SET charset_name] [COLLATE collation_name]
+	  | SET(value1,value2,value3,...)
+	      [CHARACTER SET charset_name] [COLLATE collation_name]
+	  | spatial_type
+	
+	index_col_name:
+	    col_name [(length)] [ASC | DESC]
+	
+	index_type:
+	    USING {BTREE | HASH | RTREE}
+	
+	index_option:
+	    KEY_BLOCK_SIZE [=] value
+	  | index_type
+	  | WITH PARSER parser_name
+	
+	reference_definition:
+	    REFERENCES tbl_name (index_col_name,...)
+	      [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
+	      [ON DELETE reference_option]
+	      [ON UPDATE reference_option]
+	
+	reference_option:
+	    RESTRICT | CASCADE | SET NULL | NO ACTION
+	
+	table_options:
+	    table_option [[,] table_option] ...
+	
+	table_option:
+	    ENGINE [=] engine_name
+	  | AUTO_INCREMENT [=] value
+	  | AVG_ROW_LENGTH [=] value
+	  | [DEFAULT] CHARACTER SET [=] charset_name
+	  | CHECKSUM [=] {0 | 1}
+	  | [DEFAULT] COLLATE [=] collation_name
+	  | COMMENT [=] 'string'
+	  | CONNECTION [=] 'connect_string'
+	  | DATA DIRECTORY [=] 'absolute path to directory'
+	  | DELAY_KEY_WRITE [=] {0 | 1}
+	  | INDEX DIRECTORY [=] 'absolute path to directory'
+	  | INSERT_METHOD [=] { NO | FIRST | LAST }
+	  | KEY_BLOCK_SIZE [=] value
+	  | MAX_ROWS [=] value
+	  | MIN_ROWS [=] value
+	  | PACK_KEYS [=] {0 | 1 | DEFAULT}
+	  | PASSWORD [=] 'string'
+	  | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT}
+	  | TABLESPACE tablespace_name [STORAGE {DISK|MEMORY|DEFAULT}]
+	  | UNION [=] (tbl_name[,tbl_name]...)
+	
+	partition_options:
+	    PARTITION BY
+	        { [LINEAR] HASH(expr)
+	        | [LINEAR] KEY(column_list)
+	        | RANGE(expr)
+	        | LIST(expr) }
+	    [PARTITIONS num]
+	    [SUBPARTITION BY
+	        { [LINEAR] HASH(expr)
+	        | [LINEAR] KEY(column_list) }
+	      [SUBPARTITIONS num]
+	    ]
+	    [(partition_definition [, partition_definition] ...)]
+	
+	partition_definition:
+	    PARTITION partition_name
+	        [VALUES {LESS THAN {(expr) | MAXVALUE} | IN (value_list)}]
+	        [[STORAGE] ENGINE [=] engine_name]
+	        [COMMENT [=] 'comment_text' ]
+	        [DATA DIRECTORY [=] 'data_dir']
+	        [INDEX DIRECTORY [=] 'index_dir']
+	        [MAX_ROWS [=] max_number_of_rows]
+	        [MIN_ROWS [=] min_number_of_rows]
+	        [TABLESPACE [=] tablespace_name]
+	        [NODEGROUP [=] node_group_id]
+	        [(subpartition_definition [, subpartition_definition] ...)]
+	
+	subpartition_definition:
+	    SUBPARTITION logical_name
+	        [[STORAGE] ENGINE [=] engine_name]
+	        [COMMENT [=] 'comment_text' ]
+	        [DATA DIRECTORY [=] 'data_dir']
+	        [INDEX DIRECTORY [=] 'index_dir']
+	        [MAX_ROWS [=] max_number_of_rows]
+	        [MIN_ROWS [=] min_number_of_rows]
+	        [TABLESPACE [=] tablespace_name]
+	        [NODEGROUP [=] node_group_id]
+	
+	select_statement:
+	    [IGNORE | REPLACE] [AS] SELECT ...   (Some legal select statement)
+
+	
+	* ===========================================================================================================================
+	*/
+	private static final String TERMINATOR = DEFAULT_TERMINATOR; 
+	
+	public MySqlDdlParser() {
+		initialize();
+	}
+	
+	
+	private void initialize() {
+    	setDatatypeParser(new MySqlDataTypeParser());
+    	
+		registerStatementStartPhrase(STMT_CREATE_FUNCTION);
+		registerStatementStartPhrase(STMT_CREATE_INDEX);
+		registerStatementStartPhrase(STMT_CREATE_UNIQUE_INDEX);
+		registerStatementStartPhrase(STMT_CREATE_PROCEDURE);
+		registerStatementStartPhrase(STMT_CREATE_SERVER);
+		registerStatementStartPhrase(STMT_CREATE_TRIGGER);
+		registerStatementStartPhrase(STMT_CREATE_DATABASE);
+		registerStatementStartPhrase(STMT_CREATE_EVENT);
+		
+		setDoUseTerminator(true);
+		
+		setTerminator(TERMINATOR);
+	}
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	@Override
+	protected Statement parseCreateStatement(DdlTokenStream tokens, boolean inSchema)
+			throws ParsingException {
+		Statement result = null;
+    	
+    	
+		if( tokens.matches(STMT_CREATE_INDEX) 
+	    		|| tokens.matches(STMT_CREATE_UNIQUE_INDEX) ) {
+	    		result = parseCreateIndex(tokens);
+	    } else if( tokens.matches(STMT_CREATE_FUNCTION) ) {
+	    	result = parseIgnorableStatement(tokens, "CREATE FUNCTION");
+    	} else if( tokens.matches(STMT_CREATE_PROCEDURE) ) {
+    		result = parseIgnorableStatement(tokens, "CREATE PROCEDURE");
+    	} else if( tokens.matches(STMT_CREATE_EVENT) ) {
+    		result = parseIgnorableStatement(tokens, "CREATE EVENT");
+    	} else if( tokens.matches(STMT_CREATE_SERVER) ) {
+    		result = parseIgnorableStatement(tokens, "CREATE SERVER");
+    	} else if( tokens.matches(STMT_CREATE_TRIGGER) ) {
+    		result = parseIgnorableStatement(tokens, "CREATE TRIGGER");
+    	}
+		
+		if( result == null ) {
+			result = super.parseCreateStatement(tokens, inSchema);
+		}
+		
+		return result;
+	}
+	
+    protected Statement parseCreateIndex( DdlTokenStream tokens ) throws ParsingException {
+    	// CREATE [UNIQUE] INDEX index-Name
+    	//  ON table-Name ( Simple-column-Name [ ASC | DESC ] [ , Simple-column-Name [ ASC | DESC ]] * )
+    	tokens.consume(); // CREATE
+    	boolean isUnique = tokens.canConsume("UNIQUE");
+    	tokens.consume("INDEX");
+    	String indexName = consumeIdentifier(tokens);
+    	tokens.consume("ON");
+    	String tableName = consumeIdentifier(tokens);
+    	
+    	//System.out.println("  >> PARSING Create INDEX >>  Name = " + indexName + "  For TABLE = " + tableName);
+    	
+    	List<String> columnNameList = new ArrayList<String>();
+		try {
+			if( tokens.matches('(')) {
+				tokens.consume();
+				columnNameList = parseColumnNameList(tokens);
+				tokens.consume(')');
+			}
+		} catch (ParsingException e) {
+			// Give up on parsing, probably a nested function (i.e.   banned (lower(banned_name)); 
+			parseUntilTerminator(tokens);
+		}
+		Index newIndex = new Index(indexName, tableName, columnNameList);
+		newIndex.setUnique(isUnique);
+		
+        consumeTerminator(tokens);
+        
+		return newIndex;
+    }
+    	    
+    		
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+    class MySqlDataTypeParser extends DataTypeParser {
+    	
+    	// NOTE THAT MYSQL allows "UNSIGNED" and "ZEROFILL" as options AFTER the datatype definition
+    	// Need to override and do a CHECK for and CONSUME them.
+    	
+    	
+
+		@Override
+		protected DataType parseApproxNumericType(DdlTokenStream tokens)
+				throws ParsingException {
+			return super.parseApproxNumericType(tokens);
+		}
+
+		@Override
+		protected DataType parseBitStringType(DdlTokenStream tokens)
+				throws ParsingException {
+			return super.parseBitStringType(tokens);
+		}
+
+		@Override
+		protected int parseBracketedInteger(DdlTokenStream tokens) {
+			return super.parseBracketedInteger(tokens);
+		}
+
+		@Override
+		protected DataType parseCharStringType(DdlTokenStream tokens)
+				throws ParsingException {
+	    	DataType result = super.parseCharStringType(tokens);
+	    	
+			tokens.canConsume("FOR", "BIT", "DATA");
+	        
+	    	return result;
+		}
+
+		@Override
+		protected DataType parseCustomType(DdlTokenStream tokens)
+				throws ParsingException {
+	    	DataType result = null;
+	    	String typeName = null;
+	    	int length = 0;
+	    	
+//			if( tokens.matches(DTYPE_BINARY_LARGE_OBJECT) || tokens.matches(DTYPE_CHARACTER_LARGE_OBJECT)) {
+//				typeName = tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume();
+//				boolean isKMGLength = false;
+//				String kmgValue = null;
+//				if( tokens.canConsume('(')) {
+//					String lengthValue = tokens.consume();
+//					kmgValue = getKMG(lengthValue);
+//					
+//					isKMGLength = isKMGInteger(lengthValue);
+//					
+//					length = parseInteger(lengthValue);
+//					
+//		            tokens.canConsume(')');
+//				}
+//				
+//				result = new DataType(typeName, length);
+//				
+//				result.setKMGLength(isKMGLength);
+//				result.setKMGValue(kmgValue);
+//			} else if( tokens.matches(DTYPE_CLOB) || tokens.matches(DTYPE_BLOB) ){
+//				typeName = tokens.consume();
+//				boolean isKMGLength = false;
+//				String kmgValue = null;
+//				if( tokens.canConsume('(')) {
+//					String lengthValue = tokens.consume();
+//					kmgValue = getKMG(lengthValue);
+//					
+//					isKMGLength = isKMGInteger(lengthValue);
+//					
+//					length = parseInteger(lengthValue);
+//					
+//		            tokens.canConsume(')');
+//				}
+//				
+//				result = new DataType(typeName, length);
+//				
+//				result.setKMGLength(isKMGLength);
+//				result.setKMGValue(kmgValue);
+//			} else if( tokens.matches(DTYPE_BIGINT) ){
+//				typeName = tokens.consume();
+//			} else if( tokens.matches(DTYPE_LONG_VARCHAR_FBD) ){
+//				typeName = tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume();
+//			} else if( tokens.matches(DTYPE_LONG_VARCHAR) ){
+//				typeName = tokens.consume() + SPACE + tokens.consume();
+//			} else if( tokens.matches(DTYPE_BIGINT) ){
+//				typeName = tokens.consume();
+//			} else if( tokens.matches(DTYPE_DOUBLE) ){
+//				typeName = tokens.consume();
+//			} else if( tokens.matches(DTYPE_XML) ){
+//				typeName = tokens.consume();
+//			} 
+			
+			if( result == null ) {
+				super.parseCustomType(tokens);
+			}
+			return result;
+		}
+
+		@Override
+		protected DataType parseDateTimeType(DdlTokenStream tokens)
+				throws ParsingException {
+			return super.parseDateTimeType(tokens);
+		}
+
+		@Override
+		protected DataType parseExactNumericType(DdlTokenStream tokens)
+				throws ParsingException {
+			return super.parseExactNumericType(tokens);
+		}
+    	
+    }
+
+
+
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/PostgresDdlParser.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/PostgresDdlParser.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/PostgresDdlParser.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,88 @@
+package org.jboss.dna.sequencer.ddl.dialect;
+
+import org.jboss.dna.sequencer.ddl.StandardDdlParser;
+
+public class PostgresDdlParser extends StandardDdlParser {
+	private static final String[] COMMENT_ON = {"COMMENT", "ON"};
+
+	// SQL COMMANDS FOUND @ http://www.postgresql.org/docs/8.4/static/sql-commands.html
+
+//	bigint	8 bytes	large-range integer	-9223372036854775808 to 9223372036854775807
+//	serial	4 bytes	autoincrementing integer	1 to 2147483647
+//	bigserial	8 bytes	large autoincrementing integer	1 to 9223372036854775807
+//	
+//	money	8 bytes	currency amount	-92233720368547758.08 to +92233720368547758.07
+//	text	variable unlimited length
+//	bytea	1 or 4 bytes plus the actual binary string	variable-length binary string
+//	
+//	timestamp [ (p) ] [ without time zone ]	8 bytes	both date and time (no time zone)	4713 BC	294276 AD	1 microsecond / 14 digits
+//	timestamp [ (p) ] with time zone	8 bytes	both date and time, with time zone	4713 BC	294276 AD	1 microsecond / 14 digits
+//	date	4 bytes	date (no time of day)	4713 BC	5874897 AD	1 day
+//	time [ (p) ] [ without time zone ]	8 bytes	time of day (no date)	00:00:00	24:00:00	1 microsecond / 14 digits
+//	time [ (p) ] with time zone	12 bytes	times of day only, with time zone	00:00:00+1459	24:00:00-1459	1 microsecond / 14 digits
+//	interval [ fields ] [ (p) ]	12 bytes	time interval	-178000000 years	178000000 years	1 microsecond / 14 digits
+//	boolean
+//	
+//    YEAR
+//    MONTH
+//    DAY
+//    HOUR
+//    MINUTE
+//    SECOND
+//    YEAR TO MONTH
+//    DAY TO HOUR
+//    DAY TO MINUTE
+//    DAY TO SECOND
+//    HOUR TO MINUTE
+//    HOUR TO SECOND
+//    MINUTE TO SECOND
+//    
+//    CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
+//    CREATE TABLE holidays (
+//        num_weeks integer,
+//        happiness happiness
+//    );
+//
+//    cidr	7 or 19 bytes	IPv4 and IPv6 networks
+//    inet	7 or 19 bytes	IPv4 and IPv6 hosts and networks
+//    macaddr	6 bytes	MAC addresses
+//    uuid
+//    xml
+//    CREATE TABLE sal_emp (
+//    	    name            text,
+//    	    pay_by_quarter  integer[],
+//    	    schedule        text[][]
+//    	);
+//    CREATE TABLE tictactoe (
+//    	    squares   integer[3][3]
+//    	);
+//    pay_by_quarter  integer ARRAY[4],
+//
+//    Or, if no array size is to be specified:
+//
+//        pay_by_quarter  integer ARRAY,
+    
+	
+	private static final String TERMINATOR = "GO"; 
+	
+	public PostgresDdlParser() {
+		initialize();
+	}
+	
+	
+	private void initialize() {
+		registerStatementStartPhrase(COMMENT_ON);
+		setTerminator(TERMINATOR);
+	}
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/PostgresDdlParser.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SqlServerDdlParser.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SqlServerDdlParser.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SqlServerDdlParser.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,30 @@
+package org.jboss.dna.sequencer.ddl.dialect;
+
+import org.jboss.dna.sequencer.ddl.StandardDdlParser;
+
+public class SqlServerDdlParser extends StandardDdlParser {
+	private static final String[] COMMENT_ON = {"COMMENT", "ON"};
+
+	private static final String TERMINATOR = "GO"; 
+	
+	public SqlServerDdlParser() {
+		initialize();
+	}
+	
+	
+	private void initialize() {
+		registerStatementStartPhrase(COMMENT_ON);
+		setTerminator(TERMINATOR);
+	}
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SqlServerDdlParser.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SybaseDdlParser.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SybaseDdlParser.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SybaseDdlParser.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,127 @@
+package org.jboss.dna.sequencer.ddl.dialect;
+
+import org.jboss.dna.sequencer.ddl.StandardDdlParser;
+
+public class SybaseDdlParser extends StandardDdlParser {
+	private static final String[] COMMENT_ON = {"COMMENT", "ON"};
+
+	/*
+	 * 
+	 * 
+	 * Character
+		CHAR [ ( max-length ) ]
+		CHARACTER [ ( max-length ) ]
+		CHARACTER VARYING [ ( max-length ) ]
+		VARCHAR [ ( max-length ) ]
+		UNIQUEIDENTIFIERSTR
+	 * Numeric
+		[ UNSIGNED ] BIGINT
+		[ UNSIGNED ] { INT | INTEGER }
+		SMALLINT
+		TINYINT
+		DECIMAL [ ( precision [ , scale ] ) ]
+		NUMERIC [ ( precision [ , scale ] ) ]
+		DOUBLE
+		FLOAT [ ( precision ) ]
+		REAL
+	 * Binary
+		BINARY [ ( length ) ]
+		VARBINARY [ ( max-length ) ]
+		UNIQUEIDENTIFIER
+	 * Bit
+		BIT
+	 * Date/Time
+		DATE
+		DATETIME
+		SMALLDATETIME
+		TIME
+		TIMESTAMP
+
+
+	 * ===========================================================================================================================
+	 * Data Definition Statements
+	 ALLOCATE DESCRIPTOR
+	 ALTER [DATABASE | DBSPACE | DOMAIN | EVENT | FUNCTION | INDEX | (LOGIN POLICY) | PROCEDURE | SERVER | SERVICE | TABLE | USER | VIEW]
+	 BACKUP
+	 BEGIN PARALLEL IQ ? END PARALLEL IQ statement
+	 BEGIN TRAN[SACTION] [ transaction-name ]
+	 CHECKPOINT
+	 COMMENT ON
+	 COMMIT [ WORK ]
+	 CONFIGURE
+	 CREATE [DATABASE | DBSPACE | DOMAIN | EVENT | (EXISTING TABLE) | EXTERNLOGIN | FUNCTION | INDEX | (JOIN INDEX) 
+	      | (LOGIN POLICY) | MESSAGE | PROCEDURE | SCHEMA | SERVER | SERVICE | TABLE | USER | VARIABLE | VIEW]
+	 DEALLOCATE DESCRIPTOR
+	 DECLARE LOCAL TEMPORARY TABLE
+	 DELETE
+	 DESCRIBE
+	 DROP [CONNECTION | DATABASE | DBSPACE | DOMAIN | EVENT | EXTERNALLOGIN | FUNCTION | INDEX | (JOIN INDEX) | (LOGIN POLICY) 
+	      | MESSAGE | PROCEDURE | SERVER | STATEMENT | TABLE | TRIGGER | USER | VARIABLE | VIEW]
+	 EXEC[UTE]
+	 EXIT
+	 FORWARD TO
+	 GET DESCRIPTOR
+	 GRANT
+	 INSERT
+	 INSTALL JAVA
+	 IQ UTILITIES
+	 LOAD TABLE
+	 LOCK TABLE
+	 OUTPUT TO
+	 PARAMETERS
+	 READ
+	 RELEASE SAVEPOINT
+	 REMOVE JAVA
+	 RESTORE DATABASE
+	 REVOKE
+	 ROLLBACK [WORK]
+	 ROLLBACK TO SAVEPOINT
+	 SAVEPOINT
+	 SET [CONNECTION | DESCRIPTION | OPTION | SQLCA]
+	 SIGNAL
+	 START [DATABASE | ENGINE | JAVA]
+	 STOP [DATABASE | ENGINE | JAVA]
+	 SYNCHRONIZE JOIN INDEX
+	 TRIGGER EVENT
+	 TRUNCATE TABLE
+	 UPDATE
+ 	 WAITFOR
+ 	 
+ 	 
+	 * ===========================================================================================================================
+	 * CREATE TABLE
+
+
+
+
+
+
+
+	*/
+	
+	
+	
+	
+	private static final String TERMINATOR = "go"; 
+	
+	public SybaseDdlParser() {
+		initialize();
+	}
+	
+	
+	private void initialize() {
+		registerStatementStartPhrase(COMMENT_ON);
+		setTerminator(TERMINATOR);
+	}
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+	
+	
+	// ===========================================================================================================================
+	// ===========================================================================================================================
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/SybaseDdlParser.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/derby/DerbyDdlConstants.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/derby/DerbyDdlConstants.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/derby/DerbyDdlConstants.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,115 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.ddl.dialect.derby;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.jboss.dna.sequencer.ddl.DdlConstants;
+
+/**
+ * @author blafond
+ *
+ */
+public interface DerbyDdlConstants extends DdlConstants {
+	public static final String[] CUSTOM_KEYWORDS = {
+		"TRIGGER", "SYNOMYM", "LOCK", "ISOLATION", "SQLID", INDEX, "RENAME", "DECLARE"
+	};
+	
+	interface DerbyStatementStartPhrases {
+		
+	    static final String[][] ALTER_PHRASES = { 
+		};
+	    
+		static final String[] STMT_CREATE_FUNCTION = {CREATE, "FUNCTION"};
+		static final String[] STMT_CREATE_INDEX = {CREATE, "INDEX"};
+		static final String[] STMT_CREATE_UNIQUE_INDEX = {CREATE, "UNIQUE", INDEX};
+		static final String[] STMT_CREATE_PROCEDURE = {CREATE, "PROCEDURE"};
+		static final String[] STMT_CREATE_ROLE = {CREATE, "ROLE"};
+		static final String[] STMT_CREATE_SYNONYM = {CREATE, "SYNONYM"};
+		static final String[] STMT_CREATE_TRIGGER = {CREATE, "TRIGGER"};
+		
+	    static final String[][] CREATE_PHRASES = { 
+	    	STMT_CREATE_FUNCTION, STMT_CREATE_INDEX, STMT_CREATE_UNIQUE_INDEX, STMT_CREATE_PROCEDURE, 
+	    	STMT_CREATE_ROLE, STMT_CREATE_SYNONYM, STMT_CREATE_TRIGGER
+		};
+	    
+	    static final String[] STMT_DECLARE_GLOBAL_TEMP_TABLE = {"DECLARE", "GLOBAL", "TEMPORARY", "TABLE"};
+		static final String[] STMT_DROP_FUNCTION = {DROP, "FUNCTION"};
+		static final String[] STMT_DROP_INDEX = {DROP, "INDEX"};
+		static final String[] STMT_DROP_PROCEDURE = {DROP, "PROCEDURE"};
+		static final String[] STMT_DROP_ROLE = {DROP, "ROLE"};
+		static final String[] STMT_DROP_SYNONYM = {DROP, "SYNONYM"};
+		static final String[] STMT_DROP_TRIGGER = {DROP, "TRIGGER"};
+		
+	    static final String[][] DROP_PHRASES = { 
+	    	STMT_DROP_FUNCTION, STMT_DROP_INDEX, STMT_DROP_PROCEDURE, STMT_DROP_ROLE, STMT_DROP_SYNONYM, STMT_DROP_TRIGGER 
+		};
+	    
+	    static final String[] STMT_GRANT = {"GRANT"};
+		static final String[] STMT_LOCK_TABLE = {"LOCK", TABLE};
+		static final String[] STMT_RENAME_TABLE = {"RENAME", TABLE};
+		static final String[] STMT_RENAME_INDEX = {"RENAME", INDEX};
+
+	    
+		static final String[] STMT_SET_ISOLATION = {SET, "ISOLATION"};
+		static final String[] STMT_SET_CURRENT_ISOLATION = {SET, "CURRENT", "ISOLATION"};
+		static final String[] STMT_SET_ROLE = {SET, "ROLE"};
+		static final String[] STMT_SET_SCHEMA = {SET, "SCHEMA"};
+		static final String[] STMT_SET_CURRENT_SCHEMA = {SET, "CURRENT", SCHEMA};
+		static final String[] STMT_SET_CURRENT_SQLID = {SET, "CURRENT", "SQLID"};
+		
+	    static final String[][] SET_PHRASES = { 
+	    	STMT_SET_ISOLATION, STMT_SET_CURRENT_ISOLATION, STMT_SET_ROLE, STMT_SET_SCHEMA, STMT_SET_CURRENT_SCHEMA,
+	    	STMT_SET_CURRENT_SQLID
+		};
+	    
+	    static final String[][] MISC_PHRASES = { STMT_LOCK_TABLE, STMT_RENAME_TABLE, STMT_RENAME_INDEX, STMT_GRANT,
+	    	STMT_DECLARE_GLOBAL_TEMP_TABLE
+		};
+	}
+	interface DerbyDataTypes {
+		static final String[] DTYPE_BIGINT = {"BIGINT"};
+		static final String[] DTYPE_LONG_VARCHAR = {"LONG", "VARCHAR"};
+		static final String[] DTYPE_LONG_VARCHAR_FBD = {"LONG", "VARCHAR", "FOR", "BIT", "DATA"};
+		
+		static final String[] DTYPE_DOUBLE = {"DOUBLE"};
+		static final String[] DTYPE_XML = {"XML"};
+		static final String[] DTYPE_CLOB = {"CLOB"}; // CLOB [ ( length [{K |M |G }] ) ]
+		static final String[] DTYPE_CHARACTER_LARGE_OBJECT = {"CHARACTER", "LARGE", "OBJECT"}; // [ ( length [{K |M |G }] ) ]
+		static final String[] DTYPE_BLOB = {"BLOB"}; // BLOB [ ( length [{K |M |G }] ) ]
+		static final String[] DTYPE_BINARY_LARGE_OBJECT = {"BINARY", "LARGE", "OBJECT"}; // [ ( length [{K |M |G }] ) ]
+	
+		static final List<String[]> CUSTOM_DATATYPE_START_PHRASES = 
+	  		Arrays.asList(new String[][] {
+	  				DTYPE_BIGINT, DTYPE_LONG_VARCHAR, DTYPE_LONG_VARCHAR_FBD, DTYPE_DOUBLE, DTYPE_XML,
+	  				DTYPE_CLOB, DTYPE_CHARACTER_LARGE_OBJECT, DTYPE_BLOB, DTYPE_BINARY_LARGE_OBJECT
+	  	  	});
+		
+	  	static final List<String> CUSTOM_DATATYPE_START_WORDS = 
+	  		Arrays.asList(new String[] {"BIGINT", "LONG", "DOUBLE",
+	  	  		"XML", "CLOB", "CHARACTER", "BLOB", "BINARY"
+	  	  	});
+	}
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/derby/DerbyDdlConstants.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlConstants.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlConstants.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlConstants.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,250 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.ddl.dialect.oracle;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.jboss.dna.sequencer.ddl.DdlConstants;
+
+/**
+ * Oracle-specific constants including key words and statement start phrases.
+ * 
+ * @author blafond
+ *
+ */
+public interface OracleDdlConstants extends DdlConstants {
+	
+	public static final String[] CUSTOM_KEYWORDS = {
+		"ANALYZE", "ASSOCIATE", "TRUNCATE",	"MATERIALIZED",	"SAVEPOINT", "PURGE", "LOCK", "TRIGGER", "EXPLAIN",	"PLAN",	"DIMENSION", 
+		"DIRECTORY", "DATABASE", "CONTROLFILE", "DISKGROUP", "INDEXTYPE", "SYNONYM", "SEQUENCE", "LIBRARY", "CLUSTER", "OUTLINE",
+		"PACKAGE", "SPFILE", "PFILE", "AUDIT", "COMMIT", "PURGE", "MERGE", "RENAME", "FLASHBACK", "NOAUDIT", "DISASSOCIATE", 
+		"NESTED", "REVOKE", "COMMENT", INDEX
+	};
+	
+	interface OracleStatementStartPhrases {
+		static final String[] STMT_ALTER_CLUSTER = {ALTER, "CLUSTER"};
+		static final String[] STMT_ALTER_DATABASE = {ALTER, "DATABASE"};
+		static final String[] STMT_ALTER_DIMENSION = {ALTER, "DIMENSION"};
+		static final String[] STMT_ALTER_DISKGROUP = {ALTER, "DISKGROUP"};
+		static final String[] STMT_ALTER_FUNCTION = {ALTER, "FUNCTION"};
+		static final String[] STMT_ALTER_INDEX = {ALTER, INDEX};
+		static final String[] STMT_ALTER_INDEXTYPE = {ALTER, "INDEXTYPE"};
+		static final String[] STMT_ALTER_JAVA = {ALTER, "JAVA"};
+		static final String[] STMT_ALTER_MATERIALIZED = {ALTER, "MATERIALIZED"};
+		static final String[] STMT_ALTER_OPERATOR = {ALTER, "OPERATOR"};
+		static final String[] STMT_ALTER_OUTLINE = {ALTER, "OUTLINE"};
+		static final String[] STMT_ALTER_PACKAGE = {ALTER, "PACKAGE"};
+		static final String[] STMT_ALTER_PROCEDURE = {ALTER, "PROCEDURE"};
+		static final String[] STMT_ALTER_PROFILE = {ALTER, "PROFILE"};
+		static final String[] STMT_ALTER_RESOURCE = {ALTER, "RESOURCE"};
+		static final String[] STMT_ALTER_ROLE = {ALTER, "ROLE"};
+		static final String[] STMT_ALTER_ROLLBACK = {ALTER, "ROLLBACK"};
+		static final String[] STMT_ALTER_SEQUENCE = {ALTER, "SEQUENCE"};
+		static final String[] STMT_ALTER_SESSION = {ALTER, "SESSION"};
+		static final String[] STMT_ALTER_SYSTEM = {ALTER, "SYSTEM"};
+		static final String[] STMT_ALTER_TABLESPACE = {ALTER, "TABLESPACE"};
+		static final String[] STMT_ALTER_TRIGGER = {ALTER, "TRIGGER"};
+		static final String[] STMT_ALTER_TYPE = {ALTER, "TYPE"};
+		static final String[] STMT_ALTER_USER = {ALTER, "USER"};
+		static final String[] STMT_ALTER_VIEW = {ALTER, "VIEW"};
+		
+	    static final String[][] ALTER_PHRASES = { STMT_ALTER_CLUSTER, STMT_ALTER_DATABASE, 
+			STMT_ALTER_DIMENSION, STMT_ALTER_DISKGROUP, STMT_ALTER_FUNCTION, STMT_ALTER_INDEX, STMT_ALTER_INDEXTYPE, STMT_ALTER_JAVA, 
+			STMT_ALTER_MATERIALIZED, STMT_ALTER_OPERATOR, STMT_ALTER_OUTLINE, STMT_ALTER_PACKAGE, STMT_ALTER_PROCEDURE, 
+			STMT_ALTER_PROFILE, STMT_ALTER_RESOURCE, STMT_ALTER_ROLE, STMT_ALTER_ROLLBACK, STMT_ALTER_SEQUENCE, 
+			STMT_ALTER_SESSION, STMT_ALTER_SYSTEM, STMT_ALTER_TABLESPACE, STMT_ALTER_TRIGGER, STMT_ALTER_TYPE, 
+			STMT_ALTER_USER, STMT_ALTER_VIEW  
+		};
+		
+		static final String[] STMT_ANALYZE = {"ANALYZE"};
+		static final String[] STMT_ASSOCIATE_STATISTICS = {"ASSOCIATE", "STATISTICS"};
+		static final String[] STMT_AUDIT = {"AUDIT"};
+		static final String[] STMT_COMMIT = {"COMMIT"};
+		static final String[] STMT_COMMENT_ON = {"COMMENT", "ON"};
+		
+		static final String[] STMT_CREATE_CLUSTER = {CREATE, "CLUSTER"};
+		static final String[] STMT_CREATE_CONTEXT = {CREATE, "CONTEXT"};
+		static final String[] STMT_CREATE_CONTROLFILE = {CREATE, "CONTROLFILE"};
+		static final String[] STMT_CREATE_DATABASE = {CREATE, "DATABASE"};
+		static final String[] STMT_CREATE_DIMENSION = {CREATE, "DIMENSION"};
+		static final String[] STMT_CREATE_DIRECTORY = {CREATE, "DIRECTORY"};
+		static final String[] STMT_CREATE_DISKGROUP = {CREATE, "DISKGROUP"};
+		static final String[] STMT_CREATE_FUNCTION = {CREATE, "FUNCTION"};   // PARSE UNTIL '/'
+		static final String[] STMT_CREATE_INDEX = {CREATE, "INDEX"};
+		static final String[] STMT_CREATE_INDEXTYPE = {CREATE, "INDEXTYPE"};
+		static final String[] STMT_CREATE_JAVA = {CREATE, "JAVA"};
+		static final String[] STMT_CREATE_LIBRARY = {CREATE, "LIBRARY"}; // PARSE UNTIL '/'
+		static final String[] STMT_CREATE_MATERIALIZED = {CREATE, "MATERIALIZED"};
+		static final String[] STMT_CREATE_OPERATOR = {CREATE, "OPERATOR"};
+		static final String[] STMT_CREATE_OR_REPLACE_DIRECTORY = {CREATE, "OR", "REPLACE", "DIRECTORY"};    // PARSE UNTIL '/'
+		static final String[] STMT_CREATE_OR_REPLACE_FUNCTION = {CREATE, "OR", "REPLACE", "FUNCTION"};    // PARSE UNTIL '/'
+		static final String[] STMT_CREATE_OR_REPLACE_LIBRARY = {CREATE, "OR", "REPLACE", "LIBRARY"};    // PARSE UNTIL '/'
+		static final String[] STMT_CREATE_OR_REPLACE_OUTLINE = {CREATE,"OR", "REPLACE", "OUTLINE"};    // PARSE UNTIL '/'
+		static final String[] STMT_CREATE_OR_REPLACE_PACKAGE = {CREATE,"OR", "REPLACE", "PACKAGE"};
+		static final String[] STMT_CREATE_OR_REPLACE_PUBLIC_SYNONYM = {CREATE, "OR", "REPLACE", "PUBLIC", "SYNONYM"};
+		static final String[] STMT_CREATE_OR_REPLACE_SYNONYM = {CREATE, "OR", "REPLACE", "SYNONYM"};
+		static final String[] STMT_CREATE_OR_REPLACE_TRIGGER = {CREATE, "OR", "REPLACE", "TRIGGER"}; // PARSE UNTIL '/
+		static final String[] STMT_CREATE_OR_REPLACE_TYPE = {CREATE, "OR", "REPLACE", "TYPE"};
+		static final String[] STMT_CREATE_OUTLINE = {CREATE, "OUTLINE"}; // PARSE UNTIL '/'
+		static final String[] STMT_CREATE_PACKAGE = {CREATE, "PACKAGE"};   // PARSE UNTIL '/'
+		static final String[] STMT_CREATE_PFILE = {CREATE, "PFILE"};
+		static final String[] STMT_CREATE_PROCEDURE = {CREATE, "PROCEDURE"}; // PARSE UNTIL '/'
+		static final String[] STMT_CREATE_PROFILE = {CREATE, "PROFILE"};
+		static final String[] STMT_CREATE_PUBLIC_DATABASE = {CREATE, "PUBLIC", "DATABASE"};
+		static final String[] STMT_CREATE_PUBLIC_ROLLBACK = {CREATE, "PUBLIC", "ROLLBACK"};
+		static final String[] STMT_CREATE_PUBLIC_SYNONYM = {CREATE, "PUBLIC", "SYNONYM"};
+		static final String[] STMT_CREATE_ROLE = {CREATE, "ROLE"};
+		static final String[] STMT_CREATE_ROLLBACK = {CREATE, "ROLLBACK"};
+		static final String[] STMT_CREATE_SEQUENCE = {CREATE, "SEQUENCE"};
+		static final String[] STMT_CREATE_SPFILE = {CREATE, "SPFILE"};
+		static final String[] STMT_CREATE_SYNONYM = {CREATE, "SYNONYM"};
+		static final String[] STMT_CREATE_TABLESPACE = {CREATE, "TABLESPACE"};
+		static final String[] STMT_CREATE_TRIGGER = {CREATE, "TRIGGER"};
+		static final String[] STMT_CREATE_TYPE = {CREATE, "TYPE"};
+		static final String[] STMT_CREATE_USER = {CREATE, "USER"};
+		static final String[] STMT_CREATE_UNIQUE_INDEX = {CREATE, "UNIQUE", "INDEX"};
+		static final String[] STMT_CREATE_BITMAP_INDEX = {CREATE, "BITMAP", "INDEX"};
+	
+		public static final String[][] CREATE_PHRASES = {
+			STMT_CREATE_CLUSTER, STMT_CREATE_CONTEXT, STMT_CREATE_CONTROLFILE, STMT_CREATE_DATABASE, STMT_CREATE_DIMENSION, 
+			STMT_CREATE_DIRECTORY, STMT_CREATE_DISKGROUP, STMT_CREATE_FUNCTION, STMT_CREATE_INDEX, STMT_CREATE_INDEXTYPE, 
+			STMT_CREATE_JAVA, STMT_CREATE_MATERIALIZED, STMT_CREATE_OPERATOR, STMT_CREATE_OR_REPLACE_DIRECTORY, 
+			STMT_CREATE_OR_REPLACE_OUTLINE,
+			STMT_CREATE_OR_REPLACE_PUBLIC_SYNONYM, STMT_CREATE_OR_REPLACE_SYNONYM,
+			STMT_CREATE_OR_REPLACE_TYPE, STMT_CREATE_OUTLINE, STMT_CREATE_PACKAGE, STMT_CREATE_PFILE, STMT_CREATE_PROCEDURE, 
+			STMT_CREATE_PROFILE, STMT_CREATE_PUBLIC_DATABASE, STMT_CREATE_PUBLIC_ROLLBACK, STMT_CREATE_PUBLIC_SYNONYM, STMT_CREATE_ROLE,
+			STMT_CREATE_ROLLBACK, STMT_CREATE_SEQUENCE, STMT_CREATE_SPFILE, STMT_CREATE_SYNONYM, STMT_CREATE_TABLESPACE, 
+			STMT_CREATE_TYPE, STMT_CREATE_USER, STMT_CREATE_UNIQUE_INDEX, STMT_CREATE_BITMAP_INDEX,
+			STMT_CREATE_TABLESPACE, STMT_CREATE_PROCEDURE
+		};
+		
+		public static final String[][] IGNORABLE_CREATE_PHRASES_WITH_SLASHES = {
+			STMT_CREATE_LIBRARY, STMT_CREATE_OR_REPLACE_FUNCTION,
+			STMT_CREATE_OR_REPLACE_LIBRARY,
+			STMT_CREATE_OR_REPLACE_PACKAGE, STMT_CREATE_OR_REPLACE_TRIGGER,
+			STMT_CREATE_TRIGGER
+		};
+		
+		
+		static final String[] STMT_DISASSOCIATE_STATISTICS = {"DISASSOCIATE", "STATISTICS"};
+		
+		static final String[] STMT_DROP_CLUSTER = {DROP, "CLUSTER"};
+		static final String[] STMT_DROP_CONTEXT = {DROP, "CONTEXT"};
+		static final String[] STMT_DROP_DATABASE = {DROP, "DATABASE"};
+		static final String[] STMT_DROP_DIMENSION = {DROP, "DIMENSION"};
+		static final String[] STMT_DROP_DIRECTORY = {DROP, "DIRECTORY"};
+		static final String[] STMT_DROP_DISKGROUP = {DROP, "DISKGROUP"};
+		static final String[] STMT_DROP_FUNCTION = {DROP, "FUNCTION"};
+		static final String[] STMT_DROP_INDEX = {DROP, "INDEX"};
+		static final String[] STMT_DROP_INDEXTYPE = {DROP, "INDEXTYPE"};
+		static final String[] STMT_DROP_JAVA = {DROP, "JAVA"};
+		static final String[] STMT_DROP_LIBRARY = {DROP, "LIBRARY"};
+		static final String[] STMT_DROP_MATERIALIZED = {DROP, "MATERIALIZED"};
+		static final String[] STMT_DROP_OPERATOR = {DROP, "OPERATOR"};
+		static final String[] STMT_DROP_OUTLINE = {DROP, "OUTLINE"};
+		static final String[] STMT_DROP_PACKAGE = {DROP, "PACKAGE"};
+		static final String[] STMT_DROP_PROCEDURE = {DROP, "PROCEDURE"};
+		static final String[] STMT_DROP_PROFILE = {DROP, "PROFILE"};
+		static final String[] STMT_DROP_ROLE = {DROP, "ROLE"};
+		static final String[] STMT_DROP_ROLLBACK = {DROP, "ROLLBACK"};
+		static final String[] STMT_DROP_SEQUENCE = {DROP, "SEQUENCE"};
+		static final String[] STMT_DROP_SYNONYM = {DROP, "SYNONYM"};
+		static final String[] STMT_DROP_TABLESPACE = {DROP, "TABLESPACE"};
+		static final String[] STMT_DROP_TRIGGER = {DROP, "TRIGGER"};
+		static final String[] STMT_DROP_TYPE = {DROP, "TYPE"};
+		static final String[] STMT_DROP_USER = {DROP, "USER"};
+		
+		static final String[] STMT_DROP_PUBLIC_DATABASE = {DROP, "PUBLIC", "DATABASE"};
+		static final String[] STMT_DROP_PUBLIC_SYNONYM = {DROP, "PUBLIC", "SYNONYM"};
+		
+		static final String[][] DROP_PHRASES = {
+			STMT_DROP_CLUSTER, STMT_DROP_CONTEXT, STMT_DROP_DATABASE, STMT_DROP_DIMENSION, STMT_DROP_DIRECTORY, STMT_DROP_DISKGROUP,
+			STMT_DROP_FUNCTION, STMT_DROP_INDEX, STMT_DROP_INDEXTYPE, STMT_DROP_JAVA, STMT_DROP_LIBRARY, STMT_DROP_MATERIALIZED,
+			STMT_DROP_OPERATOR, STMT_DROP_OUTLINE, STMT_DROP_PACKAGE, STMT_DROP_PROCEDURE, STMT_DROP_PROFILE, STMT_DROP_ROLE, 
+			STMT_DROP_ROLLBACK, STMT_DROP_SEQUENCE, STMT_DROP_SYNONYM, STMT_DROP_TABLESPACE, STMT_DROP_TRIGGER, STMT_DROP_TYPE,
+			STMT_DROP_USER, STMT_DROP_PUBLIC_DATABASE, STMT_DROP_PUBLIC_SYNONYM
+		};
+		
+		static final String[] STMT_EXPLAIN_PLAN = {"EXPLAIN", "PLAN"};
+		static final String[] STMT_FLASHBACK = {"FLASHBACK"};
+		static final String[] STMT_GRANT = {GRANT};
+		static final String[] STMT_LOCK_TABLE = {"LOCK", "TABLE"};
+		static final String[] STMT_MERGE = {"MERGE"};
+		static final String[] STMT_NESTED_TABLE = {"NESTED", "TABLE"};
+		static final String[] STMT_NOAUDIT = {"NOAUDIT"};
+		static final String[] STMT_PURGE = {"PURGE"};
+		static final String[] STMT_RENAME = {"RENAME"};
+		static final String[] STMT_REVOKE = {"REVOKE"};
+		static final String[] STMT_ROLLBACK_TO_SAVEPOINT = {"ROLLBACK", "TO", "SAVEPOINT"};
+		static final String[] STMT_ROLLBACK_WORK = {"ROLLBACK", "WORK"};
+		static final String[] STMT_ROLLBACK = {"ROLLBACK"};
+		static final String[] STMT_SAVEPOINT = {"SAVEPOINT"};
+		static final String[] STMT_SET_CONSTRAINT = {SET, "CONSTRAINT"};
+		static final String[] STMT_SET_CONSTRAINTS = {SET, "CONSTRAINTS"};
+		static final String[] STMT_SET_ROLE = {SET, "ROLE"};
+		static final String[] STMT_SET_TRANSACTION = {SET, "TRANSACTION"};
+		static final String[] STMT_TRUNCATE = {"TRUNCATE"};
+		
+		static final String[][] SET_PHRASES = {
+			STMT_SET_CONSTRAINT, STMT_SET_CONSTRAINTS, STMT_SET_ROLE, STMT_SET_TRANSACTION
+		};
+		
+		static final String[][] MISC_PHRASES = {
+			STMT_ANALYZE, STMT_ASSOCIATE_STATISTICS, STMT_AUDIT, STMT_COMMIT, STMT_COMMENT_ON, STMT_DISASSOCIATE_STATISTICS,
+			STMT_EXPLAIN_PLAN, STMT_FLASHBACK, STMT_GRANT, STMT_LOCK_TABLE, STMT_MERGE, STMT_NESTED_TABLE, STMT_NOAUDIT, STMT_PURGE, 
+			STMT_RENAME, STMT_REVOKE, STMT_ROLLBACK_TO_SAVEPOINT, STMT_ROLLBACK_WORK, STMT_ROLLBACK, STMT_SAVEPOINT, STMT_TRUNCATE
+		};
+	}
+	
+	interface OracleDataTypes {
+		static final String[] DTYPE_CHAR_ORACLE = {"CHAR"}; //CHAR(size [BYTE | CHAR])
+		static final String[] DTYPE_VARCHAR2 = {"VARCHAR2"}; // VARCHAR2(size [BYTE | CHAR])
+		static final String[] DTYPE_NVARCHAR2 = {"NVARCHAR2"}; // NVARCHAR2(size)
+		static final String[] DTYPE_NUMBER = {"NUMBER"}; // NUMBER(p,s)
+		static final String[] DTYPE_BINARY_FLOAT = {"BINARY_FLOAT "};
+		static final String[] DTYPE_BINARY_DOUBLE = {"BINARY_DOUBLE"};
+		static final String[] DTYPE_LONG = {"LONG"};
+		static final String[] DTYPE_LONG_RAW = {"LONG", "RAW"};
+		static final String[] DTYPE_RAW = {"RAW"}; // RAW(size)
+		static final String[] DTYPE_BLOB = {"BLOB"};
+		static final String[] DTYPE_CLOB = {"CLOB"};
+		static final String[] DTYPE_NCLOB = {"NCLOB"};
+		static final String[] DTYPE_BFILE = {"BFILE"};
+		static final String[] DTYPE_INTERVAL_YEAR = {"INTERVAL", "YEAR"}; //INTERVAL YEAR (year_precision) TO MONTH
+		static final String[] DTYPE_INTERVAL_DAY = {"INTERVAL", "DAY"}; //	INTERVAL DAY (day_precision) TO SECOND (fractional_seconds_precision)
+	
+	  	static final List<String[]> CUSTOM_DATATYPE_START_PHRASES = 
+	  		Arrays.asList(new String[][] {
+	  				DTYPE_CHAR_ORACLE, DTYPE_VARCHAR2, DTYPE_NVARCHAR2, DTYPE_NUMBER, DTYPE_BINARY_FLOAT, DTYPE_BINARY_DOUBLE,
+	  				DTYPE_LONG, DTYPE_LONG_RAW, DTYPE_RAW, DTYPE_BLOB, DTYPE_CLOB, DTYPE_NCLOB, DTYPE_BFILE, DTYPE_INTERVAL_YEAR,
+	  				DTYPE_INTERVAL_DAY
+	  	  	});
+		
+	  	static final List<String> CUSTOM_DATATYPE_START_WORDS = 
+	  		Arrays.asList(new String[] {"VARCHAR2", "NVARCHAR2", "NUMBER",
+	  	  		"BINARY_FLOAT", "BINARY_DOUBLE", "LONG", "RAW", "BLOB", "CLOB", "NCLOB", "BFILE", "INTERVAL"
+	  	  	});
+	}
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlConstants.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlParser.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlParser.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlParser.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,786 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.ddl.dialect.oracle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.sequencer.ddl.DdlTokenStream;
+import org.jboss.dna.sequencer.ddl.StandardDdlParser;
+import org.jboss.dna.sequencer.ddl.datatype.DataType;
+import org.jboss.dna.sequencer.ddl.datatype.DataTypeParser;
+import org.jboss.dna.sequencer.ddl.statement.AlterTable;
+import org.jboss.dna.sequencer.ddl.statement.Column;
+import org.jboss.dna.sequencer.ddl.statement.Index;
+import org.jboss.dna.sequencer.ddl.statement.Statement;
+import org.jboss.dna.sequencer.ddl.statement.TableConstraint;
+import org.jboss.dna.sequencer.ddl.statement.TypedStatement;
+
+
+/**
+ * Oracle-specific DDL Parser. Includes custom data types as well as custom DDL statements.
+ *
+ */
+public class OracleDdlParser extends StandardDdlParser implements OracleDdlConstants {
+	
+	static List<String[]> oracleDataTypeStrings = new ArrayList<String[]>();
+	
+	public OracleDdlParser() {
+		super();
+		
+		setDatatypeParser(new OracleDataTypeParser());
+		initialize();
+	}
+	
+	private void initialize() {
+		setTerminator(DEFAULT_TERMINATOR);
+		
+		setDoUseTerminator(true);
+		
+		oracleDataTypeStrings.addAll(OracleDataTypes.CUSTOM_DATATYPE_START_PHRASES); //
+	}
+	
+	/**
+	 * Method allows registering Oracle-specific Statement Start Keyword Phrases and Keywords
+	 * 
+	 * 
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#registerWords(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected void registerWords( DdlTokenStream tokens ) {
+		super.registerWords(tokens);
+		
+		registerKeyWords(CUSTOM_KEYWORDS);
+		registerKeyWords(OracleDataTypes.CUSTOM_DATATYPE_START_WORDS);
+		
+		registerStatementStartPhrase(OracleStatementStartPhrases.ALTER_PHRASES);
+		registerStatementStartPhrase(OracleStatementStartPhrases.CREATE_PHRASES);
+		registerStatementStartPhrase(OracleStatementStartPhrases.IGNORABLE_CREATE_PHRASES_WITH_SLASHES);
+		registerStatementStartPhrase(OracleStatementStartPhrases.DROP_PHRASES);
+		registerStatementStartPhrase(OracleStatementStartPhrases.SET_PHRASES);
+		registerStatementStartPhrase(OracleStatementStartPhrases.MISC_PHRASES);
+
+	}
+	
+	/**
+	 * 
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseCustomStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected Statement parseCustomStatement(DdlTokenStream tokens)
+			throws ParsingException {
+		Statement stmt = null;
+		
+		if( tokens.matches(OracleStatementStartPhrases.STMT_COMMENT_ON) ) {
+			stmt = parseCommentStatement(tokens);
+		} else if( tokens.matches(OracleStatementStartPhrases.STMT_NESTED_TABLE) ) {
+			// EXAMPLES:
+			// 		NESTED TABLE employees STORE AS deptemps; 
+			// or   MODIFY NESTED TABLE employees RETURN AS deptemps; 
+			tokens.consume("NESTED", TABLE, DdlTokenStream.ANY_VALUE, DdlTokenStream.ANY_VALUE, "AS", DdlTokenStream.ANY_VALUE);
+		} else if( tokens.matches(SET) ) {
+			stmt = parseSetStatement(tokens);
+		} else if( tokens.matches(GRANT)) {
+			stmt = parseGrantStatement(tokens);
+		} else {
+			stmt = parseIgnorableStatement(tokens);
+		}
+			
+		if( stmt == null ) {
+			stmt = super.parseCustomStatement(tokens);
+		}
+		return stmt;
+	}
+	
+	private Statement parseIgnorableStatement(DdlTokenStream tokens ) throws ParsingException {
+		Statement result = null;
+		for( String[] phrase : OracleStatementStartPhrases.IGNORABLE_CREATE_PHRASES_WITH_SLASHES ) {
+			if( tokens.matches(phrase) ) {
+				tokens.consume(CREATE);
+				result = parseIgnorableSlashedStatement(tokens, getStatementTypeName(phrase));
+				break;
+			}
+		}
+		if( result == null ) {
+			for( String[] phrase : OracleStatementStartPhrases.ALTER_PHRASES ) {
+				// NOTE:  Shouldn't get here. Alter statements are handled at a higher level
+				if( tokens.matches(phrase) ) {
+					tokens.consume(ALTER);
+					result = parseIgnorableStatement(tokens, getStatementTypeName(phrase));
+					consumeSlash(tokens);
+					break;
+				}
+			}
+		}
+		if( result == null ) {
+			for( String[] phrase : OracleStatementStartPhrases.CREATE_PHRASES ) {
+				// NOTE:  Shouldn't get here. Alter statements are handled at a higher level
+				if( tokens.matches(phrase) ) {
+					tokens.consume(CREATE);
+					result = parseIgnorableStatement(tokens, getStatementTypeName(phrase));
+					consumeSlash(tokens);
+				}
+			}
+		}
+		if( result == null ) {
+			for( String[] phrase : OracleStatementStartPhrases.DROP_PHRASES ) {
+				// NOTE:  Shouldn't get here. Alter statements are handled at a higher level
+				if( tokens.matches(phrase) ) {
+					tokens.consume(DROP);
+					result = parseIgnorableStatement(tokens, getStatementTypeName(phrase));
+					consumeSlash(tokens);
+					break;
+				}
+			}
+		}		
+		if( result == null ) {
+			for( String[] phrase : OracleStatementStartPhrases.SET_PHRASES ) {
+				// NOTE:  Shouldn't get here. Alter statements are handled at a higher level
+				if( tokens.matches(phrase) ) {
+					tokens.consume(SET);
+					result = parseIgnorableStatement(tokens, getStatementTypeName(phrase));
+					consumeSlash(tokens);
+					break;
+				}
+			}
+		}
+		if( result == null ) {
+			for( String[] phrase : OracleStatementStartPhrases.MISC_PHRASES ) {
+				// NOTE:  Shouldn't get here. Alter statements are handled at a higher level
+				if( tokens.matches(phrase) ) {
+					tokens.consume();
+					result = parseIgnorableStatement(tokens, getStatementTypeName(phrase));
+					consumeSlash(tokens);
+					break;
+				}
+			}
+		}
+		
+		return result;
+		
+	}
+
+	/**
+	 * 
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseCreateStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream, boolean)
+	 */
+	@Override
+	protected Statement parseCreateStatement(DdlTokenStream tokens, boolean inSchema) throws ParsingException {
+		Statement result = null;
+    	
+    	if( tokens.matches(OracleStatementStartPhrases.STMT_CREATE_INDEX) 
+    		|| tokens.matches(OracleStatementStartPhrases.STMT_CREATE_UNIQUE_INDEX)
+    		|| tokens.matches(OracleStatementStartPhrases.STMT_CREATE_BITMAP_INDEX)) {
+    		result = parseCreateIndex(tokens);
+    	} else {
+    		for( String[] phrase : OracleStatementStartPhrases.IGNORABLE_CREATE_PHRASES_WITH_SLASHES ) {
+    			if( tokens.matches(phrase) ) {
+    				tokens.consume(CREATE);
+    				result = parseIgnorableSlashedStatement(tokens, getStatementTypeName(phrase));
+    				break;
+    			}
+    		}
+    		if( result ==  null ) {
+	    		for( String[] phrase : OracleStatementStartPhrases.CREATE_PHRASES ) {
+	    			if( tokens.matches(phrase) ) {
+	    				tokens.consume(CREATE);
+	    				result = parseIgnorableStatement(tokens, getStatementTypeName(phrase));
+	    				consumeSlash(tokens);
+	    				break;
+	    			}
+	    		}
+    		}
+    	}
+		
+		if( result == null ) {
+			result = super.parseCreateStatement(tokens, inSchema);
+		}
+		
+		return result;
+	}
+	
+	/**
+	 * 
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseGrantStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected Statement parseGrantStatement( DdlTokenStream tokens ) throws ParsingException {
+		if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "TO")) {
+			tokens.consume(GRANT);
+			String privilege = tokens.consume();
+			tokens.consume("TO");
+			String toValue = tokens.consume();
+			Statement stmt = new TypedStatement();
+			String value = parseUntilTerminator(tokens);
+			stmt.setValue(value);
+			stmt.setType("GRANT" + SPACE + privilege + SPACE + "TO" + SPACE + toValue);
+			return stmt;
+		}
+		if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "ON")) {
+			tokens.consume(GRANT);
+			String privilege = tokens.consume();
+			tokens.consume("ON");
+			String toValue = tokens.consume();
+			Statement stmt = new TypedStatement();
+			String value = parseUntilTerminator(tokens);
+			stmt.setValue(value);
+			stmt.setType("GRANT" + SPACE + privilege + SPACE + "ON" + SPACE + toValue);
+			return stmt;
+		}
+		if( 	tokens.matches(GRANT, CREATE) ||
+				tokens.matches(GRANT, ALTER) || 
+				tokens.matches(GRANT, DROP) || 
+				tokens.matches(GRANT, "EXECUTE") || 
+				tokens.matches(GRANT, "MANAGE") || 
+				tokens.matches(GRANT, "QUERY") || 
+				tokens.matches(GRANT, "ON", "COMMIT") || 
+				tokens.matches(GRANT, "ANY") || 
+				tokens.matches(GRANT, "SELECT") || 
+				tokens.matches(GRANT, "RESTRICTED") || 
+				tokens.matches(GRANT, "FLASHBACK") || 
+				tokens.matches(GRANT, "GLOBAL") || 
+				tokens.matches(GRANT, "DEBUG") ||
+				tokens.matches(GRANT, "GLOBAL") ||
+				tokens.matches(GRANT, "ADVISOR") ||
+				tokens.matches(GRANT, "ADMINISTER") ||
+				tokens.matches(GRANT, "BACKUP") ||
+				tokens.matches(GRANT, "LOCK") ||
+				tokens.matches(GRANT, "UPDATE") || 
+				tokens.matches(GRANT, "DELETE") ||
+				tokens.matches(GRANT, "INSERT") ||
+				tokens.matches(GRANT, "UNLIMITED") ||
+				tokens.matches(GRANT, "UNDER") ||
+				tokens.matches(GRANT, "ANALYZE") ||
+				tokens.matches(GRANT, "AUDIT") ||
+				tokens.matches(GRANT, "COMMENT") ||
+				tokens.matches(GRANT, "EXEMPT") ||
+				tokens.matches(GRANT, "FORCE") ||
+				tokens.matches(GRANT, "RESUMABLE") ||
+				tokens.matches(GRANT, "SYSDBA") ||
+				tokens.matches(GRANT, "REFERENCES") ||
+				tokens.matches(GRANT, "SYSOPER") ||
+				tokens.matches(GRANT, "WRITE") ) {
+			tokens.consume(GRANT);
+			String nextTok = tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume();
+			Statement stmt = new TypedStatement();
+			String value = parseUntilTerminator(tokens);
+			stmt.setValue(value);
+			stmt.setType("GRANT" + " " + nextTok);
+			return stmt;
+		}
+		
+		
+		return null;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseAlterTableStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected Statement parseAlterTableStatement(DdlTokenStream tokens)
+			throws ParsingException {
+		if( tokens.matches("ALTER", "TABLE", DdlTokenStream.ANY_VALUE, "ADD")) {
+	    	tokens.consume(); // consumes 'ALTER'
+	    	tokens.consume("TABLE");
+	    	String tableName = parseName(tokens);
+	    	tokens.consume("ADD");
+	    	
+	    	//System.out.println("  >> PARSING ALTER STATEMENT >>  TABLE Name = " + tableName);
+	    	
+	    	AlterTable alterTableStatement = new AlterTable(tableName);
+    		TableConstraint constraint = parseTableConstraint(tokens);
+    		if( constraint != null ) {
+    			alterTableStatement.addConstraint(constraint);
+    		} else {
+    			// This segment can also be enclosed in "()" brackets to handle multiple Column ADDs
+    			if( tokens.matches("(", "REF")) {
+    					//ALTER TABLE staff ADD (REF(dept) WITH ROWID);
+    					tokens.consume("(", "REF", "(");
+    					parseName(tokens);
+    					tokens.consume(")", "WITH", "ROWID");
+    					
+				} else if( tokens.matches("(", "SCOPE") ) {
+					// ALTER TABLE staff ADD (SCOPE FOR (dept) IS offices); 
+					tokens.consume("(", "SCOPE", "FOR", "(");
+					parseName(tokens);
+					tokens.consume(")", "IS");
+					parseName(tokens);
+					tokens.consume(")");
+				} else if( tokens.matches('(')) {
+    				List<Column> columns = parseColumns(tokens);
+		    		for( Column newColumn : columns ) {
+		    			alterTableStatement.addColumn(newColumn);
+		    		}
+				} else {
+    				// Assume single ADD COLUMN
+	    			Column newColumn = parseSingleTerminatedColumnDefinition(tokens);
+	    			
+	    			alterTableStatement.addColumn(newColumn);
+    			}
+    		}
+    		consumeTerminator(tokens);
+    		return alterTableStatement;
+		} else if( tokens.matches(OracleStatementStartPhrases.STMT_ALTER_INDEX) ){
+	    	tokens.consume(OracleStatementStartPhrases.STMT_ALTER_INDEX); // consumes 'ALTER'
+	    	String indexName = parseName(tokens);
+	    	Statement stmt = new TypedStatement();
+	    	stmt.setName(getStatementTypeName(OracleStatementStartPhrases.STMT_ALTER_INDEX));
+	    	StringBuffer sb = new StringBuffer();
+	    	sb.append("ALTER INDEX").append(SPACE).append(indexName).append(parseUntilTerminator(tokens));
+	    	consumeTerminator(tokens);
+	    	return stmt;
+
+		} else if(tokens.matches("ALTER", "TABLE", DdlTokenStream.ANY_VALUE, "RENAME")) {
+			// ALTER TABLE customers RENAME TO my_customers;
+			// ALTER TABLE customers RENAME CONSTRAINT cust_fname_nn TO cust_firstname_nn;
+	    	tokens.consume(); // consumes 'ALTER'
+	    	tokens.consume("TABLE");
+	    	String oldName = parseName(tokens);
+	    	
+	    	AlterTable alterTableStatement = new AlterTable(oldName);
+	    	
+	    	if( tokens.canConsume("RENAME", "TO") ) {
+	    		alterTableStatement.setActionValue("RENAME TO" + parseUntilTerminator(tokens));
+	    	} else if( tokens.canConsume("RENAME", "COLUMN") ) {
+	    		alterTableStatement.setActionValue("RENAME COLUMN" + parseUntilTerminator(tokens));
+	    	} else if( tokens.canConsume("RENAME", "CONSTRAINT") ) {
+	    		alterTableStatement.setActionValue("RENAME CONSTRAINT" + parseUntilTerminator(tokens));
+	    	}
+	    	consumeTerminator(tokens);
+	    	return alterTableStatement;
+		}
+
+		return super.parseAlterTableStatement(tokens);
+	}
+
+	/**
+	 * 
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseAlterStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+	protected Statement parseAlterStatement(DdlTokenStream tokens) throws ParsingException {
+		Statement result = null;
+		
+		for( String[] phrase : OracleStatementStartPhrases.ALTER_PHRASES ) {
+			if( tokens.matches(phrase) ) {
+				tokens.consume("ALTER");
+				result = parseIgnorableStatement(tokens, getStatementTypeName(phrase));
+				break;
+			}
+		}
+
+		if( result == null ) {
+			result = super.parseAlterStatement(tokens);
+		}
+		
+		return result;
+	}
+	
+	/**
+	 * Parses DDL CREATE INDEX {@link Statement}
+	 * 
+	 * @param tokens
+	 * @return the parsed CREATE INDEX {@link Statement}
+	 * @throws ParsingException
+	 */
+    private Statement parseCreateIndex( DdlTokenStream tokens ) throws ParsingException {
+    	// CREATE INDEX STR_MODEL_NDX ON MMR_STRING_ATTRS (MODEL_ID);
+    	tokens.consume(); // CREATE
+    	boolean isUnique = tokens.canConsume("UNIQUE");
+    	tokens.canConsume("BITMAP");
+    	tokens.consume("INDEX");
+    	String indexName = consumeIdentifier(tokens);
+    	tokens.consume("ON");
+    	tokens.canConsume("CLUSTER");
+    	String tableName = consumeIdentifier(tokens);
+    	
+    	//System.out.println("  >> PARSING Create INDEX >>  Name = " + indexName + "  For TABLE = " + tableName);
+    	
+    	List<String> columnNameList = new ArrayList<String>();
+		try {
+			if( tokens.matches('(')) {
+				tokens.consume();
+				columnNameList = parseColumnNameList(tokens);
+				tokens.consume(')');
+			}
+		} catch (ParsingException e) {
+			// Give up on parsing, probably a nested function (i.e.   banned (lower(banned_name)); 
+			parseUntilTerminator(tokens);
+		}
+		Index newIndex = new Index(indexName, tableName, columnNameList);
+		newIndex.setUnique(isUnique);
+		
+        consumeTerminator(tokens);
+        
+		return newIndex;
+    }
+
+    
+    private Statement parseCommentStatement( DdlTokenStream tokens ) throws ParsingException {
+    	//  COMMENT ON COLUMN CS_EXT_FILES.FILE_UID IS
+    	//  'UNIQUE INTERNAL IDENTIFIER, NOT EXPOSED'
+    	//  %
+    	
+    	/*
+			commentCommand
+				:	'COMMENT' 'ON' 
+				  ( 'TABLE' tableName 
+				  | 'VIEW' tableName
+				  | 'INDEX' indexName
+				  | 'COLUMN' columnName 
+				  | 'PROCEDURE' procedureId
+				  | 'MATERIALIZED'? 'VIEW' tableName 
+				  ) 'IS' ('NULL' | (CharLiteral)+) commandEnd? //CharLiteral (',' unicodeCharLiteral)*) commandEnd?
+				;
+    	 */
+    	tokens.consume("COMMENT", "ON"); // consumes 'COMMENT' 'ON'
+    	String obj = tokens.consume();
+    	String objName = parseName(tokens);
+    	
+    	//System.out.println("  >> FOUND [COMMENT ON] STATEMENT >>  TABLE Name = " + objName);
+    	String commentString = null;
+    		
+    	tokens.consume("IS");
+    	if( tokens.matches("NULL") ) {
+    		tokens.consume("NULL");
+    	} else {
+    		commentString = parseUntilTerminator(tokens);
+    	}
+    	
+    	consumeTerminator(tokens);
+    	
+        return new CommentStatement(obj, objName, commentString);
+    }
+    
+    /**
+     * 
+     * {@inheritDoc}
+     *
+     * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseSetStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+     */
+	@Override
+	protected Statement parseSetStatement(DdlTokenStream tokens)
+			throws ParsingException {
+		Statement result = null;
+		
+		for( String[] phrase : OracleStatementStartPhrases.SET_PHRASES ) {
+			// NOTE:  Shouldn't get here. Alter statements are handled at a higher level
+			if( tokens.matches(phrase) ) {
+				tokens.consume("SET");
+				result = parseIgnorableStatement(tokens, getStatementTypeName(phrase));
+				break;
+			}
+		}
+		
+		return result;
+	}
+
+	/**
+	 * 
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#parseDropStatement(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+	 */
+	@Override
+    protected Statement parseDropStatement( DdlTokenStream tokens )  throws ParsingException {
+    	Statement result =  null;
+    	
+    	if( tokens.matches(StatementStartPhrases.STMT_DROP_TABLE )) {
+			//    		<drop table statement> ::=
+			//    		    DROP TABLE <table name> [{CASCADE | RESTRICT} CONSTAINTS] [PURGE]
+    		//
+    		//				<drop behavior> ::= CASCADE | RESTRICT
+    		tokens.consume(StatementStartPhrases.STMT_DROP_TABLE);
+    		String name = parseName(tokens);
+    		result = new TypedStatement();
+    		result.setName(name);
+    		result.setType("DROP TABLE");
+    		
+    		tokens.canConsume("CASCADE");
+    		tokens.canConsume("CASCADE");
+    		tokens.canConsume("RESTRICT");
+    		tokens.canConsume("PURGE");
+    	}
+    	if( result == null ) {
+			for( String[] phrase : OracleStatementStartPhrases.DROP_PHRASES ) {
+				if( tokens.matches(phrase) ) {
+					tokens.consume("DROP");
+					String name = null;
+					if( !tokens.matches(getTerminator())) {
+						name = tokens.consume();
+					}
+					result = parseIgnorableStatement(tokens, getStatementTypeName(phrase));
+					result.setName(name);
+					break;
+				}
+			}
+    	}
+		if( result == null ) {
+			result = super.parseDropStatement(tokens);
+		}
+    	return result;
+    }
+    
+    private Statement parseIgnorableSlashedStatement(DdlTokenStream tokens, String name) throws ParsingException {
+    	TypedStatement stmt = null;
+    	
+    	String value = parseUntilFwdSlash(tokens);
+    	if( value != null ) {
+    		stmt = new TypedStatement();
+    		stmt.setType(name);
+    		stmt.setValue(value);
+    	}
+    	tokens.consume("/");
+    	
+    	//System.out.println(" >>> FOUND [" + stmt.getType() +"] STATEMENT TOKEN. IGNORING");
+    	return stmt;
+    }
+    
+    /**
+     * Various Oracle statements (i.e. "CREATE OR REPLACE PACKAGE", etc...) may contain multiple SQL statements that will be 
+     * terminated by the semicolon, ';'.  In these cases, the terminator is now the '/' character.
+     * 
+     * @param tokens
+     * @return the parsed string.
+     * @throws ParsingException 
+     */
+    private String parseUntilFwdSlash( DdlTokenStream tokens ) throws ParsingException {
+    	StringBuffer sb = new StringBuffer();
+	    	while( tokens.hasNext() && !tokens.matches('/')) { // !tokens.matches(DdlTokenizer.STATEMENT_KEY) && 
+	    		sb.append(SPACE).append(tokens.consume());
+	    	} 
+    	
+    	return sb.toString();
+    }
+    
+    private void consumeSlash( DdlTokenStream tokens )  throws ParsingException {
+    	tokens.canConsume("/");
+    }
+    
+    // ===========================================================================================================================
+    // PARSE OBJECTS
+    // ===========================================================================================================================
+    
+    /**
+     * Oracle-specific COMMENT ON statement
+     */
+    public class CommentStatement extends TypedStatement {
+    	private String comment = null;
+    	private String targetType = null;
+    	private String targetName = null;
+    	
+		public CommentStatement(String targetType, String targetName, String comment) {
+			super();
+			setType("COMMENT ON");
+			this.targetType = targetType;
+			this.targetName = targetName;
+			this.comment = comment;
+		}
+		
+		public String getTargetType() {
+			return targetType;
+		}
+
+		public void setTargetType(String targetType) {
+			this.targetType = targetType;
+		}
+
+		public String getTargetName() {
+			return targetName;
+		}
+
+		public void setTargetName(String targetName) {
+			this.targetName = targetName;
+		}
+
+		public String getComment() {
+			return comment;
+		}
+
+		public void setComment(String comment) {
+			this.comment = comment;
+		}
+    	
+		
+		// ===========================================================================================================================
+		// ===========================================================================================================================	
+    }
+
+	/**
+	 * This class provides custom data type parsing for Oracle-specific data types.
+	 *
+	 */
+    class OracleDataTypeParser extends DataTypeParser {
+
+    	/*
+    	 * (non-Javadoc)
+    	 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#parseCustomType(org.jboss.dna.common.text.DdlTokenStream)
+    	 */
+		@Override
+		protected DataType parseCustomType(DdlTokenStream tokens) throws ParsingException {
+	    	DataType result = null;
+	    	String typeName = null;
+			
+			if( tokens.matches(OracleDataTypes.DTYPE_BINARY_FLOAT)) {
+				typeName = tokens.consume();
+			} else if( tokens.matches(OracleDataTypes.DTYPE_BINARY_DOUBLE) ) {
+				typeName = tokens.consume();
+			} else if( tokens.matches(OracleDataTypes.DTYPE_LONG) ){
+				typeName = tokens.consume();
+			} else if( tokens.matches(OracleDataTypes.DTYPE_LONG_RAW) ){
+				typeName = tokens.consume() + SPACE + tokens.consume();
+			} else if( tokens.matches(OracleDataTypes.DTYPE_BLOB) ){
+				typeName = tokens.consume();
+			} else if( tokens.matches(OracleDataTypes.DTYPE_CLOB) ){
+				typeName = tokens.consume();
+			} else if( tokens.matches(OracleDataTypes.DTYPE_NCLOB) ){
+				typeName = tokens.consume();
+			} else if( tokens.matches(OracleDataTypes.DTYPE_BFILE) ){
+				typeName = tokens.consume();
+			} else if(tokens.matches(OracleDataTypes.DTYPE_VARCHAR2)) {
+				// VARCHAR2(size [BYTE | CHAR])
+				typeName = tokens.consume(); // NAME
+		        tokens.consume('(');
+		        int length = parseInteger(tokens);
+		        if( tokens.matchesAnyOf("BYTE", "CHAR") ) {
+		        	tokens.consume();
+		        }
+		        tokens.consume(')');
+		        result = new DataType(typeName, length);
+			} else if(tokens.matches(OracleDataTypes.DTYPE_RAW)) {
+				typeName = tokens.consume();
+				int length = parseBracketedInteger(tokens);
+		        
+		        result = new DataType(typeName, length);
+			} else if(tokens.matches(OracleDataTypes.DTYPE_NVARCHAR2)) {
+				typeName = tokens.consume();
+				int length = parseBracketedInteger(tokens);
+		        
+		        result = new DataType(typeName, length);
+			} else if(tokens.matches(OracleDataTypes.DTYPE_NUMBER)) {
+				typeName = tokens.consume();
+	    		int precision = 0;
+	    		int scale = 0;
+	        	if( tokens.matches('(')) {
+		            tokens.consume('(');
+		            precision = parseInteger(tokens);
+		            if( tokens.canConsume(',') ) {
+		                scale =  parseInteger(tokens);
+		            } else {
+		            	scale = getDefaultScale();
+		            }
+		            tokens.consume(')');
+	        	} else {
+	        		precision = getDefaultPrecision();
+	        		scale = getDefaultScale();
+	        	}
+	        	result = new DataType(typeName, precision, scale);
+			} else if(tokens.matches(OracleDataTypes.DTYPE_INTERVAL_YEAR)) {
+				//INTERVAL YEAR (year_precision) TO MONTH
+				
+			} else if(tokens.matches(OracleDataTypes.DTYPE_INTERVAL_DAY)) {
+				//INTERVAL DAY (day_precision) TO SECOND (fractional_seconds_precision)
+			}
+			
+			if( result == null ) {
+				result = super.parseCustomType(tokens);
+			}
+			
+			return result;
+		}
+
+		/**
+		 * Because Oracle has an additional option on the CHAR datatype, we need to override the super method, check
+		 * for CHAR type and parse, else call super.parseCharStringType().
+		 * 
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#parseCharStringType(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+		 */
+		@Override
+		protected DataType parseCharStringType(DdlTokenStream tokens)
+				throws ParsingException {
+	    	DataType result = null;
+	    	
+			if( tokens.matches(OracleDataTypes.DTYPE_CHAR_ORACLE)) {
+				String typeName = tokens.consume(); 
+				// CHAR (size [BYTE | CHAR]) (i.e. CHAR (10 BYTE) )
+				int length = getDefaultLength();
+				if( tokens.matches('(')) {
+			        tokens.consume('(');
+			        length = parseInteger(tokens);
+			        if( tokens.matchesAnyOf("BYTE", OracleDataTypes.DTYPE_CHAR_ORACLE) ) {
+			        	tokens.consume();
+			        }
+			        tokens.consume(')');
+				}
+				result = new DataType(typeName, length);
+			} else {
+				result = super.parseCharStringType(tokens);
+			}
+			
+			return result;
+		}
+
+		/**
+		 * 
+		 * {@inheritDoc}
+		 *
+		 * @see org.jboss.dna.sequencer.ddl.datatype.DataTypeParser#isCustomDataType(org.jboss.dna.sequencer.ddl.DdlTokenStream)
+		 */
+		@Override
+		protected boolean isCustomDataType(DdlTokenStream tokens)
+				throws ParsingException {
+        	for( String[] stmt : oracleDataTypeStrings ) {
+        		if( tokens.matches(stmt) ) return true;
+        	}
+        	
+        	return false;
+		}
+    	
+    }
+	
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see org.jboss.dna.sequencer.ddl.StandardDdlParser#getDataTypeStartWords()
+	 */
+	@Override
+	protected List<String> getCustomDataTypeStartWords() {
+		return OracleDataTypes.CUSTOM_DATATYPE_START_WORDS;
+	}
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/dialect/oracle/OracleDdlParser.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/AlterTable.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/AlterTable.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/AlterTable.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,66 @@
+package org.jboss.dna.sequencer.ddl.statement;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class AlterTable extends Table {
+
+	private List<String> droppedColumnNames = new ArrayList<String>();
+	private HashMap<String, String> renamedColumns = new HashMap<String, String>();
+	private List<Column> modifiedColumns = new ArrayList<Column>();
+	
+	private String actionValue = null;
+
+	public AlterTable(String name) {
+		super(name);
+		// TODO Auto-generated constructor stub
+		setType("ALTER TABLE");
+	}
+
+	public List<String> getDroppedColumnNames() {
+		return droppedColumnNames;
+	}
+
+	public void addDroppedColumnName(String name) {
+		this.droppedColumnNames.add(name);
+	}
+	
+	public void addRenamedColumn(String oldName, String newName) {
+		renamedColumns.put(oldName, newName);
+	}
+	
+	public HashMap<String, String> getRenamedColumnsMap() {
+		return renamedColumns;
+	}
+	
+	public void addModifiedColumn(Column modifiedColumn) {
+		this.modifiedColumns.add(modifiedColumn);
+	}
+	
+	public List<Column> getModifiedColumns() {
+		return this.modifiedColumns;
+	}
+	
+	/**
+	 * @return actionValue
+	 */
+	public String getActionValue() {
+		return actionValue;
+	}
+
+	/**
+	 * @param actionValue Sets actionValue to the specified value.
+	 */
+	public void setActionValue(String actionValue) {
+		this.actionValue = actionValue;
+	}
+	
+	@Override
+	public String toString() {
+		StringBuffer result = new StringBuffer(100);
+		result.append("AlterTable()").append(" ").append(getName());
+				
+		return result.toString();
+	}
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/AlterTable.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Column.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Column.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Column.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,88 @@
+package org.jboss.dna.sequencer.ddl.statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jboss.dna.sequencer.ddl.datatype.DataType;
+
+public class Column extends TableElement {
+	private String name =  null;
+	private DataType dataType = null;
+	private String defaultValue = null;
+	private String nullable = DEFAULT_NULLABLE;
+	private List<TableConstraint> constraints = new ArrayList<TableConstraint>();
+	private Table parentTable =  null;
+	
+	public static final String DEFAULT_NULLABLE = "NULL";
+	
+	
+	public Column(String name, DataType dataType) {
+		super();
+		this.name = name;
+		this.dataType = dataType;
+	}
+	
+	public Column(String name, DataType dataType, Table parent) {
+		super();
+		this.name = name;
+		this.dataType = dataType;
+		this.parentTable = parent;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public DataType getDataType() {
+		return dataType;
+	}
+
+	public void setDataType(DataType dataType) {
+		this.dataType = dataType;
+	}
+
+	public String getDefaultValue() {
+		return defaultValue;
+	}
+
+	public void setDefaultValue(String defaultValue) {
+		this.defaultValue = defaultValue;
+	}
+
+	public String getNullable() {
+		return nullable;
+	}
+
+	public void setNullable(String nullable) {
+		this.nullable = nullable;
+	}
+	
+	public void addConstraint(TableConstraint constraint) {
+		constraints.add(constraint);
+	}
+	
+	public List<TableConstraint> getConstraints(){
+		return this.constraints;
+	}
+
+	@Override
+	public String toString() {
+		StringBuffer result = new StringBuffer(100);
+		result.append("Column()").append(" ").append(name);
+				
+		return result.toString();
+	}
+
+	public Table getParentTable() {
+		return parentTable;
+	}
+
+	public void setParentTable(Table parentTable) {
+		this.parentTable = parentTable;
+	}
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Column.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/ForeignKeyReference.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/ForeignKeyReference.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/ForeignKeyReference.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,35 @@
+package org.jboss.dna.sequencer.ddl.statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ForeignKeyReference extends TableConstraint {
+	private String referencedTablename = null;
+	private List<String> referencedColumnNameList = null;
+	
+
+	public ForeignKeyReference(int type, String name, List<String> columnNameList) {
+		super(type, name, columnNameList);
+		// TODO Auto-generated constructor stub
+	}
+
+
+	public String getReferencedTablename() {
+		return referencedTablename;
+	}
+
+
+	public void setReferencedTablename(String referencedTablename) {
+		this.referencedTablename = referencedTablename;
+	}
+
+
+	public List<String> getReferencedColumnNameList() {
+		return referencedColumnNameList;
+	}
+
+
+	public void setReferencedColumnNameList(List<String> referencedColumnNameList) {
+		this.referencedColumnNameList = new ArrayList<String>(referencedColumnNameList);
+	}
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/ForeignKeyReference.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Index.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Index.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Index.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,54 @@
+package org.jboss.dna.sequencer.ddl.statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Index extends TypedStatement {
+
+	private boolean isUnique = false;
+	private String tableName = null;
+	private List<String> columnNames = new ArrayList<String>();
+	
+	public Index(String name, String tableName, List<String> columnNames) {
+		super();
+		setName(name);
+		setType("CREATE INDEX");
+		this.tableName = tableName;
+		this.columnNames.addAll(columnNames);
+	}
+
+	
+	public void addColumn(String column) {
+		columnNames.add(column);
+	}
+	
+	public List<String> getColumns() {
+		return this.columnNames;
+	}
+	
+	@Override
+	public String toString() {
+		StringBuffer result = new StringBuffer(100);
+		result.append("Index()").append(" ").append(getName());
+				
+		return result.toString();
+	}
+
+	public boolean isUnique() {
+		return isUnique;
+	}
+
+	public void setUnique(boolean isUnique) {
+		this.isUnique = isUnique;
+	}
+
+
+	public String getTableName() {
+		return tableName;
+	}
+
+
+	public void setTableName(String tableName) {
+		this.tableName = tableName;
+	}
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Index.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Statement.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Statement.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Statement.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,18 @@
+package org.jboss.dna.sequencer.ddl.statement;
+
+public interface Statement {
+	static final String UKNOWN_TYPE = "UNKNOWN";
+
+	String getType();
+	
+	String getValue();
+	
+	String getName();
+	
+
+	void setType(String type);
+	
+	void setValue(String value);
+	
+	void setName(String name);
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Statement.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Table.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Table.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Table.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,41 @@
+package org.jboss.dna.sequencer.ddl.statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class Table extends TypedStatement {
+	private List<TableElement> columns = new ArrayList<TableElement>();
+	private List<TableElement> constraints = new ArrayList<TableElement>();
+	
+	public Table(String name) {
+		super();
+		setName(name);
+		setType("CREATE TABLE");
+		setValue(null);
+	}
+
+	public void addColumn(TableElement column) {
+		columns.add(column);
+	}
+	
+	public void addConstraint(TableElement tableConstraint) {
+		constraints.add(tableConstraint);
+	}
+	
+	public List<TableElement> getColumns() {
+		return this.columns;
+	}
+	
+	public List<TableElement> getConstraints() {
+		return this.constraints;
+	}
+	
+	@Override
+	public String toString() {
+		StringBuffer result = new StringBuffer(100);
+		result.append("Table()").append(" ").append(getName());
+				
+		return result.toString();
+	}
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/Table.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableConstraint.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableConstraint.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableConstraint.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,52 @@
+package org.jboss.dna.sequencer.ddl.statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TableConstraint extends TableElement {
+	private int type = 0;
+	private String name = null;
+
+	private List<String> columnNameList = new ArrayList<String>();
+	
+	public TableConstraint(int type, String name, List<String> columnNameList) {
+		super();
+		this.type = type;
+		this.name = name;
+		this.columnNameList.addAll(columnNameList);
+	}
+
+	public int getType() {
+		return type;
+	}
+
+	public void setType(int type) {
+		this.type = type;
+	}
+
+	public List<String> getColumnNameList() {
+		return columnNameList;
+	}
+	
+	/**
+	 * @return name
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * @param name Sets name to the specified value.
+	 */
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+	@Override
+	public String toString() {
+		StringBuffer result = new StringBuffer(100);
+		result.append("TableConstraint()").append(" ").append(name);
+				
+		return result.toString();
+	}
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableConstraint.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableElement.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableElement.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableElement.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,5 @@
+package org.jboss.dna.sequencer.ddl.statement;
+
+public abstract class TableElement {
+
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TableElement.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TypedStatement.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TypedStatement.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TypedStatement.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,39 @@
+package org.jboss.dna.sequencer.ddl.statement;
+
+public class TypedStatement implements Statement {
+	private String name = null;
+	private String type = Statement.UKNOWN_TYPE;
+	private String value = null;
+	
+	public String getName() {
+		return name;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+	
+	@Override
+	public String toString() {
+		StringBuffer result = new StringBuffer(100);
+		result.append(getType()).append("   NAME = ").append(getName());
+				
+		return result.toString();
+	}
+}


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/main/java/org/jboss/dna/sequencer/ddl/statement/TypedStatement.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParserTest.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParserTest.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParserTest.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,746 @@
+package org.jboss.dna.sequencer.ddl.datatype;
+
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.sequencer.ddl.DdlConstants;
+import org.jboss.dna.sequencer.ddl.DdlTokenStream;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DataTypeParserTest implements DdlConstants {
+	private DataTypeParser parser;
+
+	
+	
+    @Before
+    public void beforeEach() {
+    	parser = new DataTypeParser();
+    }
+    
+    private DdlTokenStream getTokens(String content) {
+    	DdlTokenStream tokens = new DdlTokenStream(content, DdlTokenStream.ddlTokenizer(false), false);
+    	
+    	tokens.start();
+    	
+    	return tokens;
+    }
+    
+    private String getDataTypeString(String[] input) {
+    	StringBuffer sb = new StringBuffer();
+    	
+    	for(int i=0; i<input.length; i++ ) {
+    		if( i>0 ) {
+    			sb.append(SPACE);
+    		}
+    		sb.append(input[i]);
+    	}
+    	
+    	return sb.toString();
+    }
+
+    /* ===========================================================================================================================
+     * UTILITY METHODS
+     * ==========================================================================================================================
+     */
+    
+    @Test
+    public void shouldParseBracketedInteger() {
+		String content = "(255)";
+		DdlTokenStream tokens = getTokens(content);
+		int value = parser.parseBracketedInteger(tokens);
+		
+		Assert.assertEquals("DataType length is not correct", 255, value);
+    }
+    
+    @Test
+    public void shouldParseKMGInteger() {
+		String content = "1000M";
+		DdlTokenStream tokens = getTokens(content);
+		
+		int value = parser.parseInteger(tokens);
+		Assert.assertEquals("DataType length is not correct", 1000, value);
+		
+		content = "1000G";
+		tokens = getTokens(content);
+		value = parser.parseInteger(tokens);
+		
+		Assert.assertEquals("DataType length is not correct", 1000, value);
+		
+		content = "1000K";
+		tokens = getTokens(content);
+		value = parser.parseInteger(tokens);
+		
+		Assert.assertEquals("DataType length is not correct", 1000, value);
+		
+		content = "(1000M)";
+		tokens = getTokens(content);
+		value = parser.parseBracketedInteger(tokens);
+		
+		Assert.assertEquals("DataType length is not correct", 1000, value);
+		
+		content = "(1000G)";
+		tokens = getTokens(content);
+		value = parser.parseBracketedInteger(tokens);
+		
+		Assert.assertEquals("DataType length is not correct", 1000, value);
+		
+		content = "(1000K)";
+		tokens = getTokens(content);
+		value = parser.parseBracketedInteger(tokens);
+		
+		Assert.assertEquals("DataType length is not correct", 1000, value);
+    }
+    
+    /* ===========================================================================================================================
+     * CHARACTER STRING TYPE
+     * ==========================================================================================================================
+	    <character string type> ::=
+	        CHARACTER [ <left paren> <length> <right paren> ]
+	      | CHAR [ <left paren> <length> <right paren> ]
+	      | CHARACTER VARYING <left paren> <length> <right paren>
+	      | CHAR VARYING <left paren> <length> <right paren>
+	      | VARCHAR <left paren> <length> <right paren>
+    */
+     
+	@Test
+	public void shouldParseCHAR() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_CHAR);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		content = typeString + " (255)";
+		tokens = getTokens(content);
+		
+		dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+	}
+	
+	@Test
+	public void shouldParseCHARACTER() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_CHARACTER);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		content = typeString + " (255)";
+		tokens = getTokens(content);
+		
+		dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+	}
+	
+	@Test
+	public void shouldParseCHAR_VARYING() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_CHAR_VARYING);
+		String content = typeString + " (255)";
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+	}
+	
+	@Test
+	public void shouldParseCHARACTER_VARYING() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_CHARACTER_VARYING);
+		String content = typeString + " (255)";
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+	}
+	
+	@Test
+	public void shouldParseVARCHAR() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_VARCHAR);
+		String content = typeString + " (255)";
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+		
+		content = typeString;
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+	}
+	
+    /* ===========================================================================================================================
+     * NATIONAL CHARACTER STRING TYPE
+     * ==========================================================================================================================
+     * 
+     * 
+		<national character string type> ::=
+		      NATIONAL CHARACTER [ <left paren> <length> <right paren> ]
+		    | NATIONAL CHAR [ <left paren> <length> <right paren> ]
+		    | NCHAR [ <left paren> <length> <right paren> ]
+		    | NATIONAL CHARACTER VARYING <left paren> <length> <right paren>
+		    | NATIONAL CHAR VARYING <left paren> <length> <right paren>
+		    | NCHAR VARYING <left paren> <length> <right paren>
+
+     * 
+     * 
+     */
+	@Test
+	public void shouldParseNATIONAL_CHAR() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_NATIONAL_CHAR);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		content = typeString + " (255)";
+		tokens = getTokens(content);
+		
+		dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+	}
+	
+	@Test
+	public void shouldParseNATIONAL_CHARACTER() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_NATIONAL_CHARACTER);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		content = typeString + " (255)";
+		tokens = getTokens(content);
+		
+		dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+	}
+	
+	@Test
+	public void shouldParseNATIONAL_CHAR_VARYING() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_NATIONAL_CHAR_VARYING);
+		String content = typeString + " (255)";
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+	}
+	
+	@Test
+	public void shouldParseNATIONAL_CHARACTER_VARYING() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_NATIONAL_CHARACTER_VARYING);
+		String content = typeString + " (255)";
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+	}
+	
+	@Test
+	public void shouldParseNCHAR_VARYING() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_NCHAR_VARYING);
+		String content = typeString + " (255)";
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+		
+		content = typeString;
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+	}
+	
+	
+    /* ===========================================================================================================================
+     * NATIONAL CHARACTER STRING TYPE
+     * ==========================================================================================================================
+     * 
+		<bit string type> ::=
+		      BIT [ <left paren> <length> <right paren> ]
+		    | BIT VARYING <left paren> <length> <right paren>
+     *
+     */
+	
+	@Test
+	public void shouldParseBIT() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_BIT);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+	}
+
+	@Test
+	public void shouldParseBITWithLength() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_BIT);
+		String content = typeString + " (255)";
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+	}
+	
+	@Test
+	public void shouldParseBIT_VARYINGWithLength() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_BIT_VARYING);
+		String content = typeString + " (255)";
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 255, dType.getLength());
+		
+		content = typeString;
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+	}
+	
+    /* ===========================================================================================================================
+     * EXACT NUMERIC TYPE
+     * ==========================================================================================================================
+		<exact numeric type> ::=
+		      NUMERIC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+		    | DECIMAL [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+		    | DEC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+		    | INTEGER
+		    | INT
+		    | SMALLINT
+     * 
+     */
+	
+	@Test
+	public void shouldNotParseXXXXXXTYPE() {
+		String typeString = "XXXXXXTYPE";
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		
+		
+		DataType dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		
+		Assert.assertNull("DataType should NOT have been found for Type = " + typeString, dType);
+	}
+	
+	@Test
+	public void shouldParseINT() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_INT);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+	}
+	
+	@Test
+	public void shouldParseINTEGER() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_INTEGER);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+	}
+	
+	@Test
+	public void shouldParseSMALLINT() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_SMALLINT);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+	}
+	
+	@Test
+	public void shouldParseNUMERIC() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_NUMERIC);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		content = typeString + " (5)";
+		tokens = getTokens(content);
+		dType = parser.parse(tokens);
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 5, dType.getPrecision());  // PRECISION
+		
+		content = typeString + " (5, 2)";
+		tokens = getTokens(content);
+		dType = parser.parse(tokens);
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 5, dType.getPrecision());  // PRECISION
+		Assert.assertEquals("DataType length is not correct", 2, dType.getScale());  // SCALE
+		
+		
+		// MISSING COMMA
+		content = typeString + " (5  2)";
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+		
+		
+		// INVALID Scale Integer
+		content = typeString + " (5  A)";
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+	}
+	
+	
+	@Test
+	public void shouldParseDECIMAL() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_DECIMAL);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		content = typeString + " (5)";
+		tokens = getTokens(content);
+		dType = parser.parse(tokens);
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 5, dType.getPrecision());  // PRECISION
+		
+		content = typeString + " (5, 2)";
+		tokens = getTokens(content);
+		dType = parser.parse(tokens);
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 5, dType.getPrecision());  // PRECISION
+		Assert.assertEquals("DataType length is not correct", 2, dType.getScale());  // SCALE
+		
+		// MISSING COMMA
+		content = typeString + " (5  2)";
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+		
+		
+		// INVALID Scale Integer
+		content = typeString + " (5  A)";
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+	}
+	
+	
+	@Test
+	public void shouldParseDEC() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_DEC);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		content = typeString + " (5)";
+		tokens = getTokens(content);
+		dType = parser.parse(tokens);
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 5, dType.getPrecision());  // PRECISION
+		
+		content = typeString + " (5, 2)";
+		tokens = getTokens(content);
+		dType = parser.parse(tokens);
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 5, dType.getPrecision());  // PRECISION
+		Assert.assertEquals("DataType length is not correct", 2, dType.getScale());  // SCALE
+		
+		
+		// MISSING COMMA
+		content = typeString + " (5  2)";
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+		
+		
+		// INVALID Scale Integer
+		content = typeString + " (5  A)";
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+	}
+	
+	
+    /* ===========================================================================================================================
+     * APPROXIMATE NUMERIC TYPE
+     * ==========================================================================================================================
+     * 
+		<approximate numeric type> ::=
+		      FLOAT [ <left paren> <precision> <right paren> ]
+		    | REAL
+		    | DOUBLE PRECISION
+     */
+	
+	
+	
+	@Test
+	public void shouldParseFLOAT() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_FLOAT);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		content = typeString + " (5)";
+		tokens = getTokens(content);
+		dType = parser.parse(tokens);
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 5, dType.getPrecision());  // PRECISION
+		
+		// ADDED SCALE
+		content = typeString + " (5,  2)";
+		tokens = getTokens(content);
+		dType = null;
+		try {
+			dType = parser.parse(tokens);
+		} catch (ParsingException e) {
+			// Expect exception
+		}
+		Assert.assertNull("DataType should NOT have been found for Type = " + content, dType);
+
+	}
+	
+	@Test
+	public void shouldParseREAL() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_REAL);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+	}
+	
+	
+	@Test
+	public void shouldParseDOUBLE_PRECISION() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_DOUBLE_PRECISION);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+	}
+	
+    /* ===========================================================================================================================
+     * APPROXIMATE NUMERIC TYPE
+     * ==========================================================================================================================
+     * 
+		<datetime type> ::=
+		      DATE
+		    | TIME [ <left paren> <time precision> <right paren> ] [ WITH TIME ZONE ]
+		    | TIMESTAMP [ <left paren> <timestamp precision> <right paren> ] [ WITH TIME ZONE ]
+		    
+		    NOTE:  time precision & timestamp precision is an integer from 0 to 9
+	 */
+	
+	@Test
+	public void shouldParseDATE() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_DATE);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+	}
+	
+	@Test
+	public void shouldParseTIME() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_TIME);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		
+		content = typeString + " (6)";
+		tokens = getTokens(content);
+		dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 6, dType.getPrecision());
+	}
+	
+	@Test
+	public void shouldParseTIMESTAMP() {
+		String typeString = getDataTypeString(DataTypes.DTYPE_TIMESTAMP);
+		String content = typeString;
+		
+		DdlTokenStream tokens = getTokens(content);
+		  
+		DataType dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		
+		
+		content = typeString + " (6)";
+		tokens = getTokens(content);
+		dType = parser.parse(tokens);
+		
+		Assert.assertNotNull("DataType was NOT found for Type = " + typeString, dType);
+		Assert.assertEquals("Wrong DataType found", typeString, dType.getName());
+		Assert.assertEquals("DataType length is not correct", 6, dType.getPrecision());
+	}
+}
\ No newline at end of file


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/datatype/DataTypeParserTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParserTest.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParserTest.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParserTest.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,107 @@
+package org.jboss.dna.sequencer.ddl.dialect;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.List;
+
+import org.jboss.dna.sequencer.ddl.StandardDdlParser;
+import org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParser;
+import org.jboss.dna.sequencer.ddl.statement.Statement;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DerbyDdlParserTest {
+	private StandardDdlParser parser;
+	
+	
+	public static final String DDL_FILE_PATH = "src/test/resources/ddl/";
+	public static final String MM_DDL_FILE_PATH = "src/test/resources/mmddl/";
+	
+	
+	@Before
+	public void beforeEach() {
+		parser = new DerbyDdlParser();
+	}
+	
+	protected String getFileContent(String filePath) {
+		StringBuilder sb = new StringBuilder(1000);
+		
+	    System.out.println("   Getting Content for File = " + filePath);
+		
+	    if(filePath!=null && filePath.length() > 0){
+	        FileReader fr=null;
+	        BufferedReader in=null;
+	        
+	        try{
+	            fr=new FileReader(filePath);
+	            in = new BufferedReader(fr);
+	
+	            int ch = in.read();
+	       
+	            while( ch > -1 ) {
+	            	sb.append((char)ch);
+	            	ch = in.read();
+	            }        
+	        }catch(Exception e){
+	           System.out.print(e);
+	        }
+	        finally{
+	            try{
+	                fr.close();
+	            }catch(java.io.IOException e){}
+	            try{
+	                in.close();
+	            }catch(java.io.IOException e){}
+	
+	        }
+	    }
+		return sb.toString();
+	}
+	
+	
+//	@Test
+//	public void shouldParseDerbyDDL() {
+//	  	String content = getFileContent(DDL_FILE_PATH + "derby_test_create.ddl");
+//	  
+//	  	List<Statement> stmts = parser.parse(content);
+//	  	
+//	  	assertEquals(744, stmts.size());
+//
+//	  	System.out.println("  END PARSING.  # Statements = " + stmts.size());
+//	  
+//	}
+	
+	
+    @Test
+    public void shouldParseDeclareGlobaTemporaryTable() {
+    	System.out.println("TEST:  shouldParseDeclareGlobaTemporaryTable()");
+    	String content = "declare global temporary table SESSION.t1(c11 int) not logged;";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("DECLARE", stmts.get(0).getType());
+    }
+	
+    @Test
+    public void shouldParseAlterTableAlterColumnDefaultRealNumber() {
+    	System.out.println("TEST:  shouldParseAlterTableAlterColumnDefaultRealNumber()");
+    	String content = "ALTER TABLE Employees ALTER COLUMN Salary DEFAULT 1000.0;";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("ALTER TABLE", stmts.get(0).getType());
+    }
+    
+	
+	
+	@Test
+	public void shouldParseDerbyStatements() {
+		//System.out.println("TEST:  shouldParseDerbyStatements()");
+	  	String content = getFileContent(DDL_FILE_PATH + "derby_test_statements.ddl");
+	  	parser.setDoUseTerminator(true);
+	  	List<Statement> stmts = parser.parse(content);
+	  	assertEquals(64, stmts.size());
+	  	System.out.println("  END PARSING.  # Statements = " + stmts.size());
+	  
+	}
+}
\ No newline at end of file


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParserTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/OracleDdlParserTest.java
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/OracleDdlParserTest.java	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/OracleDdlParserTest.java	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,192 @@
+package org.jboss.dna.sequencer.ddl.dialect;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.List;
+
+import org.jboss.dna.sequencer.ddl.DdlConstants;
+import org.jboss.dna.sequencer.ddl.StandardDdlParser;
+import org.jboss.dna.sequencer.ddl.dialect.oracle.OracleDdlParser;
+import org.jboss.dna.sequencer.ddl.statement.Statement;
+import org.junit.Before;
+import org.junit.Test;
+
+public class OracleDdlParserTest {
+	private StandardDdlParser parser;
+	
+
+	public static final String DDL_FILE_PATH = "src/test/resources/ddl/";
+	public static final String MM_DDL_FILE_PATH = "src/test/resources/mmddl/";
+	
+	
+    @Before
+    public void beforeEach() {
+    	parser = new OracleDdlParser();
+    }
+    
+    protected String getFileContent(String filePath) {
+    	StringBuilder sb = new StringBuilder(1000);
+    	
+        System.out.println("   Getting Content for File = " + filePath);
+    	
+        if(filePath!=null && filePath.length() > 0){
+            FileReader fr=null;
+            BufferedReader in=null;
+            
+            try{
+                fr=new FileReader(filePath);
+                in = new BufferedReader(fr);
+
+                int ch = in.read();
+           
+                while( ch > -1 ) {
+                	sb.append((char)ch);
+                	ch = in.read();
+                }        
+            }catch(Exception e){
+               System.out.print(e);
+            }
+            finally{
+                try{
+                    fr.close();
+                }catch(java.io.IOException e){}
+                try{
+                    in.close();
+                }catch(java.io.IOException e){}
+
+            }
+        }
+    	return sb.toString();
+    }
+    
+//	@Test
+//	public void shouldParseOracleDDL() {
+//	  	String content = getFileContent(DDL_FILE_PATH + "oracle_test_create.ddl");
+//	  
+//	  	List<Statement> stmts = parser.parse(content);
+//	  	
+//	  	System.out.println("  END PARSING.  # Statements = " + stmts.size());
+//	  
+//	}
+    
+    @Test
+    public void shouldParseCreateOrReplaceTrigger() {
+    	System.out.println("TEST:  shouldParseCreateOrReplaceTrigger()");
+    	String content = "CREATE OR REPLACE TRIGGER drop_trigger" + DdlConstants.SPACE + 
+    	   "BEFORE DROP ON hr.SCHEMA" + DdlConstants.SPACE + 
+    	   "BEGIN" + DdlConstants.SPACE + 
+    	   		"RAISE_APPLICATION_ERROR ( num => -20000,msg => 'Cannot drop object');" + DdlConstants.SPACE + 
+    	   "END;" + DdlConstants.SPACE +  "/";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	System.out.println("  END PARSING.  # Statements = " + stmts.size());
+    	System.out.println("     VALUE STMT 1 = " + stmts.get(0).getValue());
+    	
+    }
+    
+    @Test 
+    public void shouldParseAnalyze() {
+    	System.out.println("TEST:  shouldParseAnalyze()");
+    	String content = "ANALYZE TABLE customers VALIDATE STRUCTURE ONLINE;";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("ANALYZE", stmts.get(0).getType());
+    }
+    
+    @Test
+    public void shouldParseRollbackToSavepoint() {
+    	System.out.println("TEST:  shouldParseRollbackToSavepoint()");
+    	String content = "ROLLBACK TO SAVEPOINT banda_sal;";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("ROLLBACK TO SAVEPOINT", stmts.get(0).getType());
+    }
+    
+    @Test
+    public void shouldParseAlterTableAddREF() {
+    	System.out.println("TEST:  shouldParseAlterTableAddREF()");
+    	String content = "ALTER TABLE staff ADD (REF(dept) WITH ROWID);";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("ALTER TABLE", stmts.get(0).getType());
+    }
+	
+    @Test
+    public void shouldParseAlterTableADDWithNESTED_TABLE() {
+    	// This is a one-off case where there is a custom datatype (i.e. skill_table_type)
+    	System.out.println("TEST:  shouldParseAlterTableADDWithNESTED_TABLE()");
+    	String content = "ALTER TABLE employees ADD (skills skill_table_type) NESTED TABLE skills STORE AS nested_skill_table;";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("ALTER TABLE", stmts.get(0).getType());
+    }
+    
+    @Test
+    public void shouldParseAlterIndexRename() {
+    	System.out.println("TEST:  shouldParseAlterIndexRename()");
+    	String content = "ALTER INDEX upper_ix RENAME TO upper_name_ix;";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("ALTER INDEX", stmts.get(0).getType());
+    }
+    
+    @Test
+    public void shouldParseAlterIndexMODIFY() {
+    	System.out.println("TEST:  shouldParseAlterIndexMODIFY()");
+    	String content = "ALTER INDEX cost_ix MODIFY PARTITION p3 STORAGE(MAXEXTENTS 30) LOGGING;";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("ALTER INDEX", stmts.get(0).getType());
+    }
+    
+    @Test
+    public void shouldParseAlterIndexDROP() {
+    	System.out.println("TEST:  shouldParseAlterIndexDROP()");
+    	String content = "ALTER INDEX cost_ix DROP PARTITION p1;";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("ALTER INDEX", stmts.get(0).getType());
+    }
+    
+    @Test
+    public void shouldParseAlterIndexTypeADD() {
+    	System.out.println("TEST:  shouldParseAlterIndexTypeADD()");
+    	String content = "ALTER INDEXTYPE position_indextype ADD lob_contains(CLOB, CLOB);";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+    	assertEquals("ALTER INDEXTYPE", stmts.get(0).getType());
+    }
+    
+    @Test
+    public void shouldParseTEMP_TEST() {
+    	System.out.println("TEST:  shouldParseTEMP_TEST()");
+        String content = "COMMENT ON COLUMN employees.job_id IS 'abbreviated job title';";
+    	parser.setDoUseTerminator(true);
+    	List<Statement> stmts = parser.parse(content);
+
+    }
+      
+	@Test
+	public void shouldParseOracleStatements() {
+		System.out.println("TEST:  shouldParseOracleStatements()");
+	  	String content = getFileContent(DDL_FILE_PATH + "oracle_test_statements.ddl");
+	  	parser.setDoUseTerminator(true);
+	  	List<Statement> stmts = parser.parse(content);
+	  	assertEquals(198, stmts.size());
+	  	System.out.println("  END PARSING.  # Statements = " + stmts.size());
+	  
+	}
+	
+//	@Test
+//	public void shouldParseGFM_PhysicalDDL() {
+//	  	String content = getFileContent(DDL_FILE_PATH + "GFM_Physical.ddl");
+//	  	parser.setDoUseTerminator(true);
+//	  	List<Statement> stmts = parser.parse(content);
+//	  	
+//	  	System.out.println("  END PARSING.  # Statements = " + stmts.size());
+//	  
+//	}
+
+}
\ No newline at end of file


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/java/org/jboss/dna/sequencer/ddl/dialect/OracleDdlParserTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/resources/sql2bnf.aug92.txt
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/resources/sql2bnf.aug92.txt	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/resources/sql2bnf.aug92.txt	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,2594 @@
+This file contains a depth-first tree traversal of the BNF
+for the  language done at about 27-AUG-1992 11:03:41.64.
+The specific version of the BNF included here is:  ANSI-only, SQL2-only.
+
+
+<SQL terminal character> ::=
+      <SQL language character>
+    | <SQL embedded language character>
+
+<SQL language character> ::=
+      <simple Latin letter>
+    | <digit>
+    | <SQL special character>
+
+<simple Latin letter> ::=
+      <simple Latin upper case letter>
+    | <simple Latin lower case letter>
+
+<simple Latin upper case letter> ::=
+          A | B | C | D | E | F | G | H | I | J | K | L | M | N | O
+    | P | Q | R | S | T | U | V | W | X | Y | Z
+
+<simple Latin lower case letter> ::=
+          a | b | c | d | e | f | g | h | i | j | k | l | m | n | o
+    | p | q | r | s | t | u | v | w | x | y | z
+
+<digit> ::=
+    0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
+
+<SQL special character> ::=
+      <space>
+    | <double quote>
+    | <percent>
+    | <ampersand>
+    | <quote>
+    | <left paren>
+    | <right paren>
+    | <asterisk>
+    | <plus sign>
+    | <comma>
+    | <minus sign>
+    | <period>
+    | <solidus>
+    | <colon>
+    | <semicolon>
+    | <less than operator>
+    | <equals operator>
+    | <greater than operator>
+    | <question mark>
+    | <underscore>
+    | <vertical bar>
+
+<space> ::= !! <EMPHASIS>(space character in character set in use)
+
+<double quote> ::= "
+
+<percent> ::= %
+
+<ampersand> ::= &
+
+<quote> ::= '
+
+<left paren> ::= (
+
+<right paren> ::= )
+
+<asterisk> ::= *
+
+<plus sign> ::= +
+
+<comma> ::= ,
+
+<minus sign> ::= -
+
+<period> ::= .
+
+<solidus> ::= /
+
+<colon> ::= :
+
+<semicolon> ::= ;
+
+<less than operator> ::= <
+
+<equals operator> ::= =
+
+<greater than operator> ::= >
+
+<question mark> ::= ?
+
+<underscore> ::= _
+
+<vertical bar> ::= |
+
+<SQL embedded language character> ::=
+      <left bracket>
+    | <right bracket>
+
+<left bracket> ::= [
+
+<right bracket> ::= ]
+
+<token> ::=
+      <nondelimiter token>
+    | <delimiter token>
+
+<nondelimiter token> ::=
+      <regular identifier>
+    | <key word>
+    | <unsigned numeric literal>
+    | <national character string literal>
+    | <bit string literal>
+    | <hex string literal>
+
+<regular identifier> ::= <identifier body>
+
+<identifier body> ::=
+    <identifier start> [ { <underscore> | <identifier part> }... ]
+
+<identifier start> ::= <EMPHASIS>(!! See the Syntax Rules)
+
+<identifier part> ::=
+      <identifier start>
+    | <digit>
+
+<key word> ::=
+      <reserved word>
+    | <non-reserved word>
+
+<reserved word> ::=
+      ABSOLUTE | ACTION | ADD | ALL
+    | ALLOCATE | ALTER | AND
+    | ANY | ARE
+    | AS | ASC
+    | ASSERTION | AT
+    | AUTHORIZATION | AVG
+    | BEGIN | BETWEEN | BIT | BIT_LENGTH
+    | BOTH | BY
+    | CASCADE | CASCADED | CASE | CAST
+    | CATALOG
+    | CHAR | CHARACTER | CHAR_LENGTH
+    | CHARACTER_LENGTH | CHECK | CLOSE | COALESCE
+    | COLLATE | COLLATION
+    | COLUMN | COMMIT
+    | CONNECT
+    | CONNECTION | CONSTRAINT
+    | CONSTRAINTS | CONTINUE
+    | CONVERT | CORRESPONDING | COUNT | CREATE | CROSS
+    | CURRENT
+    | CURRENT_DATE | CURRENT_TIME
+    | CURRENT_TIMESTAMP | CURRENT_USER | CURSOR
+    | DATE | DAY | DEALLOCATE | DEC
+    | DECIMAL | DECLARE | DEFAULT | DEFERRABLE
+    | DEFERRED | DELETE | DESC | DESCRIBE | DESCRIPTOR
+    | DIAGNOSTICS
+    | DISCONNECT | DISTINCT | DOMAIN | DOUBLE | DROP
+    | ELSE | END | END-EXEC | ESCAPE
+    | EXCEPT | EXCEPTION
+    | EXEC | EXECUTE | EXISTS
+    | EXTERNAL | EXTRACT
+    | FALSE | FETCH | FIRST | FLOAT | FOR
+    | FOREIGN | FOUND | FROM | FULL
+    | GET | GLOBAL | GO | GOTO
+    | GRANT | GROUP
+    | HAVING | HOUR
+    | IDENTITY | IMMEDIATE | IN | INDICATOR
+    | INITIALLY | INNER | INPUT
+    | INSENSITIVE | INSERT | INT | INTEGER | INTERSECT
+    | INTERVAL | INTO | IS
+    | ISOLATION
+    | JOIN
+    | KEY
+    | LANGUAGE | LAST | LEADING | LEFT
+    | LEVEL | LIKE | LOCAL | LOWER
+    | MATCH | MAX | MIN | MINUTE | MODULE
+    | MONTH
+    | NAMES | NATIONAL | NATURAL | NCHAR | NEXT | NO
+    | NOT | NULL
+    | NULLIF | NUMERIC
+    | OCTET_LENGTH | OF
+    | ON | ONLY | OPEN | OPTION | OR
+    | ORDER | OUTER
+    | OUTPUT | OVERLAPS
+    | PAD | PARTIAL | POSITION | PRECISION | PREPARE
+    | PRESERVE | PRIMARY
+    | PRIOR | PRIVILEGES | PROCEDURE | PUBLIC
+    | READ | REAL | REFERENCES | RELATIVE | RESTRICT
+    | REVOKE | RIGHT
+    | ROLLBACK | ROWS
+    | SCHEMA | SCROLL | SECOND | SECTION
+    | SELECT
+    | SESSION | SESSION_USER | SET
+    | SIZE | SMALLINT | SOME | SPACE | SQL | SQLCODE
+    | SQLERROR | SQLSTATE
+    | SUBSTRING | SUM | SYSTEM_USER
+    | TABLE | TEMPORARY
+    | THEN | TIME | TIMESTAMP
+    | TIMEZONE_HOUR | TIMEZONE_MINUTE
+    | TO | TRAILING | TRANSACTION
+    | TRANSLATE | TRANSLATION | TRIM | TRUE
+    | UNION | UNIQUE | UNKNOWN | UPDATE | UPPER | USAGE
+    | USER | USING
+    | VALUE | VALUES | VARCHAR | VARYING | VIEW
+    | WHEN | WHENEVER | WHERE | WITH | WORK | WRITE
+    | YEAR
+    | ZONE
+
+<non-reserved word> ::=
+    
+      ADA
+    | C | CATALOG_NAME
+    | CHARACTER_SET_CATALOG | CHARACTER_SET_NAME
+    | CHARACTER_SET_SCHEMA | CLASS_ORIGIN | COBOL | COLLATION_CATALOG
+    | COLLATION_NAME | COLLATION_SCHEMA | COLUMN_NAME | COMMAND_FUNCTION
+    | COMMITTED
+    | CONDITION_NUMBER | CONNECTION_NAME | CONSTRAINT_CATALOG | CONSTRAINT_NAME
+    | CONSTRAINT_SCHEMA | CURSOR_NAME
+    | DATA | DATETIME_INTERVAL_CODE
+    | DATETIME_INTERVAL_PRECISION | DYNAMIC_FUNCTION
+    | FORTRAN
+    | LENGTH
+    | MESSAGE_LENGTH | MESSAGE_OCTET_LENGTH | MESSAGE_TEXT | MORE | MUMPS
+    | NAME | NULLABLE | NUMBER
+    | PASCAL | PLI
+    | REPEATABLE | RETURNED_LENGTH | RETURNED_OCTET_LENGTH | RETURNED_SQLSTATE
+    | ROW_COUNT
+    | SCALE | SCHEMA_NAME | SERIALIZABLE | SERVER_NAME | SUBCLASS_ORIGIN
+    | TABLE_NAME | TYPE
+    | UNCOMMITTED | UNNAMED
+
+<unsigned numeric literal> ::=
+      <exact numeric literal>
+    | <approximate numeric literal>
+
+<exact numeric literal> ::=
+      <unsigned integer> [ <period> [ <unsigned integer> ] ]
+    | <period> <unsigned integer>
+
+<unsigned integer> ::= <digit>...
+
+<approximate numeric literal> ::= <mantissa> E <exponent>
+
+<mantissa> ::= <exact numeric literal>
+
+<exponent> ::= <signed integer>
+
+<signed integer> ::= [ <sign> ] <unsigned integer>
+
+<sign> ::= <plus sign> | <minus sign>
+
+<national character string literal> ::=
+    N <quote> [ <character representation>... ] <quote>
+      [ { <separator>... <quote> [ <character representation>... ] <quote> }... ]
+
+<character representation> ::=
+      <nonquote character>
+    | <quote symbol>
+
+<nonquote character> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<quote symbol> ::= <quote><quote>
+
+<separator> ::= { <comment> | <space> | <newline> }...
+
+<comment> ::=
+    <comment introducer> [ <comment character>... ] <newline>
+
+<comment introducer> ::= <minus sign><minus sign>[<minus sign>...]
+
+<comment character> ::=
+      <nonquote character>
+    | <quote>
+
+<newline> ::= !! <EMPHASIS>(implementation-defined end-of-line indicator)
+
+<bit string literal> ::=
+    B <quote> [ <bit>... ] <quote>
+      [ { <separator>... <quote> [ <bit>... ] <quote> }... ]
+
+<bit> ::= 0 | 1
+
+<hex string literal> ::=
+    X <quote> [ <hexit>... ] <quote>
+      [ { <separator>... <quote> [ <hexit>... ] <quote> }... ]
+
+<hexit> ::= <digit> | A | B | C | D | E | F | a | b | c | d | e | f
+
+<delimiter token> ::=
+      <character string literal>
+    | <date string>
+    | <time string>
+    | <timestamp string>
+    | <interval string>
+    | <delimited identifier>
+    | <SQL special character>
+    | <not equals operator>
+    | <greater than or equals operator>
+    | <less than or equals operator>
+    | <concatenation operator>
+    | <double period>
+    | <left bracket>
+    | <right bracket>
+
+<character string literal> ::=
+    [ <introducer><character set specification> ]
+    <quote> [ <character representation>... ] <quote>
+      [ { <separator>... <quote> [ <character representation>... ] <quote> }... ]
+
+<introducer> ::= <underscore>
+
+<character set specification> ::=
+      <standard character repertoire name>
+    | <implementation-defined character repertoire name>
+    | <user-defined character repertoire name>
+    | <standard universal character form-of-use name>
+    | <implementation-defined universal character form-of-use name>
+
+<standard character repertoire name> ::= <character set name>
+
+<character set name> ::= [ <schema name> <period> ]
+      <SQL language identifier>
+
+<schema name> ::=
+    [ <catalog name> <period> ] <unqualified schema name>
+
+<catalog name> ::= <identifier>
+
+<identifier> ::=
+    [ <introducer><character set specification> ] <actual identifier>
+
+<actual identifier> ::=
+      <regular identifier>
+    | <delimited identifier>
+
+<delimited identifier> ::=
+    <double quote> <delimited identifier body> <double quote>
+
+<delimited identifier body> ::= <delimited identifier part>...
+
+<delimited identifier part> ::=
+      <nondoublequote character>
+    | <doublequote symbol>
+
+<nondoublequote character> ::= <EMPHASIS>(!! See the Syntax Rules)
+
+<doublequote symbol> ::= <double quote><double quote>
+
+<unqualified schema name> ::= <identifier>
+
+<SQL language identifier> ::=
+    <SQL language identifier start>
+       [ { <underscore> | <SQL language identifier part> }... ]
+
+<SQL language identifier start> ::= <simple Latin letter>
+
+<SQL language identifier part> ::=
+      <simple Latin letter>
+    | <digit>
+
+<implementation-defined character repertoire name> ::=
+    <character set name>
+
+<user-defined character repertoire name> ::= <character set name>
+
+<standard universal character form-of-use name> ::=
+    <character set name>
+
+<implementation-defined universal character form-of-use name> ::=
+    <character set name>
+
+<date string> ::=
+    <quote> <date value> <quote>
+
+<date value> ::=
+    <years value> <minus sign> <months value>
+        <minus sign> <days value>
+
+<years value> ::= <datetime value>
+
+<datetime value> ::= <unsigned integer>
+
+<months value> ::= <datetime value>
+
+<days value> ::= <datetime value>
+
+<time string> ::=
+    <quote> <time value> [ <time zone interval> ] <quote>
+
+<time value> ::=
+    <hours value> <colon> <minutes value> <colon> <seconds value>
+
+<hours value> ::= <datetime value>
+
+<minutes value> ::= <datetime value>
+
+<seconds value> ::=
+      <seconds integer value> [ <period> [ <seconds fraction> ] ]
+
+<seconds integer value> ::= <unsigned integer>
+
+<seconds fraction> ::= <unsigned integer>
+
+<time zone interval> ::=
+    <sign> <hours value> <colon> <minutes value>
+
+<timestamp string> ::=
+    <quote> <date value> <space> <time value>
+        [ <time zone interval> ] <quote>
+
+<interval string> ::=
+    <quote> { <year-month literal> | <day-time literal> } <quote>
+
+<year-month literal> ::=
+      <years value>
+    | [ <years value> <minus sign> ] <months value>
+
+<day-time literal> ::=
+      <day-time interval>
+    | <time interval>
+
+<day-time interval> ::=
+    <days value>
+      [ <space> <hours value> [ <colon> <minutes value>
+        [ <colon> <seconds value> ] ] ]
+
+<time interval> ::=
+      <hours value> [ <colon> <minutes value> [ <colon> <seconds value> ] ]
+    | <minutes value> [ <colon> <seconds value> ]
+    | <seconds value>
+
+<not equals operator> ::= <>
+
+<greater than or equals operator> ::= >=
+
+<less than or equals operator> ::= <=
+
+<concatenation operator> ::= ||
+
+<double period> ::= ..
+
+<module> ::=
+    <module name clause>
+    <language clause>
+    <module authorization clause>
+    [ <temporary table declaration>... ]
+    <module contents>...
+
+<module name clause> ::=
+    MODULE [ <module name> ]
+      [ <module character set specification> ]
+
+<module name> ::= <identifier>
+
+<module character set specification> ::=
+    NAMES ARE <character set specification>
+
+<language clause> ::=
+    LANGUAGE <language name>
+
+<language name> ::=
+    ADA | C | COBOL | FORTRAN | MUMPS | PASCAL | PLI
+
+<module authorization clause> ::=
+      SCHEMA <schema name>
+    | AUTHORIZATION <module authorization identifier>
+    | SCHEMA <schema name>
+          AUTHORIZATION <module authorization identifier>
+
+<module authorization identifier> ::=
+    <authorization identifier>
+
+<authorization identifier> ::= <identifier>
+
+<temporary table declaration> ::=
+    DECLARE LOCAL TEMPORARY TABLE
+        <qualified local table name>
+      <table element list>
+      [ ON COMMIT { PRESERVE | DELETE } ROWS ]
+
+<qualified local table name> ::=
+    MODULE <period> <local table name>
+
+<local table name> ::= <qualified identifier>
+
+<qualified identifier> ::= <identifier>
+
+<table element list> ::=
+      <left paren> <table element> [ { <comma> <table element> }... ] <right paren>
+
+<table element> ::=
+      <column definition>
+    | <table constraint definition>
+
+<column definition> ::=
+    <column name> { <data type> | <domain name> }
+    [ <default clause> ]
+    [ <column constraint definition>... ]
+    [ <collate clause> ]
+
+<column name> ::= <identifier>
+
+<data type> ::=
+      <character string type>
+           [ CHARACTER SET <character set specification> ]
+    | <national character string type>
+    | <bit string type>
+    | <numeric type>
+    | <datetime type>
+    | <interval type>
+
+<character string type> ::=
+      CHARACTER [ <left paren> <length> <right paren> ]
+    | CHAR [ <left paren> <length> <right paren> ]
+    | CHARACTER VARYING <left paren> <length> <right paren>
+    | CHAR VARYING <left paren> <length> <right paren>
+    | VARCHAR <left paren> <length> <right paren>
+
+<length> ::= <unsigned integer>
+
+<national character string type> ::=
+      NATIONAL CHARACTER [ <left paren> <length> <right paren> ]
+    | NATIONAL CHAR [ <left paren> <length> <right paren> ]
+    | NCHAR [ <left paren> <length> <right paren> ]
+    | NATIONAL CHARACTER VARYING <left paren> <length> <right paren>
+    | NATIONAL CHAR VARYING <left paren> <length> <right paren>
+    | NCHAR VARYING <left paren> <length> <right paren>
+
+<bit string type> ::=
+      BIT [ <left paren> <length> <right paren> ]
+    | BIT VARYING <left paren> <length> <right paren>
+
+<numeric type> ::=
+      <exact numeric type>
+    | <approximate numeric type>
+
+<exact numeric type> ::=
+      NUMERIC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | DECIMAL [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | DEC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | INTEGER
+    | INT
+    | SMALLINT
+
+<precision> ::= <unsigned integer>
+
+<scale> ::= <unsigned integer>
+
+<approximate numeric type> ::=
+      FLOAT [ <left paren> <precision> <right paren> ]
+    | REAL
+    | DOUBLE PRECISION
+
+<datetime type> ::=
+      DATE
+    | TIME [ <left paren> <time precision> <right paren> ]
+          [ WITH TIME ZONE ]
+    | TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+          [ WITH TIME ZONE ]
+
+<time precision> ::= <time fractional seconds precision>
+
+<time fractional seconds precision> ::= <unsigned integer>
+
+<timestamp precision> ::= <time fractional seconds precision>
+
+<interval type> ::= INTERVAL <interval qualifier>
+
+<interval qualifier> ::=
+      <start field> TO <end field>
+    | <single datetime field>
+
+<start field> ::=
+    <non-second datetime field>
+        [ <left paren> <interval leading field precision> <right paren> ]
+
+<non-second datetime field> ::= YEAR | MONTH | DAY | HOUR
+    | MINUTE
+
+<interval leading field precision> ::= <unsigned integer>
+
+<end field> ::=
+      <non-second datetime field>
+    | SECOND [ <left paren> <interval fractional seconds precision> <right paren> ]
+
+<interval fractional seconds precision> ::= <unsigned integer>
+
+<single datetime field> ::=
+      <non-second datetime field>
+          [ <left paren> <interval leading field precision> <right paren> ]
+    | SECOND [ <left paren> <interval leading field precision>
+          [ <comma> <interval fractional seconds precision> ] <right paren> ]
+
+<domain name> ::= <qualified name>
+
+<qualified name> ::=
+    [ <schema name> <period> ] <qualified identifier>
+
+<default clause> ::=
+      DEFAULT <default option>
+
+<default option> ::=
+      <literal>
+    | <datetime value function>
+    | USER
+    | CURRENT_USER
+    | SESSION_USER
+    | SYSTEM_USER
+    | NULL
+
+<literal> ::=
+      <signed numeric literal>
+    | <general literal>
+
+<signed numeric literal> ::=
+    [ <sign> ] <unsigned numeric literal>
+
+<general literal> ::=
+      <character string literal>
+    | <national character string literal>
+    | <bit string literal>
+    | <hex string literal>
+    | <datetime literal>
+    | <interval literal>
+
+<datetime literal> ::=
+      <date literal>
+    | <time literal>
+    | <timestamp literal>
+
+<date literal> ::=
+    DATE <date string>
+
+<time literal> ::=
+    TIME <time string>
+
+<timestamp literal> ::=
+    TIMESTAMP <timestamp string>
+
+<interval literal> ::=
+    INTERVAL [ <sign> ] <interval string> <interval qualifier>
+
+<datetime value function> ::=
+      <current date value function>
+    | <current time value function>
+    | <current timestamp value function>
+
+<current date value function> ::= CURRENT_DATE
+
+<current time value function> ::=
+      CURRENT_TIME [ <left paren> <time precision> <right paren> ]
+
+<current timestamp value function> ::=
+      CURRENT_TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+
+<column constraint definition> ::=
+    [ <constraint name definition> ]
+    <column constraint>
+      [ <constraint attributes> ]
+
+<constraint name definition> ::= CONSTRAINT <constraint name>
+
+<constraint name> ::= <qualified name>
+
+<column constraint> ::=
+      NOT NULL
+    | <unique specification>
+    | <references specification>
+    | <check constraint definition>
+
+<unique specification> ::=
+    UNIQUE | PRIMARY KEY
+
+<references specification> ::=
+    REFERENCES <referenced table and columns>
+      [ MATCH <match type> ]
+      [ <referential triggered action> ]
+
+<referenced table and columns> ::=
+     <table name> [ <left paren> <reference column list> <right paren> ]
+
+<table name> ::=
+      <qualified name>
+    | <qualified local table name>
+
+<reference column list> ::= <column name list>
+
+<column name list> ::=
+    <column name> [ { <comma> <column name> }... ]
+
+<match type> ::=
+      FULL
+    | PARTIAL
+
+<referential triggered action> ::=
+      <update rule> [ <delete rule> ]
+    | <delete rule> [ <update rule> ]
+
+<update rule> ::= ON UPDATE <referential action>
+
+<referential action> ::=
+      CASCADE
+    | SET NULL
+    | SET DEFAULT
+    | NO ACTION
+
+<delete rule> ::= ON DELETE <referential action>
+
+<check constraint definition> ::=
+    CHECK
+        <left paren> <search condition> <right paren>
+
+<search condition> ::=
+      <boolean term>
+    | <search condition> OR <boolean term>
+
+<boolean term> ::=
+      <boolean factor>
+    | <boolean term> AND <boolean factor>
+
+<boolean factor> ::=
+    [ NOT ] <boolean test>
+
+<boolean test> ::=
+    <boolean primary> [ IS [ NOT ]
+          <truth value> ]
+
+<boolean primary> ::=
+      <predicate>
+    | <left paren> <search condition> <right paren>
+
+<predicate> ::=
+      <comparison predicate>
+    | <between predicate>
+    | <in predicate>
+    | <like predicate>
+    | <null predicate>
+    | <quantified comparison predicate>
+    | <exists predicate>
+    | <unique predicate>
+    | <match predicate>
+    | <overlaps predicate>
+
+<comparison predicate> ::=
+    <row value constructor> <comp op>
+        <row value constructor>
+
+<row value constructor> ::=
+       <row value constructor element>
+    | <left paren> <row value constructor list> <right paren>
+    | <row subquery>
+
+<row value constructor element> ::=
+      <value expression>
+    | <null specification>
+    | <default specification>
+
+<value expression> ::=
+      <numeric value expression>
+    | <string value expression>
+    | <datetime value expression>
+    | <interval value expression>
+
+<numeric value expression> ::=
+      <term>
+    | <numeric value expression> <plus sign> <term>
+    | <numeric value expression> <minus sign> <term>
+
+<term> ::=
+      <factor>
+    | <term> <asterisk> <factor>
+    | <term> <solidus> <factor>
+
+<factor> ::=
+    [ <sign> ] <numeric primary>
+
+<numeric primary> ::=
+      <value expression primary>
+    | <numeric value function>
+
+<value expression primary> ::=
+      <unsigned value specification>
+    | <column reference>
+    | <set function specification>
+    | <scalar subquery>
+    | <case expression>
+    | <left paren> <value expression> <right paren>
+    | <cast specification>
+
+<unsigned value specification> ::=
+      <unsigned literal>
+    | <general value specification>
+
+<unsigned literal> ::=
+      <unsigned numeric literal>
+    | <general literal>
+
+<general value specification> ::=
+      <parameter specification>
+    | <dynamic parameter specification>
+    | <variable specification>
+    | USER
+    | CURRENT_USER
+    | SESSION_USER
+    | SYSTEM_USER
+    | VALUE
+
+<parameter specification> ::=
+    <parameter name> [ <indicator parameter> ]
+
+<parameter name> ::= <colon> <identifier>
+
+<indicator parameter> ::=
+    [ INDICATOR ] <parameter name>
+
+<dynamic parameter specification> ::= <question mark>
+
+<variable specification> ::=
+    <embedded variable name> [ <indicator variable> ]
+
+<embedded variable name> ::=
+    <colon><host identifier>
+
+<host identifier> ::=
+      <Ada host identifier>
+    | <C host identifier>
+    | <COBOL host identifier>
+    | <Fortran host identifier>
+    | <MUMPS host identifier>
+    | <Pascal host identifier>
+    | <PL/I host identifier>
+
+<Ada host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<C host identifier> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<COBOL host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<Fortran host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<MUMPS host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<Pascal host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<PL/I host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<indicator variable> ::=
+    [ INDICATOR ] <embedded variable name>
+
+<column reference> ::= [ <qualifier> <period> ] <column name>
+
+<qualifier> ::=
+      <table name>
+    | <correlation name>
+
+<correlation name> ::= <identifier>
+
+<set function specification> ::=
+      COUNT <left paren> <asterisk> <right paren>
+    | <general set function>
+
+<general set function> ::=
+      <set function type>
+          <left paren> [ <set quantifier> ] <value expression> <right paren>
+
+<set function type> ::=
+    AVG | MAX | MIN | SUM | COUNT
+
+<set quantifier> ::= DISTINCT | ALL
+
+<scalar subquery> ::= <subquery>
+
+<subquery> ::= <left paren> <query expression> <right paren>
+
+<query expression> ::=
+      <non-join query expression>
+    | <joined table>
+
+<non-join query expression> ::=
+      <non-join query term>
+    | <query expression> UNION  [ ALL ]
+          [ <corresponding spec> ] <query term>
+    | <query expression> EXCEPT [ ALL ]
+          [ <corresponding spec> ] <query term>
+
+<non-join query term> ::=
+      <non-join query primary>
+    | <query term> INTERSECT [ ALL ]
+          [ <corresponding spec> ] <query primary>
+
+<non-join query primary> ::=
+      <simple table>
+    | <left paren> <non-join query expression> <right paren>
+
+<simple table> ::=
+      <query specification>
+    | <table value constructor>
+    | <explicit table>
+
+<query specification> ::=
+    SELECT [ <set quantifier> ] <select list> <table expression>
+
+<select list> ::=
+      <asterisk>
+    | <select sublist> [ { <comma> <select sublist> }... ]
+
+<select sublist> ::=
+      <derived column>
+    | <qualifier> <period> <asterisk>
+
+<derived column> ::= <value expression> [ <as clause> ]
+
+<as clause> ::= [ AS ] <column name>
+
+<table expression> ::=
+    <from clause>
+    [ <where clause> ]
+    [ <group by clause> ]
+    [ <having clause> ]
+
+<from clause> ::= FROM <table reference>
+    [ { <comma> <table reference> }... ]
+
+<table reference> ::=
+      <table name> [ [ AS ] <correlation name>
+          [ <left paren> <derived column list> <right paren> ] ]
+    | <derived table> [ AS ] <correlation name>
+          [ <left paren> <derived column list> <right paren> ]
+    | <joined table>
+
+<derived column list> ::= <column name list>
+
+<derived table> ::= <table subquery>
+
+<table subquery> ::= <subquery>
+
+<joined table> ::=
+      <cross join>
+    | <qualified join>
+    | <left paren> <joined table> <right paren>
+
+<cross join> ::=
+    <table reference> CROSS JOIN <table reference>
+
+<qualified join> ::=
+    <table reference> [ NATURAL ] [ <join type> ] JOIN
+      <table reference> [ <join specification> ]
+
+<join type> ::=
+      INNER
+    | <outer join type> [ OUTER ]
+    | UNION
+
+<outer join type> ::=
+      LEFT
+    | RIGHT
+    | FULL
+
+<join specification> ::=
+      <join condition>
+    | <named columns join>
+
+<join condition> ::= ON <search condition>
+
+<named columns join> ::=
+    USING <left paren> <join column list> <right paren>
+
+<join column list> ::= <column name list>
+
+<where clause> ::= WHERE <search condition>
+
+<group by clause> ::=
+    GROUP BY <grouping column reference list>
+
+<grouping column reference list> ::=
+    <grouping column reference>
+        [ { <comma> <grouping column reference> }... ]
+
+<grouping column reference> ::=
+    <column reference> [ <collate clause> ]
+
+<collate clause> ::= COLLATE <collation name>
+
+<collation name> ::= <qualified name>
+
+<having clause> ::= HAVING <search condition>
+
+<table value constructor> ::=
+    VALUES <table value constructor list>
+
+<table value constructor list> ::=
+    <row value constructor> [ { <comma> <row value constructor> }... ]
+
+<explicit table> ::= TABLE <table name>
+
+<query term> ::=
+      <non-join query term>
+    | <joined table>
+
+<corresponding spec> ::=
+    CORRESPONDING [ BY <left paren> <corresponding column list> <right paren> ]
+
+<corresponding column list> ::= <column name list>
+
+<query primary> ::=
+      <non-join query primary>
+    | <joined table>
+
+<case expression> ::=
+      <case abbreviation>
+    | <case specification>
+
+<case abbreviation> ::=
+      NULLIF <left paren> <value expression> <comma>
+            <value expression> <right paren>
+    | COALESCE <left paren> <value expression>
+            { <comma> <value expression> }... <right paren>
+
+<case specification> ::=
+      <simple case>
+    | <searched case>
+
+<simple case> ::=
+    CASE <case operand>
+      <simple when clause>...
+      [ <else clause> ]
+    END
+
+<case operand> ::= <value expression>
+
+<simple when clause> ::= WHEN <when operand> THEN <result>
+
+<when operand> ::= <value expression>
+
+<result> ::= <result expression> | NULL
+
+<result expression> ::= <value expression>
+
+<else clause> ::= ELSE <result>
+
+<searched case> ::=
+    CASE
+      <searched when clause>...
+      [ <else clause> ]
+    END
+
+<searched when clause> ::= WHEN <search condition> THEN <result>
+
+<cast specification> ::=
+    CAST <left paren> <cast operand> AS
+        <cast target> <right paren>
+
+<cast operand> ::=
+      <value expression>
+    | NULL
+
+<cast target> ::=
+      <domain name>
+    | <data type>
+
+<numeric value function> ::=
+      <position expression>
+    | <extract expression>
+    | <length expression>
+
+<position expression> ::=
+    POSITION <left paren> <character value expression>
+        IN <character value expression> <right paren>
+
+<character value expression> ::=
+      <concatenation>
+    | <character factor>
+
+<concatenation> ::=
+    <character value expression> <concatenation operator>
+        <character factor>
+
+<character factor> ::=
+    <character primary> [ <collate clause> ]
+
+<character primary> ::=
+      <value expression primary>
+    | <string value function>
+
+<string value function> ::=
+      <character value function>
+    | <bit value function>
+
+<character value function> ::=
+      <character substring function>
+    | <fold>
+    | <form-of-use conversion>
+    | <character translation>
+    | <trim function>
+
+<character substring function> ::=
+    SUBSTRING <left paren> <character value expression> FROM <start position>
+                [ FOR <string length> ] <right paren>
+
+<start position> ::= <numeric value expression>
+
+<string length> ::= <numeric value expression>
+
+<fold> ::= { UPPER | LOWER }
+     <left paren> <character value expression> <right paren>
+
+<form-of-use conversion> ::=
+    CONVERT <left paren> <character value expression>
+        USING <form-of-use conversion name> <right paren>
+
+<form-of-use conversion name> ::= <qualified name>
+
+<character translation> ::=
+    TRANSLATE <left paren> <character value expression>
+        USING <translation name> <right paren>
+
+<translation name> ::= <qualified name>
+
+<trim function> ::=
+    TRIM <left paren> <trim operands> <right paren>
+
+<trim operands> ::=
+    [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
+
+<trim specification> ::=
+      LEADING
+    | TRAILING
+    | BOTH
+
+<trim character> ::= <character value expression>
+
+<trim source> ::= <character value expression>
+
+<bit value function> ::=
+    <bit substring function>
+
+<bit substring function> ::=
+    SUBSTRING <left paren> <bit value expression> FROM <start position>
+        [ FOR <string length> ] <right paren>
+
+<bit value expression> ::=
+      <bit concatenation>
+    | <bit factor>
+
+<bit concatenation> ::=
+    <bit value expression> <concatenation operator> <bit factor>
+
+<bit factor> ::= <bit primary>
+
+<bit primary> ::=
+      <value expression primary>
+    | <string value function>
+
+<extract expression> ::=
+    EXTRACT <left paren> <extract field>
+        FROM <extract source> <right paren>
+
+<extract field> ::=
+      <datetime field>
+    | <time zone field>
+
+<datetime field> ::=
+      <non-second datetime field>
+    | SECOND
+
+<time zone field> ::=
+      TIMEZONE_HOUR
+    | TIMEZONE_MINUTE
+
+<extract source> ::=
+      <datetime value expression>
+    | <interval value expression>
+
+<datetime value expression> ::=
+      <datetime term>
+    | <interval value expression> <plus sign> <datetime term>
+    | <datetime value expression> <plus sign> <interval term>
+    | <datetime value expression> <minus sign> <interval term>
+
+<interval term> ::=
+      <interval factor>
+    | <interval term 2> <asterisk> <factor>
+    | <interval term 2> <solidus> <factor>
+    | <term> <asterisk> <interval factor>
+
+<interval factor> ::=
+    [ <sign> ] <interval primary>
+
+<interval primary> ::=
+      <value expression primary> [ <interval qualifier> ]
+
+<interval term 2> ::= <interval term>
+
+<interval value expression> ::=
+      <interval term>
+    | <interval value expression 1> <plus sign> <interval term 1>
+    | <interval value expression 1> <minus sign> <interval term 1>
+    | <left paren> <datetime value expression> <minus sign>
+          <datetime term> <right paren> <interval qualifier>
+
+<interval value expression 1> ::= <interval value expression>
+
+<interval term 1> ::= <interval term>
+
+<datetime term> ::=
+      <datetime factor>
+
+<datetime factor> ::=
+      <datetime primary> [ <time zone> ]
+
+<datetime primary> ::=
+      <value expression primary>
+    | <datetime value function>
+
+<time zone> ::=
+    AT <time zone specifier>
+
+<time zone specifier> ::=
+      LOCAL
+    | TIME ZONE <interval value expression>
+
+<length expression> ::=
+      <char length expression>
+    | <octet length expression>
+    | <bit length expression>
+
+<char length expression> ::=
+    { CHAR_LENGTH | CHARACTER_LENGTH }
+        <left paren> <string value expression> <right paren>
+
+<string value expression> ::=
+      <character value expression>
+    | <bit value expression>
+
+<octet length expression> ::=
+    OCTET_LENGTH <left paren> <string value expression> <right paren>
+
+<bit length expression> ::=
+    BIT_LENGTH <left paren> <string value expression> <right paren>
+
+<null specification> ::=
+    NULL
+
+<default specification> ::=
+    DEFAULT
+
+<row value constructor list> ::=
+    <row value constructor element>
+        [ { <comma> <row value constructor element> }... ]
+
+<row subquery> ::= <subquery>
+
+<comp op> ::=
+      <equals operator>
+    | <not equals operator>
+    | <less than operator>
+    | <greater than operator>
+    | <less than or equals operator>
+    | <greater than or equals operator>
+
+<between predicate> ::=
+    <row value constructor> [ NOT ] BETWEEN
+      <row value constructor> AND <row value constructor>
+
+<in predicate> ::=
+    <row value constructor>
+      [ NOT ] IN <in predicate value>
+
+<in predicate value> ::=
+      <table subquery>
+    | <left paren> <in value list> <right paren>
+
+<in value list> ::=
+    <value expression> { <comma> <value expression> }...
+
+<like predicate> ::=
+    <match value> [ NOT ] LIKE <pattern>
+      [ ESCAPE <escape character> ]
+
+<match value> ::= <character value expression>
+
+<pattern> ::= <character value expression>
+
+<escape character> ::= <character value expression>
+
+<null predicate> ::= <row value constructor>
+    IS [ NOT ] NULL
+
+<quantified comparison predicate> ::=
+    <row value constructor> <comp op> <quantifier> <table subquery>
+
+<quantifier> ::= <all> | <some>
+
+<all> ::= ALL
+
+<some> ::= SOME | ANY
+
+<exists predicate> ::= EXISTS <table subquery>
+
+<unique predicate> ::= UNIQUE <table subquery>
+
+<match predicate> ::=
+    <row value constructor> MATCH [ UNIQUE ]
+        [ PARTIAL | FULL ] <table subquery>
+
+<overlaps predicate> ::=
+    <row value constructor 1> OVERLAPS <row value constructor 2>
+
+<row value constructor 1> ::= <row value constructor>
+
+<row value constructor 2> ::= <row value constructor>
+
+<truth value> ::=
+      TRUE
+    | FALSE
+    | UNKNOWN
+
+<constraint attributes> ::=
+      <constraint check time> [ [ NOT ] DEFERRABLE ]
+    | [ NOT ] DEFERRABLE [ <constraint check time> ]
+
+<constraint check time> ::=
+      INITIALLY DEFERRED
+    | INITIALLY IMMEDIATE
+
+<table constraint definition> ::=
+    [ <constraint name definition> ]
+    <table constraint> [ <constraint attributes> ]
+
+<table constraint> ::=
+      <unique constraint definition>
+    | <referential constraint definition>
+    | <check constraint definition>
+
+<unique constraint definition> ::=
+            <unique specification> even in SQL3)
+    <unique specification>
+      <left paren> <unique column list> <right paren>
+
+<unique column list> ::= <column name list>
+
+<referential constraint definition> ::=
+    FOREIGN KEY
+        <left paren> <referencing columns> <right paren>
+      <references specification>
+
+<referencing columns> ::=
+    <reference column list>
+
+<module contents> ::=
+      <declare cursor>
+    | <dynamic declare cursor>
+    | <procedure>
+
+<declare cursor> ::=
+    DECLARE <cursor name> [ INSENSITIVE ] [ SCROLL ] CURSOR
+      FOR <cursor specification>
+
+<cursor name> ::= <identifier>
+
+<cursor specification> ::=
+    <query expression> [ <order by clause> ]
+      [ <updatability clause> ]
+
+<order by clause> ::=
+    ORDER BY <sort specification list>
+
+<sort specification list> ::=
+    <sort specification> [ { <comma> <sort specification> }... ]
+
+<sort specification> ::=
+    <sort key> [ <collate clause> ] [ <ordering specification> ]
+
+<sort key> ::=
+      <column name>
+    | <unsigned integer>
+
+<ordering specification> ::= ASC | DESC
+
+<updatability clause> ::=
+    FOR
+        { READ ONLY |
+          UPDATE [ OF <column name list> ] }
+
+<dynamic declare cursor> ::=
+    DECLARE <cursor name> [ INSENSITIVE ] [ SCROLL ] CURSOR
+        FOR <statement name>
+
+<statement name> ::= <identifier>
+
+<procedure> ::=
+    PROCEDURE <procedure name>
+        <parameter declaration list> <semicolon>
+      <SQL procedure statement> <semicolon>
+
+<procedure name> ::= <identifier>
+
+<parameter declaration list> ::=
+      <left paren> <parameter declaration>
+          [ { <comma> <parameter declaration> }... ] <right paren>
+    | <parameter declaration>...
+
+<parameter declaration> ::=
+      <parameter name> <data type>
+    | <status parameter>
+
+<status parameter> ::=
+    SQLCODE | SQLSTATE
+
+<SQL procedure statement> ::=
+      <SQL schema statement>
+    | <SQL data statement>
+    | <SQL transaction statement>
+    | <SQL connection statement>
+    | <SQL session statement>
+    | <SQL dynamic statement>
+    | <SQL diagnostics statement>
+
+<SQL schema statement> ::=
+      <SQL schema definition statement>
+    | <SQL schema manipulation statement>
+
+<SQL schema definition statement> ::=
+      <schema definition>
+    | <table definition>
+    | <view definition>
+    | <grant statement>
+    | <domain definition>
+    | <character set definition>
+    | <collation definition>
+    | <translation definition>
+    | <assertion definition>
+
+<schema definition> ::=
+    CREATE SCHEMA <schema name clause>
+      [ <schema character set specification> ]
+      [ <schema element>... ]
+
+<schema name clause> ::=
+      <schema name>
+    | AUTHORIZATION <schema authorization identifier>
+    | <schema name> AUTHORIZATION
+          <schema authorization identifier>
+
+<schema authorization identifier> ::=
+    <authorization identifier>
+
+<schema character set specification> ::=
+    DEFAULT CHARACTER
+        SET <character set specification>
+
+<schema element> ::=
+      <domain definition>
+    | <table definition>
+    | <view definition>
+    | <grant statement>
+    | <assertion definition>
+    | <character set definition>
+    | <collation definition>
+    | <translation definition>
+
+<domain definition> ::=
+    CREATE DOMAIN <domain name>
+        [ AS ] <data type>
+      [ <default clause> ]
+      [ <domain constraint>... ]
+      [ <collate clause> ]
+
+<domain constraint> ::=
+    [ <constraint name definition> ]
+    <check constraint definition> [ <constraint attributes> ]
+
+<table definition> ::=
+    CREATE [ { GLOBAL | LOCAL } TEMPORARY ] TABLE
+        <table name>
+      <table element list>
+      [ ON COMMIT { DELETE | PRESERVE } ROWS ]
+
+<view definition> ::=
+    CREATE VIEW <table name> [ <left paren> <view column list>
+                                  <right paren> ]
+      AS <query expression>
+      [ WITH [ <levels clause> ] CHECK OPTION ]
+
+<view column list> ::= <column name list>
+
+<levels clause> ::=
+    CASCADED | LOCAL
+
+<grant statement> ::=
+   GRANT <privileges> ON <object name>
+     TO <grantee> [ { <comma> <grantee> }... ]
+       [ WITH GRANT OPTION ]
+
+<privileges> ::=
+      ALL PRIVILEGES
+    | <action list>
+
+<action list> ::= <action> [ { <comma> <action> }... ]
+
+<action> ::=
+      SELECT
+    | DELETE
+    | INSERT [ <left paren> <privilege column list> <right paren> ]
+    | UPDATE [ <left paren> <privilege column list> <right paren> ]
+    | REFERENCES [ <left paren> <privilege column list> <right paren> ]
+    | USAGE
+
+<privilege column list> ::= <column name list>
+
+<object name> ::=
+      [ TABLE ] <table name>
+    | DOMAIN <domain name>
+    | COLLATION <collation name>
+    | CHARACTER SET <character set name>
+    | TRANSLATION <translation name>
+
+<grantee> ::=
+      PUBLIC
+    | <authorization identifier>
+
+<assertion definition> ::=
+    CREATE ASSERTION <constraint name> <assertion check>
+      [ <constraint attributes> ]
+
+<assertion check> ::=
+    CHECK
+        <left paren> <search condition> <right paren>
+
+<character set definition> ::=
+    CREATE CHARACTER SET <character set name>
+        [ AS ]
+      <character set source>
+      [ <collate clause> | <limited collation definition> ]
+
+<character set source> ::=
+      GET <existing character set name>
+
+<existing character set name> ::=
+      <standard character repertoire name>
+    | <implementation-defined character repertoire name>
+    | <schema character set name>
+
+<schema character set name> ::= <character set name>
+
+<limited collation definition> ::=
+    COLLATION FROM <collation source>
+
+<collation source> ::=
+      <collating sequence definition>
+    | <translation collation>
+
+<collating sequence definition> ::=
+      <external collation>
+    | <schema collation name>
+    | DESC <left paren> <collation name> <right paren>
+    | DEFAULT
+
+<external collation> ::=
+    EXTERNAL <left paren> <quote> <external collation name> <quote> <right paren>
+
+<external collation name> ::=
+      <standard collation name>
+    | <implementation-defined collation name>
+
+<standard collation name> ::= <collation name>
+
+<implementation-defined collation name> ::= <collation name>
+
+<schema collation name> ::= <collation name>
+
+<translation collation> ::=
+    TRANSLATION <translation name>
+        [ THEN COLLATION <collation name> ]
+
+<collation definition> ::=
+    CREATE COLLATION <collation name> FOR
+        <character set specification>
+      FROM <collation source>
+        [ <pad attribute> ]
+
+<pad attribute> ::=
+      NO PAD
+    | PAD SPACE
+
+<translation definition> ::=
+    CREATE TRANSLATION <translation name>
+      FOR <source character set specification>
+        TO <target character set specification>
+      FROM <translation source>
+
+<source character set specification> ::= <character set specification>
+
+<target character set specification> ::= <character set specification>
+
+<translation source> ::=
+      <translation specification>
+
+<translation specification> ::=
+      <external translation>
+    | IDENTITY
+    | <schema translation name>
+
+<external translation> ::=
+    EXTERNAL <left paren> <quote> <external translation name> <quote> <right paren>
+
+<external translation name> ::=
+      <standard translation name>
+    | <implementation-defined translation name>
+
+<standard translation name> ::= <translation name>
+
+<implementation-defined translation name> ::= <translation name>
+
+<schema translation name> ::= <translation name>
+
+<SQL schema manipulation statement> ::=
+      <drop schema statement>
+    | <alter table statement>
+    | <drop table statement>
+    | <drop view statement>
+    | <revoke statement>
+    | <alter domain statement>
+    | <drop domain statement>
+    | <drop character set statement>
+    | <drop collation statement>
+    | <drop translation statement>
+    | <drop assertion statement>
+
+<drop schema statement> ::=
+    DROP SCHEMA <schema name> <drop behavior>
+
+<drop behavior> ::= CASCADE | RESTRICT
+
+<alter table statement> ::=
+    ALTER TABLE <table name> <alter table action>
+
+<alter table action> ::=
+      <add column definition>
+    | <alter column definition>
+    | <drop column definition>
+    | <add table constraint definition>
+    | <drop table constraint definition>
+
+<add column definition> ::=
+    ADD [ COLUMN ] <column definition>
+
+<alter column definition> ::=
+    ALTER [ COLUMN ] <column name> <alter column action>
+
+<alter column action> ::=
+      <set column default clause>
+    | <drop column default clause>
+
+<set column default clause> ::=
+    SET <default clause>
+
+<drop column default clause> ::=
+    DROP DEFAULT
+
+<drop column definition> ::=
+    DROP [ COLUMN ] <column name> <drop behavior>
+
+<add table constraint definition> ::=
+    ADD <table constraint definition>
+
+<drop table constraint definition> ::=
+    DROP CONSTRAINT <constraint name> <drop behavior>
+
+<drop table statement> ::=
+    DROP TABLE <table name> <drop behavior>
+
+<drop view statement> ::=
+    DROP VIEW <table name> <drop behavior>
+
+<revoke statement> ::=
+    REVOKE [ GRANT OPTION FOR ]
+        <privileges>
+        ON <object name>
+      FROM <grantee> [ { <comma> <grantee> }... ] <drop behavior>
+
+<alter domain statement> ::=
+    ALTER DOMAIN <domain name> <alter domain action>
+
+<alter domain action> ::=
+      <set domain default clause>
+    | <drop domain default clause>
+    | <add domain constraint definition>
+    | <drop domain constraint definition>
+
+<set domain default clause> ::= SET <default clause>
+
+<drop domain default clause> ::= DROP DEFAULT
+
+<add domain constraint definition> ::=
+    ADD <domain constraint>
+
+<drop domain constraint definition> ::=
+    DROP CONSTRAINT <constraint name>
+
+<drop domain statement> ::=
+    DROP DOMAIN <domain name> <drop behavior>
+
+<drop character set statement> ::=
+    DROP CHARACTER SET <character set name>
+
+<drop collation statement> ::=
+    DROP COLLATION <collation name>
+
+<drop translation statement> ::=
+    DROP TRANSLATION <translation name>
+
+<drop assertion statement> ::=
+    DROP ASSERTION <constraint name>
+
+<SQL data statement> ::=
+      <open statement>
+    | <fetch statement>
+    | <close statement>
+    | <select statement: single row>
+    | <SQL data change statement>
+
+<open statement> ::=
+    OPEN <cursor name>
+
+<fetch statement> ::=
+    FETCH [ [ <fetch orientation> ] FROM ]
+      <cursor name> INTO <fetch target list>
+
+<fetch orientation> ::=
+      NEXT
+    | PRIOR
+    | FIRST
+    | LAST
+    | { ABSOLUTE | RELATIVE } <simple value specification>
+
+<simple value specification> ::=
+      <parameter name>
+    | <embedded variable name>
+    | <literal>
+
+<fetch target list> ::=
+    <target specification> [ { <comma> <target specification> }... ]
+
+<target specification> ::=
+      <parameter specification>
+    | <variable specification>
+
+<close statement> ::=
+    CLOSE <cursor name>
+
+<select statement: single row> ::=
+    SELECT [ <set quantifier> ] <select list>
+      INTO <select target list>
+        <table expression>
+
+<select target list> ::=
+    <target specification> [ { <comma> <target specification> }... ]
+
+<SQL data change statement> ::=
+      <delete statement: positioned>
+    | <delete statement: searched>
+    | <insert statement>
+    | <update statement: positioned>
+    | <update statement: searched>
+
+<delete statement: positioned> ::=
+    DELETE FROM <table name>
+      WHERE CURRENT OF <cursor name>
+
+<delete statement: searched> ::=
+    DELETE FROM <table name>
+      [ WHERE <search condition> ]
+
+<insert statement> ::=
+    INSERT INTO <table name>
+      <insert columns and source>
+
+<insert columns and source> ::=
+      [ <left paren> <insert column list> <right paren> ]
+            <query expression>
+    | DEFAULT VALUES
+
+<insert column list> ::= <column name list>
+
+<update statement: positioned> ::=
+    UPDATE <table name>
+      SET <set clause list>
+        WHERE CURRENT OF <cursor name>
+
+<set clause list> ::=
+    <set clause> [ { <comma> <set clause> }... ]
+
+<set clause> ::=
+    <object column> <equals operator> <update source>
+
+<object column> ::= <column name>
+
+<update source> ::=
+      <value expression>
+    | <null specification>
+    | DEFAULT
+
+<update statement: searched> ::=
+    UPDATE <table name>
+      SET <set clause list>
+      [ WHERE <search condition> ]
+
+<SQL transaction statement> ::=
+      <set transaction statement>
+    | <set constraints mode statement>
+    | <commit statement>
+    | <rollback statement>
+
+<set transaction statement> ::=
+    SET TRANSACTION <transaction mode>
+        [ { <comma> <transaction mode> }... ]
+
+<transaction mode> ::=
+      <isolation level>
+    | <transaction access mode>
+    | <diagnostics size>
+
+<isolation level> ::=
+    ISOLATION LEVEL <level of isolation>
+
+<level of isolation> ::=
+      READ UNCOMMITTED
+    | READ COMMITTED
+    | REPEATABLE READ
+    | SERIALIZABLE
+
+<transaction access mode> ::=
+      READ ONLY
+    | READ WRITE
+
+<diagnostics size> ::=
+    DIAGNOSTICS SIZE <number of conditions>
+
+<number of conditions> ::= <simple value specification>
+
+<set constraints mode statement> ::=
+    SET CONSTRAINTS <constraint name list>
+        { DEFERRED | IMMEDIATE }
+
+<constraint name list> ::=
+      ALL
+    | <constraint name> [ { <comma> <constraint name> }... ]
+
+<commit statement> ::=
+    COMMIT [ WORK ]
+
+<rollback statement> ::=
+    ROLLBACK [ WORK ]
+
+<SQL connection statement> ::=
+      <connect statement>
+    | <set connection statement>
+    | <disconnect statement>
+
+<connect statement> ::=
+    CONNECT TO <connection target>
+
+<connection target> ::=
+      <SQL-server name>
+        [ AS <connection name> ]
+          correspondence with Tony Gordon)
+        [ USER <user name> ]
+    | DEFAULT
+
+<SQL-server name> ::= <simple value specification>
+
+<connection name> ::= <simple value specification>
+
+<user name> ::= <simple value specification>
+
+<set connection statement> ::=
+    SET CONNECTION <connection object>
+
+<connection object> ::=
+      DEFAULT
+    | <connection name>
+
+<disconnect statement> ::=
+    DISCONNECT <disconnect object>
+
+<disconnect object> ::=
+      <connection object>
+    | ALL
+    | CURRENT
+
+<SQL session statement> ::=
+      <set catalog statement>
+    | <set schema statement>
+    | <set names statement>
+    | <set session authorization identifier statement>
+    | <set local time zone statement>
+
+<set catalog statement> ::=
+    SET CATALOG <value specification>
+
+<value specification> ::=
+      <literal>
+    | <general value specification>
+
+<set schema statement> ::=
+    SET SCHEMA <value specification>
+
+<set names statement> ::=
+    SET NAMES <value specification>
+
+<set session authorization identifier statement> ::=
+    SET SESSION AUTHORIZATION
+        <value specification>
+
+<set local time zone statement> ::=
+    SET TIME ZONE
+        <set time zone value>
+
+<set time zone value> ::=
+      <interval value expression>
+    | LOCAL
+
+<SQL dynamic statement> ::=
+      <system descriptor statement>
+    | <prepare statement>
+    | <deallocate prepared statement>
+    | <describe statement>
+    | <execute statement>
+    | <execute immediate statement>
+    | <SQL dynamic data statement>
+
+<system descriptor statement> ::=
+      <allocate descriptor statement>
+    | <deallocate descriptor statement>
+    | <set descriptor statement>
+    | <get descriptor statement>
+
+<allocate descriptor statement> ::=
+    ALLOCATE DESCRIPTOR <descriptor name>
+       [ WITH MAX <occurrences> ]
+
+<descriptor name> ::=
+    [ <scope option> ] <simple value specification>
+
+<scope option> ::=
+      GLOBAL
+    | LOCAL
+
+<occurrences> ::= <simple value specification>
+
+<deallocate descriptor statement> ::=
+    DEALLOCATE DESCRIPTOR <descriptor name>
+
+<set descriptor statement> ::=
+    SET DESCRIPTOR <descriptor name>
+        <set descriptor information>
+
+<set descriptor information> ::=
+      <set count>
+    | VALUE <item number>
+        <set item information> [ { <comma> <set item information> }... ]
+
+<set count> ::=
+    COUNT <equals operator> <simple value specification 1>
+
+<simple value specification 1> ::= <simple value specification>
+
+<item number> ::= <simple value specification>
+
+<set item information> ::=
+    <descriptor item name> <equals operator> <simple value specification 2>
+
+<descriptor item name> ::=
+      TYPE
+    | LENGTH
+    | OCTET_LENGTH
+    | RETURNED_LENGTH
+    | RETURNED_OCTET_LENGTH
+    | PRECISION
+    | SCALE
+    | DATETIME_INTERVAL_CODE
+    | DATETIME_INTERVAL_PRECISION
+    | NULLABLE
+    | INDICATOR
+    | DATA
+    | NAME
+    | UNNAMED
+    | COLLATION_CATALOG
+    | COLLATION_SCHEMA
+    | COLLATION_NAME
+    | CHARACTER_SET_CATALOG
+    | CHARACTER_SET_SCHEMA
+    | CHARACTER_SET_NAME
+
+<simple value specification 2> ::= <simple value specification>
+
+<item number> ::= <simple value specification>
+
+<get descriptor statement> ::=
+    GET DESCRIPTOR <descriptor name> <get descriptor information>
+
+<get descriptor information> ::=
+      <get count>
+    | VALUE <item number>
+        <get item information> [ { <comma> <get item information> }... ]
+
+<get count> ::=
+    <simple target specification 1> <equals operator>
+         COUNT
+
+<simple target specification 1> ::= <simple target specification>
+
+<simple target specification> ::=
+      <parameter name>
+    | <embedded variable name>
+
+<get item information> ::=
+    <simple target specification 2> <equals operator> <descriptor item name>>
+
+<simple target specification 2> ::= <simple target specification>
+
+<prepare statement> ::=
+    PREPARE <SQL statement name> FROM <SQL statement variable>
+
+<SQL statement name> ::=
+      <statement name>
+    | <extended statement name>
+
+<extended statement name> ::=
+    [ <scope option> ] <simple value specification>
+
+<SQL statement variable> ::= <simple value specification>
+
+<deallocate prepared statement> ::=
+    DEALLOCATE PREPARE <SQL statement name>
+
+<describe statement> ::=
+      <describe input statement>
+    | <describe output statement>
+
+<describe input statement> ::=
+    DESCRIBE INPUT <SQL statement name> <using descriptor>
+
+<using descriptor> ::=
+    { USING | INTO } SQL DESCRIPTOR <descriptor name>
+
+<describe output statement> ::=
+    DESCRIBE [ OUTPUT ] <SQL statement name> <using descriptor>
+
+<execute statement> ::=
+    EXECUTE <SQL statement name>
+      [ <result using clause> ]
+      [ <parameter using clause> ]
+
+<result using clause> ::= <using clause>
+
+<using clause> ::=
+      <using arguments>
+    | <using descriptor>
+
+<using arguments> ::=
+    { USING | INTO } <argument> [ { <comma> <argument> }... ]
+
+<argument> ::= <target specification>
+
+<parameter using clause> ::= <using clause>
+
+<execute immediate statement> ::=
+    EXECUTE IMMEDIATE <SQL statement variable>
+
+<SQL dynamic data statement> ::=
+      <allocate cursor statement>
+    | <dynamic open statement>
+    | <dynamic fetch statement>
+    | <dynamic close statement>
+    | <dynamic delete statement: positioned>
+    | <dynamic update statement: positioned>
+
+<allocate cursor statement> ::=
+    ALLOCATE <extended cursor name> [ INSENSITIVE ]
+        [ SCROLL ] CURSOR
+      FOR <extended statement name>
+
+<extended cursor name> ::=
+    [ <scope option> ] <simple value specification>
+
+<dynamic open statement> ::=
+    OPEN <dynamic cursor name> [ <using clause> ]
+
+<dynamic cursor name> ::=
+      <cursor name>
+    | <extended cursor name>
+
+<dynamic fetch statement> ::=
+    FETCH [ [ <fetch orientation> ] FROM ] <dynamic cursor name>
+        <using clause>
+
+<dynamic close statement> ::=
+    CLOSE <dynamic cursor name>
+
+<dynamic delete statement: positioned> ::=
+    DELETE FROM <table name>
+      WHERE CURRENT OF
+          <dynamic cursor name>
+
+<dynamic update statement: positioned> ::=
+    UPDATE <table name>
+      SET <set clause>
+          [ { <comma> <set clause> }... ]
+        WHERE CURRENT OF
+            <dynamic cursor name>
+
+<SQL diagnostics statement> ::=
+    <get diagnostics statement>
+
+<get diagnostics statement> ::=
+    GET DIAGNOSTICS <sql diagnostics information>
+
+<sql diagnostics information> ::=
+      <statement information>
+    | <condition information>
+
+<statement information> ::=
+    <statement information item> [ { <comma> <statement information item> }... ]
+
+<statement information item> ::=
+    <simple target specification> <equals operator> <statement information item name>
+
+<statement information item name> ::=
+      NUMBER
+    | MORE
+    | COMMAND_FUNCTION
+    | DYNAMIC_FUNCTION
+    | ROW_COUNT
+
+<condition information> ::=
+    EXCEPTION <condition number>
+      <condition information item> [ { <comma> <condition information item> }... ]
+
+<condition number> ::= <simple value specification>
+
+<condition information item> ::=
+    <simple target specification> <equals operator> <condition information item name>
+
+<condition information item name> ::=
+      CONDITION_NUMBER
+    | RETURNED_SQLSTATE
+    | CLASS_ORIGIN
+    | SUBCLASS_ORIGIN
+    | SERVER_NAME
+    | CONNECTION_NAME
+    | CONSTRAINT_CATALOG
+    | CONSTRAINT_SCHEMA
+    | CONSTRAINT_NAME
+    | CATALOG_NAME
+    | SCHEMA_NAME
+    | TABLE_NAME
+    | COLUMN_NAME
+    | CURSOR_NAME
+    | MESSAGE_TEXT
+    | MESSAGE_LENGTH
+    | MESSAGE_OCTET_LENGTH
+
+<embedded SQL host program> ::=
+      <embedded SQL Ada program>
+    | <embedded SQL C program>
+    | <embedded SQL COBOL program>
+    | <embedded SQL Fortran program>
+    | <embedded SQL MUMPS program>
+    | <embedded SQL Pascal program>
+    | <embedded SQL PL/I program>
+
+<embedded SQL Ada program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL C program> ::=
+      !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL COBOL program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL Fortran program> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL MUMPS program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL Pascal program> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL PL/I program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL declare section> ::=
+      <embedded SQL begin declare>
+        [ <embedded character set declaration> ]
+        [ <host variable definition>... ]
+      <embedded SQL end declare>
+    | <embedded SQL MUMPS declare>
+
+<embedded SQL begin declare> ::=
+    <SQL prefix> BEGIN DECLARE SECTION
+        [ <SQL terminator> ]
+
+<SQL prefix> ::=
+      EXEC SQL
+    | <ampersand>SQL<left paren>
+
+<SQL terminator> ::=
+      END-EXEC
+    | <semicolon>
+    | <right paren>
+
+<embedded character set declaration> ::=
+    SQL NAMES ARE <character set specification>
+
+<host variable definition> ::=
+      <Ada variable definition>
+    | <C variable definition>
+    | <COBOL variable definition>
+    | <Fortran variable definition>
+    | <MUMPS variable definition>
+    | <Pascal variable definition>
+    | <PL/I variable definition>
+
+<Ada variable definition> ::=
+    <Ada host identifier> [ { <comma> <Ada host identifier> }... ] :
+    <Ada type specification> [ <Ada initial value> ]
+
+<Ada type specification> ::=
+      <Ada qualified type specification>
+    | <Ada unqualified type specification>
+
+<Ada qualified type specification> ::=
+      SQL_STANDARD.CHAR [ CHARACTER SET
+         [ IS ] <character set specification> ]
+          <left paren> 1 <double period> <length> <right paren>
+    | SQL_STANDARD.BIT
+          <left paren> 1 <double period> <length> <right paren>
+    | SQL_STANDARD.SMALLINT
+    | SQL_STANDARD.INT
+    | SQL_STANDARD.REAL
+    | SQL_STANDARD.DOUBLE_PRECISION
+    | SQL_STANDARD.SQLCODE_TYPE
+    | SQL_STANDARD.SQLSTATE_TYPE
+    | SQL_STANDARD.INDICATOR_TYPE
+
+<Ada unqualified type specification> ::=
+      CHAR
+          <left paren> 1 <double period> <length> <right paren>
+    | BIT
+          <left paren> 1 <double period> <length> <right paren>
+    | SMALLINT
+    | INT
+    | REAL
+    | DOUBLE_PRECISION
+    | SQLCODE_TYPE
+    | SQLSTATE_TYPE
+    | INDICATOR_TYPE
+
+<Ada initial value> ::=
+    <Ada assignment operator> <character representation>...
+
+<Ada assignment operator> ::= <colon><equals operator>
+
+<C variable definition> ::=
+      [ <C storage class> ]
+      [ <C class modifier> ]
+      <C variable specification>
+    <semicolon>
+
+<C storage class> ::=
+      auto
+    | extern
+    | static
+
+<C class modifier> ::= const | volatile
+
+<C variable specification> ::=
+      <C numeric variable>
+    | <C character variable>
+    | <C derived variable>
+
+<C numeric variable> ::=
+    { long | short | float | double }
+      <C host identifier> [ <C initial value> ]
+            [ { <comma> <C host identifier> [ <C initial value> ] }... ]
+
+<C initial value> ::=
+    <equals operator> <character representation>...
+
+<C character variable> ::=
+    char [ CHARACTER SET
+             [ IS ] <character set specification> ]
+      <C host identifier>
+        <C array specification> [ <C initial value> ]
+        [ { <comma> <C host identifier>
+          <C array specification>
+                 [ <C initial value> ] }... ]
+
+<C array specification> ::=
+    <left bracket> <length> <right bracket>
+
+<C derived variable> ::=
+      <C VARCHAR variable>
+    | <C bit variable>
+
+<C VARCHAR variable> ::=
+    VARCHAR [ CHARACTER SET [ IS ]
+        <character set specification> ]
+        <C host identifier>
+            <C array specification> [ <C initial value> ]
+          [ { <comma> <C host identifier>
+              <C array specification>
+                      [ <C initial value> ] }... ]
+
+<C bit variable> ::=
+    BIT <C host identifier>
+        <C array specification> [ <C initial value> ]
+      [ { <comma> <C host identifier>
+        <C array specification>
+                   [ <C initial value> ] }... ]
+
+<COBOL variable definition> ::=
+    {01|77} <COBOL host identifier> <COBOL type specification>
+      [ <character representation>... ] <period>
+
+<COBOL type specification> ::=
+      <COBOL character type>
+    | <COBOL bit type>
+    | <COBOL numeric type>
+    | <COBOL integer type>
+
+<COBOL character type> ::=
+    [ CHARACTER SET [ IS ]
+          <character set specification> ]
+    { PIC | PICTURE } [ IS ] { X [ <left paren> <length> <right paren> ] }...
+
+<COBOL bit type> ::=
+    { PIC | PICTURE } [ IS ]
+        { B [ <left paren> <length> <right paren> ] }...
+
+<COBOL numeric type> ::=
+    { PIC | PICTURE } [ IS ]
+      S <COBOL nines specification>
+    [ USAGE [ IS ] ] DISPLAY SIGN LEADING SEPARATE
+
+<COBOL nines specification> ::=
+      <COBOL nines> [ V [ <COBOL nines> ] ]
+    | V <COBOL nines>
+
+<COBOL nines> ::= { 9 [ <left paren> <length> <right paren> ] }...
+
+<COBOL integer type> ::=
+      <COBOL computational integer>
+    | <COBOL binary integer>
+
+<COBOL computational integer> ::=
+    { PIC | PICTURE } [ IS ] S<COBOL nines>
+      [ USAGE [ IS ] ] { COMP | COMPUTATIONAL }
+
+<COBOL binary integer> ::=
+    { PIC | PICTURE } [ IS ] S<COBOL nines>
+      [ USAGE [ IS ] ] BINARY
+
+<Fortran variable definition> ::=
+    <Fortran type specification>
+    <Fortran host identifier>
+        [ { <comma> <Fortran host identifier> }... ]
+
+<Fortran type specification> ::=
+      CHARACTER [ <asterisk> <length> ]
+          [ CHARACTER SET [ IS ]
+                <character set specification> ]
+    | BIT [ <asterisk> <length> ]
+    | INTEGER
+    | REAL
+    | DOUBLE PRECISION
+
+<MUMPS variable definition> ::=
+    { <MUMPS numeric variable> | <MUMPS character variable> }
+        <semicolon>
+
+<MUMPS numeric variable> ::=
+    <MUMPS type specification>
+      <MUMPS host identifier> [ { <comma> <MUMPS host identifier> }... ]
+
+<MUMPS type specification> ::=
+      INT
+    | DEC
+          [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | REAL
+
+<MUMPS character variable> ::=
+    VARCHAR <MUMPS host identifier> <MUMPS length specification>
+      [ { <comma> <MUMPS host identifier> <MUMPS length specification> }... ]
+
+<MUMPS length specification> ::=
+    <left paren> <length> <right paren>
+
+<Pascal variable definition> ::=
+    <Pascal host identifier> [ { <comma> <Pascal host identifier> }... ] <colon>
+      <Pascal type specification> <semicolon>
+
+<Pascal type specification> ::=
+      PACKED ARRAY
+          <left bracket> 1 <double period> <length> <right bracket>
+        OF CHAR
+          [ CHARACTER SET [ IS ]
+                <character set specification> ]
+    | PACKED ARRAY
+          <left bracket> 1 <double period> <length> <right bracket>
+        OF BIT
+    | INTEGER
+    | REAL
+    | CHAR [ CHARACTER SET
+                                [ IS ] <character set specification> ]
+    | BIT
+
+<PL/I variable definition> ::=
+    {DCL | DECLARE}
+        {   <PL/I host identifier>
+          | <left paren> <PL/I host identifier>
+                [ { <comma> <PL/I host identifier> }... ] <right paren> }
+    <PL/I type specification>
+    [ <character representation>... ] <semicolon>
+
+<PL/I type specification> ::=
+      { CHAR | CHARACTER } [ VARYING ]
+          <left paren><length><right paren>
+          [ CHARACTER SET
+                [ IS ] <character set specification> ]
+    | BIT [ VARYING ] <left paren><length><right paren>
+    | <PL/I type fixed decimal> <left paren> <precision>
+          [ <comma> <scale> ] <right paren>
+    | <PL/I type fixed binary> [ <left paren> <precision> <right paren> ]
+    | <PL/I type float binary> <left paren> <precision> <right paren>
+
+<PL/I type fixed decimal> ::=
+      { DEC | DECIMAL } FIXED
+    | FIXED { DEC | DECIMAL }
+
+<PL/I type fixed binary> ::=
+      { BIN | BINARY } FIXED
+    | FIXED { BIN | BINARY }
+
+<PL/I type float binary> ::=
+      { BIN | BINARY } FLOAT
+    | FLOAT { BIN | BINARY }
+
+<embedded SQL end declare> ::=
+    <SQL prefix> END DECLARE SECTION
+        [ <SQL terminator> ]
+
+<embedded SQL MUMPS declare> ::=
+    <SQL prefix>
+      BEGIN DECLARE SECTION
+        [ <embedded character set declaration> ]
+        [ <host variable definition>... ]
+      END DECLARE SECTION
+    <SQL terminator>
+
+<embedded SQL statement> ::=
+    <SQL prefix>
+      <statement or declaration>
+    [ <SQL terminator> ]
+
+<statement or declaration> ::=
+      <declare cursor>
+    | <dynamic declare cursor>
+    | <temporary table declaration>
+    | <embedded exception declaration>
+    | <SQL procedure statement>
+
+<embedded exception declaration> ::=
+    WHENEVER <condition> <condition action>
+
+<condition> ::=
+    SQLERROR | NOT FOUND
+
+<condition action> ::=
+    CONTINUE | <go to>
+
+<go to> ::=
+    { GOTO | GO TO } <goto target>
+
+<goto target> ::=
+      <host label identifier>
+    | <unsigned integer>
+    | <host PL/I label variable>
+
+<host label identifier> ::= !!<EMPHASIS>(See the Syntax Rules.)
+
+<host PL/I label variable> ::= !!<EMPHASIS>(See the Syntax Rules.)
+
+<preparable statement> ::=
+      <preparable SQL data statement>
+    | <preparable SQL schema statement>
+    | <preparable SQL transaction statement>
+    | <preparable SQL session statement>
+    | <preparable implementation-defined statement>
+
+<preparable SQL data statement> ::=
+      <delete statement: searched>
+    | <dynamic single row select statement>
+    | <insert statement>
+    | <dynamic select statement>
+    | <update statement: searched>
+    | <preparable dynamic delete statement: positioned>
+    | <preparable dynamic update statement: positioned>
+
+<dynamic single row select statement> ::= <query specification>
+
+<dynamic select statement> ::= <cursor specification>
+
+<preparable dynamic delete statement: positioned> ::=
+   DELETE [ FROM <table name> ]
+      WHERE CURRENT OF <cursor name>
+
+<preparable dynamic update statement: positioned> ::=
+   UPDATE [ <table name> ]
+      SET <set clause list>
+      WHERE CURRENT OF <cursor name>
+
+<preparable SQL schema statement> ::=
+      <SQL schema statement>
+
+<preparable SQL transaction statement> ::=
+      <SQL transaction statement>
+
+<preparable SQL session statement> ::=
+      <SQL session statement>
+
+<preparable implementation-defined statement> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<direct SQL statement> ::=
+    <directly executable statement> <semicolon>
+
+<directly executable statement> ::=
+      <direct SQL data statement>
+    | <SQL schema statement>
+    | <SQL transaction statement>
+    | <SQL connection statement>
+    | <SQL session statement>
+    | <direct implementation-defined statement>
+
+<direct SQL data statement> ::=
+      <delete statement: searched>
+    | <direct select statement: multiple rows>
+    | <insert statement>
+    | <update statement: searched>
+    | <temporary table declaration>
+
+<direct select statement: multiple rows> ::=
+    <query expression> [ <order by clause> ]
+
+<direct implementation-defined statement> ::=
+    !!<EMPHASIS>(See the Syntax Rules)
+
+<SQL object identifier> ::=
+    <SQL provenance> <SQL variant>
+
+<SQL provenance> ::= <arc1> <arc2> <arc3>
+
+<arc1> ::= iso | 1 | iso <left paren> 1 <right paren>
+
+<arc2> ::= standard | 0 | standard <left paren> 0 <right paren>
+
+<arc3> ::= 9075
+
+<SQL variant> ::= <SQL edition> <SQL conformance>
+
+<SQL edition> ::= <1987> | <1989> | <1992>
+
+<1987> ::= 0 | edition1987 <left paren> 0 <right paren>
+
+<1989> ::= <1989 base> <1989 package>
+
+<1989 base> ::= 1 | edition1989 <left paren> 1 <right paren>
+
+<1989 package> ::= <integrity no> | <integrity yes>
+
+<integrity no> ::= 0 | IntegrityNo <left paren> 0 <right paren>
+
+<integrity yes> ::= 1 | IntegrityYes <left paren> 1 <right paren>
+
+<1992> ::= 2 | edition1992 <left paren> 2 <right paren>
+
+<SQL conformance> ::= <low> | <intermediate> | <high>
+
+<low> ::= 0 | Low <left paren> 0 <right paren>
+
+<intermediate> ::= 1 | Intermediate <left paren> 1 <right paren>
+
+<high> ::= 2 | High <left paren> 2 <right paren>
+


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/src/test/resources/sql2bnf.aug92.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlConstants$DataTypes.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlConstants$DataTypes.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlConstants$StatementStartPhrases.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlConstants$StatementStartPhrases.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlConstants.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlConstants.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlTokenStream$DdlTokenizer.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlTokenStream$DdlTokenizer.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlTokenStream.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/DdlTokenStream.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/OldDdlTokenizer.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/OldDdlTokenizer.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/StandardDdlParser.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/StandardDdlParser.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/Db2DdlParser.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/Db2DdlParser.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser$DerbyDataTypeParser.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser$DerbyDataTypeParser.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParser.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser$MySqlDataTypeParser.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser$MySqlDataTypeParser.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/MySqlDdlParser.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/PostgresDdlParser.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/PostgresDdlParser.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/SqlServerDdlParser.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/SqlServerDdlParser.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/SybaseDdlParser.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/org/jboss/dna/sequencer/ddl/dialect/SybaseDdlParser.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/sql2bnf.aug92.txt
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/sql2bnf.aug92.txt	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/sql2bnf.aug92.txt	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,2594 @@
+This file contains a depth-first tree traversal of the BNF
+for the  language done at about 27-AUG-1992 11:03:41.64.
+The specific version of the BNF included here is:  ANSI-only, SQL2-only.
+
+
+<SQL terminal character> ::=
+      <SQL language character>
+    | <SQL embedded language character>
+
+<SQL language character> ::=
+      <simple Latin letter>
+    | <digit>
+    | <SQL special character>
+
+<simple Latin letter> ::=
+      <simple Latin upper case letter>
+    | <simple Latin lower case letter>
+
+<simple Latin upper case letter> ::=
+          A | B | C | D | E | F | G | H | I | J | K | L | M | N | O
+    | P | Q | R | S | T | U | V | W | X | Y | Z
+
+<simple Latin lower case letter> ::=
+          a | b | c | d | e | f | g | h | i | j | k | l | m | n | o
+    | p | q | r | s | t | u | v | w | x | y | z
+
+<digit> ::=
+    0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
+
+<SQL special character> ::=
+      <space>
+    | <double quote>
+    | <percent>
+    | <ampersand>
+    | <quote>
+    | <left paren>
+    | <right paren>
+    | <asterisk>
+    | <plus sign>
+    | <comma>
+    | <minus sign>
+    | <period>
+    | <solidus>
+    | <colon>
+    | <semicolon>
+    | <less than operator>
+    | <equals operator>
+    | <greater than operator>
+    | <question mark>
+    | <underscore>
+    | <vertical bar>
+
+<space> ::= !! <EMPHASIS>(space character in character set in use)
+
+<double quote> ::= "
+
+<percent> ::= %
+
+<ampersand> ::= &
+
+<quote> ::= '
+
+<left paren> ::= (
+
+<right paren> ::= )
+
+<asterisk> ::= *
+
+<plus sign> ::= +
+
+<comma> ::= ,
+
+<minus sign> ::= -
+
+<period> ::= .
+
+<solidus> ::= /
+
+<colon> ::= :
+
+<semicolon> ::= ;
+
+<less than operator> ::= <
+
+<equals operator> ::= =
+
+<greater than operator> ::= >
+
+<question mark> ::= ?
+
+<underscore> ::= _
+
+<vertical bar> ::= |
+
+<SQL embedded language character> ::=
+      <left bracket>
+    | <right bracket>
+
+<left bracket> ::= [
+
+<right bracket> ::= ]
+
+<token> ::=
+      <nondelimiter token>
+    | <delimiter token>
+
+<nondelimiter token> ::=
+      <regular identifier>
+    | <key word>
+    | <unsigned numeric literal>
+    | <national character string literal>
+    | <bit string literal>
+    | <hex string literal>
+
+<regular identifier> ::= <identifier body>
+
+<identifier body> ::=
+    <identifier start> [ { <underscore> | <identifier part> }... ]
+
+<identifier start> ::= <EMPHASIS>(!! See the Syntax Rules)
+
+<identifier part> ::=
+      <identifier start>
+    | <digit>
+
+<key word> ::=
+      <reserved word>
+    | <non-reserved word>
+
+<reserved word> ::=
+      ABSOLUTE | ACTION | ADD | ALL
+    | ALLOCATE | ALTER | AND
+    | ANY | ARE
+    | AS | ASC
+    | ASSERTION | AT
+    | AUTHORIZATION | AVG
+    | BEGIN | BETWEEN | BIT | BIT_LENGTH
+    | BOTH | BY
+    | CASCADE | CASCADED | CASE | CAST
+    | CATALOG
+    | CHAR | CHARACTER | CHAR_LENGTH
+    | CHARACTER_LENGTH | CHECK | CLOSE | COALESCE
+    | COLLATE | COLLATION
+    | COLUMN | COMMIT
+    | CONNECT
+    | CONNECTION | CONSTRAINT
+    | CONSTRAINTS | CONTINUE
+    | CONVERT | CORRESPONDING | COUNT | CREATE | CROSS
+    | CURRENT
+    | CURRENT_DATE | CURRENT_TIME
+    | CURRENT_TIMESTAMP | CURRENT_USER | CURSOR
+    | DATE | DAY | DEALLOCATE | DEC
+    | DECIMAL | DECLARE | DEFAULT | DEFERRABLE
+    | DEFERRED | DELETE | DESC | DESCRIBE | DESCRIPTOR
+    | DIAGNOSTICS
+    | DISCONNECT | DISTINCT | DOMAIN | DOUBLE | DROP
+    | ELSE | END | END-EXEC | ESCAPE
+    | EXCEPT | EXCEPTION
+    | EXEC | EXECUTE | EXISTS
+    | EXTERNAL | EXTRACT
+    | FALSE | FETCH | FIRST | FLOAT | FOR
+    | FOREIGN | FOUND | FROM | FULL
+    | GET | GLOBAL | GO | GOTO
+    | GRANT | GROUP
+    | HAVING | HOUR
+    | IDENTITY | IMMEDIATE | IN | INDICATOR
+    | INITIALLY | INNER | INPUT
+    | INSENSITIVE | INSERT | INT | INTEGER | INTERSECT
+    | INTERVAL | INTO | IS
+    | ISOLATION
+    | JOIN
+    | KEY
+    | LANGUAGE | LAST | LEADING | LEFT
+    | LEVEL | LIKE | LOCAL | LOWER
+    | MATCH | MAX | MIN | MINUTE | MODULE
+    | MONTH
+    | NAMES | NATIONAL | NATURAL | NCHAR | NEXT | NO
+    | NOT | NULL
+    | NULLIF | NUMERIC
+    | OCTET_LENGTH | OF
+    | ON | ONLY | OPEN | OPTION | OR
+    | ORDER | OUTER
+    | OUTPUT | OVERLAPS
+    | PAD | PARTIAL | POSITION | PRECISION | PREPARE
+    | PRESERVE | PRIMARY
+    | PRIOR | PRIVILEGES | PROCEDURE | PUBLIC
+    | READ | REAL | REFERENCES | RELATIVE | RESTRICT
+    | REVOKE | RIGHT
+    | ROLLBACK | ROWS
+    | SCHEMA | SCROLL | SECOND | SECTION
+    | SELECT
+    | SESSION | SESSION_USER | SET
+    | SIZE | SMALLINT | SOME | SPACE | SQL | SQLCODE
+    | SQLERROR | SQLSTATE
+    | SUBSTRING | SUM | SYSTEM_USER
+    | TABLE | TEMPORARY
+    | THEN | TIME | TIMESTAMP
+    | TIMEZONE_HOUR | TIMEZONE_MINUTE
+    | TO | TRAILING | TRANSACTION
+    | TRANSLATE | TRANSLATION | TRIM | TRUE
+    | UNION | UNIQUE | UNKNOWN | UPDATE | UPPER | USAGE
+    | USER | USING
+    | VALUE | VALUES | VARCHAR | VARYING | VIEW
+    | WHEN | WHENEVER | WHERE | WITH | WORK | WRITE
+    | YEAR
+    | ZONE
+
+<non-reserved word> ::=
+    
+      ADA
+    | C | CATALOG_NAME
+    | CHARACTER_SET_CATALOG | CHARACTER_SET_NAME
+    | CHARACTER_SET_SCHEMA | CLASS_ORIGIN | COBOL | COLLATION_CATALOG
+    | COLLATION_NAME | COLLATION_SCHEMA | COLUMN_NAME | COMMAND_FUNCTION
+    | COMMITTED
+    | CONDITION_NUMBER | CONNECTION_NAME | CONSTRAINT_CATALOG | CONSTRAINT_NAME
+    | CONSTRAINT_SCHEMA | CURSOR_NAME
+    | DATA | DATETIME_INTERVAL_CODE
+    | DATETIME_INTERVAL_PRECISION | DYNAMIC_FUNCTION
+    | FORTRAN
+    | LENGTH
+    | MESSAGE_LENGTH | MESSAGE_OCTET_LENGTH | MESSAGE_TEXT | MORE | MUMPS
+    | NAME | NULLABLE | NUMBER
+    | PASCAL | PLI
+    | REPEATABLE | RETURNED_LENGTH | RETURNED_OCTET_LENGTH | RETURNED_SQLSTATE
+    | ROW_COUNT
+    | SCALE | SCHEMA_NAME | SERIALIZABLE | SERVER_NAME | SUBCLASS_ORIGIN
+    | TABLE_NAME | TYPE
+    | UNCOMMITTED | UNNAMED
+
+<unsigned numeric literal> ::=
+      <exact numeric literal>
+    | <approximate numeric literal>
+
+<exact numeric literal> ::=
+      <unsigned integer> [ <period> [ <unsigned integer> ] ]
+    | <period> <unsigned integer>
+
+<unsigned integer> ::= <digit>...
+
+<approximate numeric literal> ::= <mantissa> E <exponent>
+
+<mantissa> ::= <exact numeric literal>
+
+<exponent> ::= <signed integer>
+
+<signed integer> ::= [ <sign> ] <unsigned integer>
+
+<sign> ::= <plus sign> | <minus sign>
+
+<national character string literal> ::=
+    N <quote> [ <character representation>... ] <quote>
+      [ { <separator>... <quote> [ <character representation>... ] <quote> }... ]
+
+<character representation> ::=
+      <nonquote character>
+    | <quote symbol>
+
+<nonquote character> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<quote symbol> ::= <quote><quote>
+
+<separator> ::= { <comment> | <space> | <newline> }...
+
+<comment> ::=
+    <comment introducer> [ <comment character>... ] <newline>
+
+<comment introducer> ::= <minus sign><minus sign>[<minus sign>...]
+
+<comment character> ::=
+      <nonquote character>
+    | <quote>
+
+<newline> ::= !! <EMPHASIS>(implementation-defined end-of-line indicator)
+
+<bit string literal> ::=
+    B <quote> [ <bit>... ] <quote>
+      [ { <separator>... <quote> [ <bit>... ] <quote> }... ]
+
+<bit> ::= 0 | 1
+
+<hex string literal> ::=
+    X <quote> [ <hexit>... ] <quote>
+      [ { <separator>... <quote> [ <hexit>... ] <quote> }... ]
+
+<hexit> ::= <digit> | A | B | C | D | E | F | a | b | c | d | e | f
+
+<delimiter token> ::=
+      <character string literal>
+    | <date string>
+    | <time string>
+    | <timestamp string>
+    | <interval string>
+    | <delimited identifier>
+    | <SQL special character>
+    | <not equals operator>
+    | <greater than or equals operator>
+    | <less than or equals operator>
+    | <concatenation operator>
+    | <double period>
+    | <left bracket>
+    | <right bracket>
+
+<character string literal> ::=
+    [ <introducer><character set specification> ]
+    <quote> [ <character representation>... ] <quote>
+      [ { <separator>... <quote> [ <character representation>... ] <quote> }... ]
+
+<introducer> ::= <underscore>
+
+<character set specification> ::=
+      <standard character repertoire name>
+    | <implementation-defined character repertoire name>
+    | <user-defined character repertoire name>
+    | <standard universal character form-of-use name>
+    | <implementation-defined universal character form-of-use name>
+
+<standard character repertoire name> ::= <character set name>
+
+<character set name> ::= [ <schema name> <period> ]
+      <SQL language identifier>
+
+<schema name> ::=
+    [ <catalog name> <period> ] <unqualified schema name>
+
+<catalog name> ::= <identifier>
+
+<identifier> ::=
+    [ <introducer><character set specification> ] <actual identifier>
+
+<actual identifier> ::=
+      <regular identifier>
+    | <delimited identifier>
+
+<delimited identifier> ::=
+    <double quote> <delimited identifier body> <double quote>
+
+<delimited identifier body> ::= <delimited identifier part>...
+
+<delimited identifier part> ::=
+      <nondoublequote character>
+    | <doublequote symbol>
+
+<nondoublequote character> ::= <EMPHASIS>(!! See the Syntax Rules)
+
+<doublequote symbol> ::= <double quote><double quote>
+
+<unqualified schema name> ::= <identifier>
+
+<SQL language identifier> ::=
+    <SQL language identifier start>
+       [ { <underscore> | <SQL language identifier part> }... ]
+
+<SQL language identifier start> ::= <simple Latin letter>
+
+<SQL language identifier part> ::=
+      <simple Latin letter>
+    | <digit>
+
+<implementation-defined character repertoire name> ::=
+    <character set name>
+
+<user-defined character repertoire name> ::= <character set name>
+
+<standard universal character form-of-use name> ::=
+    <character set name>
+
+<implementation-defined universal character form-of-use name> ::=
+    <character set name>
+
+<date string> ::=
+    <quote> <date value> <quote>
+
+<date value> ::=
+    <years value> <minus sign> <months value>
+        <minus sign> <days value>
+
+<years value> ::= <datetime value>
+
+<datetime value> ::= <unsigned integer>
+
+<months value> ::= <datetime value>
+
+<days value> ::= <datetime value>
+
+<time string> ::=
+    <quote> <time value> [ <time zone interval> ] <quote>
+
+<time value> ::=
+    <hours value> <colon> <minutes value> <colon> <seconds value>
+
+<hours value> ::= <datetime value>
+
+<minutes value> ::= <datetime value>
+
+<seconds value> ::=
+      <seconds integer value> [ <period> [ <seconds fraction> ] ]
+
+<seconds integer value> ::= <unsigned integer>
+
+<seconds fraction> ::= <unsigned integer>
+
+<time zone interval> ::=
+    <sign> <hours value> <colon> <minutes value>
+
+<timestamp string> ::=
+    <quote> <date value> <space> <time value>
+        [ <time zone interval> ] <quote>
+
+<interval string> ::=
+    <quote> { <year-month literal> | <day-time literal> } <quote>
+
+<year-month literal> ::=
+      <years value>
+    | [ <years value> <minus sign> ] <months value>
+
+<day-time literal> ::=
+      <day-time interval>
+    | <time interval>
+
+<day-time interval> ::=
+    <days value>
+      [ <space> <hours value> [ <colon> <minutes value>
+        [ <colon> <seconds value> ] ] ]
+
+<time interval> ::=
+      <hours value> [ <colon> <minutes value> [ <colon> <seconds value> ] ]
+    | <minutes value> [ <colon> <seconds value> ]
+    | <seconds value>
+
+<not equals operator> ::= <>
+
+<greater than or equals operator> ::= >=
+
+<less than or equals operator> ::= <=
+
+<concatenation operator> ::= ||
+
+<double period> ::= ..
+
+<module> ::=
+    <module name clause>
+    <language clause>
+    <module authorization clause>
+    [ <temporary table declaration>... ]
+    <module contents>...
+
+<module name clause> ::=
+    MODULE [ <module name> ]
+      [ <module character set specification> ]
+
+<module name> ::= <identifier>
+
+<module character set specification> ::=
+    NAMES ARE <character set specification>
+
+<language clause> ::=
+    LANGUAGE <language name>
+
+<language name> ::=
+    ADA | C | COBOL | FORTRAN | MUMPS | PASCAL | PLI
+
+<module authorization clause> ::=
+      SCHEMA <schema name>
+    | AUTHORIZATION <module authorization identifier>
+    | SCHEMA <schema name>
+          AUTHORIZATION <module authorization identifier>
+
+<module authorization identifier> ::=
+    <authorization identifier>
+
+<authorization identifier> ::= <identifier>
+
+<temporary table declaration> ::=
+    DECLARE LOCAL TEMPORARY TABLE
+        <qualified local table name>
+      <table element list>
+      [ ON COMMIT { PRESERVE | DELETE } ROWS ]
+
+<qualified local table name> ::=
+    MODULE <period> <local table name>
+
+<local table name> ::= <qualified identifier>
+
+<qualified identifier> ::= <identifier>
+
+<table element list> ::=
+      <left paren> <table element> [ { <comma> <table element> }... ] <right paren>
+
+<table element> ::=
+      <column definition>
+    | <table constraint definition>
+
+<column definition> ::=
+    <column name> { <data type> | <domain name> }
+    [ <default clause> ]
+    [ <column constraint definition>... ]
+    [ <collate clause> ]
+
+<column name> ::= <identifier>
+
+<data type> ::=
+      <character string type>
+           [ CHARACTER SET <character set specification> ]
+    | <national character string type>
+    | <bit string type>
+    | <numeric type>
+    | <datetime type>
+    | <interval type>
+
+<character string type> ::=
+      CHARACTER [ <left paren> <length> <right paren> ]
+    | CHAR [ <left paren> <length> <right paren> ]
+    | CHARACTER VARYING <left paren> <length> <right paren>
+    | CHAR VARYING <left paren> <length> <right paren>
+    | VARCHAR <left paren> <length> <right paren>
+
+<length> ::= <unsigned integer>
+
+<national character string type> ::=
+      NATIONAL CHARACTER [ <left paren> <length> <right paren> ]
+    | NATIONAL CHAR [ <left paren> <length> <right paren> ]
+    | NCHAR [ <left paren> <length> <right paren> ]
+    | NATIONAL CHARACTER VARYING <left paren> <length> <right paren>
+    | NATIONAL CHAR VARYING <left paren> <length> <right paren>
+    | NCHAR VARYING <left paren> <length> <right paren>
+
+<bit string type> ::=
+      BIT [ <left paren> <length> <right paren> ]
+    | BIT VARYING <left paren> <length> <right paren>
+
+<numeric type> ::=
+      <exact numeric type>
+    | <approximate numeric type>
+
+<exact numeric type> ::=
+      NUMERIC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | DECIMAL [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | DEC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | INTEGER
+    | INT
+    | SMALLINT
+
+<precision> ::= <unsigned integer>
+
+<scale> ::= <unsigned integer>
+
+<approximate numeric type> ::=
+      FLOAT [ <left paren> <precision> <right paren> ]
+    | REAL
+    | DOUBLE PRECISION
+
+<datetime type> ::=
+      DATE
+    | TIME [ <left paren> <time precision> <right paren> ]
+          [ WITH TIME ZONE ]
+    | TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+          [ WITH TIME ZONE ]
+
+<time precision> ::= <time fractional seconds precision>
+
+<time fractional seconds precision> ::= <unsigned integer>
+
+<timestamp precision> ::= <time fractional seconds precision>
+
+<interval type> ::= INTERVAL <interval qualifier>
+
+<interval qualifier> ::=
+      <start field> TO <end field>
+    | <single datetime field>
+
+<start field> ::=
+    <non-second datetime field>
+        [ <left paren> <interval leading field precision> <right paren> ]
+
+<non-second datetime field> ::= YEAR | MONTH | DAY | HOUR
+    | MINUTE
+
+<interval leading field precision> ::= <unsigned integer>
+
+<end field> ::=
+      <non-second datetime field>
+    | SECOND [ <left paren> <interval fractional seconds precision> <right paren> ]
+
+<interval fractional seconds precision> ::= <unsigned integer>
+
+<single datetime field> ::=
+      <non-second datetime field>
+          [ <left paren> <interval leading field precision> <right paren> ]
+    | SECOND [ <left paren> <interval leading field precision>
+          [ <comma> <interval fractional seconds precision> ] <right paren> ]
+
+<domain name> ::= <qualified name>
+
+<qualified name> ::=
+    [ <schema name> <period> ] <qualified identifier>
+
+<default clause> ::=
+      DEFAULT <default option>
+
+<default option> ::=
+      <literal>
+    | <datetime value function>
+    | USER
+    | CURRENT_USER
+    | SESSION_USER
+    | SYSTEM_USER
+    | NULL
+
+<literal> ::=
+      <signed numeric literal>
+    | <general literal>
+
+<signed numeric literal> ::=
+    [ <sign> ] <unsigned numeric literal>
+
+<general literal> ::=
+      <character string literal>
+    | <national character string literal>
+    | <bit string literal>
+    | <hex string literal>
+    | <datetime literal>
+    | <interval literal>
+
+<datetime literal> ::=
+      <date literal>
+    | <time literal>
+    | <timestamp literal>
+
+<date literal> ::=
+    DATE <date string>
+
+<time literal> ::=
+    TIME <time string>
+
+<timestamp literal> ::=
+    TIMESTAMP <timestamp string>
+
+<interval literal> ::=
+    INTERVAL [ <sign> ] <interval string> <interval qualifier>
+
+<datetime value function> ::=
+      <current date value function>
+    | <current time value function>
+    | <current timestamp value function>
+
+<current date value function> ::= CURRENT_DATE
+
+<current time value function> ::=
+      CURRENT_TIME [ <left paren> <time precision> <right paren> ]
+
+<current timestamp value function> ::=
+      CURRENT_TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+
+<column constraint definition> ::=
+    [ <constraint name definition> ]
+    <column constraint>
+      [ <constraint attributes> ]
+
+<constraint name definition> ::= CONSTRAINT <constraint name>
+
+<constraint name> ::= <qualified name>
+
+<column constraint> ::=
+      NOT NULL
+    | <unique specification>
+    | <references specification>
+    | <check constraint definition>
+
+<unique specification> ::=
+    UNIQUE | PRIMARY KEY
+
+<references specification> ::=
+    REFERENCES <referenced table and columns>
+      [ MATCH <match type> ]
+      [ <referential triggered action> ]
+
+<referenced table and columns> ::=
+     <table name> [ <left paren> <reference column list> <right paren> ]
+
+<table name> ::=
+      <qualified name>
+    | <qualified local table name>
+
+<reference column list> ::= <column name list>
+
+<column name list> ::=
+    <column name> [ { <comma> <column name> }... ]
+
+<match type> ::=
+      FULL
+    | PARTIAL
+
+<referential triggered action> ::=
+      <update rule> [ <delete rule> ]
+    | <delete rule> [ <update rule> ]
+
+<update rule> ::= ON UPDATE <referential action>
+
+<referential action> ::=
+      CASCADE
+    | SET NULL
+    | SET DEFAULT
+    | NO ACTION
+
+<delete rule> ::= ON DELETE <referential action>
+
+<check constraint definition> ::=
+    CHECK
+        <left paren> <search condition> <right paren>
+
+<search condition> ::=
+      <boolean term>
+    | <search condition> OR <boolean term>
+
+<boolean term> ::=
+      <boolean factor>
+    | <boolean term> AND <boolean factor>
+
+<boolean factor> ::=
+    [ NOT ] <boolean test>
+
+<boolean test> ::=
+    <boolean primary> [ IS [ NOT ]
+          <truth value> ]
+
+<boolean primary> ::=
+      <predicate>
+    | <left paren> <search condition> <right paren>
+
+<predicate> ::=
+      <comparison predicate>
+    | <between predicate>
+    | <in predicate>
+    | <like predicate>
+    | <null predicate>
+    | <quantified comparison predicate>
+    | <exists predicate>
+    | <unique predicate>
+    | <match predicate>
+    | <overlaps predicate>
+
+<comparison predicate> ::=
+    <row value constructor> <comp op>
+        <row value constructor>
+
+<row value constructor> ::=
+       <row value constructor element>
+    | <left paren> <row value constructor list> <right paren>
+    | <row subquery>
+
+<row value constructor element> ::=
+      <value expression>
+    | <null specification>
+    | <default specification>
+
+<value expression> ::=
+      <numeric value expression>
+    | <string value expression>
+    | <datetime value expression>
+    | <interval value expression>
+
+<numeric value expression> ::=
+      <term>
+    | <numeric value expression> <plus sign> <term>
+    | <numeric value expression> <minus sign> <term>
+
+<term> ::=
+      <factor>
+    | <term> <asterisk> <factor>
+    | <term> <solidus> <factor>
+
+<factor> ::=
+    [ <sign> ] <numeric primary>
+
+<numeric primary> ::=
+      <value expression primary>
+    | <numeric value function>
+
+<value expression primary> ::=
+      <unsigned value specification>
+    | <column reference>
+    | <set function specification>
+    | <scalar subquery>
+    | <case expression>
+    | <left paren> <value expression> <right paren>
+    | <cast specification>
+
+<unsigned value specification> ::=
+      <unsigned literal>
+    | <general value specification>
+
+<unsigned literal> ::=
+      <unsigned numeric literal>
+    | <general literal>
+
+<general value specification> ::=
+      <parameter specification>
+    | <dynamic parameter specification>
+    | <variable specification>
+    | USER
+    | CURRENT_USER
+    | SESSION_USER
+    | SYSTEM_USER
+    | VALUE
+
+<parameter specification> ::=
+    <parameter name> [ <indicator parameter> ]
+
+<parameter name> ::= <colon> <identifier>
+
+<indicator parameter> ::=
+    [ INDICATOR ] <parameter name>
+
+<dynamic parameter specification> ::= <question mark>
+
+<variable specification> ::=
+    <embedded variable name> [ <indicator variable> ]
+
+<embedded variable name> ::=
+    <colon><host identifier>
+
+<host identifier> ::=
+      <Ada host identifier>
+    | <C host identifier>
+    | <COBOL host identifier>
+    | <Fortran host identifier>
+    | <MUMPS host identifier>
+    | <Pascal host identifier>
+    | <PL/I host identifier>
+
+<Ada host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<C host identifier> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<COBOL host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<Fortran host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<MUMPS host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<Pascal host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<PL/I host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<indicator variable> ::=
+    [ INDICATOR ] <embedded variable name>
+
+<column reference> ::= [ <qualifier> <period> ] <column name>
+
+<qualifier> ::=
+      <table name>
+    | <correlation name>
+
+<correlation name> ::= <identifier>
+
+<set function specification> ::=
+      COUNT <left paren> <asterisk> <right paren>
+    | <general set function>
+
+<general set function> ::=
+      <set function type>
+          <left paren> [ <set quantifier> ] <value expression> <right paren>
+
+<set function type> ::=
+    AVG | MAX | MIN | SUM | COUNT
+
+<set quantifier> ::= DISTINCT | ALL
+
+<scalar subquery> ::= <subquery>
+
+<subquery> ::= <left paren> <query expression> <right paren>
+
+<query expression> ::=
+      <non-join query expression>
+    | <joined table>
+
+<non-join query expression> ::=
+      <non-join query term>
+    | <query expression> UNION  [ ALL ]
+          [ <corresponding spec> ] <query term>
+    | <query expression> EXCEPT [ ALL ]
+          [ <corresponding spec> ] <query term>
+
+<non-join query term> ::=
+      <non-join query primary>
+    | <query term> INTERSECT [ ALL ]
+          [ <corresponding spec> ] <query primary>
+
+<non-join query primary> ::=
+      <simple table>
+    | <left paren> <non-join query expression> <right paren>
+
+<simple table> ::=
+      <query specification>
+    | <table value constructor>
+    | <explicit table>
+
+<query specification> ::=
+    SELECT [ <set quantifier> ] <select list> <table expression>
+
+<select list> ::=
+      <asterisk>
+    | <select sublist> [ { <comma> <select sublist> }... ]
+
+<select sublist> ::=
+      <derived column>
+    | <qualifier> <period> <asterisk>
+
+<derived column> ::= <value expression> [ <as clause> ]
+
+<as clause> ::= [ AS ] <column name>
+
+<table expression> ::=
+    <from clause>
+    [ <where clause> ]
+    [ <group by clause> ]
+    [ <having clause> ]
+
+<from clause> ::= FROM <table reference>
+    [ { <comma> <table reference> }... ]
+
+<table reference> ::=
+      <table name> [ [ AS ] <correlation name>
+          [ <left paren> <derived column list> <right paren> ] ]
+    | <derived table> [ AS ] <correlation name>
+          [ <left paren> <derived column list> <right paren> ]
+    | <joined table>
+
+<derived column list> ::= <column name list>
+
+<derived table> ::= <table subquery>
+
+<table subquery> ::= <subquery>
+
+<joined table> ::=
+      <cross join>
+    | <qualified join>
+    | <left paren> <joined table> <right paren>
+
+<cross join> ::=
+    <table reference> CROSS JOIN <table reference>
+
+<qualified join> ::=
+    <table reference> [ NATURAL ] [ <join type> ] JOIN
+      <table reference> [ <join specification> ]
+
+<join type> ::=
+      INNER
+    | <outer join type> [ OUTER ]
+    | UNION
+
+<outer join type> ::=
+      LEFT
+    | RIGHT
+    | FULL
+
+<join specification> ::=
+      <join condition>
+    | <named columns join>
+
+<join condition> ::= ON <search condition>
+
+<named columns join> ::=
+    USING <left paren> <join column list> <right paren>
+
+<join column list> ::= <column name list>
+
+<where clause> ::= WHERE <search condition>
+
+<group by clause> ::=
+    GROUP BY <grouping column reference list>
+
+<grouping column reference list> ::=
+    <grouping column reference>
+        [ { <comma> <grouping column reference> }... ]
+
+<grouping column reference> ::=
+    <column reference> [ <collate clause> ]
+
+<collate clause> ::= COLLATE <collation name>
+
+<collation name> ::= <qualified name>
+
+<having clause> ::= HAVING <search condition>
+
+<table value constructor> ::=
+    VALUES <table value constructor list>
+
+<table value constructor list> ::=
+    <row value constructor> [ { <comma> <row value constructor> }... ]
+
+<explicit table> ::= TABLE <table name>
+
+<query term> ::=
+      <non-join query term>
+    | <joined table>
+
+<corresponding spec> ::=
+    CORRESPONDING [ BY <left paren> <corresponding column list> <right paren> ]
+
+<corresponding column list> ::= <column name list>
+
+<query primary> ::=
+      <non-join query primary>
+    | <joined table>
+
+<case expression> ::=
+      <case abbreviation>
+    | <case specification>
+
+<case abbreviation> ::=
+      NULLIF <left paren> <value expression> <comma>
+            <value expression> <right paren>
+    | COALESCE <left paren> <value expression>
+            { <comma> <value expression> }... <right paren>
+
+<case specification> ::=
+      <simple case>
+    | <searched case>
+
+<simple case> ::=
+    CASE <case operand>
+      <simple when clause>...
+      [ <else clause> ]
+    END
+
+<case operand> ::= <value expression>
+
+<simple when clause> ::= WHEN <when operand> THEN <result>
+
+<when operand> ::= <value expression>
+
+<result> ::= <result expression> | NULL
+
+<result expression> ::= <value expression>
+
+<else clause> ::= ELSE <result>
+
+<searched case> ::=
+    CASE
+      <searched when clause>...
+      [ <else clause> ]
+    END
+
+<searched when clause> ::= WHEN <search condition> THEN <result>
+
+<cast specification> ::=
+    CAST <left paren> <cast operand> AS
+        <cast target> <right paren>
+
+<cast operand> ::=
+      <value expression>
+    | NULL
+
+<cast target> ::=
+      <domain name>
+    | <data type>
+
+<numeric value function> ::=
+      <position expression>
+    | <extract expression>
+    | <length expression>
+
+<position expression> ::=
+    POSITION <left paren> <character value expression>
+        IN <character value expression> <right paren>
+
+<character value expression> ::=
+      <concatenation>
+    | <character factor>
+
+<concatenation> ::=
+    <character value expression> <concatenation operator>
+        <character factor>
+
+<character factor> ::=
+    <character primary> [ <collate clause> ]
+
+<character primary> ::=
+      <value expression primary>
+    | <string value function>
+
+<string value function> ::=
+      <character value function>
+    | <bit value function>
+
+<character value function> ::=
+      <character substring function>
+    | <fold>
+    | <form-of-use conversion>
+    | <character translation>
+    | <trim function>
+
+<character substring function> ::=
+    SUBSTRING <left paren> <character value expression> FROM <start position>
+                [ FOR <string length> ] <right paren>
+
+<start position> ::= <numeric value expression>
+
+<string length> ::= <numeric value expression>
+
+<fold> ::= { UPPER | LOWER }
+     <left paren> <character value expression> <right paren>
+
+<form-of-use conversion> ::=
+    CONVERT <left paren> <character value expression>
+        USING <form-of-use conversion name> <right paren>
+
+<form-of-use conversion name> ::= <qualified name>
+
+<character translation> ::=
+    TRANSLATE <left paren> <character value expression>
+        USING <translation name> <right paren>
+
+<translation name> ::= <qualified name>
+
+<trim function> ::=
+    TRIM <left paren> <trim operands> <right paren>
+
+<trim operands> ::=
+    [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
+
+<trim specification> ::=
+      LEADING
+    | TRAILING
+    | BOTH
+
+<trim character> ::= <character value expression>
+
+<trim source> ::= <character value expression>
+
+<bit value function> ::=
+    <bit substring function>
+
+<bit substring function> ::=
+    SUBSTRING <left paren> <bit value expression> FROM <start position>
+        [ FOR <string length> ] <right paren>
+
+<bit value expression> ::=
+      <bit concatenation>
+    | <bit factor>
+
+<bit concatenation> ::=
+    <bit value expression> <concatenation operator> <bit factor>
+
+<bit factor> ::= <bit primary>
+
+<bit primary> ::=
+      <value expression primary>
+    | <string value function>
+
+<extract expression> ::=
+    EXTRACT <left paren> <extract field>
+        FROM <extract source> <right paren>
+
+<extract field> ::=
+      <datetime field>
+    | <time zone field>
+
+<datetime field> ::=
+      <non-second datetime field>
+    | SECOND
+
+<time zone field> ::=
+      TIMEZONE_HOUR
+    | TIMEZONE_MINUTE
+
+<extract source> ::=
+      <datetime value expression>
+    | <interval value expression>
+
+<datetime value expression> ::=
+      <datetime term>
+    | <interval value expression> <plus sign> <datetime term>
+    | <datetime value expression> <plus sign> <interval term>
+    | <datetime value expression> <minus sign> <interval term>
+
+<interval term> ::=
+      <interval factor>
+    | <interval term 2> <asterisk> <factor>
+    | <interval term 2> <solidus> <factor>
+    | <term> <asterisk> <interval factor>
+
+<interval factor> ::=
+    [ <sign> ] <interval primary>
+
+<interval primary> ::=
+      <value expression primary> [ <interval qualifier> ]
+
+<interval term 2> ::= <interval term>
+
+<interval value expression> ::=
+      <interval term>
+    | <interval value expression 1> <plus sign> <interval term 1>
+    | <interval value expression 1> <minus sign> <interval term 1>
+    | <left paren> <datetime value expression> <minus sign>
+          <datetime term> <right paren> <interval qualifier>
+
+<interval value expression 1> ::= <interval value expression>
+
+<interval term 1> ::= <interval term>
+
+<datetime term> ::=
+      <datetime factor>
+
+<datetime factor> ::=
+      <datetime primary> [ <time zone> ]
+
+<datetime primary> ::=
+      <value expression primary>
+    | <datetime value function>
+
+<time zone> ::=
+    AT <time zone specifier>
+
+<time zone specifier> ::=
+      LOCAL
+    | TIME ZONE <interval value expression>
+
+<length expression> ::=
+      <char length expression>
+    | <octet length expression>
+    | <bit length expression>
+
+<char length expression> ::=
+    { CHAR_LENGTH | CHARACTER_LENGTH }
+        <left paren> <string value expression> <right paren>
+
+<string value expression> ::=
+      <character value expression>
+    | <bit value expression>
+
+<octet length expression> ::=
+    OCTET_LENGTH <left paren> <string value expression> <right paren>
+
+<bit length expression> ::=
+    BIT_LENGTH <left paren> <string value expression> <right paren>
+
+<null specification> ::=
+    NULL
+
+<default specification> ::=
+    DEFAULT
+
+<row value constructor list> ::=
+    <row value constructor element>
+        [ { <comma> <row value constructor element> }... ]
+
+<row subquery> ::= <subquery>
+
+<comp op> ::=
+      <equals operator>
+    | <not equals operator>
+    | <less than operator>
+    | <greater than operator>
+    | <less than or equals operator>
+    | <greater than or equals operator>
+
+<between predicate> ::=
+    <row value constructor> [ NOT ] BETWEEN
+      <row value constructor> AND <row value constructor>
+
+<in predicate> ::=
+    <row value constructor>
+      [ NOT ] IN <in predicate value>
+
+<in predicate value> ::=
+      <table subquery>
+    | <left paren> <in value list> <right paren>
+
+<in value list> ::=
+    <value expression> { <comma> <value expression> }...
+
+<like predicate> ::=
+    <match value> [ NOT ] LIKE <pattern>
+      [ ESCAPE <escape character> ]
+
+<match value> ::= <character value expression>
+
+<pattern> ::= <character value expression>
+
+<escape character> ::= <character value expression>
+
+<null predicate> ::= <row value constructor>
+    IS [ NOT ] NULL
+
+<quantified comparison predicate> ::=
+    <row value constructor> <comp op> <quantifier> <table subquery>
+
+<quantifier> ::= <all> | <some>
+
+<all> ::= ALL
+
+<some> ::= SOME | ANY
+
+<exists predicate> ::= EXISTS <table subquery>
+
+<unique predicate> ::= UNIQUE <table subquery>
+
+<match predicate> ::=
+    <row value constructor> MATCH [ UNIQUE ]
+        [ PARTIAL | FULL ] <table subquery>
+
+<overlaps predicate> ::=
+    <row value constructor 1> OVERLAPS <row value constructor 2>
+
+<row value constructor 1> ::= <row value constructor>
+
+<row value constructor 2> ::= <row value constructor>
+
+<truth value> ::=
+      TRUE
+    | FALSE
+    | UNKNOWN
+
+<constraint attributes> ::=
+      <constraint check time> [ [ NOT ] DEFERRABLE ]
+    | [ NOT ] DEFERRABLE [ <constraint check time> ]
+
+<constraint check time> ::=
+      INITIALLY DEFERRED
+    | INITIALLY IMMEDIATE
+
+<table constraint definition> ::=
+    [ <constraint name definition> ]
+    <table constraint> [ <constraint attributes> ]
+
+<table constraint> ::=
+      <unique constraint definition>
+    | <referential constraint definition>
+    | <check constraint definition>
+
+<unique constraint definition> ::=
+            <unique specification> even in SQL3)
+    <unique specification>
+      <left paren> <unique column list> <right paren>
+
+<unique column list> ::= <column name list>
+
+<referential constraint definition> ::=
+    FOREIGN KEY
+        <left paren> <referencing columns> <right paren>
+      <references specification>
+
+<referencing columns> ::=
+    <reference column list>
+
+<module contents> ::=
+      <declare cursor>
+    | <dynamic declare cursor>
+    | <procedure>
+
+<declare cursor> ::=
+    DECLARE <cursor name> [ INSENSITIVE ] [ SCROLL ] CURSOR
+      FOR <cursor specification>
+
+<cursor name> ::= <identifier>
+
+<cursor specification> ::=
+    <query expression> [ <order by clause> ]
+      [ <updatability clause> ]
+
+<order by clause> ::=
+    ORDER BY <sort specification list>
+
+<sort specification list> ::=
+    <sort specification> [ { <comma> <sort specification> }... ]
+
+<sort specification> ::=
+    <sort key> [ <collate clause> ] [ <ordering specification> ]
+
+<sort key> ::=
+      <column name>
+    | <unsigned integer>
+
+<ordering specification> ::= ASC | DESC
+
+<updatability clause> ::=
+    FOR
+        { READ ONLY |
+          UPDATE [ OF <column name list> ] }
+
+<dynamic declare cursor> ::=
+    DECLARE <cursor name> [ INSENSITIVE ] [ SCROLL ] CURSOR
+        FOR <statement name>
+
+<statement name> ::= <identifier>
+
+<procedure> ::=
+    PROCEDURE <procedure name>
+        <parameter declaration list> <semicolon>
+      <SQL procedure statement> <semicolon>
+
+<procedure name> ::= <identifier>
+
+<parameter declaration list> ::=
+      <left paren> <parameter declaration>
+          [ { <comma> <parameter declaration> }... ] <right paren>
+    | <parameter declaration>...
+
+<parameter declaration> ::=
+      <parameter name> <data type>
+    | <status parameter>
+
+<status parameter> ::=
+    SQLCODE | SQLSTATE
+
+<SQL procedure statement> ::=
+      <SQL schema statement>
+    | <SQL data statement>
+    | <SQL transaction statement>
+    | <SQL connection statement>
+    | <SQL session statement>
+    | <SQL dynamic statement>
+    | <SQL diagnostics statement>
+
+<SQL schema statement> ::=
+      <SQL schema definition statement>
+    | <SQL schema manipulation statement>
+
+<SQL schema definition statement> ::=
+      <schema definition>
+    | <table definition>
+    | <view definition>
+    | <grant statement>
+    | <domain definition>
+    | <character set definition>
+    | <collation definition>
+    | <translation definition>
+    | <assertion definition>
+
+<schema definition> ::=
+    CREATE SCHEMA <schema name clause>
+      [ <schema character set specification> ]
+      [ <schema element>... ]
+
+<schema name clause> ::=
+      <schema name>
+    | AUTHORIZATION <schema authorization identifier>
+    | <schema name> AUTHORIZATION
+          <schema authorization identifier>
+
+<schema authorization identifier> ::=
+    <authorization identifier>
+
+<schema character set specification> ::=
+    DEFAULT CHARACTER
+        SET <character set specification>
+
+<schema element> ::=
+      <domain definition>
+    | <table definition>
+    | <view definition>
+    | <grant statement>
+    | <assertion definition>
+    | <character set definition>
+    | <collation definition>
+    | <translation definition>
+
+<domain definition> ::=
+    CREATE DOMAIN <domain name>
+        [ AS ] <data type>
+      [ <default clause> ]
+      [ <domain constraint>... ]
+      [ <collate clause> ]
+
+<domain constraint> ::=
+    [ <constraint name definition> ]
+    <check constraint definition> [ <constraint attributes> ]
+
+<table definition> ::=
+    CREATE [ { GLOBAL | LOCAL } TEMPORARY ] TABLE
+        <table name>
+      <table element list>
+      [ ON COMMIT { DELETE | PRESERVE } ROWS ]
+
+<view definition> ::=
+    CREATE VIEW <table name> [ <left paren> <view column list>
+                                  <right paren> ]
+      AS <query expression>
+      [ WITH [ <levels clause> ] CHECK OPTION ]
+
+<view column list> ::= <column name list>
+
+<levels clause> ::=
+    CASCADED | LOCAL
+
+<grant statement> ::=
+   GRANT <privileges> ON <object name>
+     TO <grantee> [ { <comma> <grantee> }... ]
+       [ WITH GRANT OPTION ]
+
+<privileges> ::=
+      ALL PRIVILEGES
+    | <action list>
+
+<action list> ::= <action> [ { <comma> <action> }... ]
+
+<action> ::=
+      SELECT
+    | DELETE
+    | INSERT [ <left paren> <privilege column list> <right paren> ]
+    | UPDATE [ <left paren> <privilege column list> <right paren> ]
+    | REFERENCES [ <left paren> <privilege column list> <right paren> ]
+    | USAGE
+
+<privilege column list> ::= <column name list>
+
+<object name> ::=
+      [ TABLE ] <table name>
+    | DOMAIN <domain name>
+    | COLLATION <collation name>
+    | CHARACTER SET <character set name>
+    | TRANSLATION <translation name>
+
+<grantee> ::=
+      PUBLIC
+    | <authorization identifier>
+
+<assertion definition> ::=
+    CREATE ASSERTION <constraint name> <assertion check>
+      [ <constraint attributes> ]
+
+<assertion check> ::=
+    CHECK
+        <left paren> <search condition> <right paren>
+
+<character set definition> ::=
+    CREATE CHARACTER SET <character set name>
+        [ AS ]
+      <character set source>
+      [ <collate clause> | <limited collation definition> ]
+
+<character set source> ::=
+      GET <existing character set name>
+
+<existing character set name> ::=
+      <standard character repertoire name>
+    | <implementation-defined character repertoire name>
+    | <schema character set name>
+
+<schema character set name> ::= <character set name>
+
+<limited collation definition> ::=
+    COLLATION FROM <collation source>
+
+<collation source> ::=
+      <collating sequence definition>
+    | <translation collation>
+
+<collating sequence definition> ::=
+      <external collation>
+    | <schema collation name>
+    | DESC <left paren> <collation name> <right paren>
+    | DEFAULT
+
+<external collation> ::=
+    EXTERNAL <left paren> <quote> <external collation name> <quote> <right paren>
+
+<external collation name> ::=
+      <standard collation name>
+    | <implementation-defined collation name>
+
+<standard collation name> ::= <collation name>
+
+<implementation-defined collation name> ::= <collation name>
+
+<schema collation name> ::= <collation name>
+
+<translation collation> ::=
+    TRANSLATION <translation name>
+        [ THEN COLLATION <collation name> ]
+
+<collation definition> ::=
+    CREATE COLLATION <collation name> FOR
+        <character set specification>
+      FROM <collation source>
+        [ <pad attribute> ]
+
+<pad attribute> ::=
+      NO PAD
+    | PAD SPACE
+
+<translation definition> ::=
+    CREATE TRANSLATION <translation name>
+      FOR <source character set specification>
+        TO <target character set specification>
+      FROM <translation source>
+
+<source character set specification> ::= <character set specification>
+
+<target character set specification> ::= <character set specification>
+
+<translation source> ::=
+      <translation specification>
+
+<translation specification> ::=
+      <external translation>
+    | IDENTITY
+    | <schema translation name>
+
+<external translation> ::=
+    EXTERNAL <left paren> <quote> <external translation name> <quote> <right paren>
+
+<external translation name> ::=
+      <standard translation name>
+    | <implementation-defined translation name>
+
+<standard translation name> ::= <translation name>
+
+<implementation-defined translation name> ::= <translation name>
+
+<schema translation name> ::= <translation name>
+
+<SQL schema manipulation statement> ::=
+      <drop schema statement>
+    | <alter table statement>
+    | <drop table statement>
+    | <drop view statement>
+    | <revoke statement>
+    | <alter domain statement>
+    | <drop domain statement>
+    | <drop character set statement>
+    | <drop collation statement>
+    | <drop translation statement>
+    | <drop assertion statement>
+
+<drop schema statement> ::=
+    DROP SCHEMA <schema name> <drop behavior>
+
+<drop behavior> ::= CASCADE | RESTRICT
+
+<alter table statement> ::=
+    ALTER TABLE <table name> <alter table action>
+
+<alter table action> ::=
+      <add column definition>
+    | <alter column definition>
+    | <drop column definition>
+    | <add table constraint definition>
+    | <drop table constraint definition>
+
+<add column definition> ::=
+    ADD [ COLUMN ] <column definition>
+
+<alter column definition> ::=
+    ALTER [ COLUMN ] <column name> <alter column action>
+
+<alter column action> ::=
+      <set column default clause>
+    | <drop column default clause>
+
+<set column default clause> ::=
+    SET <default clause>
+
+<drop column default clause> ::=
+    DROP DEFAULT
+
+<drop column definition> ::=
+    DROP [ COLUMN ] <column name> <drop behavior>
+
+<add table constraint definition> ::=
+    ADD <table constraint definition>
+
+<drop table constraint definition> ::=
+    DROP CONSTRAINT <constraint name> <drop behavior>
+
+<drop table statement> ::=
+    DROP TABLE <table name> <drop behavior>
+
+<drop view statement> ::=
+    DROP VIEW <table name> <drop behavior>
+
+<revoke statement> ::=
+    REVOKE [ GRANT OPTION FOR ]
+        <privileges>
+        ON <object name>
+      FROM <grantee> [ { <comma> <grantee> }... ] <drop behavior>
+
+<alter domain statement> ::=
+    ALTER DOMAIN <domain name> <alter domain action>
+
+<alter domain action> ::=
+      <set domain default clause>
+    | <drop domain default clause>
+    | <add domain constraint definition>
+    | <drop domain constraint definition>
+
+<set domain default clause> ::= SET <default clause>
+
+<drop domain default clause> ::= DROP DEFAULT
+
+<add domain constraint definition> ::=
+    ADD <domain constraint>
+
+<drop domain constraint definition> ::=
+    DROP CONSTRAINT <constraint name>
+
+<drop domain statement> ::=
+    DROP DOMAIN <domain name> <drop behavior>
+
+<drop character set statement> ::=
+    DROP CHARACTER SET <character set name>
+
+<drop collation statement> ::=
+    DROP COLLATION <collation name>
+
+<drop translation statement> ::=
+    DROP TRANSLATION <translation name>
+
+<drop assertion statement> ::=
+    DROP ASSERTION <constraint name>
+
+<SQL data statement> ::=
+      <open statement>
+    | <fetch statement>
+    | <close statement>
+    | <select statement: single row>
+    | <SQL data change statement>
+
+<open statement> ::=
+    OPEN <cursor name>
+
+<fetch statement> ::=
+    FETCH [ [ <fetch orientation> ] FROM ]
+      <cursor name> INTO <fetch target list>
+
+<fetch orientation> ::=
+      NEXT
+    | PRIOR
+    | FIRST
+    | LAST
+    | { ABSOLUTE | RELATIVE } <simple value specification>
+
+<simple value specification> ::=
+      <parameter name>
+    | <embedded variable name>
+    | <literal>
+
+<fetch target list> ::=
+    <target specification> [ { <comma> <target specification> }... ]
+
+<target specification> ::=
+      <parameter specification>
+    | <variable specification>
+
+<close statement> ::=
+    CLOSE <cursor name>
+
+<select statement: single row> ::=
+    SELECT [ <set quantifier> ] <select list>
+      INTO <select target list>
+        <table expression>
+
+<select target list> ::=
+    <target specification> [ { <comma> <target specification> }... ]
+
+<SQL data change statement> ::=
+      <delete statement: positioned>
+    | <delete statement: searched>
+    | <insert statement>
+    | <update statement: positioned>
+    | <update statement: searched>
+
+<delete statement: positioned> ::=
+    DELETE FROM <table name>
+      WHERE CURRENT OF <cursor name>
+
+<delete statement: searched> ::=
+    DELETE FROM <table name>
+      [ WHERE <search condition> ]
+
+<insert statement> ::=
+    INSERT INTO <table name>
+      <insert columns and source>
+
+<insert columns and source> ::=
+      [ <left paren> <insert column list> <right paren> ]
+            <query expression>
+    | DEFAULT VALUES
+
+<insert column list> ::= <column name list>
+
+<update statement: positioned> ::=
+    UPDATE <table name>
+      SET <set clause list>
+        WHERE CURRENT OF <cursor name>
+
+<set clause list> ::=
+    <set clause> [ { <comma> <set clause> }... ]
+
+<set clause> ::=
+    <object column> <equals operator> <update source>
+
+<object column> ::= <column name>
+
+<update source> ::=
+      <value expression>
+    | <null specification>
+    | DEFAULT
+
+<update statement: searched> ::=
+    UPDATE <table name>
+      SET <set clause list>
+      [ WHERE <search condition> ]
+
+<SQL transaction statement> ::=
+      <set transaction statement>
+    | <set constraints mode statement>
+    | <commit statement>
+    | <rollback statement>
+
+<set transaction statement> ::=
+    SET TRANSACTION <transaction mode>
+        [ { <comma> <transaction mode> }... ]
+
+<transaction mode> ::=
+      <isolation level>
+    | <transaction access mode>
+    | <diagnostics size>
+
+<isolation level> ::=
+    ISOLATION LEVEL <level of isolation>
+
+<level of isolation> ::=
+      READ UNCOMMITTED
+    | READ COMMITTED
+    | REPEATABLE READ
+    | SERIALIZABLE
+
+<transaction access mode> ::=
+      READ ONLY
+    | READ WRITE
+
+<diagnostics size> ::=
+    DIAGNOSTICS SIZE <number of conditions>
+
+<number of conditions> ::= <simple value specification>
+
+<set constraints mode statement> ::=
+    SET CONSTRAINTS <constraint name list>
+        { DEFERRED | IMMEDIATE }
+
+<constraint name list> ::=
+      ALL
+    | <constraint name> [ { <comma> <constraint name> }... ]
+
+<commit statement> ::=
+    COMMIT [ WORK ]
+
+<rollback statement> ::=
+    ROLLBACK [ WORK ]
+
+<SQL connection statement> ::=
+      <connect statement>
+    | <set connection statement>
+    | <disconnect statement>
+
+<connect statement> ::=
+    CONNECT TO <connection target>
+
+<connection target> ::=
+      <SQL-server name>
+        [ AS <connection name> ]
+          correspondence with Tony Gordon)
+        [ USER <user name> ]
+    | DEFAULT
+
+<SQL-server name> ::= <simple value specification>
+
+<connection name> ::= <simple value specification>
+
+<user name> ::= <simple value specification>
+
+<set connection statement> ::=
+    SET CONNECTION <connection object>
+
+<connection object> ::=
+      DEFAULT
+    | <connection name>
+
+<disconnect statement> ::=
+    DISCONNECT <disconnect object>
+
+<disconnect object> ::=
+      <connection object>
+    | ALL
+    | CURRENT
+
+<SQL session statement> ::=
+      <set catalog statement>
+    | <set schema statement>
+    | <set names statement>
+    | <set session authorization identifier statement>
+    | <set local time zone statement>
+
+<set catalog statement> ::=
+    SET CATALOG <value specification>
+
+<value specification> ::=
+      <literal>
+    | <general value specification>
+
+<set schema statement> ::=
+    SET SCHEMA <value specification>
+
+<set names statement> ::=
+    SET NAMES <value specification>
+
+<set session authorization identifier statement> ::=
+    SET SESSION AUTHORIZATION
+        <value specification>
+
+<set local time zone statement> ::=
+    SET TIME ZONE
+        <set time zone value>
+
+<set time zone value> ::=
+      <interval value expression>
+    | LOCAL
+
+<SQL dynamic statement> ::=
+      <system descriptor statement>
+    | <prepare statement>
+    | <deallocate prepared statement>
+    | <describe statement>
+    | <execute statement>
+    | <execute immediate statement>
+    | <SQL dynamic data statement>
+
+<system descriptor statement> ::=
+      <allocate descriptor statement>
+    | <deallocate descriptor statement>
+    | <set descriptor statement>
+    | <get descriptor statement>
+
+<allocate descriptor statement> ::=
+    ALLOCATE DESCRIPTOR <descriptor name>
+       [ WITH MAX <occurrences> ]
+
+<descriptor name> ::=
+    [ <scope option> ] <simple value specification>
+
+<scope option> ::=
+      GLOBAL
+    | LOCAL
+
+<occurrences> ::= <simple value specification>
+
+<deallocate descriptor statement> ::=
+    DEALLOCATE DESCRIPTOR <descriptor name>
+
+<set descriptor statement> ::=
+    SET DESCRIPTOR <descriptor name>
+        <set descriptor information>
+
+<set descriptor information> ::=
+      <set count>
+    | VALUE <item number>
+        <set item information> [ { <comma> <set item information> }... ]
+
+<set count> ::=
+    COUNT <equals operator> <simple value specification 1>
+
+<simple value specification 1> ::= <simple value specification>
+
+<item number> ::= <simple value specification>
+
+<set item information> ::=
+    <descriptor item name> <equals operator> <simple value specification 2>
+
+<descriptor item name> ::=
+      TYPE
+    | LENGTH
+    | OCTET_LENGTH
+    | RETURNED_LENGTH
+    | RETURNED_OCTET_LENGTH
+    | PRECISION
+    | SCALE
+    | DATETIME_INTERVAL_CODE
+    | DATETIME_INTERVAL_PRECISION
+    | NULLABLE
+    | INDICATOR
+    | DATA
+    | NAME
+    | UNNAMED
+    | COLLATION_CATALOG
+    | COLLATION_SCHEMA
+    | COLLATION_NAME
+    | CHARACTER_SET_CATALOG
+    | CHARACTER_SET_SCHEMA
+    | CHARACTER_SET_NAME
+
+<simple value specification 2> ::= <simple value specification>
+
+<item number> ::= <simple value specification>
+
+<get descriptor statement> ::=
+    GET DESCRIPTOR <descriptor name> <get descriptor information>
+
+<get descriptor information> ::=
+      <get count>
+    | VALUE <item number>
+        <get item information> [ { <comma> <get item information> }... ]
+
+<get count> ::=
+    <simple target specification 1> <equals operator>
+         COUNT
+
+<simple target specification 1> ::= <simple target specification>
+
+<simple target specification> ::=
+      <parameter name>
+    | <embedded variable name>
+
+<get item information> ::=
+    <simple target specification 2> <equals operator> <descriptor item name>>
+
+<simple target specification 2> ::= <simple target specification>
+
+<prepare statement> ::=
+    PREPARE <SQL statement name> FROM <SQL statement variable>
+
+<SQL statement name> ::=
+      <statement name>
+    | <extended statement name>
+
+<extended statement name> ::=
+    [ <scope option> ] <simple value specification>
+
+<SQL statement variable> ::= <simple value specification>
+
+<deallocate prepared statement> ::=
+    DEALLOCATE PREPARE <SQL statement name>
+
+<describe statement> ::=
+      <describe input statement>
+    | <describe output statement>
+
+<describe input statement> ::=
+    DESCRIBE INPUT <SQL statement name> <using descriptor>
+
+<using descriptor> ::=
+    { USING | INTO } SQL DESCRIPTOR <descriptor name>
+
+<describe output statement> ::=
+    DESCRIBE [ OUTPUT ] <SQL statement name> <using descriptor>
+
+<execute statement> ::=
+    EXECUTE <SQL statement name>
+      [ <result using clause> ]
+      [ <parameter using clause> ]
+
+<result using clause> ::= <using clause>
+
+<using clause> ::=
+      <using arguments>
+    | <using descriptor>
+
+<using arguments> ::=
+    { USING | INTO } <argument> [ { <comma> <argument> }... ]
+
+<argument> ::= <target specification>
+
+<parameter using clause> ::= <using clause>
+
+<execute immediate statement> ::=
+    EXECUTE IMMEDIATE <SQL statement variable>
+
+<SQL dynamic data statement> ::=
+      <allocate cursor statement>
+    | <dynamic open statement>
+    | <dynamic fetch statement>
+    | <dynamic close statement>
+    | <dynamic delete statement: positioned>
+    | <dynamic update statement: positioned>
+
+<allocate cursor statement> ::=
+    ALLOCATE <extended cursor name> [ INSENSITIVE ]
+        [ SCROLL ] CURSOR
+      FOR <extended statement name>
+
+<extended cursor name> ::=
+    [ <scope option> ] <simple value specification>
+
+<dynamic open statement> ::=
+    OPEN <dynamic cursor name> [ <using clause> ]
+
+<dynamic cursor name> ::=
+      <cursor name>
+    | <extended cursor name>
+
+<dynamic fetch statement> ::=
+    FETCH [ [ <fetch orientation> ] FROM ] <dynamic cursor name>
+        <using clause>
+
+<dynamic close statement> ::=
+    CLOSE <dynamic cursor name>
+
+<dynamic delete statement: positioned> ::=
+    DELETE FROM <table name>
+      WHERE CURRENT OF
+          <dynamic cursor name>
+
+<dynamic update statement: positioned> ::=
+    UPDATE <table name>
+      SET <set clause>
+          [ { <comma> <set clause> }... ]
+        WHERE CURRENT OF
+            <dynamic cursor name>
+
+<SQL diagnostics statement> ::=
+    <get diagnostics statement>
+
+<get diagnostics statement> ::=
+    GET DIAGNOSTICS <sql diagnostics information>
+
+<sql diagnostics information> ::=
+      <statement information>
+    | <condition information>
+
+<statement information> ::=
+    <statement information item> [ { <comma> <statement information item> }... ]
+
+<statement information item> ::=
+    <simple target specification> <equals operator> <statement information item name>
+
+<statement information item name> ::=
+      NUMBER
+    | MORE
+    | COMMAND_FUNCTION
+    | DYNAMIC_FUNCTION
+    | ROW_COUNT
+
+<condition information> ::=
+    EXCEPTION <condition number>
+      <condition information item> [ { <comma> <condition information item> }... ]
+
+<condition number> ::= <simple value specification>
+
+<condition information item> ::=
+    <simple target specification> <equals operator> <condition information item name>
+
+<condition information item name> ::=
+      CONDITION_NUMBER
+    | RETURNED_SQLSTATE
+    | CLASS_ORIGIN
+    | SUBCLASS_ORIGIN
+    | SERVER_NAME
+    | CONNECTION_NAME
+    | CONSTRAINT_CATALOG
+    | CONSTRAINT_SCHEMA
+    | CONSTRAINT_NAME
+    | CATALOG_NAME
+    | SCHEMA_NAME
+    | TABLE_NAME
+    | COLUMN_NAME
+    | CURSOR_NAME
+    | MESSAGE_TEXT
+    | MESSAGE_LENGTH
+    | MESSAGE_OCTET_LENGTH
+
+<embedded SQL host program> ::=
+      <embedded SQL Ada program>
+    | <embedded SQL C program>
+    | <embedded SQL COBOL program>
+    | <embedded SQL Fortran program>
+    | <embedded SQL MUMPS program>
+    | <embedded SQL Pascal program>
+    | <embedded SQL PL/I program>
+
+<embedded SQL Ada program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL C program> ::=
+      !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL COBOL program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL Fortran program> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL MUMPS program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL Pascal program> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL PL/I program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL declare section> ::=
+      <embedded SQL begin declare>
+        [ <embedded character set declaration> ]
+        [ <host variable definition>... ]
+      <embedded SQL end declare>
+    | <embedded SQL MUMPS declare>
+
+<embedded SQL begin declare> ::=
+    <SQL prefix> BEGIN DECLARE SECTION
+        [ <SQL terminator> ]
+
+<SQL prefix> ::=
+      EXEC SQL
+    | <ampersand>SQL<left paren>
+
+<SQL terminator> ::=
+      END-EXEC
+    | <semicolon>
+    | <right paren>
+
+<embedded character set declaration> ::=
+    SQL NAMES ARE <character set specification>
+
+<host variable definition> ::=
+      <Ada variable definition>
+    | <C variable definition>
+    | <COBOL variable definition>
+    | <Fortran variable definition>
+    | <MUMPS variable definition>
+    | <Pascal variable definition>
+    | <PL/I variable definition>
+
+<Ada variable definition> ::=
+    <Ada host identifier> [ { <comma> <Ada host identifier> }... ] :
+    <Ada type specification> [ <Ada initial value> ]
+
+<Ada type specification> ::=
+      <Ada qualified type specification>
+    | <Ada unqualified type specification>
+
+<Ada qualified type specification> ::=
+      SQL_STANDARD.CHAR [ CHARACTER SET
+         [ IS ] <character set specification> ]
+          <left paren> 1 <double period> <length> <right paren>
+    | SQL_STANDARD.BIT
+          <left paren> 1 <double period> <length> <right paren>
+    | SQL_STANDARD.SMALLINT
+    | SQL_STANDARD.INT
+    | SQL_STANDARD.REAL
+    | SQL_STANDARD.DOUBLE_PRECISION
+    | SQL_STANDARD.SQLCODE_TYPE
+    | SQL_STANDARD.SQLSTATE_TYPE
+    | SQL_STANDARD.INDICATOR_TYPE
+
+<Ada unqualified type specification> ::=
+      CHAR
+          <left paren> 1 <double period> <length> <right paren>
+    | BIT
+          <left paren> 1 <double period> <length> <right paren>
+    | SMALLINT
+    | INT
+    | REAL
+    | DOUBLE_PRECISION
+    | SQLCODE_TYPE
+    | SQLSTATE_TYPE
+    | INDICATOR_TYPE
+
+<Ada initial value> ::=
+    <Ada assignment operator> <character representation>...
+
+<Ada assignment operator> ::= <colon><equals operator>
+
+<C variable definition> ::=
+      [ <C storage class> ]
+      [ <C class modifier> ]
+      <C variable specification>
+    <semicolon>
+
+<C storage class> ::=
+      auto
+    | extern
+    | static
+
+<C class modifier> ::= const | volatile
+
+<C variable specification> ::=
+      <C numeric variable>
+    | <C character variable>
+    | <C derived variable>
+
+<C numeric variable> ::=
+    { long | short | float | double }
+      <C host identifier> [ <C initial value> ]
+            [ { <comma> <C host identifier> [ <C initial value> ] }... ]
+
+<C initial value> ::=
+    <equals operator> <character representation>...
+
+<C character variable> ::=
+    char [ CHARACTER SET
+             [ IS ] <character set specification> ]
+      <C host identifier>
+        <C array specification> [ <C initial value> ]
+        [ { <comma> <C host identifier>
+          <C array specification>
+                 [ <C initial value> ] }... ]
+
+<C array specification> ::=
+    <left bracket> <length> <right bracket>
+
+<C derived variable> ::=
+      <C VARCHAR variable>
+    | <C bit variable>
+
+<C VARCHAR variable> ::=
+    VARCHAR [ CHARACTER SET [ IS ]
+        <character set specification> ]
+        <C host identifier>
+            <C array specification> [ <C initial value> ]
+          [ { <comma> <C host identifier>
+              <C array specification>
+                      [ <C initial value> ] }... ]
+
+<C bit variable> ::=
+    BIT <C host identifier>
+        <C array specification> [ <C initial value> ]
+      [ { <comma> <C host identifier>
+        <C array specification>
+                   [ <C initial value> ] }... ]
+
+<COBOL variable definition> ::=
+    {01|77} <COBOL host identifier> <COBOL type specification>
+      [ <character representation>... ] <period>
+
+<COBOL type specification> ::=
+      <COBOL character type>
+    | <COBOL bit type>
+    | <COBOL numeric type>
+    | <COBOL integer type>
+
+<COBOL character type> ::=
+    [ CHARACTER SET [ IS ]
+          <character set specification> ]
+    { PIC | PICTURE } [ IS ] { X [ <left paren> <length> <right paren> ] }...
+
+<COBOL bit type> ::=
+    { PIC | PICTURE } [ IS ]
+        { B [ <left paren> <length> <right paren> ] }...
+
+<COBOL numeric type> ::=
+    { PIC | PICTURE } [ IS ]
+      S <COBOL nines specification>
+    [ USAGE [ IS ] ] DISPLAY SIGN LEADING SEPARATE
+
+<COBOL nines specification> ::=
+      <COBOL nines> [ V [ <COBOL nines> ] ]
+    | V <COBOL nines>
+
+<COBOL nines> ::= { 9 [ <left paren> <length> <right paren> ] }...
+
+<COBOL integer type> ::=
+      <COBOL computational integer>
+    | <COBOL binary integer>
+
+<COBOL computational integer> ::=
+    { PIC | PICTURE } [ IS ] S<COBOL nines>
+      [ USAGE [ IS ] ] { COMP | COMPUTATIONAL }
+
+<COBOL binary integer> ::=
+    { PIC | PICTURE } [ IS ] S<COBOL nines>
+      [ USAGE [ IS ] ] BINARY
+
+<Fortran variable definition> ::=
+    <Fortran type specification>
+    <Fortran host identifier>
+        [ { <comma> <Fortran host identifier> }... ]
+
+<Fortran type specification> ::=
+      CHARACTER [ <asterisk> <length> ]
+          [ CHARACTER SET [ IS ]
+                <character set specification> ]
+    | BIT [ <asterisk> <length> ]
+    | INTEGER
+    | REAL
+    | DOUBLE PRECISION
+
+<MUMPS variable definition> ::=
+    { <MUMPS numeric variable> | <MUMPS character variable> }
+        <semicolon>
+
+<MUMPS numeric variable> ::=
+    <MUMPS type specification>
+      <MUMPS host identifier> [ { <comma> <MUMPS host identifier> }... ]
+
+<MUMPS type specification> ::=
+      INT
+    | DEC
+          [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | REAL
+
+<MUMPS character variable> ::=
+    VARCHAR <MUMPS host identifier> <MUMPS length specification>
+      [ { <comma> <MUMPS host identifier> <MUMPS length specification> }... ]
+
+<MUMPS length specification> ::=
+    <left paren> <length> <right paren>
+
+<Pascal variable definition> ::=
+    <Pascal host identifier> [ { <comma> <Pascal host identifier> }... ] <colon>
+      <Pascal type specification> <semicolon>
+
+<Pascal type specification> ::=
+      PACKED ARRAY
+          <left bracket> 1 <double period> <length> <right bracket>
+        OF CHAR
+          [ CHARACTER SET [ IS ]
+                <character set specification> ]
+    | PACKED ARRAY
+          <left bracket> 1 <double period> <length> <right bracket>
+        OF BIT
+    | INTEGER
+    | REAL
+    | CHAR [ CHARACTER SET
+                                [ IS ] <character set specification> ]
+    | BIT
+
+<PL/I variable definition> ::=
+    {DCL | DECLARE}
+        {   <PL/I host identifier>
+          | <left paren> <PL/I host identifier>
+                [ { <comma> <PL/I host identifier> }... ] <right paren> }
+    <PL/I type specification>
+    [ <character representation>... ] <semicolon>
+
+<PL/I type specification> ::=
+      { CHAR | CHARACTER } [ VARYING ]
+          <left paren><length><right paren>
+          [ CHARACTER SET
+                [ IS ] <character set specification> ]
+    | BIT [ VARYING ] <left paren><length><right paren>
+    | <PL/I type fixed decimal> <left paren> <precision>
+          [ <comma> <scale> ] <right paren>
+    | <PL/I type fixed binary> [ <left paren> <precision> <right paren> ]
+    | <PL/I type float binary> <left paren> <precision> <right paren>
+
+<PL/I type fixed decimal> ::=
+      { DEC | DECIMAL } FIXED
+    | FIXED { DEC | DECIMAL }
+
+<PL/I type fixed binary> ::=
+      { BIN | BINARY } FIXED
+    | FIXED { BIN | BINARY }
+
+<PL/I type float binary> ::=
+      { BIN | BINARY } FLOAT
+    | FLOAT { BIN | BINARY }
+
+<embedded SQL end declare> ::=
+    <SQL prefix> END DECLARE SECTION
+        [ <SQL terminator> ]
+
+<embedded SQL MUMPS declare> ::=
+    <SQL prefix>
+      BEGIN DECLARE SECTION
+        [ <embedded character set declaration> ]
+        [ <host variable definition>... ]
+      END DECLARE SECTION
+    <SQL terminator>
+
+<embedded SQL statement> ::=
+    <SQL prefix>
+      <statement or declaration>
+    [ <SQL terminator> ]
+
+<statement or declaration> ::=
+      <declare cursor>
+    | <dynamic declare cursor>
+    | <temporary table declaration>
+    | <embedded exception declaration>
+    | <SQL procedure statement>
+
+<embedded exception declaration> ::=
+    WHENEVER <condition> <condition action>
+
+<condition> ::=
+    SQLERROR | NOT FOUND
+
+<condition action> ::=
+    CONTINUE | <go to>
+
+<go to> ::=
+    { GOTO | GO TO } <goto target>
+
+<goto target> ::=
+      <host label identifier>
+    | <unsigned integer>
+    | <host PL/I label variable>
+
+<host label identifier> ::= !!<EMPHASIS>(See the Syntax Rules.)
+
+<host PL/I label variable> ::= !!<EMPHASIS>(See the Syntax Rules.)
+
+<preparable statement> ::=
+      <preparable SQL data statement>
+    | <preparable SQL schema statement>
+    | <preparable SQL transaction statement>
+    | <preparable SQL session statement>
+    | <preparable implementation-defined statement>
+
+<preparable SQL data statement> ::=
+      <delete statement: searched>
+    | <dynamic single row select statement>
+    | <insert statement>
+    | <dynamic select statement>
+    | <update statement: searched>
+    | <preparable dynamic delete statement: positioned>
+    | <preparable dynamic update statement: positioned>
+
+<dynamic single row select statement> ::= <query specification>
+
+<dynamic select statement> ::= <cursor specification>
+
+<preparable dynamic delete statement: positioned> ::=
+   DELETE [ FROM <table name> ]
+      WHERE CURRENT OF <cursor name>
+
+<preparable dynamic update statement: positioned> ::=
+   UPDATE [ <table name> ]
+      SET <set clause list>
+      WHERE CURRENT OF <cursor name>
+
+<preparable SQL schema statement> ::=
+      <SQL schema statement>
+
+<preparable SQL transaction statement> ::=
+      <SQL transaction statement>
+
+<preparable SQL session statement> ::=
+      <SQL session statement>
+
+<preparable implementation-defined statement> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<direct SQL statement> ::=
+    <directly executable statement> <semicolon>
+
+<directly executable statement> ::=
+      <direct SQL data statement>
+    | <SQL schema statement>
+    | <SQL transaction statement>
+    | <SQL connection statement>
+    | <SQL session statement>
+    | <direct implementation-defined statement>
+
+<direct SQL data statement> ::=
+      <delete statement: searched>
+    | <direct select statement: multiple rows>
+    | <insert statement>
+    | <update statement: searched>
+    | <temporary table declaration>
+
+<direct select statement: multiple rows> ::=
+    <query expression> [ <order by clause> ]
+
+<direct implementation-defined statement> ::=
+    !!<EMPHASIS>(See the Syntax Rules)
+
+<SQL object identifier> ::=
+    <SQL provenance> <SQL variant>
+
+<SQL provenance> ::= <arc1> <arc2> <arc3>
+
+<arc1> ::= iso | 1 | iso <left paren> 1 <right paren>
+
+<arc2> ::= standard | 0 | standard <left paren> 0 <right paren>
+
+<arc3> ::= 9075
+
+<SQL variant> ::= <SQL edition> <SQL conformance>
+
+<SQL edition> ::= <1987> | <1989> | <1992>
+
+<1987> ::= 0 | edition1987 <left paren> 0 <right paren>
+
+<1989> ::= <1989 base> <1989 package>
+
+<1989 base> ::= 1 | edition1989 <left paren> 1 <right paren>
+
+<1989 package> ::= <integrity no> | <integrity yes>
+
+<integrity no> ::= 0 | IntegrityNo <left paren> 0 <right paren>
+
+<integrity yes> ::= 1 | IntegrityYes <left paren> 1 <right paren>
+
+<1992> ::= 2 | edition1992 <left paren> 2 <right paren>
+
+<SQL conformance> ::= <low> | <intermediate> | <high>
+
+<low> ::= 0 | Low <left paren> 0 <right paren>
+
+<intermediate> ::= 1 | Intermediate <left paren> 1 <right paren>
+
+<high> ::= 2 | High <left paren> 2 <right paren>
+


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/classes/sql2bnf.aug92.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.BasicDdlParserTest.xml
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.BasicDdlParserTest.xml	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.BasicDdlParserTest.xml	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuite failures="0" time="0.162" errors="1" skipped="0" tests="5" name="org.jboss.dna.sequencer.ddl.BasicDdlParserTest">
+  <properties>
+    <property name="java.vendor" value="Sun Microsystems Inc."/>
+    <property name="env.SESSION_MANAGER" value="local/work-laptop:/tmp/.ICE-unix/5913"/>
+    <property name="localRepository" value="/home/blafond/.m2/repository"/>
+    <property name="sun.java.launcher" value="SUN_STANDARD"/>
+    <property name="sun.management.compiler" value="HotSpot Tiered Compilers"/>
+    <property name="os.name" value="Linux"/>
+    <property name="sun.boot.class.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/resources.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/rt.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/jsse.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/jce.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/charsets.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/classes"/>
+    <property name="env.PWD" value="/home/blafond"/>
+    <property name="env.XDG_SESSION_COOKIE" value="70bef359dccaf87f474bc0d7490f06a9-1253116029.282562-1962947517"/>
+    <property name="sun.desktop" value="gnome"/>
+    <property name="env.LANG" value="en_US.UTF-8"/>
+    <property name="java.vm.specification.vendor" value="Sun Microsystems Inc."/>
+    <property name="java.runtime.version" value="1.6.0_14-b08"/>
+    <property name="env.DISPLAY" value=":0.0"/>
+    <property name="user.name" value="blafond"/>
+    <property name="env.USER" value="blafond"/>
+    <property name="env.SHELL" value="/bin/bash"/>
+    <property name="env.MOZILLA_FIVE_HOME" value="/usr/lib/xulrunner-addons"/>
+    <property name="env.DESKTOP_SESSION" value="default"/>
+    <property name="env.XDG_DATA_DIRS" value="/usr/local/share/:/usr/share/:/usr/share/gdm/"/>
+    <property name="env.NLSPATH" value="/usr/dt/lib/nls/msg/%L/%N.cat"/>
+    <property name="env.PATH" value="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"/>
+    <property name="user.language" value="en"/>
+    <property name="sun.boot.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386"/>
+    <property name="classworlds.conf" value="/home/blafond/Workspaces/DNA/.metadata/.plugins/org.maven.ide.eclipse/launches/m2conf7839333797857365463.tmp"/>
+    <property name="java.version" value="1.6.0_14"/>
+    <property name="user.timezone" value="US/Central"/>
+    <property name="env.DBUS_SESSION_BUS_ADDRESS" value="unix:abstract=/tmp/dbus-Cmyp7m4oLS,guid=f006087070ce2e1077eb8f884ab1087e"/>
+    <property name="sun.arch.data.model" value="32"/>
+    <property name="java.endorsed.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/endorsed"/>
+    <property name="sun.cpu.isalist" value=""/>
+    <property name="sun.jnu.encoding" value="UTF-8"/>
+    <property name="file.encoding.pkg" value="sun.io"/>
+    <property name="file.separator" value="/"/>
+    <property name="java.specification.name" value="Java Platform API Specification"/>
+    <property name="env.GNOME_DESKTOP_SESSION_ID" value="Default"/>
+    <property name="java.class.version" value="50.0"/>
+    <property name="env.GDMSESSION" value="default"/>
+    <property name="user.country" value="US"/>
+    <property name="env.GNOME_KEYRING_PID" value="5912"/>
+    <property name="java.home" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre"/>
+    <property name="java.vm.info" value="mixed mode"/>
+    <property name="env.LOGNAME" value="blafond"/>
+    <property name="os.version" value="2.6.24-24-generic"/>
+    <property name="path.separator" value=":"/>
+    <property name="java.vm.version" value="14.0-b16"/>
+    <property name="java.awt.printerjob" value="sun.print.PSPrinterJob"/>
+    <property name="env.WINDOWPATH" value="7"/>
+    <property name="sun.io.unicode.encoding" value="UnicodeLittle"/>
+    <property name="env.GDM_LANG" value="en_US.UTF-8"/>
+    <property name="user.home" value="/home/blafond"/>
+    <property name="java.specification.vendor" value="Sun Microsystems Inc."/>
+    <property name="java.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386/server:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/../lib/i386:/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/java/packages/lib/i386:/lib:/usr/lib"/>
+    <property name="java.vendor.url" value="http://java.sun.com/"/>
+    <property name="env.GDM_XSERVER_LOCATION" value="local"/>
+    <property name="java.vm.vendor" value="Sun Microsystems Inc."/>
+    <property name="maven.home" value="EMBEDDED"/>
+    <property name="java.runtime.name" value="Java(TM) SE Runtime Environment"/>
+    <property name="java.class.path" value="/home/blafond/Programs/eclipse-SDK-3.5/plugins/org.maven.ide.components.maven_embedder_2.1.0.20080530-2300/jars/plexus-classworlds-1.2-alpha-12.jar"/>
+    <property name="env.XFILESEARCHPATH" value="/usr/dt/app-defaults/%L/Dt"/>
+    <property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
+    <property name="java.vm.specification.version" value="1.0"/>
+    <property name="m2eclipse.workspace.state" value="/home/blafond/Workspaces/DNA/.metadata/.plugins/org.maven.ide.eclipse/workspacestate.properties"/>
+    <property name="sun.cpu.endian" value="little"/>
+    <property name="env.GTK_RC_FILES" value="/etc/gtk/gtkrc:/home/blafond/.gtkrc-1.2-gnome2"/>
+    <property name="sun.os.patch.level" value="unknown"/>
+    <property name="env.HOME" value="/home/blafond"/>
+    <property name="surefire.test.class.path" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target/test-classes:/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target/classes:/home/blafond/.m2/repository/hsqldb/hsqldb/1.8.0.2/hsqldb-1.8.0.2.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-common/0.7-SNAPSHOT/dna-common-0.7-SNAPSHOT.jar:/home/blafond/.m2/repository/org/slf4j/slf4j-api/1.5.8/slf4j-api-1.5.8.jar:/home/blafond/.m2/repository/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-graph/0.7-SNAPSHOT/dna-graph-0.7-SNAPSHOT.jar:/home/blafond/.m2/repository/joda-time/joda-time/1.4/joda-time-1.4.jar:/home/blafond/.m2/repository/com/google/code/google-collections/google-collect/snapshot-20080530/google-collect-snapshot-20080530.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-common/0.7-SNAPSHOT/dna-common-0.7-SNAPSHOT-tests.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-graph/!
 0.7-SNAPSHOT/dna-graph-0.7-SNAPSHOT-tests.jar:/home/blafond/.m2/repository/junit/junit/4.4/junit-4.4.jar:/home/blafond/.m2/repository/org/hamcrest/hamcrest-library/1.1/hamcrest-library-1.1.jar:/home/blafond/.m2/repository/org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar:/home/blafond/.m2/repository/org/mockito/mockito-all/1.5/mockito-all-1.5.jar:/home/blafond/.m2/repository/org/slf4j/slf4j-log4j12/1.5.8/slf4j-log4j12-1.5.8.jar:/home/blafond/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar:"/>
+    <property name="java.io.tmpdir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target"/>
+    <property name="env.LD_LIBRARY_PATH" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386/server:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/../lib/i386:/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons"/>
+    <property name="java.vendor.url.bug" value="http://java.sun.com/cgi-bin/bugreport.cgi"/>
+    <property name="env.SSH_AUTH_SOCK" value="/tmp/keyring-dF941n/ssh"/>
+    <property name="java.awt.graphicsenv" value="sun.awt.X11GraphicsEnvironment"/>
+    <property name="os.arch" value="i386"/>
+    <property name="java.ext.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/ext:/usr/java/packages/lib/ext"/>
+    <property name="user.dir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl"/>
+    <property name="env.XAUTHORITY" value="/home/blafond/.Xauthority"/>
+    <property name="line.separator" value="
+"/>
+    <property name="java.vm.name" value="Java HotSpot(TM) Server VM"/>
+    <property name="basedir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl"/>
+    <property name="maven.mode.standalone" value="true"/>
+    <property name="env.USERNAME" value="blafond"/>
+    <property name="file.encoding" value="UTF-8"/>
+    <property name="env.GNOME_KEYRING_SOCKET" value="/tmp/keyring-dF941n/socket"/>
+    <property name="env.GPG_AGENT_INFO" value="/tmp/seahorse-ZO5q59/S.gpg-agent:5994:1"/>
+    <property name="java.specification.version" value="1.6"/>
+  </properties>
+  <testcase time="0.04" classname="org.jboss.dna.sequencer.ddl.BasicDdlParserTest" name="shouldParseColumnDefinition"/>
+  <testcase time="0.038" classname="org.jboss.dna.sequencer.ddl.BasicDdlParserTest" name="shouldGetTableElementString"/>
+  <testcase time="0.028" classname="org.jboss.dna.sequencer.ddl.BasicDdlParserTest" name="shouldParseCreateTable"/>
+  <testcase time="0.027" classname="org.jboss.dna.sequencer.ddl.BasicDdlParserTest" name="shouldParseCreateTableWithConstraint"/>
+  <testcase time="0.022" classname="org.jboss.dna.sequencer.ddl.BasicDdlParserTest" name="shouldParseCreateTables">
+    <error type="java.lang.NullPointerException">java.lang.NullPointerException
+	at org.jboss.dna.sequencer.ddl.BasicDdlParserTest.getFileContent(BasicDdlParserTest.java:59)
+	at org.jboss.dna.sequencer.ddl.BasicDdlParserTest.shouldParseCreateTables(BasicDdlParserTest.java:159)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
+	at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
+	at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
+	at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
+	at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
+	at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
+	at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
+	at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
+	at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
+	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
+	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
+	at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
+	at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
+	at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:338)
+	at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:997)
+</error>
+    <system-out>   Getting Content for File = src/test/resources/ddl/createTables.ddl
+java.io.FileNotFoundException: src/test/resources/ddl/createTables.ddl (No such file or directory)</system-out>
+  </testcase>
+</testsuite>
\ No newline at end of file


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.BasicDdlParserTest.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.xml
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.xml	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.xml	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuite failures="0" time="0.276" errors="0" skipped="0" tests="28" name="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest">
+  <properties>
+    <property name="java.vendor" value="Sun Microsystems Inc."/>
+    <property name="env.SESSION_MANAGER" value="local/work-laptop:/tmp/.ICE-unix/5913"/>
+    <property name="localRepository" value="/home/blafond/.m2/repository"/>
+    <property name="sun.java.launcher" value="SUN_STANDARD"/>
+    <property name="sun.management.compiler" value="HotSpot Tiered Compilers"/>
+    <property name="os.name" value="Linux"/>
+    <property name="sun.boot.class.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/resources.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/rt.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/jsse.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/jce.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/charsets.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/classes"/>
+    <property name="env.PWD" value="/home/blafond"/>
+    <property name="env.XDG_SESSION_COOKIE" value="70bef359dccaf87f474bc0d7490f06a9-1253116029.282562-1962947517"/>
+    <property name="sun.desktop" value="gnome"/>
+    <property name="env.LANG" value="en_US.UTF-8"/>
+    <property name="java.vm.specification.vendor" value="Sun Microsystems Inc."/>
+    <property name="java.runtime.version" value="1.6.0_14-b08"/>
+    <property name="env.DISPLAY" value=":0.0"/>
+    <property name="user.name" value="blafond"/>
+    <property name="env.USER" value="blafond"/>
+    <property name="env.SHELL" value="/bin/bash"/>
+    <property name="env.MOZILLA_FIVE_HOME" value="/usr/lib/xulrunner-addons"/>
+    <property name="env.DESKTOP_SESSION" value="default"/>
+    <property name="env.XDG_DATA_DIRS" value="/usr/local/share/:/usr/share/:/usr/share/gdm/"/>
+    <property name="env.NLSPATH" value="/usr/dt/lib/nls/msg/%L/%N.cat"/>
+    <property name="env.PATH" value="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"/>
+    <property name="user.language" value="en"/>
+    <property name="sun.boot.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386"/>
+    <property name="classworlds.conf" value="/home/blafond/Workspaces/DNA/.metadata/.plugins/org.maven.ide.eclipse/launches/m2conf7839333797857365463.tmp"/>
+    <property name="java.version" value="1.6.0_14"/>
+    <property name="user.timezone" value=""/>
+    <property name="env.DBUS_SESSION_BUS_ADDRESS" value="unix:abstract=/tmp/dbus-Cmyp7m4oLS,guid=f006087070ce2e1077eb8f884ab1087e"/>
+    <property name="sun.arch.data.model" value="32"/>
+    <property name="java.endorsed.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/endorsed"/>
+    <property name="sun.cpu.isalist" value=""/>
+    <property name="sun.jnu.encoding" value="UTF-8"/>
+    <property name="file.encoding.pkg" value="sun.io"/>
+    <property name="file.separator" value="/"/>
+    <property name="java.specification.name" value="Java Platform API Specification"/>
+    <property name="env.GNOME_DESKTOP_SESSION_ID" value="Default"/>
+    <property name="java.class.version" value="50.0"/>
+    <property name="env.GDMSESSION" value="default"/>
+    <property name="user.country" value="US"/>
+    <property name="env.GNOME_KEYRING_PID" value="5912"/>
+    <property name="java.home" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre"/>
+    <property name="java.vm.info" value="mixed mode"/>
+    <property name="env.LOGNAME" value="blafond"/>
+    <property name="os.version" value="2.6.24-24-generic"/>
+    <property name="path.separator" value=":"/>
+    <property name="java.vm.version" value="14.0-b16"/>
+    <property name="java.awt.printerjob" value="sun.print.PSPrinterJob"/>
+    <property name="env.WINDOWPATH" value="7"/>
+    <property name="sun.io.unicode.encoding" value="UnicodeLittle"/>
+    <property name="env.GDM_LANG" value="en_US.UTF-8"/>
+    <property name="user.home" value="/home/blafond"/>
+    <property name="java.specification.vendor" value="Sun Microsystems Inc."/>
+    <property name="java.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386/server:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/../lib/i386:/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/java/packages/lib/i386:/lib:/usr/lib"/>
+    <property name="java.vendor.url" value="http://java.sun.com/"/>
+    <property name="env.GDM_XSERVER_LOCATION" value="local"/>
+    <property name="java.vm.vendor" value="Sun Microsystems Inc."/>
+    <property name="maven.home" value="EMBEDDED"/>
+    <property name="java.runtime.name" value="Java(TM) SE Runtime Environment"/>
+    <property name="java.class.path" value="/home/blafond/Programs/eclipse-SDK-3.5/plugins/org.maven.ide.components.maven_embedder_2.1.0.20080530-2300/jars/plexus-classworlds-1.2-alpha-12.jar"/>
+    <property name="env.XFILESEARCHPATH" value="/usr/dt/app-defaults/%L/Dt"/>
+    <property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
+    <property name="java.vm.specification.version" value="1.0"/>
+    <property name="m2eclipse.workspace.state" value="/home/blafond/Workspaces/DNA/.metadata/.plugins/org.maven.ide.eclipse/workspacestate.properties"/>
+    <property name="sun.cpu.endian" value="little"/>
+    <property name="env.GTK_RC_FILES" value="/etc/gtk/gtkrc:/home/blafond/.gtkrc-1.2-gnome2"/>
+    <property name="sun.os.patch.level" value="unknown"/>
+    <property name="env.HOME" value="/home/blafond"/>
+    <property name="surefire.test.class.path" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target/test-classes:/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target/classes:/home/blafond/.m2/repository/hsqldb/hsqldb/1.8.0.2/hsqldb-1.8.0.2.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-common/0.7-SNAPSHOT/dna-common-0.7-SNAPSHOT.jar:/home/blafond/.m2/repository/org/slf4j/slf4j-api/1.5.8/slf4j-api-1.5.8.jar:/home/blafond/.m2/repository/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-graph/0.7-SNAPSHOT/dna-graph-0.7-SNAPSHOT.jar:/home/blafond/.m2/repository/joda-time/joda-time/1.4/joda-time-1.4.jar:/home/blafond/.m2/repository/com/google/code/google-collections/google-collect/snapshot-20080530/google-collect-snapshot-20080530.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-common/0.7-SNAPSHOT/dna-common-0.7-SNAPSHOT-tests.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-graph/!
 0.7-SNAPSHOT/dna-graph-0.7-SNAPSHOT-tests.jar:/home/blafond/.m2/repository/junit/junit/4.4/junit-4.4.jar:/home/blafond/.m2/repository/org/hamcrest/hamcrest-library/1.1/hamcrest-library-1.1.jar:/home/blafond/.m2/repository/org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar:/home/blafond/.m2/repository/org/mockito/mockito-all/1.5/mockito-all-1.5.jar:/home/blafond/.m2/repository/org/slf4j/slf4j-log4j12/1.5.8/slf4j-log4j12-1.5.8.jar:/home/blafond/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar:"/>
+    <property name="java.io.tmpdir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target"/>
+    <property name="env.LD_LIBRARY_PATH" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386/server:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/../lib/i386:/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons"/>
+    <property name="java.vendor.url.bug" value="http://java.sun.com/cgi-bin/bugreport.cgi"/>
+    <property name="env.SSH_AUTH_SOCK" value="/tmp/keyring-dF941n/ssh"/>
+    <property name="java.awt.graphicsenv" value="sun.awt.X11GraphicsEnvironment"/>
+    <property name="os.arch" value="i386"/>
+    <property name="java.ext.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/ext:/usr/java/packages/lib/ext"/>
+    <property name="user.dir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl"/>
+    <property name="env.XAUTHORITY" value="/home/blafond/.Xauthority"/>
+    <property name="line.separator" value="
+"/>
+    <property name="java.vm.name" value="Java HotSpot(TM) Server VM"/>
+    <property name="basedir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl"/>
+    <property name="maven.mode.standalone" value="true"/>
+    <property name="env.USERNAME" value="blafond"/>
+    <property name="file.encoding" value="UTF-8"/>
+    <property name="env.GNOME_KEYRING_SOCKET" value="/tmp/keyring-dF941n/socket"/>
+    <property name="env.GPG_AGENT_INFO" value="/tmp/seahorse-ZO5q59/S.gpg-agent:5994:1"/>
+    <property name="java.specification.version" value="1.6"/>
+  </properties>
+  <testcase time="0.028" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseBracketedInteger"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseKMGInteger"/>
+  <testcase time="0.006" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseCHAR"/>
+  <testcase time="0.006" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseCHARACTER"/>
+  <testcase time="0.006" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseCHAR_VARYING"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseCHARACTER_VARYING"/>
+  <testcase time="0.036" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseVARCHAR"/>
+  <testcase time="0.02" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseNATIONAL_CHAR"/>
+  <testcase time="0.007" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseNATIONAL_CHARACTER"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseNATIONAL_CHAR_VARYING"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseNATIONAL_CHARACTER_VARYING"/>
+  <testcase time="0.004" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseNCHAR_VARYING"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseBIT"/>
+  <testcase time="0.004" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseBITWithLength"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseBIT_VARYINGWithLength"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldNotParseXXXXXXTYPE"/>
+  <testcase time="0.004" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseINT"/>
+  <testcase time="0.004" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseINTEGER"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseSMALLINT"/>
+  <testcase time="0.009" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseNUMERIC"/>
+  <testcase time="0.007" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseDECIMAL"/>
+  <testcase time="0.006" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseDEC"/>
+  <testcase time="0.007" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseFLOAT"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseREAL"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseDOUBLE_PRECISION"/>
+  <testcase time="0.004" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseDATE"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseTIME"/>
+  <testcase time="0.005" classname="org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest" name="shouldParseTIMESTAMP"/>
+</testsuite>
\ No newline at end of file


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.xml
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.xml	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.xml	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuite failures="0" time="2.197" errors="1" skipped="0" tests="2" name="org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest">
+  <properties>
+    <property name="java.vendor" value="Sun Microsystems Inc."/>
+    <property name="env.SESSION_MANAGER" value="local/work-laptop:/tmp/.ICE-unix/5913"/>
+    <property name="localRepository" value="/home/blafond/.m2/repository"/>
+    <property name="sun.java.launcher" value="SUN_STANDARD"/>
+    <property name="sun.management.compiler" value="HotSpot Tiered Compilers"/>
+    <property name="os.name" value="Linux"/>
+    <property name="sun.boot.class.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/resources.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/rt.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/jsse.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/jce.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/charsets.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/classes"/>
+    <property name="env.PWD" value="/home/blafond"/>
+    <property name="env.XDG_SESSION_COOKIE" value="70bef359dccaf87f474bc0d7490f06a9-1253116029.282562-1962947517"/>
+    <property name="sun.desktop" value="gnome"/>
+    <property name="env.LANG" value="en_US.UTF-8"/>
+    <property name="java.vm.specification.vendor" value="Sun Microsystems Inc."/>
+    <property name="java.runtime.version" value="1.6.0_14-b08"/>
+    <property name="env.DISPLAY" value=":0.0"/>
+    <property name="user.name" value="blafond"/>
+    <property name="env.USER" value="blafond"/>
+    <property name="env.SHELL" value="/bin/bash"/>
+    <property name="env.MOZILLA_FIVE_HOME" value="/usr/lib/xulrunner-addons"/>
+    <property name="env.DESKTOP_SESSION" value="default"/>
+    <property name="env.XDG_DATA_DIRS" value="/usr/local/share/:/usr/share/:/usr/share/gdm/"/>
+    <property name="env.NLSPATH" value="/usr/dt/lib/nls/msg/%L/%N.cat"/>
+    <property name="env.PATH" value="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"/>
+    <property name="user.language" value="en"/>
+    <property name="sun.boot.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386"/>
+    <property name="classworlds.conf" value="/home/blafond/Workspaces/DNA/.metadata/.plugins/org.maven.ide.eclipse/launches/m2conf7839333797857365463.tmp"/>
+    <property name="java.version" value="1.6.0_14"/>
+    <property name="user.timezone" value="US/Central"/>
+    <property name="env.DBUS_SESSION_BUS_ADDRESS" value="unix:abstract=/tmp/dbus-Cmyp7m4oLS,guid=f006087070ce2e1077eb8f884ab1087e"/>
+    <property name="sun.arch.data.model" value="32"/>
+    <property name="java.endorsed.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/endorsed"/>
+    <property name="sun.cpu.isalist" value=""/>
+    <property name="sun.jnu.encoding" value="UTF-8"/>
+    <property name="file.encoding.pkg" value="sun.io"/>
+    <property name="file.separator" value="/"/>
+    <property name="java.specification.name" value="Java Platform API Specification"/>
+    <property name="env.GNOME_DESKTOP_SESSION_ID" value="Default"/>
+    <property name="java.class.version" value="50.0"/>
+    <property name="env.GDMSESSION" value="default"/>
+    <property name="user.country" value="US"/>
+    <property name="env.GNOME_KEYRING_PID" value="5912"/>
+    <property name="java.home" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre"/>
+    <property name="java.vm.info" value="mixed mode"/>
+    <property name="env.LOGNAME" value="blafond"/>
+    <property name="os.version" value="2.6.24-24-generic"/>
+    <property name="path.separator" value=":"/>
+    <property name="java.vm.version" value="14.0-b16"/>
+    <property name="java.awt.printerjob" value="sun.print.PSPrinterJob"/>
+    <property name="env.WINDOWPATH" value="7"/>
+    <property name="sun.io.unicode.encoding" value="UnicodeLittle"/>
+    <property name="env.GDM_LANG" value="en_US.UTF-8"/>
+    <property name="user.home" value="/home/blafond"/>
+    <property name="java.specification.vendor" value="Sun Microsystems Inc."/>
+    <property name="java.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386/server:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/../lib/i386:/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/java/packages/lib/i386:/lib:/usr/lib"/>
+    <property name="java.vendor.url" value="http://java.sun.com/"/>
+    <property name="env.GDM_XSERVER_LOCATION" value="local"/>
+    <property name="java.vm.vendor" value="Sun Microsystems Inc."/>
+    <property name="maven.home" value="EMBEDDED"/>
+    <property name="java.runtime.name" value="Java(TM) SE Runtime Environment"/>
+    <property name="java.class.path" value="/home/blafond/Programs/eclipse-SDK-3.5/plugins/org.maven.ide.components.maven_embedder_2.1.0.20080530-2300/jars/plexus-classworlds-1.2-alpha-12.jar"/>
+    <property name="env.XFILESEARCHPATH" value="/usr/dt/app-defaults/%L/Dt"/>
+    <property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
+    <property name="java.vm.specification.version" value="1.0"/>
+    <property name="m2eclipse.workspace.state" value="/home/blafond/Workspaces/DNA/.metadata/.plugins/org.maven.ide.eclipse/workspacestate.properties"/>
+    <property name="sun.cpu.endian" value="little"/>
+    <property name="env.GTK_RC_FILES" value="/etc/gtk/gtkrc:/home/blafond/.gtkrc-1.2-gnome2"/>
+    <property name="sun.os.patch.level" value="unknown"/>
+    <property name="env.HOME" value="/home/blafond"/>
+    <property name="surefire.test.class.path" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target/test-classes:/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target/classes:/home/blafond/.m2/repository/hsqldb/hsqldb/1.8.0.2/hsqldb-1.8.0.2.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-common/0.7-SNAPSHOT/dna-common-0.7-SNAPSHOT.jar:/home/blafond/.m2/repository/org/slf4j/slf4j-api/1.5.8/slf4j-api-1.5.8.jar:/home/blafond/.m2/repository/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-graph/0.7-SNAPSHOT/dna-graph-0.7-SNAPSHOT.jar:/home/blafond/.m2/repository/joda-time/joda-time/1.4/joda-time-1.4.jar:/home/blafond/.m2/repository/com/google/code/google-collections/google-collect/snapshot-20080530/google-collect-snapshot-20080530.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-common/0.7-SNAPSHOT/dna-common-0.7-SNAPSHOT-tests.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-graph/!
 0.7-SNAPSHOT/dna-graph-0.7-SNAPSHOT-tests.jar:/home/blafond/.m2/repository/junit/junit/4.4/junit-4.4.jar:/home/blafond/.m2/repository/org/hamcrest/hamcrest-library/1.1/hamcrest-library-1.1.jar:/home/blafond/.m2/repository/org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar:/home/blafond/.m2/repository/org/mockito/mockito-all/1.5/mockito-all-1.5.jar:/home/blafond/.m2/repository/org/slf4j/slf4j-log4j12/1.5.8/slf4j-log4j12-1.5.8.jar:/home/blafond/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar:"/>
+    <property name="java.io.tmpdir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target"/>
+    <property name="env.LD_LIBRARY_PATH" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386/server:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/../lib/i386:/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons"/>
+    <property name="java.vendor.url.bug" value="http://java.sun.com/cgi-bin/bugreport.cgi"/>
+    <property name="env.SSH_AUTH_SOCK" value="/tmp/keyring-dF941n/ssh"/>
+    <property name="java.awt.graphicsenv" value="sun.awt.X11GraphicsEnvironment"/>
+    <property name="os.arch" value="i386"/>
+    <property name="java.ext.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/ext:/usr/java/packages/lib/ext"/>
+    <property name="user.dir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl"/>
+    <property name="env.XAUTHORITY" value="/home/blafond/.Xauthority"/>
+    <property name="line.separator" value="
+"/>
+    <property name="java.vm.name" value="Java HotSpot(TM) Server VM"/>
+    <property name="basedir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl"/>
+    <property name="maven.mode.standalone" value="true"/>
+    <property name="env.USERNAME" value="blafond"/>
+    <property name="file.encoding" value="UTF-8"/>
+    <property name="env.GNOME_KEYRING_SOCKET" value="/tmp/keyring-dF941n/socket"/>
+    <property name="env.GPG_AGENT_INFO" value="/tmp/seahorse-ZO5q59/S.gpg-agent:5994:1"/>
+    <property name="java.specification.version" value="1.6"/>
+  </properties>
+  <testcase time="0.024" classname="org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest" name="shouldParseDerbyTestDDL">
+    <error type="java.lang.NullPointerException">java.lang.NullPointerException
+	at org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.getFileContent(DerbyDdlParserTest.java:50)
+	at org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.shouldParseDerbyTestDDL(DerbyDdlParserTest.java:63)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
+	at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
+	at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
+	at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
+	at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
+	at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
+	at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
+	at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
+	at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
+	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
+	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
+	at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
+	at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
+	at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:338)
+	at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:997)
+</error>
+    <system-out>   Getting Content for File = src/test/resources/ddl/derby_test_bugs.ddl
+java.io.FileNotFoundException: src/test/resources/ddl/derby_test_bugs.ddl (No such file or directory)</system-out>
+  </testcase>
+  <testcase time="2.171" classname="org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest" name="shouldParseDerbyDDL"/>
+</testsuite>
\ No newline at end of file


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.xml
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.xml	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.xml	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuite failures="0" time="0.517" errors="1" skipped="0" tests="1" name="org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest">
+  <properties>
+    <property name="java.vendor" value="Sun Microsystems Inc."/>
+    <property name="env.SESSION_MANAGER" value="local/work-laptop:/tmp/.ICE-unix/5913"/>
+    <property name="localRepository" value="/home/blafond/.m2/repository"/>
+    <property name="sun.java.launcher" value="SUN_STANDARD"/>
+    <property name="sun.management.compiler" value="HotSpot Tiered Compilers"/>
+    <property name="os.name" value="Linux"/>
+    <property name="sun.boot.class.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/resources.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/rt.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/jsse.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/jce.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/charsets.jar:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/classes"/>
+    <property name="env.PWD" value="/home/blafond"/>
+    <property name="env.XDG_SESSION_COOKIE" value="70bef359dccaf87f474bc0d7490f06a9-1253116029.282562-1962947517"/>
+    <property name="sun.desktop" value="gnome"/>
+    <property name="env.LANG" value="en_US.UTF-8"/>
+    <property name="java.vm.specification.vendor" value="Sun Microsystems Inc."/>
+    <property name="java.runtime.version" value="1.6.0_14-b08"/>
+    <property name="env.DISPLAY" value=":0.0"/>
+    <property name="user.name" value="blafond"/>
+    <property name="env.USER" value="blafond"/>
+    <property name="env.SHELL" value="/bin/bash"/>
+    <property name="env.MOZILLA_FIVE_HOME" value="/usr/lib/xulrunner-addons"/>
+    <property name="env.DESKTOP_SESSION" value="default"/>
+    <property name="env.XDG_DATA_DIRS" value="/usr/local/share/:/usr/share/:/usr/share/gdm/"/>
+    <property name="env.NLSPATH" value="/usr/dt/lib/nls/msg/%L/%N.cat"/>
+    <property name="env.PATH" value="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"/>
+    <property name="user.language" value="en"/>
+    <property name="sun.boot.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386"/>
+    <property name="classworlds.conf" value="/home/blafond/Workspaces/DNA/.metadata/.plugins/org.maven.ide.eclipse/launches/m2conf7839333797857365463.tmp"/>
+    <property name="java.version" value="1.6.0_14"/>
+    <property name="user.timezone" value="US/Central"/>
+    <property name="env.DBUS_SESSION_BUS_ADDRESS" value="unix:abstract=/tmp/dbus-Cmyp7m4oLS,guid=f006087070ce2e1077eb8f884ab1087e"/>
+    <property name="sun.arch.data.model" value="32"/>
+    <property name="java.endorsed.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/endorsed"/>
+    <property name="sun.cpu.isalist" value=""/>
+    <property name="sun.jnu.encoding" value="UTF-8"/>
+    <property name="file.encoding.pkg" value="sun.io"/>
+    <property name="file.separator" value="/"/>
+    <property name="java.specification.name" value="Java Platform API Specification"/>
+    <property name="env.GNOME_DESKTOP_SESSION_ID" value="Default"/>
+    <property name="java.class.version" value="50.0"/>
+    <property name="env.GDMSESSION" value="default"/>
+    <property name="user.country" value="US"/>
+    <property name="env.GNOME_KEYRING_PID" value="5912"/>
+    <property name="java.home" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre"/>
+    <property name="java.vm.info" value="mixed mode"/>
+    <property name="env.LOGNAME" value="blafond"/>
+    <property name="os.version" value="2.6.24-24-generic"/>
+    <property name="path.separator" value=":"/>
+    <property name="java.vm.version" value="14.0-b16"/>
+    <property name="java.awt.printerjob" value="sun.print.PSPrinterJob"/>
+    <property name="env.WINDOWPATH" value="7"/>
+    <property name="sun.io.unicode.encoding" value="UnicodeLittle"/>
+    <property name="env.GDM_LANG" value="en_US.UTF-8"/>
+    <property name="user.home" value="/home/blafond"/>
+    <property name="java.specification.vendor" value="Sun Microsystems Inc."/>
+    <property name="java.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386/server:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/../lib/i386:/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/java/packages/lib/i386:/lib:/usr/lib"/>
+    <property name="java.vendor.url" value="http://java.sun.com/"/>
+    <property name="env.GDM_XSERVER_LOCATION" value="local"/>
+    <property name="java.vm.vendor" value="Sun Microsystems Inc."/>
+    <property name="maven.home" value="EMBEDDED"/>
+    <property name="java.runtime.name" value="Java(TM) SE Runtime Environment"/>
+    <property name="java.class.path" value="/home/blafond/Programs/eclipse-SDK-3.5/plugins/org.maven.ide.components.maven_embedder_2.1.0.20080530-2300/jars/plexus-classworlds-1.2-alpha-12.jar"/>
+    <property name="env.XFILESEARCHPATH" value="/usr/dt/app-defaults/%L/Dt"/>
+    <property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
+    <property name="java.vm.specification.version" value="1.0"/>
+    <property name="m2eclipse.workspace.state" value="/home/blafond/Workspaces/DNA/.metadata/.plugins/org.maven.ide.eclipse/workspacestate.properties"/>
+    <property name="sun.cpu.endian" value="little"/>
+    <property name="env.GTK_RC_FILES" value="/etc/gtk/gtkrc:/home/blafond/.gtkrc-1.2-gnome2"/>
+    <property name="sun.os.patch.level" value="unknown"/>
+    <property name="env.HOME" value="/home/blafond"/>
+    <property name="surefire.test.class.path" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target/test-classes:/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target/classes:/home/blafond/.m2/repository/hsqldb/hsqldb/1.8.0.2/hsqldb-1.8.0.2.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-common/0.7-SNAPSHOT/dna-common-0.7-SNAPSHOT.jar:/home/blafond/.m2/repository/org/slf4j/slf4j-api/1.5.8/slf4j-api-1.5.8.jar:/home/blafond/.m2/repository/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-graph/0.7-SNAPSHOT/dna-graph-0.7-SNAPSHOT.jar:/home/blafond/.m2/repository/joda-time/joda-time/1.4/joda-time-1.4.jar:/home/blafond/.m2/repository/com/google/code/google-collections/google-collect/snapshot-20080530/google-collect-snapshot-20080530.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-common/0.7-SNAPSHOT/dna-common-0.7-SNAPSHOT-tests.jar:/home/blafond/.m2/repository/org/jboss/dna/dna-graph/!
 0.7-SNAPSHOT/dna-graph-0.7-SNAPSHOT-tests.jar:/home/blafond/.m2/repository/junit/junit/4.4/junit-4.4.jar:/home/blafond/.m2/repository/org/hamcrest/hamcrest-library/1.1/hamcrest-library-1.1.jar:/home/blafond/.m2/repository/org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar:/home/blafond/.m2/repository/org/mockito/mockito-all/1.5/mockito-all-1.5.jar:/home/blafond/.m2/repository/org/slf4j/slf4j-log4j12/1.5.8/slf4j-log4j12-1.5.8.jar:/home/blafond/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar:"/>
+    <property name="java.io.tmpdir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl/target"/>
+    <property name="env.LD_LIBRARY_PATH" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386/server:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/../lib/i386:/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons"/>
+    <property name="java.vendor.url.bug" value="http://java.sun.com/cgi-bin/bugreport.cgi"/>
+    <property name="env.SSH_AUTH_SOCK" value="/tmp/keyring-dF941n/ssh"/>
+    <property name="java.awt.graphicsenv" value="sun.awt.X11GraphicsEnvironment"/>
+    <property name="os.arch" value="i386"/>
+    <property name="java.ext.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/ext:/usr/java/packages/lib/ext"/>
+    <property name="user.dir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl"/>
+    <property name="env.XAUTHORITY" value="/home/blafond/.Xauthority"/>
+    <property name="line.separator" value="
+"/>
+    <property name="java.vm.name" value="Java HotSpot(TM) Server VM"/>
+    <property name="basedir" value="/home/blafond/Workspaces/DNA/dna/extensions/dna-sequencer-ddl"/>
+    <property name="maven.mode.standalone" value="true"/>
+    <property name="env.USERNAME" value="blafond"/>
+    <property name="file.encoding" value="UTF-8"/>
+    <property name="env.GNOME_KEYRING_SOCKET" value="/tmp/keyring-dF941n/socket"/>
+    <property name="env.GPG_AGENT_INFO" value="/tmp/seahorse-ZO5q59/S.gpg-agent:5994:1"/>
+    <property name="java.specification.version" value="1.6"/>
+  </properties>
+  <testcase time="0.514" classname="org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest" name="shouldParseGFM_PhysicalDDL">
+    <error type="java.lang.NullPointerException">java.lang.NullPointerException
+	at org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.getFileContent(OracleDdlParserTest.java:50)
+	at org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.shouldParseGFM_PhysicalDDL(OracleDdlParserTest.java:83)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
+	at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
+	at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
+	at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
+	at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
+	at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
+	at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
+	at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
+	at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
+	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
+	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
+	at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
+	at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
+	at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:338)
+	at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:997)
+</error>
+    <system-out>   Getting Content for File = src/test/resources/ddl/GFM_Physical.ddl
+java.io.FileNotFoundException: src/test/resources/ddl/GFM_Physical.ddl (No such file or directory)</system-out>
+  </testcase>
+</testsuite>
\ No newline at end of file


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/TEST-org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.BasicDdlParserTest.txt
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.BasicDdlParserTest.txt	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.BasicDdlParserTest.txt	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,35 @@
+-------------------------------------------------------------------------------
+Test set: org.jboss.dna.sequencer.ddl.BasicDdlParserTest
+-------------------------------------------------------------------------------
+Tests run: 5, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.166 sec <<< FAILURE!
+shouldParseCreateTables(org.jboss.dna.sequencer.ddl.BasicDdlParserTest)  Time elapsed: 0.023 sec  <<< ERROR!
+java.lang.NullPointerException
+	at org.jboss.dna.sequencer.ddl.BasicDdlParserTest.getFileContent(BasicDdlParserTest.java:59)
+	at org.jboss.dna.sequencer.ddl.BasicDdlParserTest.shouldParseCreateTables(BasicDdlParserTest.java:159)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
+	at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
+	at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
+	at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
+	at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
+	at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
+	at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
+	at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
+	at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
+	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
+	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
+	at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
+	at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
+	at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:338)
+	at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:997)
+


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.BasicDdlParserTest.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.txt
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.txt	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.txt	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,4 @@
+-------------------------------------------------------------------------------
+Test set: org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest
+-------------------------------------------------------------------------------
+Tests run: 28, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.286 sec


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.datatype.DataTypeParserTest.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.txt
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.txt	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.txt	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,35 @@
+-------------------------------------------------------------------------------
+Test set: org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest
+-------------------------------------------------------------------------------
+Tests run: 2, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.2 sec <<< FAILURE!
+shouldParseDerbyTestDDL(org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest)  Time elapsed: 0.024 sec  <<< ERROR!
+java.lang.NullPointerException
+	at org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.getFileContent(DerbyDdlParserTest.java:50)
+	at org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.shouldParseDerbyTestDDL(DerbyDdlParserTest.java:63)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
+	at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
+	at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
+	at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
+	at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
+	at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
+	at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
+	at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
+	at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
+	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
+	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
+	at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
+	at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
+	at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:338)
+	at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:997)
+


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.DerbyDdlParserTest.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.txt
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.txt	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.txt	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,35 @@
+-------------------------------------------------------------------------------
+Test set: org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest
+-------------------------------------------------------------------------------
+Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.534 sec <<< FAILURE!
+shouldParseGFM_PhysicalDDL(org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest)  Time elapsed: 0.515 sec  <<< ERROR!
+java.lang.NullPointerException
+	at org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.getFileContent(OracleDdlParserTest.java:50)
+	at org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.shouldParseGFM_PhysicalDDL(OracleDdlParserTest.java:83)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
+	at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
+	at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
+	at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
+	at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
+	at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
+	at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
+	at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
+	at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
+	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
+	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
+	at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
+	at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
+	at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
+	at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
+	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+	at java.lang.reflect.Method.invoke(Method.java:597)
+	at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:338)
+	at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:997)
+


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/surefire-reports/org.jboss.dna.sequencer.ddl.dialect.OracleDdlParserTest.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/datatype/DataTypeParserTest.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/datatype/DataTypeParserTest.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParserTest.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/dialect/DerbyDdlParserTest.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/dialect/OracleDdlParserTest.class
===================================================================
(Binary files differ)


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/org/jboss/dna/sequencer/ddl/dialect/OracleDdlParserTest.class
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/sql2bnf.aug92.txt
===================================================================
--- branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/sql2bnf.aug92.txt	                        (rev 0)
+++ branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/sql2bnf.aug92.txt	2009-10-02 16:13:40 UTC (rev 1280)
@@ -0,0 +1,2594 @@
+This file contains a depth-first tree traversal of the BNF
+for the  language done at about 27-AUG-1992 11:03:41.64.
+The specific version of the BNF included here is:  ANSI-only, SQL2-only.
+
+
+<SQL terminal character> ::=
+      <SQL language character>
+    | <SQL embedded language character>
+
+<SQL language character> ::=
+      <simple Latin letter>
+    | <digit>
+    | <SQL special character>
+
+<simple Latin letter> ::=
+      <simple Latin upper case letter>
+    | <simple Latin lower case letter>
+
+<simple Latin upper case letter> ::=
+          A | B | C | D | E | F | G | H | I | J | K | L | M | N | O
+    | P | Q | R | S | T | U | V | W | X | Y | Z
+
+<simple Latin lower case letter> ::=
+          a | b | c | d | e | f | g | h | i | j | k | l | m | n | o
+    | p | q | r | s | t | u | v | w | x | y | z
+
+<digit> ::=
+    0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
+
+<SQL special character> ::=
+      <space>
+    | <double quote>
+    | <percent>
+    | <ampersand>
+    | <quote>
+    | <left paren>
+    | <right paren>
+    | <asterisk>
+    | <plus sign>
+    | <comma>
+    | <minus sign>
+    | <period>
+    | <solidus>
+    | <colon>
+    | <semicolon>
+    | <less than operator>
+    | <equals operator>
+    | <greater than operator>
+    | <question mark>
+    | <underscore>
+    | <vertical bar>
+
+<space> ::= !! <EMPHASIS>(space character in character set in use)
+
+<double quote> ::= "
+
+<percent> ::= %
+
+<ampersand> ::= &
+
+<quote> ::= '
+
+<left paren> ::= (
+
+<right paren> ::= )
+
+<asterisk> ::= *
+
+<plus sign> ::= +
+
+<comma> ::= ,
+
+<minus sign> ::= -
+
+<period> ::= .
+
+<solidus> ::= /
+
+<colon> ::= :
+
+<semicolon> ::= ;
+
+<less than operator> ::= <
+
+<equals operator> ::= =
+
+<greater than operator> ::= >
+
+<question mark> ::= ?
+
+<underscore> ::= _
+
+<vertical bar> ::= |
+
+<SQL embedded language character> ::=
+      <left bracket>
+    | <right bracket>
+
+<left bracket> ::= [
+
+<right bracket> ::= ]
+
+<token> ::=
+      <nondelimiter token>
+    | <delimiter token>
+
+<nondelimiter token> ::=
+      <regular identifier>
+    | <key word>
+    | <unsigned numeric literal>
+    | <national character string literal>
+    | <bit string literal>
+    | <hex string literal>
+
+<regular identifier> ::= <identifier body>
+
+<identifier body> ::=
+    <identifier start> [ { <underscore> | <identifier part> }... ]
+
+<identifier start> ::= <EMPHASIS>(!! See the Syntax Rules)
+
+<identifier part> ::=
+      <identifier start>
+    | <digit>
+
+<key word> ::=
+      <reserved word>
+    | <non-reserved word>
+
+<reserved word> ::=
+      ABSOLUTE | ACTION | ADD | ALL
+    | ALLOCATE | ALTER | AND
+    | ANY | ARE
+    | AS | ASC
+    | ASSERTION | AT
+    | AUTHORIZATION | AVG
+    | BEGIN | BETWEEN | BIT | BIT_LENGTH
+    | BOTH | BY
+    | CASCADE | CASCADED | CASE | CAST
+    | CATALOG
+    | CHAR | CHARACTER | CHAR_LENGTH
+    | CHARACTER_LENGTH | CHECK | CLOSE | COALESCE
+    | COLLATE | COLLATION
+    | COLUMN | COMMIT
+    | CONNECT
+    | CONNECTION | CONSTRAINT
+    | CONSTRAINTS | CONTINUE
+    | CONVERT | CORRESPONDING | COUNT | CREATE | CROSS
+    | CURRENT
+    | CURRENT_DATE | CURRENT_TIME
+    | CURRENT_TIMESTAMP | CURRENT_USER | CURSOR
+    | DATE | DAY | DEALLOCATE | DEC
+    | DECIMAL | DECLARE | DEFAULT | DEFERRABLE
+    | DEFERRED | DELETE | DESC | DESCRIBE | DESCRIPTOR
+    | DIAGNOSTICS
+    | DISCONNECT | DISTINCT | DOMAIN | DOUBLE | DROP
+    | ELSE | END | END-EXEC | ESCAPE
+    | EXCEPT | EXCEPTION
+    | EXEC | EXECUTE | EXISTS
+    | EXTERNAL | EXTRACT
+    | FALSE | FETCH | FIRST | FLOAT | FOR
+    | FOREIGN | FOUND | FROM | FULL
+    | GET | GLOBAL | GO | GOTO
+    | GRANT | GROUP
+    | HAVING | HOUR
+    | IDENTITY | IMMEDIATE | IN | INDICATOR
+    | INITIALLY | INNER | INPUT
+    | INSENSITIVE | INSERT | INT | INTEGER | INTERSECT
+    | INTERVAL | INTO | IS
+    | ISOLATION
+    | JOIN
+    | KEY
+    | LANGUAGE | LAST | LEADING | LEFT
+    | LEVEL | LIKE | LOCAL | LOWER
+    | MATCH | MAX | MIN | MINUTE | MODULE
+    | MONTH
+    | NAMES | NATIONAL | NATURAL | NCHAR | NEXT | NO
+    | NOT | NULL
+    | NULLIF | NUMERIC
+    | OCTET_LENGTH | OF
+    | ON | ONLY | OPEN | OPTION | OR
+    | ORDER | OUTER
+    | OUTPUT | OVERLAPS
+    | PAD | PARTIAL | POSITION | PRECISION | PREPARE
+    | PRESERVE | PRIMARY
+    | PRIOR | PRIVILEGES | PROCEDURE | PUBLIC
+    | READ | REAL | REFERENCES | RELATIVE | RESTRICT
+    | REVOKE | RIGHT
+    | ROLLBACK | ROWS
+    | SCHEMA | SCROLL | SECOND | SECTION
+    | SELECT
+    | SESSION | SESSION_USER | SET
+    | SIZE | SMALLINT | SOME | SPACE | SQL | SQLCODE
+    | SQLERROR | SQLSTATE
+    | SUBSTRING | SUM | SYSTEM_USER
+    | TABLE | TEMPORARY
+    | THEN | TIME | TIMESTAMP
+    | TIMEZONE_HOUR | TIMEZONE_MINUTE
+    | TO | TRAILING | TRANSACTION
+    | TRANSLATE | TRANSLATION | TRIM | TRUE
+    | UNION | UNIQUE | UNKNOWN | UPDATE | UPPER | USAGE
+    | USER | USING
+    | VALUE | VALUES | VARCHAR | VARYING | VIEW
+    | WHEN | WHENEVER | WHERE | WITH | WORK | WRITE
+    | YEAR
+    | ZONE
+
+<non-reserved word> ::=
+    
+      ADA
+    | C | CATALOG_NAME
+    | CHARACTER_SET_CATALOG | CHARACTER_SET_NAME
+    | CHARACTER_SET_SCHEMA | CLASS_ORIGIN | COBOL | COLLATION_CATALOG
+    | COLLATION_NAME | COLLATION_SCHEMA | COLUMN_NAME | COMMAND_FUNCTION
+    | COMMITTED
+    | CONDITION_NUMBER | CONNECTION_NAME | CONSTRAINT_CATALOG | CONSTRAINT_NAME
+    | CONSTRAINT_SCHEMA | CURSOR_NAME
+    | DATA | DATETIME_INTERVAL_CODE
+    | DATETIME_INTERVAL_PRECISION | DYNAMIC_FUNCTION
+    | FORTRAN
+    | LENGTH
+    | MESSAGE_LENGTH | MESSAGE_OCTET_LENGTH | MESSAGE_TEXT | MORE | MUMPS
+    | NAME | NULLABLE | NUMBER
+    | PASCAL | PLI
+    | REPEATABLE | RETURNED_LENGTH | RETURNED_OCTET_LENGTH | RETURNED_SQLSTATE
+    | ROW_COUNT
+    | SCALE | SCHEMA_NAME | SERIALIZABLE | SERVER_NAME | SUBCLASS_ORIGIN
+    | TABLE_NAME | TYPE
+    | UNCOMMITTED | UNNAMED
+
+<unsigned numeric literal> ::=
+      <exact numeric literal>
+    | <approximate numeric literal>
+
+<exact numeric literal> ::=
+      <unsigned integer> [ <period> [ <unsigned integer> ] ]
+    | <period> <unsigned integer>
+
+<unsigned integer> ::= <digit>...
+
+<approximate numeric literal> ::= <mantissa> E <exponent>
+
+<mantissa> ::= <exact numeric literal>
+
+<exponent> ::= <signed integer>
+
+<signed integer> ::= [ <sign> ] <unsigned integer>
+
+<sign> ::= <plus sign> | <minus sign>
+
+<national character string literal> ::=
+    N <quote> [ <character representation>... ] <quote>
+      [ { <separator>... <quote> [ <character representation>... ] <quote> }... ]
+
+<character representation> ::=
+      <nonquote character>
+    | <quote symbol>
+
+<nonquote character> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<quote symbol> ::= <quote><quote>
+
+<separator> ::= { <comment> | <space> | <newline> }...
+
+<comment> ::=
+    <comment introducer> [ <comment character>... ] <newline>
+
+<comment introducer> ::= <minus sign><minus sign>[<minus sign>...]
+
+<comment character> ::=
+      <nonquote character>
+    | <quote>
+
+<newline> ::= !! <EMPHASIS>(implementation-defined end-of-line indicator)
+
+<bit string literal> ::=
+    B <quote> [ <bit>... ] <quote>
+      [ { <separator>... <quote> [ <bit>... ] <quote> }... ]
+
+<bit> ::= 0 | 1
+
+<hex string literal> ::=
+    X <quote> [ <hexit>... ] <quote>
+      [ { <separator>... <quote> [ <hexit>... ] <quote> }... ]
+
+<hexit> ::= <digit> | A | B | C | D | E | F | a | b | c | d | e | f
+
+<delimiter token> ::=
+      <character string literal>
+    | <date string>
+    | <time string>
+    | <timestamp string>
+    | <interval string>
+    | <delimited identifier>
+    | <SQL special character>
+    | <not equals operator>
+    | <greater than or equals operator>
+    | <less than or equals operator>
+    | <concatenation operator>
+    | <double period>
+    | <left bracket>
+    | <right bracket>
+
+<character string literal> ::=
+    [ <introducer><character set specification> ]
+    <quote> [ <character representation>... ] <quote>
+      [ { <separator>... <quote> [ <character representation>... ] <quote> }... ]
+
+<introducer> ::= <underscore>
+
+<character set specification> ::=
+      <standard character repertoire name>
+    | <implementation-defined character repertoire name>
+    | <user-defined character repertoire name>
+    | <standard universal character form-of-use name>
+    | <implementation-defined universal character form-of-use name>
+
+<standard character repertoire name> ::= <character set name>
+
+<character set name> ::= [ <schema name> <period> ]
+      <SQL language identifier>
+
+<schema name> ::=
+    [ <catalog name> <period> ] <unqualified schema name>
+
+<catalog name> ::= <identifier>
+
+<identifier> ::=
+    [ <introducer><character set specification> ] <actual identifier>
+
+<actual identifier> ::=
+      <regular identifier>
+    | <delimited identifier>
+
+<delimited identifier> ::=
+    <double quote> <delimited identifier body> <double quote>
+
+<delimited identifier body> ::= <delimited identifier part>...
+
+<delimited identifier part> ::=
+      <nondoublequote character>
+    | <doublequote symbol>
+
+<nondoublequote character> ::= <EMPHASIS>(!! See the Syntax Rules)
+
+<doublequote symbol> ::= <double quote><double quote>
+
+<unqualified schema name> ::= <identifier>
+
+<SQL language identifier> ::=
+    <SQL language identifier start>
+       [ { <underscore> | <SQL language identifier part> }... ]
+
+<SQL language identifier start> ::= <simple Latin letter>
+
+<SQL language identifier part> ::=
+      <simple Latin letter>
+    | <digit>
+
+<implementation-defined character repertoire name> ::=
+    <character set name>
+
+<user-defined character repertoire name> ::= <character set name>
+
+<standard universal character form-of-use name> ::=
+    <character set name>
+
+<implementation-defined universal character form-of-use name> ::=
+    <character set name>
+
+<date string> ::=
+    <quote> <date value> <quote>
+
+<date value> ::=
+    <years value> <minus sign> <months value>
+        <minus sign> <days value>
+
+<years value> ::= <datetime value>
+
+<datetime value> ::= <unsigned integer>
+
+<months value> ::= <datetime value>
+
+<days value> ::= <datetime value>
+
+<time string> ::=
+    <quote> <time value> [ <time zone interval> ] <quote>
+
+<time value> ::=
+    <hours value> <colon> <minutes value> <colon> <seconds value>
+
+<hours value> ::= <datetime value>
+
+<minutes value> ::= <datetime value>
+
+<seconds value> ::=
+      <seconds integer value> [ <period> [ <seconds fraction> ] ]
+
+<seconds integer value> ::= <unsigned integer>
+
+<seconds fraction> ::= <unsigned integer>
+
+<time zone interval> ::=
+    <sign> <hours value> <colon> <minutes value>
+
+<timestamp string> ::=
+    <quote> <date value> <space> <time value>
+        [ <time zone interval> ] <quote>
+
+<interval string> ::=
+    <quote> { <year-month literal> | <day-time literal> } <quote>
+
+<year-month literal> ::=
+      <years value>
+    | [ <years value> <minus sign> ] <months value>
+
+<day-time literal> ::=
+      <day-time interval>
+    | <time interval>
+
+<day-time interval> ::=
+    <days value>
+      [ <space> <hours value> [ <colon> <minutes value>
+        [ <colon> <seconds value> ] ] ]
+
+<time interval> ::=
+      <hours value> [ <colon> <minutes value> [ <colon> <seconds value> ] ]
+    | <minutes value> [ <colon> <seconds value> ]
+    | <seconds value>
+
+<not equals operator> ::= <>
+
+<greater than or equals operator> ::= >=
+
+<less than or equals operator> ::= <=
+
+<concatenation operator> ::= ||
+
+<double period> ::= ..
+
+<module> ::=
+    <module name clause>
+    <language clause>
+    <module authorization clause>
+    [ <temporary table declaration>... ]
+    <module contents>...
+
+<module name clause> ::=
+    MODULE [ <module name> ]
+      [ <module character set specification> ]
+
+<module name> ::= <identifier>
+
+<module character set specification> ::=
+    NAMES ARE <character set specification>
+
+<language clause> ::=
+    LANGUAGE <language name>
+
+<language name> ::=
+    ADA | C | COBOL | FORTRAN | MUMPS | PASCAL | PLI
+
+<module authorization clause> ::=
+      SCHEMA <schema name>
+    | AUTHORIZATION <module authorization identifier>
+    | SCHEMA <schema name>
+          AUTHORIZATION <module authorization identifier>
+
+<module authorization identifier> ::=
+    <authorization identifier>
+
+<authorization identifier> ::= <identifier>
+
+<temporary table declaration> ::=
+    DECLARE LOCAL TEMPORARY TABLE
+        <qualified local table name>
+      <table element list>
+      [ ON COMMIT { PRESERVE | DELETE } ROWS ]
+
+<qualified local table name> ::=
+    MODULE <period> <local table name>
+
+<local table name> ::= <qualified identifier>
+
+<qualified identifier> ::= <identifier>
+
+<table element list> ::=
+      <left paren> <table element> [ { <comma> <table element> }... ] <right paren>
+
+<table element> ::=
+      <column definition>
+    | <table constraint definition>
+
+<column definition> ::=
+    <column name> { <data type> | <domain name> }
+    [ <default clause> ]
+    [ <column constraint definition>... ]
+    [ <collate clause> ]
+
+<column name> ::= <identifier>
+
+<data type> ::=
+      <character string type>
+           [ CHARACTER SET <character set specification> ]
+    | <national character string type>
+    | <bit string type>
+    | <numeric type>
+    | <datetime type>
+    | <interval type>
+
+<character string type> ::=
+      CHARACTER [ <left paren> <length> <right paren> ]
+    | CHAR [ <left paren> <length> <right paren> ]
+    | CHARACTER VARYING <left paren> <length> <right paren>
+    | CHAR VARYING <left paren> <length> <right paren>
+    | VARCHAR <left paren> <length> <right paren>
+
+<length> ::= <unsigned integer>
+
+<national character string type> ::=
+      NATIONAL CHARACTER [ <left paren> <length> <right paren> ]
+    | NATIONAL CHAR [ <left paren> <length> <right paren> ]
+    | NCHAR [ <left paren> <length> <right paren> ]
+    | NATIONAL CHARACTER VARYING <left paren> <length> <right paren>
+    | NATIONAL CHAR VARYING <left paren> <length> <right paren>
+    | NCHAR VARYING <left paren> <length> <right paren>
+
+<bit string type> ::=
+      BIT [ <left paren> <length> <right paren> ]
+    | BIT VARYING <left paren> <length> <right paren>
+
+<numeric type> ::=
+      <exact numeric type>
+    | <approximate numeric type>
+
+<exact numeric type> ::=
+      NUMERIC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | DECIMAL [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | DEC [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | INTEGER
+    | INT
+    | SMALLINT
+
+<precision> ::= <unsigned integer>
+
+<scale> ::= <unsigned integer>
+
+<approximate numeric type> ::=
+      FLOAT [ <left paren> <precision> <right paren> ]
+    | REAL
+    | DOUBLE PRECISION
+
+<datetime type> ::=
+      DATE
+    | TIME [ <left paren> <time precision> <right paren> ]
+          [ WITH TIME ZONE ]
+    | TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+          [ WITH TIME ZONE ]
+
+<time precision> ::= <time fractional seconds precision>
+
+<time fractional seconds precision> ::= <unsigned integer>
+
+<timestamp precision> ::= <time fractional seconds precision>
+
+<interval type> ::= INTERVAL <interval qualifier>
+
+<interval qualifier> ::=
+      <start field> TO <end field>
+    | <single datetime field>
+
+<start field> ::=
+    <non-second datetime field>
+        [ <left paren> <interval leading field precision> <right paren> ]
+
+<non-second datetime field> ::= YEAR | MONTH | DAY | HOUR
+    | MINUTE
+
+<interval leading field precision> ::= <unsigned integer>
+
+<end field> ::=
+      <non-second datetime field>
+    | SECOND [ <left paren> <interval fractional seconds precision> <right paren> ]
+
+<interval fractional seconds precision> ::= <unsigned integer>
+
+<single datetime field> ::=
+      <non-second datetime field>
+          [ <left paren> <interval leading field precision> <right paren> ]
+    | SECOND [ <left paren> <interval leading field precision>
+          [ <comma> <interval fractional seconds precision> ] <right paren> ]
+
+<domain name> ::= <qualified name>
+
+<qualified name> ::=
+    [ <schema name> <period> ] <qualified identifier>
+
+<default clause> ::=
+      DEFAULT <default option>
+
+<default option> ::=
+      <literal>
+    | <datetime value function>
+    | USER
+    | CURRENT_USER
+    | SESSION_USER
+    | SYSTEM_USER
+    | NULL
+
+<literal> ::=
+      <signed numeric literal>
+    | <general literal>
+
+<signed numeric literal> ::=
+    [ <sign> ] <unsigned numeric literal>
+
+<general literal> ::=
+      <character string literal>
+    | <national character string literal>
+    | <bit string literal>
+    | <hex string literal>
+    | <datetime literal>
+    | <interval literal>
+
+<datetime literal> ::=
+      <date literal>
+    | <time literal>
+    | <timestamp literal>
+
+<date literal> ::=
+    DATE <date string>
+
+<time literal> ::=
+    TIME <time string>
+
+<timestamp literal> ::=
+    TIMESTAMP <timestamp string>
+
+<interval literal> ::=
+    INTERVAL [ <sign> ] <interval string> <interval qualifier>
+
+<datetime value function> ::=
+      <current date value function>
+    | <current time value function>
+    | <current timestamp value function>
+
+<current date value function> ::= CURRENT_DATE
+
+<current time value function> ::=
+      CURRENT_TIME [ <left paren> <time precision> <right paren> ]
+
+<current timestamp value function> ::=
+      CURRENT_TIMESTAMP [ <left paren> <timestamp precision> <right paren> ]
+
+<column constraint definition> ::=
+    [ <constraint name definition> ]
+    <column constraint>
+      [ <constraint attributes> ]
+
+<constraint name definition> ::= CONSTRAINT <constraint name>
+
+<constraint name> ::= <qualified name>
+
+<column constraint> ::=
+      NOT NULL
+    | <unique specification>
+    | <references specification>
+    | <check constraint definition>
+
+<unique specification> ::=
+    UNIQUE | PRIMARY KEY
+
+<references specification> ::=
+    REFERENCES <referenced table and columns>
+      [ MATCH <match type> ]
+      [ <referential triggered action> ]
+
+<referenced table and columns> ::=
+     <table name> [ <left paren> <reference column list> <right paren> ]
+
+<table name> ::=
+      <qualified name>
+    | <qualified local table name>
+
+<reference column list> ::= <column name list>
+
+<column name list> ::=
+    <column name> [ { <comma> <column name> }... ]
+
+<match type> ::=
+      FULL
+    | PARTIAL
+
+<referential triggered action> ::=
+      <update rule> [ <delete rule> ]
+    | <delete rule> [ <update rule> ]
+
+<update rule> ::= ON UPDATE <referential action>
+
+<referential action> ::=
+      CASCADE
+    | SET NULL
+    | SET DEFAULT
+    | NO ACTION
+
+<delete rule> ::= ON DELETE <referential action>
+
+<check constraint definition> ::=
+    CHECK
+        <left paren> <search condition> <right paren>
+
+<search condition> ::=
+      <boolean term>
+    | <search condition> OR <boolean term>
+
+<boolean term> ::=
+      <boolean factor>
+    | <boolean term> AND <boolean factor>
+
+<boolean factor> ::=
+    [ NOT ] <boolean test>
+
+<boolean test> ::=
+    <boolean primary> [ IS [ NOT ]
+          <truth value> ]
+
+<boolean primary> ::=
+      <predicate>
+    | <left paren> <search condition> <right paren>
+
+<predicate> ::=
+      <comparison predicate>
+    | <between predicate>
+    | <in predicate>
+    | <like predicate>
+    | <null predicate>
+    | <quantified comparison predicate>
+    | <exists predicate>
+    | <unique predicate>
+    | <match predicate>
+    | <overlaps predicate>
+
+<comparison predicate> ::=
+    <row value constructor> <comp op>
+        <row value constructor>
+
+<row value constructor> ::=
+       <row value constructor element>
+    | <left paren> <row value constructor list> <right paren>
+    | <row subquery>
+
+<row value constructor element> ::=
+      <value expression>
+    | <null specification>
+    | <default specification>
+
+<value expression> ::=
+      <numeric value expression>
+    | <string value expression>
+    | <datetime value expression>
+    | <interval value expression>
+
+<numeric value expression> ::=
+      <term>
+    | <numeric value expression> <plus sign> <term>
+    | <numeric value expression> <minus sign> <term>
+
+<term> ::=
+      <factor>
+    | <term> <asterisk> <factor>
+    | <term> <solidus> <factor>
+
+<factor> ::=
+    [ <sign> ] <numeric primary>
+
+<numeric primary> ::=
+      <value expression primary>
+    | <numeric value function>
+
+<value expression primary> ::=
+      <unsigned value specification>
+    | <column reference>
+    | <set function specification>
+    | <scalar subquery>
+    | <case expression>
+    | <left paren> <value expression> <right paren>
+    | <cast specification>
+
+<unsigned value specification> ::=
+      <unsigned literal>
+    | <general value specification>
+
+<unsigned literal> ::=
+      <unsigned numeric literal>
+    | <general literal>
+
+<general value specification> ::=
+      <parameter specification>
+    | <dynamic parameter specification>
+    | <variable specification>
+    | USER
+    | CURRENT_USER
+    | SESSION_USER
+    | SYSTEM_USER
+    | VALUE
+
+<parameter specification> ::=
+    <parameter name> [ <indicator parameter> ]
+
+<parameter name> ::= <colon> <identifier>
+
+<indicator parameter> ::=
+    [ INDICATOR ] <parameter name>
+
+<dynamic parameter specification> ::= <question mark>
+
+<variable specification> ::=
+    <embedded variable name> [ <indicator variable> ]
+
+<embedded variable name> ::=
+    <colon><host identifier>
+
+<host identifier> ::=
+      <Ada host identifier>
+    | <C host identifier>
+    | <COBOL host identifier>
+    | <Fortran host identifier>
+    | <MUMPS host identifier>
+    | <Pascal host identifier>
+    | <PL/I host identifier>
+
+<Ada host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<C host identifier> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<COBOL host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<Fortran host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<MUMPS host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<Pascal host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<PL/I host identifier> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<indicator variable> ::=
+    [ INDICATOR ] <embedded variable name>
+
+<column reference> ::= [ <qualifier> <period> ] <column name>
+
+<qualifier> ::=
+      <table name>
+    | <correlation name>
+
+<correlation name> ::= <identifier>
+
+<set function specification> ::=
+      COUNT <left paren> <asterisk> <right paren>
+    | <general set function>
+
+<general set function> ::=
+      <set function type>
+          <left paren> [ <set quantifier> ] <value expression> <right paren>
+
+<set function type> ::=
+    AVG | MAX | MIN | SUM | COUNT
+
+<set quantifier> ::= DISTINCT | ALL
+
+<scalar subquery> ::= <subquery>
+
+<subquery> ::= <left paren> <query expression> <right paren>
+
+<query expression> ::=
+      <non-join query expression>
+    | <joined table>
+
+<non-join query expression> ::=
+      <non-join query term>
+    | <query expression> UNION  [ ALL ]
+          [ <corresponding spec> ] <query term>
+    | <query expression> EXCEPT [ ALL ]
+          [ <corresponding spec> ] <query term>
+
+<non-join query term> ::=
+      <non-join query primary>
+    | <query term> INTERSECT [ ALL ]
+          [ <corresponding spec> ] <query primary>
+
+<non-join query primary> ::=
+      <simple table>
+    | <left paren> <non-join query expression> <right paren>
+
+<simple table> ::=
+      <query specification>
+    | <table value constructor>
+    | <explicit table>
+
+<query specification> ::=
+    SELECT [ <set quantifier> ] <select list> <table expression>
+
+<select list> ::=
+      <asterisk>
+    | <select sublist> [ { <comma> <select sublist> }... ]
+
+<select sublist> ::=
+      <derived column>
+    | <qualifier> <period> <asterisk>
+
+<derived column> ::= <value expression> [ <as clause> ]
+
+<as clause> ::= [ AS ] <column name>
+
+<table expression> ::=
+    <from clause>
+    [ <where clause> ]
+    [ <group by clause> ]
+    [ <having clause> ]
+
+<from clause> ::= FROM <table reference>
+    [ { <comma> <table reference> }... ]
+
+<table reference> ::=
+      <table name> [ [ AS ] <correlation name>
+          [ <left paren> <derived column list> <right paren> ] ]
+    | <derived table> [ AS ] <correlation name>
+          [ <left paren> <derived column list> <right paren> ]
+    | <joined table>
+
+<derived column list> ::= <column name list>
+
+<derived table> ::= <table subquery>
+
+<table subquery> ::= <subquery>
+
+<joined table> ::=
+      <cross join>
+    | <qualified join>
+    | <left paren> <joined table> <right paren>
+
+<cross join> ::=
+    <table reference> CROSS JOIN <table reference>
+
+<qualified join> ::=
+    <table reference> [ NATURAL ] [ <join type> ] JOIN
+      <table reference> [ <join specification> ]
+
+<join type> ::=
+      INNER
+    | <outer join type> [ OUTER ]
+    | UNION
+
+<outer join type> ::=
+      LEFT
+    | RIGHT
+    | FULL
+
+<join specification> ::=
+      <join condition>
+    | <named columns join>
+
+<join condition> ::= ON <search condition>
+
+<named columns join> ::=
+    USING <left paren> <join column list> <right paren>
+
+<join column list> ::= <column name list>
+
+<where clause> ::= WHERE <search condition>
+
+<group by clause> ::=
+    GROUP BY <grouping column reference list>
+
+<grouping column reference list> ::=
+    <grouping column reference>
+        [ { <comma> <grouping column reference> }... ]
+
+<grouping column reference> ::=
+    <column reference> [ <collate clause> ]
+
+<collate clause> ::= COLLATE <collation name>
+
+<collation name> ::= <qualified name>
+
+<having clause> ::= HAVING <search condition>
+
+<table value constructor> ::=
+    VALUES <table value constructor list>
+
+<table value constructor list> ::=
+    <row value constructor> [ { <comma> <row value constructor> }... ]
+
+<explicit table> ::= TABLE <table name>
+
+<query term> ::=
+      <non-join query term>
+    | <joined table>
+
+<corresponding spec> ::=
+    CORRESPONDING [ BY <left paren> <corresponding column list> <right paren> ]
+
+<corresponding column list> ::= <column name list>
+
+<query primary> ::=
+      <non-join query primary>
+    | <joined table>
+
+<case expression> ::=
+      <case abbreviation>
+    | <case specification>
+
+<case abbreviation> ::=
+      NULLIF <left paren> <value expression> <comma>
+            <value expression> <right paren>
+    | COALESCE <left paren> <value expression>
+            { <comma> <value expression> }... <right paren>
+
+<case specification> ::=
+      <simple case>
+    | <searched case>
+
+<simple case> ::=
+    CASE <case operand>
+      <simple when clause>...
+      [ <else clause> ]
+    END
+
+<case operand> ::= <value expression>
+
+<simple when clause> ::= WHEN <when operand> THEN <result>
+
+<when operand> ::= <value expression>
+
+<result> ::= <result expression> | NULL
+
+<result expression> ::= <value expression>
+
+<else clause> ::= ELSE <result>
+
+<searched case> ::=
+    CASE
+      <searched when clause>...
+      [ <else clause> ]
+    END
+
+<searched when clause> ::= WHEN <search condition> THEN <result>
+
+<cast specification> ::=
+    CAST <left paren> <cast operand> AS
+        <cast target> <right paren>
+
+<cast operand> ::=
+      <value expression>
+    | NULL
+
+<cast target> ::=
+      <domain name>
+    | <data type>
+
+<numeric value function> ::=
+      <position expression>
+    | <extract expression>
+    | <length expression>
+
+<position expression> ::=
+    POSITION <left paren> <character value expression>
+        IN <character value expression> <right paren>
+
+<character value expression> ::=
+      <concatenation>
+    | <character factor>
+
+<concatenation> ::=
+    <character value expression> <concatenation operator>
+        <character factor>
+
+<character factor> ::=
+    <character primary> [ <collate clause> ]
+
+<character primary> ::=
+      <value expression primary>
+    | <string value function>
+
+<string value function> ::=
+      <character value function>
+    | <bit value function>
+
+<character value function> ::=
+      <character substring function>
+    | <fold>
+    | <form-of-use conversion>
+    | <character translation>
+    | <trim function>
+
+<character substring function> ::=
+    SUBSTRING <left paren> <character value expression> FROM <start position>
+                [ FOR <string length> ] <right paren>
+
+<start position> ::= <numeric value expression>
+
+<string length> ::= <numeric value expression>
+
+<fold> ::= { UPPER | LOWER }
+     <left paren> <character value expression> <right paren>
+
+<form-of-use conversion> ::=
+    CONVERT <left paren> <character value expression>
+        USING <form-of-use conversion name> <right paren>
+
+<form-of-use conversion name> ::= <qualified name>
+
+<character translation> ::=
+    TRANSLATE <left paren> <character value expression>
+        USING <translation name> <right paren>
+
+<translation name> ::= <qualified name>
+
+<trim function> ::=
+    TRIM <left paren> <trim operands> <right paren>
+
+<trim operands> ::=
+    [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
+
+<trim specification> ::=
+      LEADING
+    | TRAILING
+    | BOTH
+
+<trim character> ::= <character value expression>
+
+<trim source> ::= <character value expression>
+
+<bit value function> ::=
+    <bit substring function>
+
+<bit substring function> ::=
+    SUBSTRING <left paren> <bit value expression> FROM <start position>
+        [ FOR <string length> ] <right paren>
+
+<bit value expression> ::=
+      <bit concatenation>
+    | <bit factor>
+
+<bit concatenation> ::=
+    <bit value expression> <concatenation operator> <bit factor>
+
+<bit factor> ::= <bit primary>
+
+<bit primary> ::=
+      <value expression primary>
+    | <string value function>
+
+<extract expression> ::=
+    EXTRACT <left paren> <extract field>
+        FROM <extract source> <right paren>
+
+<extract field> ::=
+      <datetime field>
+    | <time zone field>
+
+<datetime field> ::=
+      <non-second datetime field>
+    | SECOND
+
+<time zone field> ::=
+      TIMEZONE_HOUR
+    | TIMEZONE_MINUTE
+
+<extract source> ::=
+      <datetime value expression>
+    | <interval value expression>
+
+<datetime value expression> ::=
+      <datetime term>
+    | <interval value expression> <plus sign> <datetime term>
+    | <datetime value expression> <plus sign> <interval term>
+    | <datetime value expression> <minus sign> <interval term>
+
+<interval term> ::=
+      <interval factor>
+    | <interval term 2> <asterisk> <factor>
+    | <interval term 2> <solidus> <factor>
+    | <term> <asterisk> <interval factor>
+
+<interval factor> ::=
+    [ <sign> ] <interval primary>
+
+<interval primary> ::=
+      <value expression primary> [ <interval qualifier> ]
+
+<interval term 2> ::= <interval term>
+
+<interval value expression> ::=
+      <interval term>
+    | <interval value expression 1> <plus sign> <interval term 1>
+    | <interval value expression 1> <minus sign> <interval term 1>
+    | <left paren> <datetime value expression> <minus sign>
+          <datetime term> <right paren> <interval qualifier>
+
+<interval value expression 1> ::= <interval value expression>
+
+<interval term 1> ::= <interval term>
+
+<datetime term> ::=
+      <datetime factor>
+
+<datetime factor> ::=
+      <datetime primary> [ <time zone> ]
+
+<datetime primary> ::=
+      <value expression primary>
+    | <datetime value function>
+
+<time zone> ::=
+    AT <time zone specifier>
+
+<time zone specifier> ::=
+      LOCAL
+    | TIME ZONE <interval value expression>
+
+<length expression> ::=
+      <char length expression>
+    | <octet length expression>
+    | <bit length expression>
+
+<char length expression> ::=
+    { CHAR_LENGTH | CHARACTER_LENGTH }
+        <left paren> <string value expression> <right paren>
+
+<string value expression> ::=
+      <character value expression>
+    | <bit value expression>
+
+<octet length expression> ::=
+    OCTET_LENGTH <left paren> <string value expression> <right paren>
+
+<bit length expression> ::=
+    BIT_LENGTH <left paren> <string value expression> <right paren>
+
+<null specification> ::=
+    NULL
+
+<default specification> ::=
+    DEFAULT
+
+<row value constructor list> ::=
+    <row value constructor element>
+        [ { <comma> <row value constructor element> }... ]
+
+<row subquery> ::= <subquery>
+
+<comp op> ::=
+      <equals operator>
+    | <not equals operator>
+    | <less than operator>
+    | <greater than operator>
+    | <less than or equals operator>
+    | <greater than or equals operator>
+
+<between predicate> ::=
+    <row value constructor> [ NOT ] BETWEEN
+      <row value constructor> AND <row value constructor>
+
+<in predicate> ::=
+    <row value constructor>
+      [ NOT ] IN <in predicate value>
+
+<in predicate value> ::=
+      <table subquery>
+    | <left paren> <in value list> <right paren>
+
+<in value list> ::=
+    <value expression> { <comma> <value expression> }...
+
+<like predicate> ::=
+    <match value> [ NOT ] LIKE <pattern>
+      [ ESCAPE <escape character> ]
+
+<match value> ::= <character value expression>
+
+<pattern> ::= <character value expression>
+
+<escape character> ::= <character value expression>
+
+<null predicate> ::= <row value constructor>
+    IS [ NOT ] NULL
+
+<quantified comparison predicate> ::=
+    <row value constructor> <comp op> <quantifier> <table subquery>
+
+<quantifier> ::= <all> | <some>
+
+<all> ::= ALL
+
+<some> ::= SOME | ANY
+
+<exists predicate> ::= EXISTS <table subquery>
+
+<unique predicate> ::= UNIQUE <table subquery>
+
+<match predicate> ::=
+    <row value constructor> MATCH [ UNIQUE ]
+        [ PARTIAL | FULL ] <table subquery>
+
+<overlaps predicate> ::=
+    <row value constructor 1> OVERLAPS <row value constructor 2>
+
+<row value constructor 1> ::= <row value constructor>
+
+<row value constructor 2> ::= <row value constructor>
+
+<truth value> ::=
+      TRUE
+    | FALSE
+    | UNKNOWN
+
+<constraint attributes> ::=
+      <constraint check time> [ [ NOT ] DEFERRABLE ]
+    | [ NOT ] DEFERRABLE [ <constraint check time> ]
+
+<constraint check time> ::=
+      INITIALLY DEFERRED
+    | INITIALLY IMMEDIATE
+
+<table constraint definition> ::=
+    [ <constraint name definition> ]
+    <table constraint> [ <constraint attributes> ]
+
+<table constraint> ::=
+      <unique constraint definition>
+    | <referential constraint definition>
+    | <check constraint definition>
+
+<unique constraint definition> ::=
+            <unique specification> even in SQL3)
+    <unique specification>
+      <left paren> <unique column list> <right paren>
+
+<unique column list> ::= <column name list>
+
+<referential constraint definition> ::=
+    FOREIGN KEY
+        <left paren> <referencing columns> <right paren>
+      <references specification>
+
+<referencing columns> ::=
+    <reference column list>
+
+<module contents> ::=
+      <declare cursor>
+    | <dynamic declare cursor>
+    | <procedure>
+
+<declare cursor> ::=
+    DECLARE <cursor name> [ INSENSITIVE ] [ SCROLL ] CURSOR
+      FOR <cursor specification>
+
+<cursor name> ::= <identifier>
+
+<cursor specification> ::=
+    <query expression> [ <order by clause> ]
+      [ <updatability clause> ]
+
+<order by clause> ::=
+    ORDER BY <sort specification list>
+
+<sort specification list> ::=
+    <sort specification> [ { <comma> <sort specification> }... ]
+
+<sort specification> ::=
+    <sort key> [ <collate clause> ] [ <ordering specification> ]
+
+<sort key> ::=
+      <column name>
+    | <unsigned integer>
+
+<ordering specification> ::= ASC | DESC
+
+<updatability clause> ::=
+    FOR
+        { READ ONLY |
+          UPDATE [ OF <column name list> ] }
+
+<dynamic declare cursor> ::=
+    DECLARE <cursor name> [ INSENSITIVE ] [ SCROLL ] CURSOR
+        FOR <statement name>
+
+<statement name> ::= <identifier>
+
+<procedure> ::=
+    PROCEDURE <procedure name>
+        <parameter declaration list> <semicolon>
+      <SQL procedure statement> <semicolon>
+
+<procedure name> ::= <identifier>
+
+<parameter declaration list> ::=
+      <left paren> <parameter declaration>
+          [ { <comma> <parameter declaration> }... ] <right paren>
+    | <parameter declaration>...
+
+<parameter declaration> ::=
+      <parameter name> <data type>
+    | <status parameter>
+
+<status parameter> ::=
+    SQLCODE | SQLSTATE
+
+<SQL procedure statement> ::=
+      <SQL schema statement>
+    | <SQL data statement>
+    | <SQL transaction statement>
+    | <SQL connection statement>
+    | <SQL session statement>
+    | <SQL dynamic statement>
+    | <SQL diagnostics statement>
+
+<SQL schema statement> ::=
+      <SQL schema definition statement>
+    | <SQL schema manipulation statement>
+
+<SQL schema definition statement> ::=
+      <schema definition>
+    | <table definition>
+    | <view definition>
+    | <grant statement>
+    | <domain definition>
+    | <character set definition>
+    | <collation definition>
+    | <translation definition>
+    | <assertion definition>
+
+<schema definition> ::=
+    CREATE SCHEMA <schema name clause>
+      [ <schema character set specification> ]
+      [ <schema element>... ]
+
+<schema name clause> ::=
+      <schema name>
+    | AUTHORIZATION <schema authorization identifier>
+    | <schema name> AUTHORIZATION
+          <schema authorization identifier>
+
+<schema authorization identifier> ::=
+    <authorization identifier>
+
+<schema character set specification> ::=
+    DEFAULT CHARACTER
+        SET <character set specification>
+
+<schema element> ::=
+      <domain definition>
+    | <table definition>
+    | <view definition>
+    | <grant statement>
+    | <assertion definition>
+    | <character set definition>
+    | <collation definition>
+    | <translation definition>
+
+<domain definition> ::=
+    CREATE DOMAIN <domain name>
+        [ AS ] <data type>
+      [ <default clause> ]
+      [ <domain constraint>... ]
+      [ <collate clause> ]
+
+<domain constraint> ::=
+    [ <constraint name definition> ]
+    <check constraint definition> [ <constraint attributes> ]
+
+<table definition> ::=
+    CREATE [ { GLOBAL | LOCAL } TEMPORARY ] TABLE
+        <table name>
+      <table element list>
+      [ ON COMMIT { DELETE | PRESERVE } ROWS ]
+
+<view definition> ::=
+    CREATE VIEW <table name> [ <left paren> <view column list>
+                                  <right paren> ]
+      AS <query expression>
+      [ WITH [ <levels clause> ] CHECK OPTION ]
+
+<view column list> ::= <column name list>
+
+<levels clause> ::=
+    CASCADED | LOCAL
+
+<grant statement> ::=
+   GRANT <privileges> ON <object name>
+     TO <grantee> [ { <comma> <grantee> }... ]
+       [ WITH GRANT OPTION ]
+
+<privileges> ::=
+      ALL PRIVILEGES
+    | <action list>
+
+<action list> ::= <action> [ { <comma> <action> }... ]
+
+<action> ::=
+      SELECT
+    | DELETE
+    | INSERT [ <left paren> <privilege column list> <right paren> ]
+    | UPDATE [ <left paren> <privilege column list> <right paren> ]
+    | REFERENCES [ <left paren> <privilege column list> <right paren> ]
+    | USAGE
+
+<privilege column list> ::= <column name list>
+
+<object name> ::=
+      [ TABLE ] <table name>
+    | DOMAIN <domain name>
+    | COLLATION <collation name>
+    | CHARACTER SET <character set name>
+    | TRANSLATION <translation name>
+
+<grantee> ::=
+      PUBLIC
+    | <authorization identifier>
+
+<assertion definition> ::=
+    CREATE ASSERTION <constraint name> <assertion check>
+      [ <constraint attributes> ]
+
+<assertion check> ::=
+    CHECK
+        <left paren> <search condition> <right paren>
+
+<character set definition> ::=
+    CREATE CHARACTER SET <character set name>
+        [ AS ]
+      <character set source>
+      [ <collate clause> | <limited collation definition> ]
+
+<character set source> ::=
+      GET <existing character set name>
+
+<existing character set name> ::=
+      <standard character repertoire name>
+    | <implementation-defined character repertoire name>
+    | <schema character set name>
+
+<schema character set name> ::= <character set name>
+
+<limited collation definition> ::=
+    COLLATION FROM <collation source>
+
+<collation source> ::=
+      <collating sequence definition>
+    | <translation collation>
+
+<collating sequence definition> ::=
+      <external collation>
+    | <schema collation name>
+    | DESC <left paren> <collation name> <right paren>
+    | DEFAULT
+
+<external collation> ::=
+    EXTERNAL <left paren> <quote> <external collation name> <quote> <right paren>
+
+<external collation name> ::=
+      <standard collation name>
+    | <implementation-defined collation name>
+
+<standard collation name> ::= <collation name>
+
+<implementation-defined collation name> ::= <collation name>
+
+<schema collation name> ::= <collation name>
+
+<translation collation> ::=
+    TRANSLATION <translation name>
+        [ THEN COLLATION <collation name> ]
+
+<collation definition> ::=
+    CREATE COLLATION <collation name> FOR
+        <character set specification>
+      FROM <collation source>
+        [ <pad attribute> ]
+
+<pad attribute> ::=
+      NO PAD
+    | PAD SPACE
+
+<translation definition> ::=
+    CREATE TRANSLATION <translation name>
+      FOR <source character set specification>
+        TO <target character set specification>
+      FROM <translation source>
+
+<source character set specification> ::= <character set specification>
+
+<target character set specification> ::= <character set specification>
+
+<translation source> ::=
+      <translation specification>
+
+<translation specification> ::=
+      <external translation>
+    | IDENTITY
+    | <schema translation name>
+
+<external translation> ::=
+    EXTERNAL <left paren> <quote> <external translation name> <quote> <right paren>
+
+<external translation name> ::=
+      <standard translation name>
+    | <implementation-defined translation name>
+
+<standard translation name> ::= <translation name>
+
+<implementation-defined translation name> ::= <translation name>
+
+<schema translation name> ::= <translation name>
+
+<SQL schema manipulation statement> ::=
+      <drop schema statement>
+    | <alter table statement>
+    | <drop table statement>
+    | <drop view statement>
+    | <revoke statement>
+    | <alter domain statement>
+    | <drop domain statement>
+    | <drop character set statement>
+    | <drop collation statement>
+    | <drop translation statement>
+    | <drop assertion statement>
+
+<drop schema statement> ::=
+    DROP SCHEMA <schema name> <drop behavior>
+
+<drop behavior> ::= CASCADE | RESTRICT
+
+<alter table statement> ::=
+    ALTER TABLE <table name> <alter table action>
+
+<alter table action> ::=
+      <add column definition>
+    | <alter column definition>
+    | <drop column definition>
+    | <add table constraint definition>
+    | <drop table constraint definition>
+
+<add column definition> ::=
+    ADD [ COLUMN ] <column definition>
+
+<alter column definition> ::=
+    ALTER [ COLUMN ] <column name> <alter column action>
+
+<alter column action> ::=
+      <set column default clause>
+    | <drop column default clause>
+
+<set column default clause> ::=
+    SET <default clause>
+
+<drop column default clause> ::=
+    DROP DEFAULT
+
+<drop column definition> ::=
+    DROP [ COLUMN ] <column name> <drop behavior>
+
+<add table constraint definition> ::=
+    ADD <table constraint definition>
+
+<drop table constraint definition> ::=
+    DROP CONSTRAINT <constraint name> <drop behavior>
+
+<drop table statement> ::=
+    DROP TABLE <table name> <drop behavior>
+
+<drop view statement> ::=
+    DROP VIEW <table name> <drop behavior>
+
+<revoke statement> ::=
+    REVOKE [ GRANT OPTION FOR ]
+        <privileges>
+        ON <object name>
+      FROM <grantee> [ { <comma> <grantee> }... ] <drop behavior>
+
+<alter domain statement> ::=
+    ALTER DOMAIN <domain name> <alter domain action>
+
+<alter domain action> ::=
+      <set domain default clause>
+    | <drop domain default clause>
+    | <add domain constraint definition>
+    | <drop domain constraint definition>
+
+<set domain default clause> ::= SET <default clause>
+
+<drop domain default clause> ::= DROP DEFAULT
+
+<add domain constraint definition> ::=
+    ADD <domain constraint>
+
+<drop domain constraint definition> ::=
+    DROP CONSTRAINT <constraint name>
+
+<drop domain statement> ::=
+    DROP DOMAIN <domain name> <drop behavior>
+
+<drop character set statement> ::=
+    DROP CHARACTER SET <character set name>
+
+<drop collation statement> ::=
+    DROP COLLATION <collation name>
+
+<drop translation statement> ::=
+    DROP TRANSLATION <translation name>
+
+<drop assertion statement> ::=
+    DROP ASSERTION <constraint name>
+
+<SQL data statement> ::=
+      <open statement>
+    | <fetch statement>
+    | <close statement>
+    | <select statement: single row>
+    | <SQL data change statement>
+
+<open statement> ::=
+    OPEN <cursor name>
+
+<fetch statement> ::=
+    FETCH [ [ <fetch orientation> ] FROM ]
+      <cursor name> INTO <fetch target list>
+
+<fetch orientation> ::=
+      NEXT
+    | PRIOR
+    | FIRST
+    | LAST
+    | { ABSOLUTE | RELATIVE } <simple value specification>
+
+<simple value specification> ::=
+      <parameter name>
+    | <embedded variable name>
+    | <literal>
+
+<fetch target list> ::=
+    <target specification> [ { <comma> <target specification> }... ]
+
+<target specification> ::=
+      <parameter specification>
+    | <variable specification>
+
+<close statement> ::=
+    CLOSE <cursor name>
+
+<select statement: single row> ::=
+    SELECT [ <set quantifier> ] <select list>
+      INTO <select target list>
+        <table expression>
+
+<select target list> ::=
+    <target specification> [ { <comma> <target specification> }... ]
+
+<SQL data change statement> ::=
+      <delete statement: positioned>
+    | <delete statement: searched>
+    | <insert statement>
+    | <update statement: positioned>
+    | <update statement: searched>
+
+<delete statement: positioned> ::=
+    DELETE FROM <table name>
+      WHERE CURRENT OF <cursor name>
+
+<delete statement: searched> ::=
+    DELETE FROM <table name>
+      [ WHERE <search condition> ]
+
+<insert statement> ::=
+    INSERT INTO <table name>
+      <insert columns and source>
+
+<insert columns and source> ::=
+      [ <left paren> <insert column list> <right paren> ]
+            <query expression>
+    | DEFAULT VALUES
+
+<insert column list> ::= <column name list>
+
+<update statement: positioned> ::=
+    UPDATE <table name>
+      SET <set clause list>
+        WHERE CURRENT OF <cursor name>
+
+<set clause list> ::=
+    <set clause> [ { <comma> <set clause> }... ]
+
+<set clause> ::=
+    <object column> <equals operator> <update source>
+
+<object column> ::= <column name>
+
+<update source> ::=
+      <value expression>
+    | <null specification>
+    | DEFAULT
+
+<update statement: searched> ::=
+    UPDATE <table name>
+      SET <set clause list>
+      [ WHERE <search condition> ]
+
+<SQL transaction statement> ::=
+      <set transaction statement>
+    | <set constraints mode statement>
+    | <commit statement>
+    | <rollback statement>
+
+<set transaction statement> ::=
+    SET TRANSACTION <transaction mode>
+        [ { <comma> <transaction mode> }... ]
+
+<transaction mode> ::=
+      <isolation level>
+    | <transaction access mode>
+    | <diagnostics size>
+
+<isolation level> ::=
+    ISOLATION LEVEL <level of isolation>
+
+<level of isolation> ::=
+      READ UNCOMMITTED
+    | READ COMMITTED
+    | REPEATABLE READ
+    | SERIALIZABLE
+
+<transaction access mode> ::=
+      READ ONLY
+    | READ WRITE
+
+<diagnostics size> ::=
+    DIAGNOSTICS SIZE <number of conditions>
+
+<number of conditions> ::= <simple value specification>
+
+<set constraints mode statement> ::=
+    SET CONSTRAINTS <constraint name list>
+        { DEFERRED | IMMEDIATE }
+
+<constraint name list> ::=
+      ALL
+    | <constraint name> [ { <comma> <constraint name> }... ]
+
+<commit statement> ::=
+    COMMIT [ WORK ]
+
+<rollback statement> ::=
+    ROLLBACK [ WORK ]
+
+<SQL connection statement> ::=
+      <connect statement>
+    | <set connection statement>
+    | <disconnect statement>
+
+<connect statement> ::=
+    CONNECT TO <connection target>
+
+<connection target> ::=
+      <SQL-server name>
+        [ AS <connection name> ]
+          correspondence with Tony Gordon)
+        [ USER <user name> ]
+    | DEFAULT
+
+<SQL-server name> ::= <simple value specification>
+
+<connection name> ::= <simple value specification>
+
+<user name> ::= <simple value specification>
+
+<set connection statement> ::=
+    SET CONNECTION <connection object>
+
+<connection object> ::=
+      DEFAULT
+    | <connection name>
+
+<disconnect statement> ::=
+    DISCONNECT <disconnect object>
+
+<disconnect object> ::=
+      <connection object>
+    | ALL
+    | CURRENT
+
+<SQL session statement> ::=
+      <set catalog statement>
+    | <set schema statement>
+    | <set names statement>
+    | <set session authorization identifier statement>
+    | <set local time zone statement>
+
+<set catalog statement> ::=
+    SET CATALOG <value specification>
+
+<value specification> ::=
+      <literal>
+    | <general value specification>
+
+<set schema statement> ::=
+    SET SCHEMA <value specification>
+
+<set names statement> ::=
+    SET NAMES <value specification>
+
+<set session authorization identifier statement> ::=
+    SET SESSION AUTHORIZATION
+        <value specification>
+
+<set local time zone statement> ::=
+    SET TIME ZONE
+        <set time zone value>
+
+<set time zone value> ::=
+      <interval value expression>
+    | LOCAL
+
+<SQL dynamic statement> ::=
+      <system descriptor statement>
+    | <prepare statement>
+    | <deallocate prepared statement>
+    | <describe statement>
+    | <execute statement>
+    | <execute immediate statement>
+    | <SQL dynamic data statement>
+
+<system descriptor statement> ::=
+      <allocate descriptor statement>
+    | <deallocate descriptor statement>
+    | <set descriptor statement>
+    | <get descriptor statement>
+
+<allocate descriptor statement> ::=
+    ALLOCATE DESCRIPTOR <descriptor name>
+       [ WITH MAX <occurrences> ]
+
+<descriptor name> ::=
+    [ <scope option> ] <simple value specification>
+
+<scope option> ::=
+      GLOBAL
+    | LOCAL
+
+<occurrences> ::= <simple value specification>
+
+<deallocate descriptor statement> ::=
+    DEALLOCATE DESCRIPTOR <descriptor name>
+
+<set descriptor statement> ::=
+    SET DESCRIPTOR <descriptor name>
+        <set descriptor information>
+
+<set descriptor information> ::=
+      <set count>
+    | VALUE <item number>
+        <set item information> [ { <comma> <set item information> }... ]
+
+<set count> ::=
+    COUNT <equals operator> <simple value specification 1>
+
+<simple value specification 1> ::= <simple value specification>
+
+<item number> ::= <simple value specification>
+
+<set item information> ::=
+    <descriptor item name> <equals operator> <simple value specification 2>
+
+<descriptor item name> ::=
+      TYPE
+    | LENGTH
+    | OCTET_LENGTH
+    | RETURNED_LENGTH
+    | RETURNED_OCTET_LENGTH
+    | PRECISION
+    | SCALE
+    | DATETIME_INTERVAL_CODE
+    | DATETIME_INTERVAL_PRECISION
+    | NULLABLE
+    | INDICATOR
+    | DATA
+    | NAME
+    | UNNAMED
+    | COLLATION_CATALOG
+    | COLLATION_SCHEMA
+    | COLLATION_NAME
+    | CHARACTER_SET_CATALOG
+    | CHARACTER_SET_SCHEMA
+    | CHARACTER_SET_NAME
+
+<simple value specification 2> ::= <simple value specification>
+
+<item number> ::= <simple value specification>
+
+<get descriptor statement> ::=
+    GET DESCRIPTOR <descriptor name> <get descriptor information>
+
+<get descriptor information> ::=
+      <get count>
+    | VALUE <item number>
+        <get item information> [ { <comma> <get item information> }... ]
+
+<get count> ::=
+    <simple target specification 1> <equals operator>
+         COUNT
+
+<simple target specification 1> ::= <simple target specification>
+
+<simple target specification> ::=
+      <parameter name>
+    | <embedded variable name>
+
+<get item information> ::=
+    <simple target specification 2> <equals operator> <descriptor item name>>
+
+<simple target specification 2> ::= <simple target specification>
+
+<prepare statement> ::=
+    PREPARE <SQL statement name> FROM <SQL statement variable>
+
+<SQL statement name> ::=
+      <statement name>
+    | <extended statement name>
+
+<extended statement name> ::=
+    [ <scope option> ] <simple value specification>
+
+<SQL statement variable> ::= <simple value specification>
+
+<deallocate prepared statement> ::=
+    DEALLOCATE PREPARE <SQL statement name>
+
+<describe statement> ::=
+      <describe input statement>
+    | <describe output statement>
+
+<describe input statement> ::=
+    DESCRIBE INPUT <SQL statement name> <using descriptor>
+
+<using descriptor> ::=
+    { USING | INTO } SQL DESCRIPTOR <descriptor name>
+
+<describe output statement> ::=
+    DESCRIBE [ OUTPUT ] <SQL statement name> <using descriptor>
+
+<execute statement> ::=
+    EXECUTE <SQL statement name>
+      [ <result using clause> ]
+      [ <parameter using clause> ]
+
+<result using clause> ::= <using clause>
+
+<using clause> ::=
+      <using arguments>
+    | <using descriptor>
+
+<using arguments> ::=
+    { USING | INTO } <argument> [ { <comma> <argument> }... ]
+
+<argument> ::= <target specification>
+
+<parameter using clause> ::= <using clause>
+
+<execute immediate statement> ::=
+    EXECUTE IMMEDIATE <SQL statement variable>
+
+<SQL dynamic data statement> ::=
+      <allocate cursor statement>
+    | <dynamic open statement>
+    | <dynamic fetch statement>
+    | <dynamic close statement>
+    | <dynamic delete statement: positioned>
+    | <dynamic update statement: positioned>
+
+<allocate cursor statement> ::=
+    ALLOCATE <extended cursor name> [ INSENSITIVE ]
+        [ SCROLL ] CURSOR
+      FOR <extended statement name>
+
+<extended cursor name> ::=
+    [ <scope option> ] <simple value specification>
+
+<dynamic open statement> ::=
+    OPEN <dynamic cursor name> [ <using clause> ]
+
+<dynamic cursor name> ::=
+      <cursor name>
+    | <extended cursor name>
+
+<dynamic fetch statement> ::=
+    FETCH [ [ <fetch orientation> ] FROM ] <dynamic cursor name>
+        <using clause>
+
+<dynamic close statement> ::=
+    CLOSE <dynamic cursor name>
+
+<dynamic delete statement: positioned> ::=
+    DELETE FROM <table name>
+      WHERE CURRENT OF
+          <dynamic cursor name>
+
+<dynamic update statement: positioned> ::=
+    UPDATE <table name>
+      SET <set clause>
+          [ { <comma> <set clause> }... ]
+        WHERE CURRENT OF
+            <dynamic cursor name>
+
+<SQL diagnostics statement> ::=
+    <get diagnostics statement>
+
+<get diagnostics statement> ::=
+    GET DIAGNOSTICS <sql diagnostics information>
+
+<sql diagnostics information> ::=
+      <statement information>
+    | <condition information>
+
+<statement information> ::=
+    <statement information item> [ { <comma> <statement information item> }... ]
+
+<statement information item> ::=
+    <simple target specification> <equals operator> <statement information item name>
+
+<statement information item name> ::=
+      NUMBER
+    | MORE
+    | COMMAND_FUNCTION
+    | DYNAMIC_FUNCTION
+    | ROW_COUNT
+
+<condition information> ::=
+    EXCEPTION <condition number>
+      <condition information item> [ { <comma> <condition information item> }... ]
+
+<condition number> ::= <simple value specification>
+
+<condition information item> ::=
+    <simple target specification> <equals operator> <condition information item name>
+
+<condition information item name> ::=
+      CONDITION_NUMBER
+    | RETURNED_SQLSTATE
+    | CLASS_ORIGIN
+    | SUBCLASS_ORIGIN
+    | SERVER_NAME
+    | CONNECTION_NAME
+    | CONSTRAINT_CATALOG
+    | CONSTRAINT_SCHEMA
+    | CONSTRAINT_NAME
+    | CATALOG_NAME
+    | SCHEMA_NAME
+    | TABLE_NAME
+    | COLUMN_NAME
+    | CURSOR_NAME
+    | MESSAGE_TEXT
+    | MESSAGE_LENGTH
+    | MESSAGE_OCTET_LENGTH
+
+<embedded SQL host program> ::=
+      <embedded SQL Ada program>
+    | <embedded SQL C program>
+    | <embedded SQL COBOL program>
+    | <embedded SQL Fortran program>
+    | <embedded SQL MUMPS program>
+    | <embedded SQL Pascal program>
+    | <embedded SQL PL/I program>
+
+<embedded SQL Ada program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL C program> ::=
+      !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL COBOL program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL Fortran program> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL MUMPS program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL Pascal program> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL PL/I program> ::= !! <EMPHASIS>(See the Syntax Rules.)
+
+<embedded SQL declare section> ::=
+      <embedded SQL begin declare>
+        [ <embedded character set declaration> ]
+        [ <host variable definition>... ]
+      <embedded SQL end declare>
+    | <embedded SQL MUMPS declare>
+
+<embedded SQL begin declare> ::=
+    <SQL prefix> BEGIN DECLARE SECTION
+        [ <SQL terminator> ]
+
+<SQL prefix> ::=
+      EXEC SQL
+    | <ampersand>SQL<left paren>
+
+<SQL terminator> ::=
+      END-EXEC
+    | <semicolon>
+    | <right paren>
+
+<embedded character set declaration> ::=
+    SQL NAMES ARE <character set specification>
+
+<host variable definition> ::=
+      <Ada variable definition>
+    | <C variable definition>
+    | <COBOL variable definition>
+    | <Fortran variable definition>
+    | <MUMPS variable definition>
+    | <Pascal variable definition>
+    | <PL/I variable definition>
+
+<Ada variable definition> ::=
+    <Ada host identifier> [ { <comma> <Ada host identifier> }... ] :
+    <Ada type specification> [ <Ada initial value> ]
+
+<Ada type specification> ::=
+      <Ada qualified type specification>
+    | <Ada unqualified type specification>
+
+<Ada qualified type specification> ::=
+      SQL_STANDARD.CHAR [ CHARACTER SET
+         [ IS ] <character set specification> ]
+          <left paren> 1 <double period> <length> <right paren>
+    | SQL_STANDARD.BIT
+          <left paren> 1 <double period> <length> <right paren>
+    | SQL_STANDARD.SMALLINT
+    | SQL_STANDARD.INT
+    | SQL_STANDARD.REAL
+    | SQL_STANDARD.DOUBLE_PRECISION
+    | SQL_STANDARD.SQLCODE_TYPE
+    | SQL_STANDARD.SQLSTATE_TYPE
+    | SQL_STANDARD.INDICATOR_TYPE
+
+<Ada unqualified type specification> ::=
+      CHAR
+          <left paren> 1 <double period> <length> <right paren>
+    | BIT
+          <left paren> 1 <double period> <length> <right paren>
+    | SMALLINT
+    | INT
+    | REAL
+    | DOUBLE_PRECISION
+    | SQLCODE_TYPE
+    | SQLSTATE_TYPE
+    | INDICATOR_TYPE
+
+<Ada initial value> ::=
+    <Ada assignment operator> <character representation>...
+
+<Ada assignment operator> ::= <colon><equals operator>
+
+<C variable definition> ::=
+      [ <C storage class> ]
+      [ <C class modifier> ]
+      <C variable specification>
+    <semicolon>
+
+<C storage class> ::=
+      auto
+    | extern
+    | static
+
+<C class modifier> ::= const | volatile
+
+<C variable specification> ::=
+      <C numeric variable>
+    | <C character variable>
+    | <C derived variable>
+
+<C numeric variable> ::=
+    { long | short | float | double }
+      <C host identifier> [ <C initial value> ]
+            [ { <comma> <C host identifier> [ <C initial value> ] }... ]
+
+<C initial value> ::=
+    <equals operator> <character representation>...
+
+<C character variable> ::=
+    char [ CHARACTER SET
+             [ IS ] <character set specification> ]
+      <C host identifier>
+        <C array specification> [ <C initial value> ]
+        [ { <comma> <C host identifier>
+          <C array specification>
+                 [ <C initial value> ] }... ]
+
+<C array specification> ::=
+    <left bracket> <length> <right bracket>
+
+<C derived variable> ::=
+      <C VARCHAR variable>
+    | <C bit variable>
+
+<C VARCHAR variable> ::=
+    VARCHAR [ CHARACTER SET [ IS ]
+        <character set specification> ]
+        <C host identifier>
+            <C array specification> [ <C initial value> ]
+          [ { <comma> <C host identifier>
+              <C array specification>
+                      [ <C initial value> ] }... ]
+
+<C bit variable> ::=
+    BIT <C host identifier>
+        <C array specification> [ <C initial value> ]
+      [ { <comma> <C host identifier>
+        <C array specification>
+                   [ <C initial value> ] }... ]
+
+<COBOL variable definition> ::=
+    {01|77} <COBOL host identifier> <COBOL type specification>
+      [ <character representation>... ] <period>
+
+<COBOL type specification> ::=
+      <COBOL character type>
+    | <COBOL bit type>
+    | <COBOL numeric type>
+    | <COBOL integer type>
+
+<COBOL character type> ::=
+    [ CHARACTER SET [ IS ]
+          <character set specification> ]
+    { PIC | PICTURE } [ IS ] { X [ <left paren> <length> <right paren> ] }...
+
+<COBOL bit type> ::=
+    { PIC | PICTURE } [ IS ]
+        { B [ <left paren> <length> <right paren> ] }...
+
+<COBOL numeric type> ::=
+    { PIC | PICTURE } [ IS ]
+      S <COBOL nines specification>
+    [ USAGE [ IS ] ] DISPLAY SIGN LEADING SEPARATE
+
+<COBOL nines specification> ::=
+      <COBOL nines> [ V [ <COBOL nines> ] ]
+    | V <COBOL nines>
+
+<COBOL nines> ::= { 9 [ <left paren> <length> <right paren> ] }...
+
+<COBOL integer type> ::=
+      <COBOL computational integer>
+    | <COBOL binary integer>
+
+<COBOL computational integer> ::=
+    { PIC | PICTURE } [ IS ] S<COBOL nines>
+      [ USAGE [ IS ] ] { COMP | COMPUTATIONAL }
+
+<COBOL binary integer> ::=
+    { PIC | PICTURE } [ IS ] S<COBOL nines>
+      [ USAGE [ IS ] ] BINARY
+
+<Fortran variable definition> ::=
+    <Fortran type specification>
+    <Fortran host identifier>
+        [ { <comma> <Fortran host identifier> }... ]
+
+<Fortran type specification> ::=
+      CHARACTER [ <asterisk> <length> ]
+          [ CHARACTER SET [ IS ]
+                <character set specification> ]
+    | BIT [ <asterisk> <length> ]
+    | INTEGER
+    | REAL
+    | DOUBLE PRECISION
+
+<MUMPS variable definition> ::=
+    { <MUMPS numeric variable> | <MUMPS character variable> }
+        <semicolon>
+
+<MUMPS numeric variable> ::=
+    <MUMPS type specification>
+      <MUMPS host identifier> [ { <comma> <MUMPS host identifier> }... ]
+
+<MUMPS type specification> ::=
+      INT
+    | DEC
+          [ <left paren> <precision> [ <comma> <scale> ] <right paren> ]
+    | REAL
+
+<MUMPS character variable> ::=
+    VARCHAR <MUMPS host identifier> <MUMPS length specification>
+      [ { <comma> <MUMPS host identifier> <MUMPS length specification> }... ]
+
+<MUMPS length specification> ::=
+    <left paren> <length> <right paren>
+
+<Pascal variable definition> ::=
+    <Pascal host identifier> [ { <comma> <Pascal host identifier> }... ] <colon>
+      <Pascal type specification> <semicolon>
+
+<Pascal type specification> ::=
+      PACKED ARRAY
+          <left bracket> 1 <double period> <length> <right bracket>
+        OF CHAR
+          [ CHARACTER SET [ IS ]
+                <character set specification> ]
+    | PACKED ARRAY
+          <left bracket> 1 <double period> <length> <right bracket>
+        OF BIT
+    | INTEGER
+    | REAL
+    | CHAR [ CHARACTER SET
+                                [ IS ] <character set specification> ]
+    | BIT
+
+<PL/I variable definition> ::=
+    {DCL | DECLARE}
+        {   <PL/I host identifier>
+          | <left paren> <PL/I host identifier>
+                [ { <comma> <PL/I host identifier> }... ] <right paren> }
+    <PL/I type specification>
+    [ <character representation>... ] <semicolon>
+
+<PL/I type specification> ::=
+      { CHAR | CHARACTER } [ VARYING ]
+          <left paren><length><right paren>
+          [ CHARACTER SET
+                [ IS ] <character set specification> ]
+    | BIT [ VARYING ] <left paren><length><right paren>
+    | <PL/I type fixed decimal> <left paren> <precision>
+          [ <comma> <scale> ] <right paren>
+    | <PL/I type fixed binary> [ <left paren> <precision> <right paren> ]
+    | <PL/I type float binary> <left paren> <precision> <right paren>
+
+<PL/I type fixed decimal> ::=
+      { DEC | DECIMAL } FIXED
+    | FIXED { DEC | DECIMAL }
+
+<PL/I type fixed binary> ::=
+      { BIN | BINARY } FIXED
+    | FIXED { BIN | BINARY }
+
+<PL/I type float binary> ::=
+      { BIN | BINARY } FLOAT
+    | FLOAT { BIN | BINARY }
+
+<embedded SQL end declare> ::=
+    <SQL prefix> END DECLARE SECTION
+        [ <SQL terminator> ]
+
+<embedded SQL MUMPS declare> ::=
+    <SQL prefix>
+      BEGIN DECLARE SECTION
+        [ <embedded character set declaration> ]
+        [ <host variable definition>... ]
+      END DECLARE SECTION
+    <SQL terminator>
+
+<embedded SQL statement> ::=
+    <SQL prefix>
+      <statement or declaration>
+    [ <SQL terminator> ]
+
+<statement or declaration> ::=
+      <declare cursor>
+    | <dynamic declare cursor>
+    | <temporary table declaration>
+    | <embedded exception declaration>
+    | <SQL procedure statement>
+
+<embedded exception declaration> ::=
+    WHENEVER <condition> <condition action>
+
+<condition> ::=
+    SQLERROR | NOT FOUND
+
+<condition action> ::=
+    CONTINUE | <go to>
+
+<go to> ::=
+    { GOTO | GO TO } <goto target>
+
+<goto target> ::=
+      <host label identifier>
+    | <unsigned integer>
+    | <host PL/I label variable>
+
+<host label identifier> ::= !!<EMPHASIS>(See the Syntax Rules.)
+
+<host PL/I label variable> ::= !!<EMPHASIS>(See the Syntax Rules.)
+
+<preparable statement> ::=
+      <preparable SQL data statement>
+    | <preparable SQL schema statement>
+    | <preparable SQL transaction statement>
+    | <preparable SQL session statement>
+    | <preparable implementation-defined statement>
+
+<preparable SQL data statement> ::=
+      <delete statement: searched>
+    | <dynamic single row select statement>
+    | <insert statement>
+    | <dynamic select statement>
+    | <update statement: searched>
+    | <preparable dynamic delete statement: positioned>
+    | <preparable dynamic update statement: positioned>
+
+<dynamic single row select statement> ::= <query specification>
+
+<dynamic select statement> ::= <cursor specification>
+
+<preparable dynamic delete statement: positioned> ::=
+   DELETE [ FROM <table name> ]
+      WHERE CURRENT OF <cursor name>
+
+<preparable dynamic update statement: positioned> ::=
+   UPDATE [ <table name> ]
+      SET <set clause list>
+      WHERE CURRENT OF <cursor name>
+
+<preparable SQL schema statement> ::=
+      <SQL schema statement>
+
+<preparable SQL transaction statement> ::=
+      <SQL transaction statement>
+
+<preparable SQL session statement> ::=
+      <SQL session statement>
+
+<preparable implementation-defined statement> ::=
+    !! <EMPHASIS>(See the Syntax Rules.)
+
+<direct SQL statement> ::=
+    <directly executable statement> <semicolon>
+
+<directly executable statement> ::=
+      <direct SQL data statement>
+    | <SQL schema statement>
+    | <SQL transaction statement>
+    | <SQL connection statement>
+    | <SQL session statement>
+    | <direct implementation-defined statement>
+
+<direct SQL data statement> ::=
+      <delete statement: searched>
+    | <direct select statement: multiple rows>
+    | <insert statement>
+    | <update statement: searched>
+    | <temporary table declaration>
+
+<direct select statement: multiple rows> ::=
+    <query expression> [ <order by clause> ]
+
+<direct implementation-defined statement> ::=
+    !!<EMPHASIS>(See the Syntax Rules)
+
+<SQL object identifier> ::=
+    <SQL provenance> <SQL variant>
+
+<SQL provenance> ::= <arc1> <arc2> <arc3>
+
+<arc1> ::= iso | 1 | iso <left paren> 1 <right paren>
+
+<arc2> ::= standard | 0 | standard <left paren> 0 <right paren>
+
+<arc3> ::= 9075
+
+<SQL variant> ::= <SQL edition> <SQL conformance>
+
+<SQL edition> ::= <1987> | <1989> | <1992>
+
+<1987> ::= 0 | edition1987 <left paren> 0 <right paren>
+
+<1989> ::= <1989 base> <1989 package>
+
+<1989 base> ::= 1 | edition1989 <left paren> 1 <right paren>
+
+<1989 package> ::= <integrity no> | <integrity yes>
+
+<integrity no> ::= 0 | IntegrityNo <left paren> 0 <right paren>
+
+<integrity yes> ::= 1 | IntegrityYes <left paren> 1 <right paren>
+
+<1992> ::= 2 | edition1992 <left paren> 2 <right paren>
+
+<SQL conformance> ::= <low> | <intermediate> | <high>
+
+<low> ::= 0 | Low <left paren> 0 <right paren>
+
+<intermediate> ::= 1 | Intermediate <left paren> 1 <right paren>
+
+<high> ::= 2 | High <left paren> 2 <right paren>
+


Property changes on: branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/test-classes/sql2bnf.aug92.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain


From dna-commits at lists.jboss.org  Fri Oct  2 13:03:04 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 2 Oct 2009 13:03:04 -0400
Subject: [dna-commits] DNA SVN: r1281 - in
	trunk/tools/org.jboss.dna.eclipse.jcr.rest.client:
	src/org/jboss/dna/eclipse/jcr/rest/client and 1 other directories.
Message-ID: <200910021703.n92H34f0026833@svn01.web.mwc.hst.phx2.redhat.com>

Author: elvisisking
Date: 2009-10-02 13:03:04 -0400 (Fri, 02 Oct 2009)
New Revision: 1281

Modified:
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/dna-web-jcr-rest-client-0.7-SNAPSHOT-jar-with-dependencies.jar
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/dna-web-jcr-rest-client-0.7-SNAPSHOT-sources.jar
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/DnaResourceHelper.java
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/ServerView.java
Log:
DNA 531 Servers View Should Provide Global Action Handlers: Setup no-op actions for cut, copy, and paste, and hooked up the delete server action to work with the delete key and the global delete action. Also added updated client jars from Maven project.

Modified: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/dna-web-jcr-rest-client-0.7-SNAPSHOT-jar-with-dependencies.jar
===================================================================
(Binary files differ)

Modified: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/dna-web-jcr-rest-client-0.7-SNAPSHOT-sources.jar
===================================================================
(Binary files differ)

Modified: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/DnaResourceHelper.java
===================================================================
--- trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/DnaResourceHelper.java	2009-10-02 16:13:40 UTC (rev 1280)
+++ trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/DnaResourceHelper.java	2009-10-02 17:03:04 UTC (rev 1281)
@@ -63,7 +63,7 @@
      * file has been published to at least one DNA repository. The value of the property is a list of DNA repository workspaces
      * where this file has been published.
      */
-    private static QualifiedName PUBLISHED_RESOURCE_PROPERTY = new QualifiedName(IUiConstants.PLUGIN_ID, "publishedIn"); //$NON-NLS-1$
+    private static QualifiedName PUBLISHED_RESOURCE_PROPERTY = new QualifiedName(IUiConstants.PLUGIN_ID, "publishedLocations"); //$NON-NLS-1$
 
     // ===========================================================================================================================
     // Fields

Modified: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/ServerView.java
===================================================================
--- trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/ServerView.java	2009-10-02 16:13:40 UTC (rev 1280)
+++ trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/ServerView.java	2009-10-02 17:03:04 UTC (rev 1281)
@@ -39,9 +39,13 @@
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.actions.ActionFactory;
 import org.eclipse.ui.actions.BaseSelectionListenerAction;
 import org.eclipse.ui.help.IWorkbenchHelpSystem;
 import org.eclipse.ui.part.ViewPart;
@@ -208,6 +212,7 @@
         constructActions();
         constructToolBar();
         constructContextMenu();
+        hookGlobalActions();
 
         setTitleToolTip(RestClientI18n.serverViewToolTip.text());
 
@@ -230,6 +235,10 @@
         super.dispose();
     }
 
+    IAction getDeleteAction() {
+        return this.deleteAction;
+    }
+
     /**
      * @return the server manager being used by this view
      */
@@ -259,6 +268,36 @@
     }
 
     /**
+     * Sets global action handlers.
+     */
+    private void hookGlobalActions() {
+        IActionBars bars = getViewSite().getActionBars();
+
+        // hook delete server action up
+        bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), this.deleteAction);
+        this.viewer.getControl().addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyPressed( KeyEvent event ) {
+                if ((event.character == SWT.DEL) && (event.stateMask == 0) && getDeleteAction().isEnabled()) {
+                    getDeleteAction().run();
+                }
+            }
+        });
+        
+        // don't want cut, copy, or paste actions so hook them up with a disabled action
+        class NoOpAction extends Action {
+            NoOpAction() {
+                setEnabled(false);
+            }
+        }
+        
+        IAction noop = new NoOpAction();
+        bars.setGlobalActionHandler(ActionFactory.CUT.getId(), noop);
+        bars.setGlobalActionHandler(ActionFactory.COPY.getId(), noop);
+        bars.setGlobalActionHandler(ActionFactory.PASTE.getId(), noop);
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.web.jcr.rest.client.IServerRegistryListener#serverRegistryChanged(org.jboss.dna.web.jcr.rest.client.ServerRegistryEvent)


From dna-commits at lists.jboss.org  Fri Oct  2 13:05:21 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 2 Oct 2009 13:05:21 -0400
Subject: [dna-commits] DNA SVN: r1282 - in
	trunk/web/dna-web-jcr-rest-client/src/main:
	java/org/jboss/dna/web/jcr/rest/client/http and 2 other directories.
Message-ID: <200910021705.n92H5L1Z027604@svn01.web.mwc.hst.phx2.redhat.com>

Author: elvisisking
Date: 2009-10-02 13:05:21 -0400 (Fri, 02 Oct 2009)
New Revision: 1282

Removed:
   trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/ServerAuthenticator.java
   trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/HttpUrlConnection.java
   trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/IHttpConnection.java
Modified:
   trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/RestClientI18n.java
   trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/ServerManager.java
   trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/HttpClientConnection.java
   trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClient.java
   trunk/web/dna-web-jcr-rest-client/src/main/resources/org/jboss/dna/web/jcr/rest/client/RestClientI18n.properties
Log:
DNA 532 Delete IHttpConnection Interface Since There Is Only One Implementation That Works Correctly: Deleted IHttpConnection and HttpUrlConnection. Since the Apache HttpClient implementation is the only one that works properly no need to keep the java.net implementation or the interface around.

Modified: trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/RestClientI18n.java
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/RestClientI18n.java	2009-10-02 17:03:04 UTC (rev 1281)
+++ trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/RestClientI18n.java	2009-10-02 17:05:21 UTC (rev 1282)
@@ -71,6 +71,8 @@
     public static I18n serverManagerUnregisteredServer;
 
     public static I18n serverShortDescription;
+    
+    public static I18n unknownHttpRequestMethodMsg;
 
     public static I18n workspaceEmptyNameMsg;
 

Deleted: trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/ServerAuthenticator.java
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/ServerAuthenticator.java	2009-10-02 17:03:04 UTC (rev 1281)
+++ trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/ServerAuthenticator.java	2009-10-02 17:05:21 UTC (rev 1282)
@@ -1,51 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.web.jcr.rest.client;
-
-import java.net.Authenticator;
-import java.net.PasswordAuthentication;
-import org.jboss.dna.web.jcr.rest.client.domain.Server;
-
-/**
- * The <code>ServerAuthenticator</code> class provides user names and passwords to the HTTP connection.
- */
-public class ServerAuthenticator extends Authenticator {
-
-    // ===========================================================================================================================
-    // Methods
-    // ===========================================================================================================================
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.net.Authenticator#getPasswordAuthentication()
-     */
-    @Override
-    protected PasswordAuthentication getPasswordAuthentication() {
-        Server server = ServerManager.getCurrentServer();
-        String password = ((server.getPassword() == null) ? "" : server.getPassword()); //$NON-NLS-1$
-        return new PasswordAuthentication(server.getUser(), password.toCharArray());
-    }
-
-}

Modified: trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/ServerManager.java
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/ServerManager.java	2009-10-02 17:03:04 UTC (rev 1281)
+++ trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/ServerManager.java	2009-10-02 17:05:21 UTC (rev 1282)
@@ -98,29 +98,6 @@
     private static final String USER_TAG = "user"; //$NON-NLS-1$
 
     // ===========================================================================================================================
-    // Class Fields
-    // ===========================================================================================================================
-
-    /**
-     * The java.net.Authenticator needs server information to complete the publish, unpublish, getRepositories(), and
-     * getWorkspaces() operations. Setting a thread variable was a way to do that.
-     * 
-     * @see ServerAuthenticator
-     */
-    private static ThreadLocal<Server> currentServer = new ThreadLocal<Server>();
-
-    // ===========================================================================================================================
-    // Class Methods
-    // ===========================================================================================================================
-
-    /**
-     * @return the server set on the current thread or <code>null</code>
-     */
-    public static Server getCurrentServer() {
-        return ServerManager.currentServer.get();
-    }
-
-    // ===========================================================================================================================
     // Fields
     // ===========================================================================================================================
 
@@ -275,7 +252,6 @@
             this.serverLock.readLock().lock();
 
             if (isRegistered(server)) {
-                ServerManager.currentServer.set(server);
                 Collection<Repository> repositories = this.delegate.getRepositories(server);
                 return Collections.unmodifiableCollection(new ArrayList<Repository>(repositories));
             }
@@ -313,7 +289,6 @@
             this.serverLock.readLock().lock();
 
             if (isRegistered(repository.getServer())) {
-                ServerManager.currentServer.set(repository.getServer());
                 Collection<Workspace> workspaces = this.delegate.getWorkspaces(repository);
                 return Collections.unmodifiableCollection(new ArrayList<Workspace>(workspaces));
             }
@@ -482,11 +457,10 @@
         CheckArg.isNotNull(server, "server"); //$NON-NLS-1$
 
         try {
-            ServerManager.currentServer.set(server);
             this.delegate.getRepositories(server);
             return new Status(Severity.OK, RestClientI18n.serverManagerConnectionEstablishedMsg.text(), null);
         } catch (Exception e) {
-            return new Status(Severity.ERROR, RestClientI18n.serverManagerConnectionFailedMsg.text(), null);
+            return new Status(Severity.ERROR, RestClientI18n.serverManagerConnectionFailedMsg.text(e), null);
         }
     }
 
@@ -509,7 +483,6 @@
         Server server = workspace.getServer();
 
         if (isRegistered(server)) {
-            ServerManager.currentServer.set(server);
             return this.delegate.publish(workspace, path, file);
         }
 
@@ -652,7 +625,6 @@
         Server server = workspace.getServer();
 
         if (isRegistered(server)) {
-            ServerManager.currentServer.set(server);
             return this.delegate.unpublish(workspace, path, file);
         }
 

Modified: trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/HttpClientConnection.java
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/HttpClientConnection.java	2009-10-02 17:03:04 UTC (rev 1281)
+++ trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/HttpClientConnection.java	2009-10-02 17:05:21 UTC (rev 1282)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.web.jcr.rest.client.http;
 
+import static org.jboss.dna.web.jcr.rest.client.RestClientI18n.unknownHttpRequestMethodMsg;
 import java.net.URL;
 import javax.ws.rs.core.MediaType;
 import org.apache.http.HttpResponse;
@@ -38,13 +39,14 @@
 import org.apache.http.impl.client.AbstractHttpClient;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.util.EntityUtils;
+import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.web.jcr.rest.client.domain.Server;
 import org.jboss.dna.web.jcr.rest.client.json.IJsonConstants.RequestMethod;
 
 /**
- * <code>HttpClientConnection</code> is an <code>Apache HttpClient</code> implementation of <code>IHttpConnection</code>.
+ * <code>HttpClientConnection</code> uses the <code>Apache HttpClient</code>.
  */
-public final class HttpClientConnection implements IHttpConnection {
+public final class HttpClientConnection {
 
     // ===========================================================================================================================
     // Fields
@@ -70,14 +72,18 @@
     // ===========================================================================================================================
 
     /**
-     * @param server the server with the host and port information
-     * @param url the URL that will be used in the request
-     * @param method the HTTP request method
+     * @param server the server with the host and port information (never <code>null</code>)
+     * @param url the URL that will be used in the request (never <code>null</code>)
+     * @param method the HTTP request method (never <code>null</code>)
      * @throws Exception if there is a problem establishing the connection
      */
     public HttpClientConnection( Server server,
                                  URL url,
                                  RequestMethod method ) throws Exception {
+        CheckArg.isNotNull(server, "server"); //$NON-NLS-1$
+        CheckArg.isNotNull(url, "url"); //$NON-NLS-1$
+        CheckArg.isNotNull(method, "method"); //$NON-NLS-1$
+
         this.httpClient = new DefaultHttpClient();
         this.httpClient.getCredentialsProvider().setCredentials(new AuthScope(url.getHost(), url.getPort()),
                                                                 new UsernamePasswordCredentials(server.getUser(),
@@ -92,8 +98,7 @@
         } else if (RequestMethod.PUT == method) {
             this.request = new HttpPut();
         } else {
-            // TODO need exception message
-            throw new RuntimeException();
+            throw new RuntimeException(unknownHttpRequestMethodMsg.text(method));
         }
 
         // set request URI
@@ -105,9 +110,7 @@
     // ===========================================================================================================================
 
     /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.web.jcr.rest.client.http.IHttpConnection#disconnect()
+     * Disconnects this connection.
      */
     public void disconnect() {
         this.httpClient.getConnectionManager().shutdown();
@@ -126,29 +129,28 @@
     }
 
     /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.web.jcr.rest.client.http.IHttpConnection#getResponseCode()
+     * @return the HTTP response code
+     * @throws Exception if there is a problem getting the response code
      */
     public int getResponseCode() throws Exception {
         return getResponse().getStatusLine().getStatusCode();
     }
 
     /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.web.jcr.rest.client.http.IHttpConnection#read()
+     * @return the HTTP response body as a string
+     * @throws Exception if there is a problem reading from the connection
      */
     public String read() throws Exception {
         return EntityUtils.toString(getResponse().getEntity());
     }
 
     /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.web.jcr.rest.client.http.IHttpConnection#write(byte[])
+     * @param bytes the bytes being posted to the HTTP connection (never <code>null</code>)
+     * @throws Exception if there is a problem writing to the connection
      */
     public void write( byte[] bytes ) throws Exception {
+        CheckArg.isNotNull(bytes, "bytes"); //$NON-NLS-1$
+
         ByteArrayEntity entity = new ByteArrayEntity(bytes);
         entity.setContentType(MediaType.APPLICATION_JSON);
 

Deleted: trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/HttpUrlConnection.java
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/HttpUrlConnection.java	2009-10-02 17:03:04 UTC (rev 1281)
+++ trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/HttpUrlConnection.java	2009-10-02 17:05:21 UTC (rev 1282)
@@ -1,124 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.web.jcr.rest.client.http;
-
-import java.net.Authenticator;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import javax.ws.rs.core.MediaType;
-import org.jboss.dna.common.util.Base64;
-import org.jboss.dna.web.jcr.rest.client.ServerAuthenticator;
-import org.jboss.dna.web.jcr.rest.client.domain.Server;
-import org.jboss.dna.web.jcr.rest.client.json.JsonUtils;
-import org.jboss.dna.web.jcr.rest.client.json.IJsonConstants.RequestMethod;
-
-/**
- * <code>HttpUrlConnection</code> is a <code>java.net</code> implementation of <code>IHttpConnection</code>.
- */
-public final class HttpUrlConnection implements IHttpConnection {
-
-    // ===========================================================================================================================
-    // Class Initializer
-    // ===========================================================================================================================
-
-    static {
-        // must be set when using java.net framework but a problem occurs when other subsytems have their own Authenticator and
-        // also call this method
-        Authenticator.setDefault(new ServerAuthenticator());
-    }
-
-    // ===========================================================================================================================
-    // Fields
-    // ===========================================================================================================================
-
-    /**
-     * The HTTP connection that executes the request and obtains the response.
-     */
-    private final HttpURLConnection connection;
-
-    // ===========================================================================================================================
-    // Constructors
-    // ===========================================================================================================================
-
-    /**
-     * @param server the server with the host and port information
-     * @param url the URL that will be used in the request
-     * @param method the HTTP request method
-     * @throws Exception if there is a problem establishing the connection
-     */
-    public HttpUrlConnection( Server server,
-                              URL url,
-                              RequestMethod method ) throws Exception {
-        this.connection = (HttpURLConnection)url.openConnection();
-        this.connection.setDoOutput(true);
-
-        String encoding = Base64.encodeBytes((server.getUser() + ':' + server.getPassword()).getBytes());
-        this.connection.setRequestProperty("Authorization", "Basic " + encoding); //$NON-NLS-1$ //$NON-NLS-2$
-
-        this.connection.setRequestMethod(method.toString());
-        this.connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON); //$NON-NLS-1$
-        this.connection.setUseCaches(false);
-    }
-
-    // ===========================================================================================================================
-    // Methods
-    // ===========================================================================================================================
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.web.jcr.rest.client.http.IHttpConnection#disconnect()
-     */
-    public void disconnect() {
-        this.connection.disconnect();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.web.jcr.rest.client.http.IHttpConnection#getResponseCode()
-     */
-    public int getResponseCode() throws Exception {
-        return this.connection.getResponseCode();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.web.jcr.rest.client.http.IHttpConnection#read()
-     */
-    public String read() throws Exception {
-        return JsonUtils.readInputStream(this.connection);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.web.jcr.rest.client.http.IHttpConnection#write(byte[])
-     */
-    public void write( byte[] bytes ) throws Exception {
-        connection.getOutputStream().write(bytes);
-    }
-
-}

Deleted: trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/IHttpConnection.java
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/IHttpConnection.java	2009-10-02 17:03:04 UTC (rev 1281)
+++ trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/http/IHttpConnection.java	2009-10-02 17:05:21 UTC (rev 1282)
@@ -1,55 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.web.jcr.rest.client.http;
-
-/**
- * The <code>IHttpConnection</code> is the contract for all HTTP connection frameworks must implement to work with the DNA REST
- * server.
- */
-public interface IHttpConnection {
-
-    /**
-     * Disconnects this connection.
-     */
-    void disconnect();
-
-    /**
-     * @return the HTTP response code
-     * @throws Exception if there is a problem getting the response code
-     */
-    int getResponseCode() throws Exception;
-
-    /**
-     * @return the HTTP response body as a string
-     * @throws Exception if there is a problem reading from the connection
-     */
-    String read() throws Exception;
-
-    /**
-     * @param bytes the bytes being posted to the HTTP connection
-     * @throws Exception if there is a problem writing to the connection
-     */
-    void write( byte[] bytes ) throws Exception;
-
-}

Modified: trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClient.java
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClient.java	2009-10-02 17:03:04 UTC (rev 1281)
+++ trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClient.java	2009-10-02 17:05:21 UTC (rev 1282)
@@ -37,7 +37,6 @@
 import org.jboss.dna.web.jcr.rest.client.domain.Server;
 import org.jboss.dna.web.jcr.rest.client.domain.Workspace;
 import org.jboss.dna.web.jcr.rest.client.http.HttpClientConnection;
-import org.jboss.dna.web.jcr.rest.client.http.IHttpConnection;
 import org.jboss.dna.web.jcr.rest.client.json.IJsonConstants.RequestMethod;
 
 /**
@@ -66,11 +65,10 @@
      * @return the connection which <strong>MUST</strong> be disconnected
      * @throws Exception if there is a problem establishing the connection
      */
-    private IHttpConnection connect( Server server,
-                                     URL url,
-                                     RequestMethod method ) throws Exception {
+    private HttpClientConnection connect( Server server,
+                                          URL url,
+                                          RequestMethod method ) throws Exception {
         this.logger.trace("connect: url={0}, method={1}", url, method); //$NON-NLS-1$
-        // return new HttpUrlConnection(server, url, method);
         return new HttpClientConnection(server, url, method);
     }
 
@@ -87,7 +85,7 @@
                                  File file ) throws Exception {
         this.logger.trace("createFileNode: workspace={0}, path={1}, file={2}", workspace.getName(), path, file.getAbsolutePath()); //$NON-NLS-1$
         FileNode fileNode = new FileNode(workspace, path, file);
-        IHttpConnection connection = connect(workspace.getServer(), fileNode.getUrl(), RequestMethod.POST);
+        HttpClientConnection connection = connect(workspace.getServer(), fileNode.getUrl(), RequestMethod.POST);
 
         try {
             this.logger.trace("createFileNode: create node={0}", fileNode); //$NON-NLS-1$
@@ -122,7 +120,7 @@
                                    String path ) throws Exception {
         this.logger.trace("createFolderNode: workspace={0}, path={1}", workspace.getName(), path); //$NON-NLS-1$
         FolderNode folderNode = new FolderNode(workspace, path);
-        IHttpConnection connection = connect(workspace.getServer(), folderNode.getUrl(), RequestMethod.POST);
+        HttpClientConnection connection = connect(workspace.getServer(), folderNode.getUrl(), RequestMethod.POST);
 
         try {
             this.logger.trace("createFolderNode: create node={0}", folderNode); //$NON-NLS-1$
@@ -194,7 +192,7 @@
         this.logger.trace("getRepositories: server={0}", server); //$NON-NLS-1$
 
         ServerNode serverNode = new ServerNode(server);
-        IHttpConnection connection = connect(server, serverNode.getFindRepositoriesUrl(), RequestMethod.GET);
+        HttpClientConnection connection = connect(server, serverNode.getFindRepositoriesUrl(), RequestMethod.GET);
 
         try {
             if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
@@ -244,7 +242,7 @@
         this.logger.trace("getWorkspaces: repository={0}", repository); //$NON-NLS-1$
 
         RepositoryNode repositoryNode = new RepositoryNode(repository);
-        IHttpConnection connection = connect(repository.getServer(), repositoryNode.getUrl(), RequestMethod.GET);
+        HttpClientConnection connection = connect(repository.getServer(), repositoryNode.getUrl(), RequestMethod.GET);
 
         try {
             if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
@@ -278,7 +276,7 @@
                             String path,
                             File file ) throws Exception {
         FileNode fileNode = new FileNode(workspace, path, file);
-        IHttpConnection connection = connect(workspace.getServer(), fileNode.getFileContentsUrl(), RequestMethod.GET);
+        HttpClientConnection connection = connect(workspace.getServer(), fileNode.getFileContentsUrl(), RequestMethod.GET);
         int responseCode = connection.getResponseCode();
 
         if (responseCode == HttpURLConnection.HTTP_OK) {
@@ -298,7 +296,7 @@
     private boolean pathExists( Server server,
                                 URL url ) throws Exception {
         this.logger.trace("pathExists: url={0}", url); //$NON-NLS-1$
-        IHttpConnection connection = connect(server, url, RequestMethod.GET);
+        HttpClientConnection connection = connect(server, url, RequestMethod.GET);
 
         try {
             int responseCode = connection.getResponseCode();
@@ -373,7 +371,7 @@
         CheckArg.isNotNull(file, "file"); //$NON-NLS-1$
         this.logger.trace("unpublish: workspace={0}, path={1}, file={2}", workspace.getName(), path, file.getAbsolutePath()); //$NON-NLS-1$
 
-        IHttpConnection connection = null;
+        HttpClientConnection connection = null;
 
         try {
             FileNode fileNode = new FileNode(workspace, path, file);

Modified: trunk/web/dna-web-jcr-rest-client/src/main/resources/org/jboss/dna/web/jcr/rest/client/RestClientI18n.properties
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/main/resources/org/jboss/dna/web/jcr/rest/client/RestClientI18n.properties	2009-10-02 17:03:04 UTC (rev 1281)
+++ trunk/web/dna-web-jcr-rest-client/src/main/resources/org/jboss/dna/web/jcr/rest/client/RestClientI18n.properties	2009-10-02 17:05:21 UTC (rev 1282)
@@ -38,7 +38,7 @@
 serverShortDescription = DNA Server - URL: {0} User: {1}
 
 serverManagerConnectionEstablishedMsg = Connection established.
-serverManagerConnectionFailedMsg = Connection failed.
+serverManagerConnectionFailedMsg = Connection failed. Error Message: {0}
 serverManagerRegistryAddUnexpectedError = Unexpected error adding server to registry
 serverManagerRegistryListenerError = This error was reported by an IServerRegistryListener
 serverManagerRegistryListenerErrorsOccurred = Errors occurred processing a server registry event. Check error log for more details.
@@ -47,6 +47,8 @@
 serverManagerRegistryUpdateRemoveError = There was an unexpected error updating the server in the registry. The server has not been updated in the server registry. Detail: {0}
 serverManagerUnregisteredServer = Server "{0}" is not registered so it's repositories and workspaces cannot be obtained and it cannot be involved in a publishing operation.
 
+unknownHttpRequestMethodMsg = The "{0}" HTTP request method is not valid.
+
 workspaceEmptyNameMsg = A workspace name cannot be empty
 workspaceNullRepositoryMsg = A workspace repository cannot be null
 workspaceShortDescription = DNA Workspace - Name: {0}, Repository: {1}


From dna-commits at lists.jboss.org  Fri Oct  2 13:35:34 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 2 Oct 2009 13:35:34 -0400
Subject: [dna-commits] DNA SVN: r1284 -
	branches/ddl_sequencer/extensions/dna-sequencer-ddl.
Message-ID: <200910021735.n92HZY3G002622@svn01.web.mwc.hst.phx2.redhat.com>

Author: blafond
Date: 2009-10-02 13:35:34 -0400 (Fri, 02 Oct 2009)
New Revision: 1284

Removed:
   branches/ddl_sequencer/extensions/dna-sequencer-ddl/target/
Log:
Messed up


From dna-commits at lists.jboss.org  Sat Oct  3 00:07:44 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Sat, 3 Oct 2009 00:07:44 -0400
Subject: [dna-commits] DNA SVN: r1285 - in trunk:
	dna-jcr/src/main/java/org/jboss/dna/jcr and 3 other directories.
Message-ID: <200910030407.n9347iGw001381@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-03 00:07:41 -0400 (Sat, 03 Oct 2009)
New Revision: 1285

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
Log:
DNA-467 Added XPath parser with associated unit tests, though this doesn't yet convert the parsed XPath AST into a query using the Abstract Query Model

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java	2009-10-02 17:35:34 UTC (rev 1284)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java	2009-10-03 04:07:41 UTC (rev 1285)
@@ -437,7 +437,7 @@
      * 
      * @return the current token's position; never null
      * @throws IllegalStateException if this method was called before the stream was {@link #start() started}
-     * @throws NoSuchElementException if there is no previous token
+     * @throws NoSuchElementException if there is no next token
      */
     public Position nextPosition() {
         return currentToken().position();
@@ -1047,7 +1047,7 @@
         ListIterator<Token> iter = tokens.listIterator(tokenIterator.previousIndex());
         if (!iter.hasNext()) return false;
         Token token = iter.next();
-        if (currentExpectedType != ANY_TYPE || currentToken().type() != currentExpectedType) return false;
+        if (currentExpectedType != ANY_TYPE && currentToken().type() != currentExpectedType) return false;
         for (int nextExpectedType : expectedTypeForNextTokens) {
             if (!iter.hasNext()) return false;
             token = iter.next();
@@ -1366,6 +1366,22 @@
         boolean isNextValidXmlCharacter();
 
         /**
+         * Determine if the next character on the sream is a {@link XmlCharacters#isValidName(int) valid XML NCName character}.
+         * This method does <i>not</i> advance the stream.
+         * 
+         * @return true if there is a {@link #next() next} character and it is a valid XML Name character, or false otherwise
+         */
+        boolean isNextValidXmlNameCharacter();
+
+        /**
+         * Determine if the next character on the sream is a {@link XmlCharacters#isValidNcName(int) valid XML NCName character}.
+         * This method does <i>not</i> advance the stream.
+         * 
+         * @return true if there is a {@link #next() next} character and it is a valid XML NCName character, or false otherwise
+         */
+        boolean isNextValidXmlNcNameCharacter();
+
+        /**
          * Determine if the next character on the sream is the supplied value. This method does <i>not</i> advance the stream.
          * 
          * @param c the character value to compare to the next character on the stream
@@ -1882,6 +1898,26 @@
             int nextIndex = lastIndex + 1;
             return nextIndex <= maxIndex && XmlCharacters.isValid(content[nextIndex]);
         }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.common.text.TokenStream.CharacterStream#isNextValidXmlNameCharacter()
+         */
+        public boolean isNextValidXmlNameCharacter() {
+            int nextIndex = lastIndex + 1;
+            return nextIndex <= maxIndex && XmlCharacters.isValidName(content[nextIndex]);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.common.text.TokenStream.CharacterStream#isNextValidXmlNcNameCharacter()
+         */
+        public boolean isNextValidXmlNcNameCharacter() {
+            int nextIndex = lastIndex + 1;
+            return nextIndex <= maxIndex && XmlCharacters.isValidNcName(content[nextIndex]);
+        }
     }
 
     /**

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java	2009-10-03 04:07:41 UTC (rev 1285)
@@ -0,0 +1,1132 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.xpath;
+
+import java.util.List;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.ObjectUtil;
+import org.jboss.dna.graph.query.model.Operator;
+
+/**
+ * 
+ */
+public class XPath {
+
+    public static enum NodeComparisonOperator {
+        IS,
+        PRECEDES,
+        FOLLOWS;
+    }
+
+    public static interface Component {
+    }
+
+    public static abstract class UnaryComponent implements Component {
+        protected final Component wrapped;
+
+        public UnaryComponent( Component wrapped ) {
+            this.wrapped = wrapped;
+        }
+    }
+
+    public static class Negation extends UnaryComponent {
+        public Negation( Component wrapped ) {
+            super(wrapped);
+        }
+
+        public Component getNegated() {
+            return wrapped;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "-" + wrapped;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof Negation) {
+                Negation that = (Negation)obj;
+                return this.wrapped.equals(that.wrapped);
+            }
+            return false;
+        }
+    }
+
+    public static abstract class BinaryComponent implements Component {
+        private final Component left;
+        private final Component right;
+
+        public BinaryComponent( Component left,
+                                Component right ) {
+            this.left = left;
+            this.right = right;
+        }
+
+        /**
+         * @return left
+         */
+        public Component getLeft() {
+            return left;
+        }
+
+        /**
+         * @return right
+         */
+        public Component getRight() {
+            return right;
+        }
+    }
+
+    public static class Comparison extends BinaryComponent {
+        private final Operator operator;
+
+        public Comparison( Component left,
+                           Operator operator,
+                           Component right ) {
+            super(left, right);
+            this.operator = operator;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getLeft() + " " + operator + " " + getRight();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof Comparison) {
+                Comparison that = (Comparison)obj;
+                if (this.operator != that.operator) return false;
+                return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
+            }
+            return false;
+        }
+    }
+
+    public static class NodeComparison extends BinaryComponent {
+        private final NodeComparisonOperator operator;
+
+        public NodeComparison( Component left,
+                               NodeComparisonOperator operator,
+                               Component right ) {
+            super(left, right);
+            this.operator = operator;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getLeft() + " " + operator + " " + getRight();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof NodeComparison) {
+                NodeComparison that = (NodeComparison)obj;
+                if (this.operator != that.operator) return false;
+                return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
+            }
+            return false;
+        }
+    }
+
+    public static class Add extends BinaryComponent {
+        public Add( Component left,
+                    Component right ) {
+            super(left, right);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getLeft() + " + " + getRight();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof Add) {
+                Add that = (Add)obj;
+                return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
+            }
+            return false;
+        }
+    }
+
+    public static class Subtract extends BinaryComponent {
+        public Subtract( Component left,
+                         Component right ) {
+            super(left, right);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getLeft() + " - " + getRight();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof Subtract) {
+                Subtract that = (Subtract)obj;
+                return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
+            }
+            return false;
+        }
+    }
+
+    public static class And extends BinaryComponent {
+        public And( Component left,
+                    Component right ) {
+            super(left, right);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getLeft() + " and " + getRight();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof And) {
+                And that = (And)obj;
+                return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
+            }
+            return false;
+        }
+    }
+
+    public static class Union extends BinaryComponent {
+        public Union( Component left,
+                      Component right ) {
+            super(left, right);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getLeft() + " union " + getRight();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof Union) {
+                Union that = (Union)obj;
+                return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
+            }
+            return false;
+        }
+    }
+
+    public static class Or extends BinaryComponent {
+        public Or( Component left,
+                   Component right ) {
+            super(left, right);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getLeft() + " or " + getRight();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof Or) {
+                Or that = (Or)obj;
+                return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
+            }
+            return false;
+        }
+    }
+
+    public static class ContextItem implements Component {
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            return obj == this || obj instanceof ContextItem;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return ".";
+        }
+    }
+
+    public static class Literal implements Component {
+        private final String value;
+
+        public Literal( String value ) {
+            this.value = value;
+        }
+
+        /**
+         * @return value
+         */
+        public String getValue() {
+            return value;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof Literal) {
+                return this.value.equals(((Literal)obj).value);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return value;
+        }
+    }
+
+    public static class FunctionCall implements Component {
+        private final NameTest name;
+        private final List<Component> arguments;
+
+        public FunctionCall( NameTest name,
+                             List<Component> arguments ) {
+            assert name != null;
+            assert arguments != null;
+            this.name = name;
+            this.arguments = arguments;
+        }
+
+        /**
+         * @return name
+         */
+        public NameTest getName() {
+            return name;
+        }
+
+        /**
+         * @return arguments
+         */
+        public List<Component> getParameters() {
+            return arguments;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof FunctionCall) {
+                FunctionCall that = (FunctionCall)obj;
+                return this.name.equals(that.name) && this.arguments.equals(that.arguments);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return name + "(" + asString(arguments, ",") + ")";
+        }
+    }
+
+    protected static String asString( Iterable<?> components,
+                                      String delimiter ) {
+        StringBuilder sb = new StringBuilder();
+        for (Object component : components) {
+            if (sb.length() != 0) sb.append(delimiter);
+            sb.append(component);
+        }
+        return sb.toString();
+    }
+
+    public static class PathExpression implements Component {
+        private final List<StepExpression> steps;
+        private final boolean relative;
+
+        public PathExpression( boolean relative,
+                               List<StepExpression> steps ) {
+            this.steps = steps;
+            this.relative = relative;
+        }
+
+        /**
+         * @return relative
+         */
+        public boolean isRelative() {
+            return relative;
+        }
+
+        /**
+         * @return steps
+         */
+        public List<StepExpression> getSteps() {
+            return steps;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof PathExpression) {
+                PathExpression that = (PathExpression)obj;
+                return this.relative == that.relative && this.steps.equals(that.steps);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return (relative ? "" : "/") + asString(steps, "/");
+        }
+    }
+
+    public static interface StepExpression extends Component {
+    }
+
+    public static class FilterStep implements StepExpression {
+        private final Component primaryExpression;
+        private final List<Component> predicates;
+
+        public FilterStep( Component primaryExpression,
+                           List<Component> predicates ) {
+            assert primaryExpression != null;
+            assert predicates != null;
+            this.primaryExpression = primaryExpression;
+            this.predicates = predicates;
+        }
+
+        /**
+         * @return nodeTest
+         */
+        public Component getPrimaryExpression() {
+            return primaryExpression;
+        }
+
+        /**
+         * @return predicates
+         */
+        public List<Component> getPredicates() {
+            return predicates;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof FilterStep) {
+                FilterStep that = (FilterStep)obj;
+                return this.primaryExpression.equals(that.primaryExpression) && this.predicates.equals(that.predicates);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return primaryExpression + (predicates.isEmpty() ? "" : predicates.toString());
+        }
+    }
+
+    public static class DescendantOrSelf implements StepExpression {
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            return obj == this || obj instanceof DescendantOrSelf;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "descendant-or-self::node()";
+        }
+    }
+
+    public static class AxisStep implements StepExpression {
+        private final NodeTest nodeTest;
+        private final List<Component> predicates;
+
+        public AxisStep( NodeTest nodeTest,
+                         List<Component> predicates ) {
+            assert nodeTest != null;
+            assert predicates != null;
+            this.nodeTest = nodeTest;
+            this.predicates = predicates;
+        }
+
+        /**
+         * @return nodeTest
+         */
+        public NodeTest getNodeTest() {
+            return nodeTest;
+        }
+
+        /**
+         * @return predicates
+         */
+        public List<Component> getPredicates() {
+            return predicates;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof AxisStep) {
+                AxisStep that = (AxisStep)obj;
+                return this.nodeTest.equals(that.nodeTest) && this.predicates.equals(that.predicates);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return nodeTest + (predicates.isEmpty() ? "" : predicates.toString());
+        }
+    }
+
+    public static class ParenthesizedExpression implements Component {
+        private final Component wrapped;
+
+        public ParenthesizedExpression() {
+            this.wrapped = null;
+        }
+
+        public ParenthesizedExpression( Component wrapped ) {
+            this.wrapped = wrapped; // may be null
+        }
+
+        /**
+         * @return wrapped
+         */
+        public Component getWrapped() {
+            return wrapped;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof ParenthesizedExpression) {
+                ParenthesizedExpression that = (ParenthesizedExpression)obj;
+                return this.wrapped.equals(that.wrapped);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "(" + wrapped + ")";
+        }
+    }
+
+    public static interface NodeTest extends Component {
+    }
+
+    public static interface KindTest extends NodeTest {
+    }
+
+    public static class NameTest implements NodeTest {
+        private final String prefixTest;
+        private final String localTest;
+
+        public NameTest( String prefixTest,
+                         String localTest ) {
+            this.prefixTest = prefixTest;
+            this.localTest = localTest;
+        }
+
+        /**
+         * @return prefixTest
+         */
+        public String getPrefixTest() {
+            return prefixTest;
+        }
+
+        /**
+         * @return localTest
+         */
+        public String getLocalTest() {
+            return localTest;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof NameTest) {
+                NameTest that = (NameTest)obj;
+                return ObjectUtil.isEqualWithNulls(this.prefixTest, that.prefixTest)
+                       && ObjectUtil.isEqualWithNulls(this.localTest, that.localTest);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            String local = localTest != null ? localTest : "*";
+            return prefixTest == null ? local : (prefixTest + ":" + local);
+        }
+    }
+
+    public static class AttributeNameTest implements NodeTest {
+        private final NodeTest nodeTest;
+
+        public AttributeNameTest( NodeTest nodeTest ) {
+            this.nodeTest = nodeTest;
+        }
+
+        /**
+         * @return nodeTest
+         */
+        public NodeTest getNodeTest() {
+            return nodeTest;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see XPath.NameTest#toString()
+         */
+        @Override
+        public String toString() {
+            return "@" + nodeTest;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof AttributeNameTest) {
+                AttributeNameTest that = (AttributeNameTest)obj;
+                return this.nodeTest.equals(that.nodeTest);
+            }
+            return false;
+        }
+
+    }
+
+    public static class AnyKindTest implements KindTest {
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            return obj == this || obj instanceof AnyKindTest;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "node()";
+        }
+    }
+
+    public static class TextTest implements KindTest {
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            return obj == this || obj instanceof TextTest;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "text()";
+        }
+    }
+
+    public static class CommentTest implements KindTest {
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            return obj == this || obj instanceof CommentTest;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "comment()";
+        }
+    }
+
+    public static class ProcessingInstructionTest implements KindTest {
+        private final String nameOrStringLiteral;
+
+        public ProcessingInstructionTest( String nameOrStringLiteral ) {
+            this.nameOrStringLiteral = nameOrStringLiteral;
+        }
+
+        /**
+         * @return nameOrStringLiteral
+         */
+        public String getNameOrStringLiteral() {
+            return nameOrStringLiteral;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof ProcessingInstructionTest) {
+                ProcessingInstructionTest that = (ProcessingInstructionTest)obj;
+                return this.nameOrStringLiteral.equals(that.nameOrStringLiteral);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "processing-instruction(" + nameOrStringLiteral + ")";
+        }
+    }
+
+    public static class DocumentTest implements KindTest {
+        private KindTest elementOrSchemaElementTest;
+
+        public DocumentTest( ElementTest elementTest ) {
+            CheckArg.isNotNull(elementTest, "elementTest");
+            this.elementOrSchemaElementTest = elementTest;
+        }
+
+        public DocumentTest( SchemaElementTest schemaElementTest ) {
+            CheckArg.isNotNull(schemaElementTest, "schemaElementTest");
+            this.elementOrSchemaElementTest = schemaElementTest;
+        }
+
+        /**
+         * @return elementOrSchemaElementTest
+         */
+        public ElementTest getElementTest() {
+            return elementOrSchemaElementTest instanceof ElementTest ? (ElementTest)elementOrSchemaElementTest : null;
+        }
+
+        /**
+         * @return elementOrSchemaElementTest
+         */
+        public SchemaElementTest getSchemaElementTest() {
+            return elementOrSchemaElementTest instanceof SchemaElementTest ? (SchemaElementTest)elementOrSchemaElementTest : null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof DocumentTest) {
+                DocumentTest that = (DocumentTest)obj;
+                return this.elementOrSchemaElementTest.equals(that.elementOrSchemaElementTest);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "document-node(" + elementOrSchemaElementTest + ")";
+        }
+    }
+
+    public static class AttributeTest implements KindTest {
+        private final NameTest attributeNameOrWildcard;
+        private final NameTest typeName;
+
+        public AttributeTest( NameTest attributeNameOrWildcard,
+                              NameTest typeName ) {
+            this.attributeNameOrWildcard = attributeNameOrWildcard;
+            this.typeName = typeName;
+        }
+
+        /**
+         *@return the attribute name, which may be a wilcard
+         */
+        public NameTest getAttributeName() {
+            return attributeNameOrWildcard;
+        }
+
+        /**
+         * @return typeName
+         */
+        public NameTest getTypeName() {
+            return typeName;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof AttributeTest) {
+                AttributeTest that = (AttributeTest)obj;
+                return ObjectUtil.isEqualWithNulls(this.typeName, that.typeName)
+                       && ObjectUtil.isEqualWithNulls(this.attributeNameOrWildcard, that.attributeNameOrWildcard);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "attribute(" + attributeNameOrWildcard + (typeName != null ? "," + typeName : "") + ")";
+        }
+    }
+
+    public static class ElementTest implements KindTest {
+        private final NameTest elementNameOrWildcard;
+        private final NameTest typeName;
+
+        public ElementTest( NameTest elementNameOrWildcard,
+                            NameTest typeName ) {
+            this.elementNameOrWildcard = elementNameOrWildcard;
+            this.typeName = typeName;
+        }
+
+        /**
+         *@return the element name, which may be a wilcard
+         */
+        public NameTest getElementName() {
+            return elementNameOrWildcard;
+        }
+
+        /**
+         * @return typeName
+         */
+        public NameTest getTypeName() {
+            return typeName;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof ElementTest) {
+                ElementTest that = (ElementTest)obj;
+                return ObjectUtil.isEqualWithNulls(this.typeName, that.typeName)
+                       && ObjectUtil.isEqualWithNulls(this.elementNameOrWildcard, that.elementNameOrWildcard);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "element(" + elementNameOrWildcard + (typeName != null ? "," + typeName : "") + ")";
+        }
+    }
+
+    public static class SchemaElementTest implements KindTest {
+        private final NameTest elementDeclarationName;
+
+        public SchemaElementTest( NameTest elementDeclarationName ) {
+            this.elementDeclarationName = elementDeclarationName;
+        }
+
+        /**
+         *@return the element declaration name, which will be a qualified name
+         */
+        public NameTest getElementDeclarationName() {
+            return elementDeclarationName;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof SchemaElementTest) {
+                SchemaElementTest that = (SchemaElementTest)obj;
+                return this.elementDeclarationName.equals(that.elementDeclarationName);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "schema-element(" + elementDeclarationName + ")";
+        }
+    }
+
+    public static class SchemaAttributeTest implements KindTest {
+        private final NameTest attributeDeclarationName;
+
+        public SchemaAttributeTest( NameTest attributeDeclarationName ) {
+            this.attributeDeclarationName = attributeDeclarationName;
+        }
+
+        /**
+         *@return the attribute declaration name, which will be a qualified name
+         */
+        public NameTest getAttributeDeclarationName() {
+            return attributeDeclarationName;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof SchemaAttributeTest) {
+                SchemaAttributeTest that = (SchemaAttributeTest)obj;
+                return this.attributeDeclarationName.equals(that.attributeDeclarationName);
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "schema-attribute(" + attributeDeclarationName + ")";
+        }
+    }
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java	2009-10-03 04:07:41 UTC (rev 1285)
@@ -0,0 +1,812 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.xpath;
+
+import static org.jboss.dna.common.text.TokenStream.ANY_VALUE;
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.common.CommonI18n;
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.common.text.Position;
+import org.jboss.dna.common.text.TokenStream;
+import org.jboss.dna.common.text.TokenStream.CharacterStream;
+import org.jboss.dna.common.text.TokenStream.Tokenizer;
+import org.jboss.dna.common.text.TokenStream.Tokens;
+import org.jboss.dna.common.xml.XmlCharacters;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.parse.QueryParser;
+import org.jboss.dna.jcr.xpath.XPath.Add;
+import org.jboss.dna.jcr.xpath.XPath.And;
+import org.jboss.dna.jcr.xpath.XPath.AnyKindTest;
+import org.jboss.dna.jcr.xpath.XPath.AttributeNameTest;
+import org.jboss.dna.jcr.xpath.XPath.AttributeTest;
+import org.jboss.dna.jcr.xpath.XPath.AxisStep;
+import org.jboss.dna.jcr.xpath.XPath.CommentTest;
+import org.jboss.dna.jcr.xpath.XPath.Comparison;
+import org.jboss.dna.jcr.xpath.XPath.Component;
+import org.jboss.dna.jcr.xpath.XPath.ContextItem;
+import org.jboss.dna.jcr.xpath.XPath.DescendantOrSelf;
+import org.jboss.dna.jcr.xpath.XPath.DocumentTest;
+import org.jboss.dna.jcr.xpath.XPath.ElementTest;
+import org.jboss.dna.jcr.xpath.XPath.FilterStep;
+import org.jboss.dna.jcr.xpath.XPath.FunctionCall;
+import org.jboss.dna.jcr.xpath.XPath.KindTest;
+import org.jboss.dna.jcr.xpath.XPath.Literal;
+import org.jboss.dna.jcr.xpath.XPath.NameTest;
+import org.jboss.dna.jcr.xpath.XPath.Negation;
+import org.jboss.dna.jcr.xpath.XPath.NodeComparison;
+import org.jboss.dna.jcr.xpath.XPath.NodeComparisonOperator;
+import org.jboss.dna.jcr.xpath.XPath.NodeTest;
+import org.jboss.dna.jcr.xpath.XPath.Or;
+import org.jboss.dna.jcr.xpath.XPath.ParenthesizedExpression;
+import org.jboss.dna.jcr.xpath.XPath.PathExpression;
+import org.jboss.dna.jcr.xpath.XPath.ProcessingInstructionTest;
+import org.jboss.dna.jcr.xpath.XPath.SchemaAttributeTest;
+import org.jboss.dna.jcr.xpath.XPath.SchemaElementTest;
+import org.jboss.dna.jcr.xpath.XPath.StepExpression;
+import org.jboss.dna.jcr.xpath.XPath.Subtract;
+import org.jboss.dna.jcr.xpath.XPath.TextTest;
+import org.jboss.dna.jcr.xpath.XPath.Union;
+
+/**
+ * 
+ */
+public class XPathParser implements QueryParser {
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String, org.jboss.dna.graph.ExecutionContext)
+     */
+    public QueryCommand parseQuery( String query,
+                                    ExecutionContext context ) {
+        Tokenizer tokenizer = new XPathTokenizer(false); // skip comments
+        TokenStream tokens = new TokenStream(query, tokenizer, true).start(); // case sensitive!!
+        Component result = new Parser(context).parseXPath(tokens);
+        System.out.println(query);
+        System.out.println(" --> " + result);
+        return null;
+    }
+
+    protected static class Parser {
+        private final ExecutionContext context;
+
+        protected Parser( ExecutionContext context ) {
+            this.context = context;
+        }
+
+        protected Component parseXPath( TokenStream tokens ) {
+            return parseExpr(tokens);
+        }
+
+        protected Component parseExpr( TokenStream tokens ) {
+            Component result = parseExprSingle(tokens);
+            if (tokens.matches(',')) {
+                throw new ParsingException(tokens.nextPosition(), "Multiple XPath expressions are not supported");
+            }
+            return result;
+        }
+
+        protected Component parseExprSingle( TokenStream tokens ) {
+            if (tokens.matches("for", "$", ANY_VALUE, "IN")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath 'for' expressions are not supported");
+            }
+            if (tokens.matches("some", "$", ANY_VALUE, "IN")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath 'some' expressions are not supported");
+            }
+            if (tokens.matches("every", "$", ANY_VALUE, "IN")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath 'every' expressions are not supported");
+            }
+            if (tokens.matches("if", "(", ANY_VALUE, "IN")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath if-then-else expressions are not supported");
+            }
+            return parseOrExpr(tokens);
+        }
+
+        protected Component parseOrExpr( TokenStream tokens ) {
+            Component result = parseAndExpr(tokens);
+            while (tokens.canConsume("or")) {
+                result = new Or(result, parseInstanceofExpr(tokens));
+            }
+            return result;
+        }
+
+        protected Component parseAndExpr( TokenStream tokens ) {
+            Component result = parseInstanceofExpr(tokens);
+            while (tokens.canConsume("and")) {
+                result = new And(result, parseInstanceofExpr(tokens));
+            }
+            return result;
+        }
+
+        protected Component parseInstanceofExpr( TokenStream tokens ) {
+            Component result = parseTreatExpr(tokens);
+            if (tokens.matches("instance", "of")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath 'instance of' expressions are not supported");
+            }
+            return result;
+        }
+
+        protected Component parseTreatExpr( TokenStream tokens ) {
+            Component result = parseCastableExpr(tokens);
+            if (tokens.matches("treat", "as")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath 'treat as' expressions are not supported");
+            }
+            return result;
+        }
+
+        protected Component parseCastableExpr( TokenStream tokens ) {
+            Component result = parseCastExpr(tokens);
+            if (tokens.matches("castable", "as")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath 'castable as' expressions are not supported");
+            }
+            return result;
+        }
+
+        protected Component parseCastExpr( TokenStream tokens ) {
+            Component result = parseComparisonExpr(tokens);
+            if (tokens.matches("cast", "as")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath 'cast as' expressions are not supported");
+            }
+            return result;
+        }
+
+        protected Component parseComparisonExpr( TokenStream tokens ) {
+            Component result = parseRangeExpr(tokens);
+            // General comparison is optional ...
+            Operator operator = parseGeneralComp(tokens);
+            if (operator == null) parseValueComp(tokens);
+            if (operator != null) {
+                return new Comparison(result, operator, parseRangeExpr(tokens));
+            }
+            NodeComparisonOperator nodeComp = parseNodeComp(tokens);
+            if (nodeComp != null) {
+                return new NodeComparison(result, nodeComp, parseRangeExpr(tokens));
+            }
+            return result;
+        }
+
+        protected Component parseValueComp( TokenStream tokens ) {
+            if (tokens.matchesAnyOf("eq", "ne", "lt", "le", "gt")) {
+                throw new ParsingException(tokens.nextPosition(),
+                                           "XPath value comparisons using 'eq', 'ne', 'lt', 'le', or 'gt' are not supported");
+            }
+            return null;
+        }
+
+        protected NodeComparisonOperator parseNodeComp( TokenStream tokens ) {
+            if (tokens.matches("is") || tokens.matches("<", "<") || tokens.matches(">", ">")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath 'is', '<<' and '>>' expressions are not supported");
+            }
+            return null;
+        }
+
+        protected Component parseRangeExpr( TokenStream tokens ) {
+            Component result = parseAdditiveExpr(tokens);
+            if (tokens.matches("to")) {
+                throw new ParsingException(tokens.nextPosition(), "XPath range expressions with 'to' are not supported");
+            }
+            return result;
+        }
+
+        protected Component parseAdditiveExpr( TokenStream tokens ) {
+            Component result = parseMultiplicativeExpr(tokens);
+            while (true) {
+                if (tokens.canConsume("+")) {
+                    result = new Add(result, parseMultiplicativeExpr(tokens));
+                } else if (tokens.canConsume("-")) {
+                    result = new Subtract(result, parseMultiplicativeExpr(tokens));
+                } else {
+                    break; // no more additions
+                }
+            }
+            return result;
+        }
+
+        protected Component parseMultiplicativeExpr( TokenStream tokens ) {
+            Component result = parseUnaryExpr(tokens);
+            if (tokens.matchesAnyOf("+", "div", "idiv", "mod")) {
+                throw new ParsingException(tokens.nextPosition(),
+                                           "XPath multiplicative expressions using '+', 'div', 'idiv', or 'mod' are not supported");
+            }
+            return result;
+        }
+
+        protected Component parseUnaryExpr( TokenStream tokens ) {
+            boolean negative = false;
+            // Technically more than one +/- are allowed by the spec
+            while (tokens.matchesAnyOf("+", "-")) {
+                if (tokens.canConsume("-")) negative = true;
+                tokens.canConsume("+");
+            }
+            Component result = parseUnionExpr(tokens);
+            return negative ? new Negation(result) : result;
+        }
+
+        protected Component parseUnionExpr( TokenStream tokens ) {
+            Component result = parseIntersectExceptExpr(tokens);
+            while (true) {
+                if (tokens.canConsumeAnyOf("union", "|")) {
+                    result = new Union(result, parseIntersectExceptExpr(tokens));
+                } else {
+                    break; // no more
+                }
+            }
+            return result;
+        }
+
+        protected Component parseIntersectExceptExpr( TokenStream tokens ) {
+            Component result = parseValueExpr(tokens);
+            if (tokens.matchesAnyOf("intersect", "except")) {
+                throw new ParsingException(tokens.nextPosition(),
+                                           "XPath multiplicative expressions using '+', 'div', 'idiv', or 'mod' are not supported");
+            }
+            return result;
+        }
+
+        protected Component parseValueExpr( TokenStream tokens ) {
+            return parsePathExpr(tokens);
+        }
+
+        protected PathExpression parsePathExpr( TokenStream tokens ) {
+            boolean relative = true;
+            boolean prependDependentOrSelf = false;
+            if (tokens.canConsume('/')) {
+                if (tokens.canConsume('/')) {
+                    if (!tokens.hasNext()) {
+                        // See http://www.w3.org/XML/2007/qt-errata/xpath20-errata.html#E3
+                        throw new ParsingException(tokens.previousPosition(), "'//' is not a valid XPath expression");
+                    }
+                    prependDependentOrSelf = true;
+                }
+                relative = false;
+            }
+            PathExpression result = new PathExpression(relative, parseRelativePathExpr(tokens).getSteps());
+            if (prependDependentOrSelf) {
+                result.getSteps().add(0, new DescendantOrSelf());
+            }
+            return result;
+        }
+
+        protected PathExpression parseRelativePathExpr( TokenStream tokens ) {
+            List<StepExpression> steps = new ArrayList<StepExpression>();
+            steps.add(parseStepExpr(tokens));
+            while (tokens.canConsume('/')) {
+                if (tokens.canConsume('/')) {
+                    steps.add(new DescendantOrSelf());
+                }
+                if (tokens.hasNext()) {
+                    steps.add(parseStepExpr(tokens));
+                }
+            }
+            return new PathExpression(true, steps);
+        }
+
+        protected StepExpression parseStepExpr( TokenStream tokens ) {
+            if (tokens.matches('(') || tokens.matches('.') || tokens.matches(XPathTokenizer.QUOTED_STRING)
+                || tokens.matches(ANY_VALUE, "(") || tokens.matches(ANY_VALUE, ":", ANY_VALUE, "(")) {
+                // We know its a filter expression (though literals don't fit this pattern) ...
+                return parseFilterExpr(tokens);
+            }
+            AxisStep result = parseAxisStep(tokens);
+            if (result != null) return result;
+            // It must be the remaining kind of filter expression ...
+            return parseFilterExpr(tokens);
+        }
+
+        protected AxisStep parseAxisStep( TokenStream tokens ) {
+            NodeTest nodeTest = null;
+            if (tokens.canConsume('@')) {
+                // Abbreviated forward step with an attribute...
+                nodeTest = new AttributeNameTest(parseNodeTest(tokens));
+            } else if (tokens.matches('*')) {
+                // Abbreviated forward step with an wildcard element ...
+                nodeTest = parseNodeTest(tokens);
+
+            } else if (tokens.matches("child", ":", ":") || tokens.matches("attribute", ":", ":")
+                       || tokens.matches("self", ":", ":") || tokens.matches("descendant", ":", ":")
+                       || tokens.matches("descendant-or-self", ":", ":") || tokens.matches("following-sibling", ":", ":")
+                       || tokens.matches("following", ":", ":") || tokens.matches("namespace", ":", ":")) {
+                // No non-abbreviated forward steps allowed
+                throw new ParsingException(
+                                           tokens.nextPosition(),
+                                           "XPath non-abbreviated forward steps (e.g., 'child::', 'attribute::', 'self::', 'descendant::', 'descendant-or-self::', 'following-sibling::', 'following::', or 'namespace::') are not supported");
+            } else if (tokens.matches("..")) {
+                // No abbreviated reverse steps allowed ...
+                throw new ParsingException(tokens.nextPosition(),
+                                           "XPath abbreviated reverse steps (e.g., '..') are not supported");
+            } else if (tokens.matches("parent", ":", ":") || tokens.matches("ancestor-or-self", ":", ":")
+                       || tokens.matches("preceding-sibling", ":", ":") || tokens.matches("preceding", ":", ":")
+                       || tokens.matches("ancestor", ":", ":")) {
+                // No non-abbreviated reverse steps allowed ...
+                throw new ParsingException(
+                                           tokens.nextPosition(),
+                                           "XPath non-abbreviated reverse steps (e.g., 'parent::', 'ancestor::', 'ancestor-or-self::', 'preceding-or-sibling::', or 'preceding::') are not supported");
+            } else if (tokens.matches(ANY_VALUE, ":", ANY_VALUE)
+                       && tokens.matches(XPathTokenizer.NAME, XPathTokenizer.SYMBOL, XPathTokenizer.NAME)) {
+                // This is probably a forward step with a (qualified) name test ...
+                nodeTest = parseQName(tokens);
+            } else if (tokens.matches(XPathTokenizer.NAME)) {
+                // This is probably a forward step with an unqualified name test ...
+                nodeTest = parseNodeTest(tokens);
+            } else {
+                return null;
+            }
+
+            // Parse the predicates
+            List<Component> predicates = parsePredicates(tokens);
+            return new AxisStep(nodeTest, predicates);
+        }
+
+        protected List<Component> parsePredicates( TokenStream tokens ) {
+            List<Component> predicates = new ArrayList<Component>();
+            while (tokens.canConsume('[')) {
+                predicates.add(parseExpr(tokens));
+                tokens.consume(']');
+            }
+            return predicates;
+        }
+
+        protected FilterStep parseFilterExpr( TokenStream tokens ) {
+            Component primaryExpr = parsePrimaryExpr(tokens);
+            List<Component> predicates = parsePredicates(tokens);
+            return new FilterStep(primaryExpr, predicates);
+        }
+
+        protected Component parsePrimaryExpr( TokenStream tokens ) {
+            if (tokens.matches('(')) {
+                return parseParenthesizedExpr(tokens);
+            }
+            if (tokens.matches('.')) {
+                return parseContextItemExpr(tokens);
+            }
+            if (tokens.matches(XPathTokenizer.QUOTED_STRING)) {
+                return parseStringLiteral(tokens);
+            }
+            if (tokens.matches(ANY_VALUE, "(") || tokens.matches(ANY_VALUE, ":", ANY_VALUE, "(")) {
+                return parseFunctionCall(tokens);
+            }
+            return parseNumericLiteral(tokens);
+        }
+
+        protected ContextItem parseContextItemExpr( TokenStream tokens ) {
+            tokens.consume('.');
+            return new ContextItem();
+        }
+
+        protected ParenthesizedExpression parseParenthesizedExpr( TokenStream tokens ) {
+            tokens.consume('(');
+            if (tokens.canConsume(')')) {
+                return new ParenthesizedExpression();
+            }
+            Component expr = parseExpr(tokens);
+            tokens.consume(')');
+            return new ParenthesizedExpression(expr);
+        }
+
+        protected Literal parseNumericLiteral( TokenStream tokens ) {
+            Position pos = tokens.nextPosition();
+            String sign = "";
+            if (tokens.canConsume('-')) sign = "-";
+            else if (tokens.canConsume('+')) sign = "";
+
+            // Try to parse this value as a number ...
+            ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
+            String number = tokens.consume();
+            if (number.indexOf(".") != -1) {
+                String value = sign + number;
+                if (value.endsWith("e") && (tokens.matches('+') || tokens.matches('-'))) {
+                    // There's more to the number ...
+                    value = value + tokens.consume() + tokens.consume(); // +/-EXP
+                }
+                try {
+                    // Convert to a double and then back to a string to get canonical form ...
+                    String canonical = stringFactory.create(context.getValueFactories().getDoubleFactory().create(value));
+                    return new Literal(canonical);
+                } catch (ValueFormatException e) {
+                    String msg = GraphI18n.expectingLiteralAndUnableToParseAsDouble.text(value, pos.getLine(), pos.getColumn());
+                    throw new ParsingException(pos, msg);
+                }
+            }
+            // try to parse an a long ...
+            String value = sign + number;
+            try {
+                // Convert to a long and then back to a string to get canonical form ...
+                String canonical = stringFactory.create(context.getValueFactories().getLongFactory().create(value));
+                return new Literal(canonical);
+            } catch (ValueFormatException e) {
+                String msg = GraphI18n.expectingLiteralAndUnableToParseAsLong.text(value, pos.getLine(), pos.getColumn());
+                throw new ParsingException(pos, msg);
+            }
+        }
+
+        protected Literal parseStringLiteral( TokenStream tokens ) {
+            boolean removeQuotes = tokens.matches(XPathTokenizer.QUOTED_STRING);
+            String value = tokens.consume();
+            if (removeQuotes) value = removeQuotes(value);
+            return new Literal(value);
+        }
+
+        protected FunctionCall parseFunctionCall( TokenStream tokens ) {
+            NameTest name = parseQName(tokens);
+            tokens.consume("(");
+            List<Component> args = new ArrayList<Component>();
+            if (!tokens.matches(')')) {
+                do {
+                    args.add(parseExprSingle(tokens));
+                } while (tokens.canConsume(","));
+                tokens.consume(")");
+            }
+            return new FunctionCall(name, args);
+        }
+
+        protected Operator parseGeneralComp( TokenStream tokens ) {
+            if (tokens.canConsume("!", "=")) return Operator.NOT_EQUAL_TO;
+            if (tokens.canConsume("=")) return Operator.EQUAL_TO;
+            if (tokens.canConsume("<", "=")) return Operator.LESS_THAN_OR_EQUAL_TO;
+            if (tokens.canConsume(">", "=")) return Operator.GREATER_THAN_OR_EQUAL_TO;
+            if (tokens.canConsume("<")) return Operator.LESS_THAN;
+            if (tokens.canConsume(">")) return Operator.GREATER_THAN;
+            return null;
+        }
+
+        protected NodeTest parseNodeTest( TokenStream tokens ) {
+            KindTest kind = parseKindTest(tokens);
+            if (kind != null) return kind;
+            return parseNameTest(tokens);
+        }
+
+        protected NameTest parseNameTest( TokenStream tokens ) {
+            NameTest wildcard = parseWildcard(tokens);
+            if (wildcard != null) return wildcard;
+            return parseQName(tokens);
+        }
+
+        protected NameTest parseQName( TokenStream tokens ) {
+            String firstPart = parseNCName(tokens);
+            if (tokens.canConsume(':')) {
+                String secondPart = tokens.consume();
+                return new NameTest(firstPart, secondPart);
+            }
+            return new NameTest(null, firstPart);
+        }
+
+        protected String parseNCName( TokenStream tokens ) {
+            String name = tokens.consume();
+            if (!XmlCharacters.isValidNcName(name)) {
+                throw new ParsingException(tokens.previousPosition(), "Expected valid NCName but found " + name);
+            }
+            return name;
+        }
+
+        protected NameTest parseWildcard( TokenStream tokens ) {
+            if (tokens.canConsume('*')) {
+                if (tokens.canConsume(':')) {
+                    if (tokens.canConsume('*')) {
+                        return new NameTest(null, null);
+                    }
+                    String localName = tokens.consume();
+                    return new NameTest(null, localName);
+                }
+                return new NameTest(null, null);
+            }
+            if (tokens.matches(XPathTokenizer.NAME, XPathTokenizer.SYMBOL, XPathTokenizer.SYMBOL)
+                && tokens.matches(TokenStream.ANY_VALUE, ":", "*")) {
+                String prefix = tokens.consume();
+                tokens.consume(':');
+                tokens.consume('*');
+                return new NameTest(prefix, null);
+            }
+            return null;
+        }
+
+        protected NameTest parseItemType( TokenStream tokens ) {
+            return parseQName(tokens);
+        }
+
+        protected NameTest parseAtomicType( TokenStream tokens ) {
+            return parseQName(tokens);
+        }
+
+        protected KindTest parseKindTest( TokenStream tokens ) {
+            KindTest result = parseAnyKindTest(tokens);
+            if (result == null) result = parseDocumentTest(tokens);
+            if (result == null) result = parseElementTest(tokens);
+            if (result == null) result = parseAttributeTest(tokens);
+            if (result == null) result = parseSchemaElementTest(tokens);
+            if (result == null) result = parseSchemaAttributeTest(tokens);
+            if (result == null) result = parsePITest(tokens);
+            if (result == null) result = parseCommentTest(tokens);
+            if (result == null) result = parseTextTest(tokens);
+            return result;
+        }
+
+        protected AnyKindTest parseAnyKindTest( TokenStream tokens ) {
+            if (tokens.canConsume("node", "(", ")")) {
+                return new AnyKindTest();
+            }
+            return null;
+        }
+
+        protected ProcessingInstructionTest parsePITest( TokenStream tokens ) {
+            if (tokens.canConsume("processing-instruction", "(")) {
+                if (tokens.canConsume(")")) return new ProcessingInstructionTest(null);
+                String nameOrStringLiteral = tokens.consume();
+                tokens.consume(")");
+                return new ProcessingInstructionTest(nameOrStringLiteral);
+            }
+            return null;
+        }
+
+        protected CommentTest parseCommentTest( TokenStream tokens ) {
+            if (tokens.canConsume("comment", "(", ")")) {
+                return new CommentTest();
+            }
+            return null;
+        }
+
+        protected TextTest parseTextTest( TokenStream tokens ) {
+            if (tokens.canConsume("text", "(", ")")) {
+                return new TextTest();
+            }
+            return null;
+        }
+
+        protected DocumentTest parseDocumentTest( TokenStream tokens ) {
+            if (tokens.canConsume("document-node", "(")) {
+                // Document test ...
+                ElementTest elementTest = parseElementTest(tokens);
+                DocumentTest result = null;
+                if (elementTest != null) {
+                    result = new DocumentTest(elementTest);
+                } else {
+                    SchemaElementTest schemaTest = parseSchemaElementTest(tokens);
+                    result = schemaTest != null ? new DocumentTest(schemaTest) : null;
+                }
+                tokens.consume(")");
+                return result;
+            }
+            return null;
+        }
+
+        protected ElementTest parseElementTest( TokenStream tokens ) {
+            if (tokens.canConsume("element", "(")) {
+                if (tokens.canConsume(")") || tokens.canConsume("*", ")")) {
+                    return new ElementTest(new NameTest(null, null), new NameTest(null, null));
+                }
+                ElementTest result = null;
+                NameTest elementName = parseNameTest(tokens);
+                if (tokens.canConsume(",")) {
+                    NameTest typeName = parseNameTest(tokens);
+                    result = new ElementTest(elementName, typeName);
+                } else {
+                    result = new ElementTest(elementName, new NameTest(null, null));
+                }
+                tokens.consume(")");
+                return result;
+            }
+            return null;
+        }
+
+        protected SchemaElementTest parseSchemaElementTest( TokenStream tokens ) {
+            if (tokens.canConsume("schema-element", "(")) {
+                NameTest elementDeclarationName = parseNameTest(tokens);
+                SchemaElementTest result = new SchemaElementTest(elementDeclarationName);
+                tokens.consume(")");
+                return result;
+            }
+            return null;
+        }
+
+        protected AttributeTest parseAttributeTest( TokenStream tokens ) {
+            if (tokens.canConsume("attribute", "(")) {
+                if (tokens.canConsume(")") || tokens.canConsume("*", ")")) {
+                    return new AttributeTest(new NameTest(null, null), new NameTest(null, null));
+                }
+                AttributeTest result = null;
+                NameTest attributeName = parseNameTest(tokens);
+                if (tokens.canConsume(",")) {
+                    NameTest typeName = parseNameTest(tokens);
+                    result = new AttributeTest(attributeName, typeName);
+                } else {
+                    result = new AttributeTest(attributeName, new NameTest(null, null));
+                }
+                tokens.consume(")");
+                return result;
+            }
+            return null;
+        }
+
+        protected SchemaAttributeTest parseSchemaAttributeTest( TokenStream tokens ) {
+            if (tokens.canConsume("schema-attribute", "(")) {
+                NameTest attributeDeclarationName = parseNameTest(tokens);
+                SchemaAttributeTest result = new SchemaAttributeTest(attributeDeclarationName);
+                tokens.consume(")");
+                return result;
+            }
+            return null;
+        }
+
+        protected void parseSingleType( TokenStream tokens ) {
+        }
+
+        protected void parseSequenceType( TokenStream tokens ) {
+        }
+
+        /**
+         * Remove any leading and trailing single-quotes or double-quotes from the supplied text.
+         * 
+         * @param text the input text; may not be null
+         * @return the text without leading and trailing quotes, or <code>text</code> if there were no square brackets or quotes
+         */
+        protected String removeQuotes( String text ) {
+            return text.replaceFirst("^['\"]+", "").replaceAll("['\"]+$", "");
+        }
+    }
+
+    /**
+     * A {@link TokenStream.Tokenizer} implementation that parses single- and double-quoted strings, symbols, words consisting of
+     * {@link TokenStream.CharacterStream#isNextValidXmlNcNameCharacter() NCName}s (as defined by the <a
+     * href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">Namespaces in XML 1.0</a> specification), XPath comments,and
+     * {@link #OTHER other} single-character tokens. Quoted phrases are delimited by single-quote and double-quote characters
+     * (which may be escaped within the quote). XPath comments begin with a "(:" and end with a ":)".
+     */
+    public static class XPathTokenizer implements TokenStream.Tokenizer {
+        /**
+         * The token type for tokens that represent an unquoted string containing a character sequence made up of non-whitespace
+         * and non-symbol characters.
+         */
+        public static final int NAME = 2 << 0;
+        /**
+         * The token type for tokens that consist of an individual "symbol" character. The set of characters includes:
+         * <code>(){}*.,;+%?$!<>|=:-[]^/\#@</code>
+         */
+        public static final int SYMBOL = 2 << 1;
+        /**
+         * The token type for tokens that consist of all the characters within single-quotes, double-quotes, or square brackets.
+         */
+        public static final int QUOTED_STRING = 2 << 2;
+        /**
+         * The token type for tokens that consist of all the characters between "(:" and ":)".
+         */
+        public static final int COMMENT = 2 << 3;
+        /**
+         * The token type for tokens that consist of single characters that are not a {@link #SYMBOL}, valid {@link #NAME}, or
+         * {@link #QUOTED_STRING}.
+         */
+        public static final int OTHER = 2 << 4;
+
+        private final boolean useComments;
+
+        public XPathTokenizer( boolean useComments ) {
+            this.useComments = useComments;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.common.text.TokenStream.Tokenizer#tokenize(CharacterStream, Tokens)
+         */
+        public void tokenize( CharacterStream input,
+                              Tokens tokens ) throws ParsingException {
+            while (input.hasNext()) {
+                char c = input.next();
+                switch (c) {
+                    case ' ':
+                    case '\t':
+                    case '\n':
+                    case '\r':
+                        // Just skip these whitespace characters ...
+                        break;
+                    case ')':
+                    case '{':
+                    case '}':
+                    case '*':
+                    case '.':
+                    case ',':
+                    case ';':
+                    case '+':
+                    case '%':
+                    case '?':
+                    case '$':
+                    case '!':
+                    case '<':
+                    case '>':
+                    case '|':
+                    case '=':
+                    case ':':
+                    case '-':
+                    case '[':
+                    case ']':
+                    case '^':
+                    case '/':
+                    case '\\':
+                    case '#':
+                    case '@':
+                        tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                        break;
+                    case '\'':
+                    case '\"':
+                        int startIndex = input.index();
+                        char closingChar = c;
+                        Position pos = input.position();
+                        boolean foundClosingQuote = false;
+                        while (input.hasNext()) {
+                            c = input.next();
+                            if (c == closingChar && input.isNext(closingChar)) {
+                                c = input.next(); // consume the next closeChar since it is escaped
+                            } else if (c == closingChar) {
+                                foundClosingQuote = true;
+                                break;
+                            }
+                        }
+                        if (!foundClosingQuote) {
+                            String msg = CommonI18n.noMatchingDoubleQuoteFound.text(pos.getLine(), pos.getColumn());
+                            if (closingChar == '\'') {
+                                msg = CommonI18n.noMatchingSingleQuoteFound.text(pos.getLine(), pos.getColumn());
+                            }
+                            throw new ParsingException(pos, msg);
+                        }
+                        int endIndex = input.index() + 1; // beyond last character read
+                        tokens.addToken(pos, startIndex, endIndex, QUOTED_STRING);
+                        break;
+                    case '(':
+                        startIndex = input.index();
+                        if (input.isNext(':')) {
+                            // This is a comment ...
+                            pos = input.position();
+                            while (input.hasNext() && !input.isNext(':', ')')) {
+                                c = input.next();
+                            }
+                            if (input.hasNext()) input.next(); // consume the ':'
+                            if (input.hasNext()) input.next(); // consume the ')'
+                            if (useComments) {
+                                endIndex = input.index() + 1; // the token will include the closing ':' and ')' characters
+                                tokens.addToken(pos, startIndex, endIndex, COMMENT);
+                            }
+                        } else {
+                            tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+                            break;
+                        }
+                        break;
+                    default:
+                        startIndex = input.index();
+                        pos = input.position();
+                        // Read as long as there is a valid XML character ...
+                        int tokenType = (XmlCharacters.isValidNcNameStart(c)) ? NAME : OTHER;
+                        while (input.isNextValidXmlNcNameCharacter()) {
+                            c = input.next();
+                        }
+                        endIndex = input.index() + 1; // beyond last character that was included
+                        tokens.addToken(pos, startIndex, endIndex, tokenType);
+                }
+            }
+        }
+    }
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java	2009-10-03 04:07:41 UTC (rev 1285)
@@ -0,0 +1,867 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.xpath;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import java.util.Arrays;
+import java.util.List;
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.common.text.TokenStream;
+import org.jboss.dna.common.text.TokenStream.Tokenizer;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.jcr.xpath.XPath.AnyKindTest;
+import org.jboss.dna.jcr.xpath.XPath.AttributeNameTest;
+import org.jboss.dna.jcr.xpath.XPath.AttributeTest;
+import org.jboss.dna.jcr.xpath.XPath.AxisStep;
+import org.jboss.dna.jcr.xpath.XPath.CommentTest;
+import org.jboss.dna.jcr.xpath.XPath.Component;
+import org.jboss.dna.jcr.xpath.XPath.ContextItem;
+import org.jboss.dna.jcr.xpath.XPath.DescendantOrSelf;
+import org.jboss.dna.jcr.xpath.XPath.DocumentTest;
+import org.jboss.dna.jcr.xpath.XPath.ElementTest;
+import org.jboss.dna.jcr.xpath.XPath.FilterStep;
+import org.jboss.dna.jcr.xpath.XPath.FunctionCall;
+import org.jboss.dna.jcr.xpath.XPath.Literal;
+import org.jboss.dna.jcr.xpath.XPath.NameTest;
+import org.jboss.dna.jcr.xpath.XPath.NodeTest;
+import org.jboss.dna.jcr.xpath.XPath.ParenthesizedExpression;
+import org.jboss.dna.jcr.xpath.XPath.PathExpression;
+import org.jboss.dna.jcr.xpath.XPath.SchemaAttributeTest;
+import org.jboss.dna.jcr.xpath.XPath.SchemaElementTest;
+import org.jboss.dna.jcr.xpath.XPath.StepExpression;
+import org.jboss.dna.jcr.xpath.XPath.TextTest;
+import org.jboss.dna.jcr.xpath.XPathParser.XPathTokenizer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class XPathParserTest {
+
+    private ExecutionContext context;
+    private XPathParser.Parser parser;
+
+    @Before
+    public void beforeEach() {
+        context = new ExecutionContext();
+        parser = new XPathParser.Parser(context);
+    }
+
+    @After
+    public void afterEach() {
+        parser = null;
+    }
+
+    @Test
+    public void shouldParseXPathExpressions() {
+        assertParsable("/jcr:root/a/b/c");
+        assertParsable("/jcr:root/a/b/c[*]");
+        assertParsable("/jcr:root/some[1]/element(nodes, my:type)[1]");
+        assertParsable("//element(*,my:type)");
+        assertParsable("//element(*,my:type)[@jcr:title='something' and @globalProperty='something else']");
+        assertParsable("//element(*,my:type)[@jcr:title | @globalProperty]");
+        assertParsable("//element(*, my:type) order by @my:title");
+        assertParsable("//element(*, my:type) [jcr:contains(., 'jcr')] order by jcr:score() descending");
+        assertParsable("//element(*, employee)[@secretary and @assistant]");
+    }
+
+    @Test
+    public void shouldParseXPathExpressionsThatCombineSeparateExpressions() {
+        assertParsable("/jcr:root/a/b/c and /jcr:root/c/d/e");
+        assertParsable("/jcr:root/a/b/c and /jcr:root/c/d/e or /jcr:root/f/g/h");
+    }
+
+    @Test
+    public void shouldFailToParseInvalidXPathExpressions() {
+        assertNotParsable("//"); // http://www.w3.org/XML/2007/qt-errata/xpath20-errata.html#E3
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Path expression
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParsePathExpressionWithAbbreviatedDescendantOrSelfWithNameTestAfter() {
+        assertThat(parser.parsePathExpr(tokenize("//b:c")), is(pathExpr(descendantOrSelf(), axisStep(nameTest("b", "c")))));
+    }
+
+    @Test
+    public void shouldParsePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNamePathPredicate() {
+        assertThat(parser.parsePathExpr(tokenize("//.[c]")), is(pathExpr(descendantOrSelf(),
+                                                                         filterStep(contextItem(),
+                                                                                    relativePathExpr(axisStep(nameTest("c")))))));
+    }
+
+    @Test
+    public void shouldParsePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNumericLiteralPredicate() {
+        assertThat(parser.parsePathExpr(tokenize("//.[3]")), is(pathExpr(descendantOrSelf(),
+                                                                         filterStep(contextItem(),
+                                                                                    relativePathExpr(filterStep(literal("3")))))));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Relative path expression
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfWithNameTestAfter() {
+        assertThat(parser.parseRelativePathExpr(tokenize("a//b:c")), is(relativePathExpr(axisStep(nameTest("a")),
+                                                                                         descendantOrSelf(),
+                                                                                         axisStep(nameTest("b", "c")))));
+    }
+
+    @Test
+    public void shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfAtEnd() {
+        assertThat(parser.parseRelativePathExpr(tokenize("a//")),
+                   is(relativePathExpr(axisStep(nameTest("a")), descendantOrSelf())));
+    }
+
+    @Test
+    public void shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNamePathPredicate() {
+        assertThat(parser.parseRelativePathExpr(tokenize("a//.[c]")),
+                   is(relativePathExpr(axisStep(nameTest("a")),
+                                       descendantOrSelf(),
+                                       filterStep(contextItem(), relativePathExpr(axisStep(nameTest("c")))))));
+    }
+
+    @Test
+    public void shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNumericLiteralPredicate() {
+        assertThat(parser.parseRelativePathExpr(tokenize("a//.[3]")),
+                   is(relativePathExpr(axisStep(nameTest("a")),
+                                       descendantOrSelf(),
+                                       filterStep(contextItem(), relativePathExpr(filterStep(literal("3")))))));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Step Expression
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseStepExpressionFromParenthesizedLiteral() {
+        assertThat(parser.parseStepExpr(tokenize("('foo')")),
+                   is((StepExpression)filterStep(paren(relativePathExpr(filterStep(literal("foo")))))));
+    }
+
+    @Test
+    public void shouldParseStepExpressionFromQuotedStringLiteral() {
+        assertThat(parser.parseStepExpr(tokenize("'foo'")), is((StepExpression)filterStep(literal("foo"))));
+    }
+
+    @Test
+    public void shouldParseStepExpressionFromFunctionCallWithUnqualifiedName() {
+        assertThat(parser.parseStepExpr(tokenize("element(*,*)")),
+                   is((StepExpression)filterStep(functionCall(nameTest("element"),
+                                                              relativePathExpr(axisStep(wildcard())),
+                                                              relativePathExpr(axisStep(wildcard()))))));
+    }
+
+    @Test
+    public void shouldParseStepExpressionFromFunctionCallWithQualifiedName() {
+        assertThat(parser.parseStepExpr(tokenize("foo:bar(*)")),
+                   is((StepExpression)filterStep(functionCall(nameTest("foo", "bar"), relativePathExpr(axisStep(wildcard()))))));
+    }
+
+    @Test
+    public void shouldParseStepExpressionFromQualifiedNameWithPredicate() {
+        assertThat(parser.parseStepExpr(tokenize("foo:bar[3]")),
+                   is((StepExpression)axisStep(nameTest("foo", "bar"), relativePathExpr(filterStep(literal("3"))))));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Axis Step - attribute
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseAxisStepFromAttributeWithNoPrefix() {
+        assertThat(parser.parseAxisStep(tokenize("@foo")), is(axisStep(attributeNameTest(nameTest("foo")))));
+    }
+
+    @Test
+    public void shouldParseAxisStepFromAttributeWithPrefix() {
+        assertThat(parser.parseAxisStep(tokenize("@foo:bar")), is(axisStep(attributeNameTest(nameTest("foo", "bar")))));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseAxisStepFromInvalidAttributeName() {
+        parser.parseAxisStep(tokenize("@3:invalidName"));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Axis Step - name
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseAxisStepFromNameWithNoPrefix() {
+        assertThat(parser.parseAxisStep(tokenize("foo")), is(axisStep(nameTest("foo"))));
+    }
+
+    @Test
+    public void shouldParseAxisStepFromNameWithPrefix() {
+        assertThat(parser.parseAxisStep(tokenize("foo:bar")), is(axisStep(nameTest("foo", "bar"))));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseAxisStepIfInvalidName() {
+        assertThat(parser.parseAxisStep(tokenize("3:invalidName")), is(nullValue()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Axis Step - wildcard
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseAxisStepFromWildcard() {
+        assertThat(parser.parseAxisStep(tokenize("*")), is(axisStep(wildcard())));
+    }
+
+    @Test
+    public void shouldParseAxisStepFromWildcardPrefixAndNonWildcardLocalName() {
+        assertThat(parser.parseAxisStep(tokenize("*:foo")), is(axisStep(nameTest(null, "foo"))));
+    }
+
+    @Test
+    public void shouldParseAxisStepFromWithPrefixAndWildcardLocalName() {
+        assertThat(parser.parseAxisStep(tokenize("foo:*")), is(axisStep(nameTest("foo", null))));
+    }
+
+    @Test
+    public void shouldParseAxisStepFromWildcardPrefixAndNonWildcardLocalNameWithPredicates() {
+        assertThat(parser.parseAxisStep(tokenize("*:name[3]")), is(axisStep(nameTest(null, "name"),
+                                                                            relativePathExpr(filterStep(literal("3"))))));
+    }
+
+    @Test
+    public void shouldParseAxisStepFromWildcardLocalNameWithPredicates() {
+        assertThat(parser.parseAxisStep(tokenize("*[3]")), is(axisStep(wildcard(), relativePathExpr(filterStep(literal("3"))))));
+    }
+
+    @Test
+    public void shouldParseAxisStepFromWildcardPrefixAndLocalNameWithPredicates() {
+        assertThat(parser.parseAxisStep(tokenize("*:*[3]")), is(axisStep(wildcard(), relativePathExpr(filterStep(literal("3"))))));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Axis Step - self::, child::, descendant::, descendant-or-self::, following::, following-sibling::, namespace::
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedSelfAxis() {
+        parser.parseAxisStep(tokenize("self::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedChildAxis() {
+        parser.parseAxisStep(tokenize("child::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedDescendantAxis() {
+        parser.parseAxisStep(tokenize("descendant::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedDescendantOrSelfAxis() {
+        parser.parseAxisStep(tokenize("descendant-or-self::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedFollowingAxis() {
+        parser.parseAxisStep(tokenize("following::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedFollowingSiblingAxis() {
+        parser.parseAxisStep(tokenize("following-sibling::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedNamespaceAxis() {
+        parser.parseAxisStep(tokenize("namespace::x"));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Axis Step - parent::, ancestor::, ancestor-or-self::, preceding::, preceding-sibling::
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedParentAxis() {
+        parser.parseAxisStep(tokenize("parent::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedAncestorAxis() {
+        parser.parseAxisStep(tokenize("ancestor::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedAncestorOrSelfAxis() {
+        parser.parseAxisStep(tokenize("ancestor-or-self::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedPrecedingAxis() {
+        parser.parseAxisStep(tokenize("preceding::x"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNonAbbreviatedPrecedingSiblingAxis() {
+        parser.parseAxisStep(tokenize("preceding-sibling::x"));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Predicates
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParsePredicatesWhenThereIsOnlyOnePredicate() {
+        assertThat(parser.parsePredicates(tokenize("[foo]")), is(predicates(relativePathExpr(axisStep(nameTest("foo"))))));
+    }
+
+    @Test
+    public void shouldParsePredicatesWhenThereAreMultiplePredicates() {
+        assertThat(parser.parsePredicates(tokenize("['foo']['bar']")),
+                   is(predicates(relativePathExpr(filterStep(literal("foo"))), relativePathExpr(filterStep(literal("bar"))))));
+        assertThat(parser.parsePredicates(tokenize("[foo][bar]")), is(predicates(relativePathExpr(axisStep(nameTest("foo"))),
+                                                                                 relativePathExpr(axisStep(nameTest("bar"))))));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Context item
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseContextItemExpr() {
+        assertThat(parser.parseContextItemExpr(tokenize(".")), is(new ContextItem()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Parenthesized expression
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseParenthesizedExpression() {
+        assertThat(parser.parseParenthesizedExpr(tokenize("('foo')")), is(paren(relativePathExpr(filterStep(literal("foo"))))));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Number literal
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseNumberLiteralContainingUnquotedIntegralValuesIntoCanonicalStringRepresentation() {
+        assertThat(parser.parseNumericLiteral(tokenize("1")), is(literal("1")));
+        assertThat(parser.parseNumericLiteral(tokenize("-1")), is(literal("-1")));
+        assertThat(parser.parseNumericLiteral(tokenize("0")), is(literal("0")));
+        assertThat(parser.parseNumericLiteral(tokenize("+1001")), is(literal("1001")));
+    }
+
+    @Test
+    public void shouldParseNumberLiteralContainingUnquotedDecimalValuesIntoCanonicalStringRepresentation() {
+        assertThat(parser.parseNumericLiteral(tokenize("1.2")), is(literal("1.2")));
+        assertThat(parser.parseNumericLiteral(tokenize("-1.2")), is(literal("-1.2")));
+        assertThat(parser.parseNumericLiteral(tokenize("0.2")), is(literal("0.2")));
+        assertThat(parser.parseNumericLiteral(tokenize("+1001.2")), is(literal("1001.2")));
+        assertThat(parser.parseNumericLiteral(tokenize("1.2e10")), is(literal("1.2E10")));
+        assertThat(parser.parseNumericLiteral(tokenize("-1.2e10")), is(literal("-1.2E10")));
+        assertThat(parser.parseNumericLiteral(tokenize("0.2e10")), is(literal("2.0E9")));
+        assertThat(parser.parseNumericLiteral(tokenize("1.2e+10")), is(literal("1.2E10")));
+        assertThat(parser.parseNumericLiteral(tokenize("-1.2e+10")), is(literal("-1.2E10")));
+        assertThat(parser.parseNumericLiteral(tokenize("0.2e+10")), is(literal("2.0E9")));
+        assertThat(parser.parseNumericLiteral(tokenize("1.2e-10")), is(literal("1.2E-10")));
+        assertThat(parser.parseNumericLiteral(tokenize("-1.2e-10")), is(literal("-1.2E-10")));
+        assertThat(parser.parseNumericLiteral(tokenize("0.2e-10")), is(literal("2.0E-11")));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // String literal
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseStringLiteral() {
+        assertThat(parser.parseStringLiteral(tokenize("one")), is(literal("one")));
+        assertThat(parser.parseStringLiteral(tokenize("'one'")), is(literal("one")));
+        assertThat(parser.parseStringLiteral(tokenize("'one word as a quote'")), is(literal("one word as a quote")));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Function call
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseFunctionCallWithZeroParameters() {
+        FunctionCall func = parser.parseFunctionCall(tokenize("a()"));
+        assertThat(func, is(notNullValue()));
+        assertThat(func.getName(), is(nameTest("a")));
+        assertThat(func.getParameters().isEmpty(), is(true));
+    }
+
+    @Test
+    public void shouldParseFunctionCallWithOneQuotedStringLiteralParameter() {
+        FunctionCall func = parser.parseFunctionCall(tokenize("a('foo')"));
+        assertThat(func, is(notNullValue()));
+        assertThat(func.getName(), is(nameTest("a")));
+        assertThat(func.getParameters().size(), is(1));
+        assertThat(func.getParameters().get(0), is((Component)relativePathExpr(filterStep(literal("foo")))));
+    }
+
+    @Test
+    public void shouldParseFunctionCallWithOneUnquotedStringLiteralParameter() {
+        FunctionCall func = parser.parseFunctionCall(tokenize("a(foo)"));
+        assertThat(func, is(notNullValue()));
+        assertThat(func.getName(), is(nameTest("a")));
+        assertThat(func.getParameters().size(), is(1));
+        assertThat(func.getParameters().get(0), is((Component)relativePathExpr(axisStep(nameTest("foo")))));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // General comparison
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseGeneralComp() {
+        assertThat(parser.parseGeneralComp(tokenize("!=")), is(Operator.NOT_EQUAL_TO));
+        assertThat(parser.parseGeneralComp(tokenize("=")), is(Operator.EQUAL_TO));
+        assertThat(parser.parseGeneralComp(tokenize("<")), is(Operator.LESS_THAN));
+        assertThat(parser.parseGeneralComp(tokenize("<=")), is(Operator.LESS_THAN_OR_EQUAL_TO));
+        assertThat(parser.parseGeneralComp(tokenize(">")), is(Operator.GREATER_THAN));
+        assertThat(parser.parseGeneralComp(tokenize(">=")), is(Operator.GREATER_THAN_OR_EQUAL_TO));
+        assertThat(parser.parseGeneralComp(tokenize("<5")), is(Operator.LESS_THAN));
+        assertThat(parser.parseGeneralComp(tokenize(">5")), is(Operator.GREATER_THAN));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseGeneralCompIfOperatorPatternIsNotFound() {
+        assertThat(parser.parseGeneralComp(tokenize("name")), is(nullValue()));
+        assertThat(parser.parseGeneralComp(tokenize("+")), is(nullValue()));
+        assertThat(parser.parseGeneralComp(tokenize("!+")), is(nullValue()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Name
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseName() {
+        assertThat(parser.parseNameTest(tokenize("*")), is(wildcard()));
+        assertThat(parser.parseNameTest(tokenize("*:*")), is(wildcard()));
+        assertThat(parser.parseNameTest(tokenize("*:name")), is(nameTest(null, "name")));
+        assertThat(parser.parseNameTest(tokenize("name:*")), is(nameTest("name", null)));
+        assertThat(parser.parseNameTest(tokenize("abc")), is(nameTest("abc")));
+        assertThat(parser.parseNameTest(tokenize("abc:def")), is(nameTest("abc", "def")));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNameIfNotValidWildcardOrQName() {
+        parser.parseNameTest(tokenize("3ABC:def"));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // QName
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseQName() {
+        assertThat(parser.parseQName(tokenize("abc")), is(nameTest("abc")));
+        assertThat(parser.parseQName(tokenize("abc:def")), is(nameTest("abc", "def")));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseQNameIfStartsWithDigit() {
+        parser.parseNCName(tokenize("3ABC:def"));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // NCName
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseNCName() {
+        assertThat(parser.parseNCName(tokenize("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), is("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+        assertThat(parser.parseNCName(tokenize("abcdefghijklmnopqrstuvwxyz")), is("abcdefghijklmnopqrstuvwxyz"));
+        assertThat(parser.parseNCName(tokenize("a0123456789")), is("a0123456789"));
+        assertThat(parser.parseNCName(tokenize("a_-3b")), is("a_-3b"));
+        assertThat(parser.parseNCName(tokenize("abc:def")), is("abc"));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseNCNameIfStartsWithDigit() {
+        parser.parseNCName(tokenize("3ABC"));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // wildcard
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseWildcard() {
+        assertThat(parser.parseWildcard(tokenize("*")), is(wildcard()));
+        assertThat(parser.parseWildcard(tokenize("*:*")), is(wildcard()));
+        assertThat(parser.parseWildcard(tokenize("*:name")), is(nameTest(null, "name")));
+        assertThat(parser.parseWildcard(tokenize("name:*")), is(nameTest("name", null)));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseWildcardIfNotWildcard() {
+        assertThat(parser.parseWildcard(tokenize("name")), is(nullValue()));
+        assertThat(parser.parseWildcard(tokenize("name:foo")), is(nullValue()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // kind test
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseKindTest() {
+        assertThat(parser.parseKindTest(tokenize("node()")), is(instanceOf(AnyKindTest.class)));
+        assertThat(parser.parseKindTest(tokenize("comment()")), is(instanceOf(CommentTest.class)));
+        assertThat(parser.parseKindTest(tokenize("text()")), is(instanceOf(TextTest.class)));
+        assertThat(parser.parseKindTest(tokenize("document-node(element(foo:bar))")), is(instanceOf(DocumentTest.class)));
+        assertThat(parser.parseKindTest(tokenize("element(foo:bar)")), is(instanceOf(ElementTest.class)));
+        assertThat(parser.parseKindTest(tokenize("schema-element(foo:bar)")), is(instanceOf(SchemaElementTest.class)));
+        assertThat(parser.parseKindTest(tokenize("attribute(foo:bar)")), is(instanceOf(AttributeTest.class)));
+        assertThat(parser.parseKindTest(tokenize("schema-attribute(foo:bar)")), is(instanceOf(SchemaAttributeTest.class)));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // node
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseAnyKindTest() {
+        assertThat(parser.parseAnyKindTest(tokenize("node()")), is(notNullValue()));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseAnyKindTestIfNotFollowedByOpenAndCloseParenthesis() {
+        assertThat(parser.parseAnyKindTest(tokenize("node x )")), is(nullValue()));
+        assertThat(parser.parseAnyKindTest(tokenize("node(x )")), is(nullValue()));
+        assertThat(parser.parseAnyKindTest(tokenize("node(x")), is(nullValue()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // comment
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseCommentTest() {
+        assertThat(parser.parseCommentTest(tokenize("comment()")), is(notNullValue()));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseCommentTestIfNotFollowedByOpenAndCloseParenthesis() {
+        assertThat(parser.parseCommentTest(tokenize("comment x )")), is(nullValue()));
+        assertThat(parser.parseCommentTest(tokenize("comment(x )")), is(nullValue()));
+        assertThat(parser.parseCommentTest(tokenize("comment(x")), is(nullValue()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // text
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseTextTest() {
+        assertThat(parser.parseTextTest(tokenize("text()")), is(notNullValue()));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseTextTestIfNotFollowedByOpenAndCloseParenthesis() {
+        assertThat(parser.parseTextTest(tokenize("text x )")), is(nullValue()));
+        assertThat(parser.parseTextTest(tokenize("text(x )")), is(nullValue()));
+        assertThat(parser.parseTextTest(tokenize("text(x")), is(nullValue()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // document-node
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseDocumentTest() {
+        DocumentTest result = parser.parseDocumentTest(tokenize("document-node(element(foo:bar))"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementTest().getElementName(), is(nameTest("foo", "bar")));
+        assertThat(result.getElementTest().getTypeName(), is(wildcard()));
+        assertThat(result.getSchemaElementTest(), is(nullValue()));
+
+        result = parser.parseDocumentTest(tokenize("document-node(schema-element(foo))"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getSchemaElementTest().getElementDeclarationName(), is(nameTest("foo")));
+        assertThat(result.getElementTest(), is(nullValue()));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseDocumentTestIfOpenParenthesisIsNotIncluded() {
+        assertThat(parser.parseDocumentTest(tokenize("document-node foo")), is(nullValue()));
+        assertThat(parser.parseDocumentTest(tokenize("document-node foo")), is(nullValue()));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseDocumentTestIfDocumentNodeDoesNotContainElementOrSchemaElement() {
+        parser.parseDocumentTest(tokenize("document-node(foo)"));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // element
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseElementTest() {
+        ElementTest result = parser.parseElementTest(tokenize("element(foo)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementName(), is(nameTest("foo")));
+        assertThat(result.getTypeName(), is(wildcard()));
+
+        result = parser.parseElementTest(tokenize("element(foo:bar)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementName(), is(nameTest("foo", "bar")));
+        assertThat(result.getTypeName(), is(wildcard()));
+
+        result = parser.parseElementTest(tokenize("element(foo:bar,baz)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementName(), is(nameTest("foo", "bar")));
+        assertThat(result.getTypeName(), is(nameTest("baz")));
+
+        result = parser.parseElementTest(tokenize("element(foo:bar,baz:bam)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementName(), is(nameTest("foo", "bar")));
+        assertThat(result.getTypeName(), is(nameTest("baz", "bam")));
+
+        result = parser.parseElementTest(tokenize("element(foo:bar,*)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementName(), is(nameTest("foo", "bar")));
+        assertThat(result.getTypeName(), is(wildcard()));
+
+        result = parser.parseElementTest(tokenize("element(*,foo:bar)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementName(), is(wildcard()));
+        assertThat(result.getTypeName(), is(nameTest("foo", "bar")));
+
+        result = parser.parseElementTest(tokenize("element(*,*)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementName(), is(wildcard()));
+        assertThat(result.getTypeName(), is(wildcard()));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseElementTestIfOpenParenthesisIsNotIncluded() {
+        ElementTest result = parser.parseElementTest(tokenize("attribute foo"));
+        assertThat(result, is(nullValue()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // schema-element
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseSchemaElementTest() {
+        SchemaElementTest result = parser.parseSchemaElementTest(tokenize("schema-element(foo)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementDeclarationName(), is(nameTest("foo")));
+
+        result = parser.parseSchemaElementTest(tokenize("schema-element(foo:bar)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementDeclarationName(), is(nameTest("foo", "bar")));
+
+        result = parser.parseSchemaElementTest(tokenize("schema-element(*)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getElementDeclarationName(), is(wildcard()));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseSchemaElementTestIfOpenParenthesisIsNotIncluded() {
+        SchemaElementTest result = parser.parseSchemaElementTest(tokenize("schema-element foo"));
+        assertThat(result, is(nullValue()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // attribute
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseAttributeTest() {
+        AttributeTest result = parser.parseAttributeTest(tokenize("attribute(foo)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getAttributeName(), is(nameTest("foo")));
+        assertThat(result.getTypeName(), is(wildcard()));
+
+        result = parser.parseAttributeTest(tokenize("attribute(foo:bar)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getAttributeName(), is(nameTest("foo", "bar")));
+        assertThat(result.getTypeName(), is(wildcard()));
+
+        result = parser.parseAttributeTest(tokenize("attribute(foo:bar,baz)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getAttributeName(), is(nameTest("foo", "bar")));
+        assertThat(result.getTypeName(), is(nameTest("baz")));
+
+        result = parser.parseAttributeTest(tokenize("attribute(foo:bar,baz:bar)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getAttributeName(), is(nameTest("foo", "bar")));
+        assertThat(result.getTypeName(), is(nameTest("baz", "bar")));
+
+        result = parser.parseAttributeTest(tokenize("attribute(*,baz:bar)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getAttributeName(), is(wildcard()));
+        assertThat(result.getTypeName(), is(nameTest("baz", "bar")));
+
+        result = parser.parseAttributeTest(tokenize("attribute(foo:bar,*)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getAttributeName(), is(nameTest("foo", "bar")));
+        assertThat(result.getTypeName(), is(wildcard()));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseAttributeTestIfOpenParenthesisIsNotIncluded() {
+        AttributeTest result = parser.parseAttributeTest(tokenize("attribute foo"));
+        assertThat(result, is(nullValue()));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // schema-attribute
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseSchemaAttributeTest() {
+        SchemaAttributeTest result = parser.parseSchemaAttributeTest(tokenize("schema-attribute(foo)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getAttributeDeclarationName(), is(nameTest("foo")));
+
+        result = parser.parseSchemaAttributeTest(tokenize("schema-attribute(foo:bar)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getAttributeDeclarationName(), is(nameTest("foo", "bar")));
+
+        result = parser.parseSchemaAttributeTest(tokenize("schema-attribute(*)"));
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getAttributeDeclarationName(), is(wildcard()));
+    }
+
+    @Test
+    public void shouldReturnNullFromParseSchemaAttributeTestIfOpenParenthesisIsNotIncluded() {
+        SchemaAttributeTest result = parser.parseSchemaAttributeTest(tokenize("schema-attribute foo"));
+        assertThat(result, is(nullValue()));
+    }
+
+    protected TokenStream tokenize( String xpath ) {
+        Tokenizer tokenizer = new XPathTokenizer(false); // skip comments
+        return new TokenStream(xpath, tokenizer, true).start(); // case sensitive!!
+    }
+
+    protected DescendantOrSelf descendantOrSelf() {
+        return new DescendantOrSelf();
+    }
+
+    protected AxisStep axisStep( NodeTest nodeTest,
+                                 Component... predicates ) {
+        return new AxisStep(nodeTest, predicates(predicates));
+    }
+
+    protected FilterStep filterStep( Component primaryExpression,
+                                     Component... predicates ) {
+        return new FilterStep(primaryExpression, predicates(predicates));
+    }
+
+    protected PathExpression pathExpr( StepExpression... steps ) {
+        return pathExpr(Arrays.asList(steps));
+    }
+
+    protected PathExpression pathExpr( List<StepExpression> steps ) {
+        return new PathExpression(false, steps);
+    }
+
+    protected PathExpression relativePathExpr( StepExpression... steps ) {
+        return relativePathExpr(Arrays.asList(steps));
+    }
+
+    protected PathExpression relativePathExpr( List<StepExpression> steps ) {
+        return new PathExpression(true, steps);
+    }
+
+    protected Literal literal( String value ) {
+        return new Literal(value);
+    }
+
+    protected ContextItem contextItem() {
+        return new ContextItem();
+    }
+
+    protected List<Component> predicates( Component... predicates ) {
+        return Arrays.asList(predicates);
+    }
+
+    protected ParenthesizedExpression paren( Component value ) {
+        return new ParenthesizedExpression(value);
+    }
+
+    protected AttributeNameTest attributeNameTest( NameTest nameTest ) {
+        return new AttributeNameTest(nameTest);
+    }
+
+    protected AttributeTest attribute( NameTest name ) {
+        return new AttributeTest(name, null);
+    }
+
+    protected AttributeTest attribute( NameTest name,
+                                       NameTest type ) {
+        return new AttributeTest(name, type);
+    }
+
+    protected FunctionCall functionCall( NameTest name,
+                                         Component... parameters ) {
+        return new FunctionCall(name, Arrays.asList(parameters));
+    }
+
+    protected NameTest wildcard() {
+        return new NameTest(null, null);
+    }
+
+    protected NameTest nameTest( String localPart ) {
+        return new NameTest(null, localPart);
+    }
+
+    protected NameTest nameTest( String prefix,
+                                 String local ) {
+        return new NameTest(prefix, local);
+    }
+
+    protected void assertParsable( String xpath ) {
+        new XPathParser().parseQuery(xpath, context);
+    }
+
+    protected void assertNotParsable( String xpath ) {
+        try {
+            new XPathParser().parseQuery(xpath, context);
+            fail("Expected an invalid XPath:  " + xpath);
+        } catch (ParsingException e) {
+            // expected
+        }
+    }
+
+}


Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Sat Oct  3 00:09:31 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Sat, 3 Oct 2009 00:09:31 -0400
Subject: [dna-commits] DNA SVN: r1286 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph and 14 other
	directories.
Message-ID: <200910030409.n9349Vhi001870@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-03 00:09:29 -0400 (Sat, 03 Oct 2009)
New Revision: 1286

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslator.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java
Removed:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/InvalidQueryException.java
Modified:
   trunk/.gitignore
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/DateTime.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/JodaDateTime.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java
   trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
Log:
DNA-467 Add search/query support to the graph API
DNA-468 Add XPath query language support

Added a lot more implementation and unit tests behind the XPath parser, the XPath AST objects, and the translator that converts an XPath AST into a SQL query model. Found and fixed a number of issues in several of the 'dna-graph' classes, including some tweaks to the SQL query odel, the SQL parser, minor improvements to the JodaDateTime class, and some changes to the exception handling in the 'dna-graph' query model and builder.

At this point, the XPath support is pretty good, though still not complete.  It may be good enough to use for a while - until we have more examples.  The unit tests are verifying not only that the XPath expressions can be parsed, but that they're also converted correctly to an expected SQL representation.

The XPath functionality has not yet been integrated into the JCR implementation, since that requires hooking up the LuceneQueryEngine (which still needs some work).  However, I plan to start putting more of the pieces together and focusing on wrapping up the LuceneQueryEngine.

Modified: trunk/.gitignore
===================================================================
--- trunk/.gitignore	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/.gitignore	2009-10-03 04:09:29 UTC (rev 1286)
@@ -39,6 +39,7 @@
 
 /web/dna-web-jcr-rest/target/
 /web/dna-web-jcr-rest-war/target/
+/web/dna-web-jcr-rest-client/target/
 
 /extensions/dna-classloader-maven/target
 /extensions/dna-common-jdbc/target

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -128,6 +128,7 @@
     public static I18n childNotFound;
 
     /* Query */
+    public static I18n unknownQueryLanguage;
     public static I18n tableDoesNotExist;
     public static I18n columnDoesNotExistOnTable;
     public static I18n columnDoesNotExistInQuery;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/DateTime.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/DateTime.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/DateTime.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -71,6 +71,14 @@
     long getMilliseconds();
 
     /**
+     * Get the number of milliseconds from 1970-01-01T00:00Z with this time converted to UTC. This value is consistent with the
+     * JDK {@link java.util.Date Date} and {@link java.util.Calendar Calendar} classes.
+     * 
+     * @return the number of milliseconds from 1970-01-01T00:00Z in the UTC time zone
+     */
+    long getMillisecondsInUtc();
+
+    /**
      * Get this instance represented as a standard JDK {@link java.util.Date} instance. Note that this conversion loses the time
      * zone information, as the standard JDK {@link java.util.Date} does not represent time zones.
      * 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/JodaDateTime.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/JodaDateTime.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/basic/JodaDateTime.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -40,6 +40,8 @@
 @Immutable
 public class JodaDateTime implements org.jboss.dna.graph.property.DateTime {
 
+    private static final DateTimeZone UTC_ZONE = DateTimeZone.forID("UTC");
+
     /**
      */
     private static final long serialVersionUID = -730188225988292422L;
@@ -47,36 +49,44 @@
     private static final int MILLIS_IN_HOUR = 1000 * 60 * 60;
 
     private final DateTime instance;
+    private final long millisInUtc;
 
     public JodaDateTime() {
         this.instance = new DateTime();
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( String iso8601 ) {
         this.instance = new DateTime(iso8601);
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( String iso8601,
                          String timeZoneId ) {
         this.instance = new DateTime(iso8601, DateTimeZone.forID(timeZoneId));
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( long milliseconds ) {
         this.instance = new DateTime(milliseconds);
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( long milliseconds,
                          Chronology chronology ) {
         this.instance = new DateTime(milliseconds, chronology);
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( long milliseconds,
                          String timeZoneId ) {
         this.instance = new DateTime(milliseconds, DateTimeZone.forID(timeZoneId));
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( DateTimeZone dateTimeZone ) {
         this.instance = new DateTime(dateTimeZone);
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( int year,
@@ -87,6 +97,7 @@
                          int secondOfMinute,
                          int millisecondsOfSecond ) {
         this.instance = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisecondsOfSecond);
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( int year,
@@ -99,6 +110,7 @@
                          Chronology chronology ) {
         this.instance = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute,
                                      millisecondsOfSecond, chronology);
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( int year,
@@ -111,6 +123,7 @@
                          DateTimeZone dateTimeZone ) {
         this.instance = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute,
                                      millisecondsOfSecond, dateTimeZone);
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( int year,
@@ -123,6 +136,7 @@
                          int timeZoneOffsetHours ) {
         this.instance = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute,
                                      millisecondsOfSecond, DateTimeZone.forOffsetHours(timeZoneOffsetHours));
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( int year,
@@ -135,18 +149,22 @@
                          String timeZoneId ) {
         this.instance = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute,
                                      millisecondsOfSecond, DateTimeZone.forID(timeZoneId));
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( java.util.Date jdkDate ) {
         this.instance = new DateTime(jdkDate);
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( java.util.Calendar jdkCalendar ) {
         this.instance = new DateTime(jdkCalendar);
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     public JodaDateTime( DateTime dateTime ) {
         this.instance = dateTime; // it's immutable, so just hold onto the supplied instance
+        this.millisInUtc = instance.withZone(UTC_ZONE).getMillis();
     }
 
     /**
@@ -207,7 +225,16 @@
 
     /**
      * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.property.DateTime#getMillisecondsInUtc()
      */
+    public long getMillisecondsInUtc() {
+        return millisInUtc;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public int getMinuteOfHour() {
         return this.instance.getMinuteOfHour();
     }
@@ -315,12 +342,8 @@
      * {@inheritDoc}
      */
     public int compareTo( org.jboss.dna.graph.property.DateTime that ) {
-        if (that instanceof JodaDateTime) {
-            return this.instance.compareTo(((JodaDateTime)that).instance);
-        }
-        if (that == null) return 1;
-        long diff = this.toUtcTimeZone().getMilliseconds() - that.toUtcTimeZone().getMilliseconds();
-        return (int)diff;
+        long diff = this.getMillisecondsInUtc() - that.getMillisecondsInUtc();
+        return diff == 0 ? 0 : diff > 0 ? 1 : -1;
     }
 
     /**
@@ -328,7 +351,7 @@
      */
     @Override
     public int hashCode() {
-        return this.instance.hashCode();
+        return (int)this.millisInUtc;
     }
 
     /**
@@ -337,18 +360,13 @@
     @Override
     public boolean equals( Object obj ) {
         if (obj == this) return true;
-        if (obj instanceof JodaDateTime) {
-            JodaDateTime that = (JodaDateTime)obj;
-
-            /*
-             * The equals semantics for JodaDateTimes are very strict, implying that not only are the two instants represented
-             * by the JodaDateTimes logically equivalent, but also that the Chronology and DateTimeZone are the same.  
-             * Instead, use isEqual, which only checks that the two JodaDateTimes are logically equivalent.
-             */
-            return this.instance.isEqual(that.instance);
+        if (obj instanceof org.jboss.dna.graph.property.DateTime) {
+            org.jboss.dna.graph.property.DateTime that = (org.jboss.dna.graph.property.DateTime)obj;
+            return this.getMillisecondsInUtc() == that.getMillisecondsInUtc();
         }
         if (obj instanceof DateTime) {
-            return this.instance.equals(obj);
+            DateTime that = (DateTime)obj;
+            return this.getMillisecondsInUtc() == that.withZone(UTC_ZONE).getMillis();
         }
         return false;
     }
@@ -365,7 +383,9 @@
      * {@inheritDoc}
      */
     public org.jboss.dna.graph.property.DateTime toUtcTimeZone() {
-        DateTime jodaTime = this.instance.withZone(DateTimeZone.forID("UTC"));
+        DateTimeZone utc = DateTimeZone.forID("UTC");
+        if (this.instance.getZone().equals(utc)) return this;
+        DateTime jodaTime = this.instance.withZone(utc);
         return new JodaDateTime(jodaTime);
     }
 

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/InvalidQueryException.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/InvalidQueryException.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/InvalidQueryException.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -1,65 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.graph.query;
-
-/**
- * 
- */
-public class InvalidQueryException extends RuntimeException {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 
-     */
-    public InvalidQueryException() {
-    }
-
-    /**
-     * @param message
-     */
-    public InvalidQueryException( String message ) {
-        super(message);
-
-    }
-
-    /**
-     * @param cause
-     */
-    public InvalidQueryException( Throwable cause ) {
-        super(cause);
-
-    }
-
-    /**
-     * @param message
-     * @param cause
-     */
-    public InvalidQueryException( String message,
-                                  Throwable cause ) {
-        super(message, cause);
-
-    }
-
-}

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -23,20 +23,28 @@
  */
 package org.jboss.dna.graph.query;
 
+import java.math.BigDecimal;
+import java.net.URI;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.DateTime;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.property.ValueFactories;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.query.model.AllNodes;
 import org.jboss.dna.graph.query.model.And;
 import org.jboss.dna.graph.query.model.BindVariableName;
 import org.jboss.dna.graph.query.model.ChildNode;
+import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Comparison;
 import org.jboss.dna.graph.query.model.Constraint;
@@ -54,8 +62,10 @@
 import org.jboss.dna.graph.query.model.Literal;
 import org.jboss.dna.graph.query.model.LowerCase;
 import org.jboss.dna.graph.query.model.NamedSelector;
+import org.jboss.dna.graph.query.model.NodeDepth;
 import org.jboss.dna.graph.query.model.NodeLocalName;
 import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
 import org.jboss.dna.graph.query.model.Not;
 import org.jboss.dna.graph.query.model.Operator;
 import org.jboss.dna.graph.query.model.Or;
@@ -302,13 +312,13 @@
      * 
      * @param name the name; may not be null
      * @return the name; never null
-     * @throws InvalidQueryException if the supplied name is not a valid {@link Name} object
+     * @throws IllegalArgumentException if the supplied name is not a valid {@link Name} object
      */
     protected Name name( String name ) {
         try {
             return context.getValueFactories().getNameFactory().create(name.trim());
         } catch (ValueFormatException e) {
-            throw new InvalidQueryException(GraphI18n.expectingValidName.text(name));
+            throw new IllegalArgumentException(GraphI18n.expectingValidName.text(name));
         }
     }
 
@@ -318,13 +328,13 @@
      * 
      * @param path the path; may not be null
      * @return the path; never null
-     * @throws InvalidQueryException if the supplied string is not a valid {@link Path} object
+     * @throws IllegalArgumentException if the supplied string is not a valid {@link Path} object
      */
     protected Path path( String path ) {
         try {
             return context.getValueFactories().getPathFactory().create(path.trim());
         } catch (ValueFormatException e) {
-            throw new InvalidQueryException(GraphI18n.expectingValidPath.text(path));
+            throw new IllegalArgumentException(GraphI18n.expectingValidPath.text(path));
         }
     }
 
@@ -335,7 +345,7 @@
      * 
      * @param nameExpression the expression specifying the columm name and (optionally) the table's name or alias; may not be null
      * @return the column; never null
-     * @throws InvalidQueryException if the table's name/alias is not specified, but the query has more than one named source
+     * @throws IllegalArgumentException if the table's name/alias is not specified, but the query has more than one named source
      */
     protected Column column( String nameExpression ) {
         String[] parts = nameExpression.split("(?<!\\\\)\\."); // a . not preceded by an escaping slash
@@ -356,7 +366,7 @@
                 propertyName = name(parts[0]);
                 columnName = parts[0];
             } else {
-                throw new InvalidQueryException(GraphI18n.columnMustBeScoped.text(parts[0]));
+                throw new IllegalArgumentException(GraphI18n.columnMustBeScoped.text(parts[0]));
             }
         }
         return new Column(name, propertyName, columnName);
@@ -373,16 +383,15 @@
     }
 
     /**
-     * Select the columns with the supplied names. Each column name has the form "<code>[tableName.]columnName</code>", where "
-     * <code>tableName</code>" must be a valid table name or alias. If the table name/alias is not specified, then there is
-     * expected to be a single FROM clause with a single named selector.
+     * Add to the select clause the columns with the supplied names. Each column name has the form "
+     * <code>[tableName.]columnName</code>", where " <code>tableName</code>" must be a valid table name or alias. If the table
+     * name/alias is not specified, then there is expected to be a single FROM clause with a single named selector.
      * 
      * @param columnNames the column expressions; may not be null
      * @return this builder object, for convenience in method chaining
-     * @throws InvalidQueryException if the table's name/alias is not specified, but the query has more than one named source
+     * @throws IllegalArgumentException if the table's name/alias is not specified, but the query has more than one named source
      */
     public QueryBuilder select( String... columnNames ) {
-        columns.clear();
         for (String expression : columnNames) {
             columns.add(column(expression));
         }
@@ -406,7 +415,7 @@
      * 
      * @param columnNames the column expressions; may not be null
      * @return this builder object, for convenience in method chaining
-     * @throws InvalidQueryException if the table's name/alias is not specified, but the query has more than one named source
+     * @throws IllegalArgumentException if the table's name/alias is not specified, but the query has more than one named source
      */
     public QueryBuilder selectDistinct( String... columnNames ) {
         distinct = true;
@@ -549,6 +558,73 @@
     }
 
     /**
+     * Perform an inner join between the already defined source with the "__ALLNODES__" table using the supplied alias.
+     * 
+     * @param alias the alias for the "__ALL_NODES" table; may not be null
+     * @return the component that must be used to complete the join specification; never null
+     */
+    public JoinClause joinAllNodesAs( String alias ) {
+        return innerJoinAllNodesAs(alias);
+    }
+
+    /**
+     * Perform an inner join between the already defined source with the "__ALL_NODES" table using the supplied alias.
+     * 
+     * @param alias the alias for the "__ALL_NODES" table; may not be null
+     * @return the component that must be used to complete the join specification; never null
+     */
+    public JoinClause innerJoinAllNodesAs( String alias ) {
+        // Expect there to be a source already ...
+        return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.INNER);
+    }
+
+    /**
+     * Perform a cross join between the already defined source with the "__ALL_NODES" table using the supplied alias. Cross joins
+     * have a higher precedent than other join types, so if this is called after another join was defined, the resulting cross
+     * join will be between the previous join's right-hand side and the supplied table.
+     * 
+     * @param alias the alias for the "__ALL_NODES" table; may not be null
+     * @return the component that must be used to complete the join specification; never null
+     */
+    public JoinClause crossJoinAllNodesAs( String alias ) {
+        // Expect there to be a source already ...
+        return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.CROSS);
+    }
+
+    /**
+     * Perform a full outer join between the already defined source with the "__ALL_NODES" table using the supplied alias.
+     * 
+     * @param alias the alias for the "__ALL_NODES" table; may not be null
+     * @return the component that must be used to complete the join specification; never null
+     */
+    public JoinClause fullOuterJoinAllNodesAs( String alias ) {
+        // Expect there to be a source already ...
+        return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.FULL_OUTER);
+    }
+
+    /**
+     * Perform a left outer join between the already defined source with the "__ALL_NODES" table using the supplied alias.
+     * 
+     * @param alias the alias for the "__ALL_NODES" table; may not be null
+     * @return the component that must be used to complete the join specification; never null
+     */
+    public JoinClause leftOuterJoinAllNodesAs( String alias ) {
+        // Expect there to be a source already ...
+        return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.LEFT_OUTER);
+    }
+
+    /**
+     * Perform a right outer join between the already defined source with the "__ALL_NODES" table using the supplied alias.
+     * 
+     * @param alias the alias for the "__ALL_NODES" table; may not be null
+     * @return the component that must be used to complete the join specification; never null
+     */
+    public JoinClause rightOuterJoinAllNodesAs( String alias ) {
+        // Expect there to be a source already ...
+        return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.RIGHT_OUTER);
+    }
+
+    /**
      * Specify the maximum number of rows that are to be returned in the results. By default there is no limit.
      * 
      * @param rowLimit the maximum number of rows
@@ -703,7 +779,7 @@
          * 
          * @param tableName the table name
          * @return the selector name matching the supplied table name; never null
-         * @throws InvalidQueryException if the table name could not be resolved
+         * @throws IllegalArgumentException if the table name could not be resolved
          */
         protected SelectorName nameOf( String tableName ) {
             final SelectorName name = new SelectorName(tableName);
@@ -723,7 +799,7 @@
                 }
             });
             if (notFound.get()) {
-                throw new InvalidQueryException("Expected \"" + tableName + "\" to be a valid table name or alias");
+                throw new IllegalArgumentException("Expected \"" + tableName + "\" to be a valid table name or alias");
             }
             return name;
         }
@@ -734,12 +810,13 @@
          * 
          * @param columnEqualExpression the equality expression between the two tables; may not be null
          * @return the query builder instance, for method chaining purposes
+         * @throws IllegalArgumentException if the supplied expression is not an equality expression
          */
         public QueryBuilder on( String columnEqualExpression ) {
             String[] parts = columnEqualExpression.split("=");
             if (parts.length != 2) {
-                throw new InvalidQueryException("Expected equality expression for columns, but found \"" + columnEqualExpression
-                                                + "\"");
+                throw new IllegalArgumentException("Expected equality expression for columns, but found \""
+                                                   + columnEqualExpression + "\"");
             }
             return createJoin(new EquiJoinCondition(column(parts[0]), column(parts[1])));
         }
@@ -780,7 +857,7 @@
          */
         public QueryBuilder onChildNode( String parentTable,
                                          String childTable ) {
-            return createJoin(new DescendantNodeJoinCondition(nameOf(parentTable), nameOf(childTable)));
+            return createJoin(new ChildNodeJoinCondition(nameOf(parentTable), nameOf(childTable)));
         }
 
         protected QueryBuilder createJoin( JoinCondition condition ) {
@@ -836,16 +913,34 @@
         public ComparisonBuilder fullTextSearchScore( String table );
 
         /**
-         * Constrains the nodes in the the supplied table such that they must have a matching node local name.
+         * Constrains the nodes in the the supplied table based upon criteria on the node's depth.
          * 
          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
          *        FROM clause
          * @return the interface for completing the value portion of the criteria specification; never null
          */
+        public ComparisonBuilder depth( String table );
+
+        /**
+         * Constrains the nodes in the the supplied table based upon criteria on the node's path.
+         * 
+         * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
+         *        FROM clause
+         * @return the interface for completing the value portion of the criteria specification; never null
+         */
+        public ComparisonBuilder path( String table );
+
+        /**
+         * Constrains the nodes in the the supplied table based upon criteria on the node's local name.
+         * 
+         * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
+         *        FROM clause
+         * @return the interface for completing the value portion of the criteria specification; never null
+         */
         public ComparisonBuilder nodeLocalName( String table );
 
         /**
-         * Constrains the nodes in the the supplied table such that they must have a matching node name.
+         * Constrains the nodes in the the supplied table based upon criteria on the node's name.
          * 
          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
          *        FROM clause
@@ -987,8 +1082,7 @@
          */
         public ConstraintBuilder isSameNode( String table,
                                              String asNodeAtPath ) {
-            if (constraint != null) throw new InvalidQueryException("Existing constraint; call in proper order");
-            return setConstraint(new SameNode(selector(table), path(asNodeAtPath)));
+            return setConstraint(new SameNode(selector(table), QueryBuilder.this.path(asNodeAtPath)));
         }
 
         /**
@@ -1001,8 +1095,7 @@
          */
         public ConstraintBuilder isChild( String childTable,
                                           String parentPath ) {
-            if (constraint != null) throw new InvalidQueryException("Existing constraint; call in proper order");
-            return setConstraint(new ChildNode(selector(childTable), path(parentPath)));
+            return setConstraint(new ChildNode(selector(childTable), QueryBuilder.this.path(parentPath)));
         }
 
         /**
@@ -1015,12 +1108,11 @@
          */
         public ConstraintBuilder isBelowPath( String descendantTable,
                                               String ancestorPath ) {
-            if (constraint != null) throw new InvalidQueryException("Existing constraint; call in proper order");
-            return setConstraint(new DescendantNode(selector(descendantTable), path(ancestorPath)));
+            return setConstraint(new DescendantNode(selector(descendantTable), QueryBuilder.this.path(ancestorPath)));
         }
 
         /**
-         * Define a constraint clause that the node within the named table has at least one value for the named property. path.
+         * Define a constraint clause that the node within the named table has at least one value for the named property.
          * 
          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
          *        FROM clause
@@ -1029,7 +1121,6 @@
          */
         public ConstraintBuilder hasProperty( String table,
                                               String propertyName ) {
-            if (constraint != null) throw new InvalidQueryException("Existing constraint; call in proper order");
             return setConstraint(new PropertyExistence(selector(table), name(propertyName)));
         }
 
@@ -1044,7 +1135,6 @@
          */
         public ConstraintBuilder search( String table,
                                          String searchExpression ) {
-            if (constraint != null) throw new InvalidQueryException("Existing constraint; call in proper order");
             return setConstraint(new FullTextSearch(selector(table), searchExpression));
         }
 
@@ -1061,7 +1151,6 @@
         public ConstraintBuilder search( String table,
                                          String propertyName,
                                          String searchExpression ) {
-            if (constraint != null) throw new InvalidQueryException("Existing constraint; call in proper order");
             return setConstraint(new FullTextSearch(selector(table), name(propertyName), searchExpression));
         }
 
@@ -1097,6 +1186,24 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#depth(java.lang.String)
+         */
+        public ComparisonBuilder depth( String table ) {
+            return new ComparisonBuilder(this, new NodeDepth(selector(table)));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#path(java.lang.String)
+         */
+        public ComparisonBuilder path( String table ) {
+            return new ComparisonBuilder(this, new NodePath(selector(table)));
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#nodeLocalName(String)
          */
         public ComparisonBuilder nodeLocalName( String table ) {
@@ -1179,6 +1286,400 @@
         }
     }
 
+    public class CastAs {
+        private final RightHandSide rhs;
+        private final Object value;
+
+        protected CastAs( RightHandSide rhs,
+                          Object value ) {
+            this.rhs = rhs;
+            this.value = value;
+        }
+
+        private ValueFactories factories() {
+            return QueryBuilder.this.context.getValueFactories();
+        }
+
+        /**
+         * Define the right-hand side literal value cast as the specified type.
+         * 
+         * @param type the property type; may not be null
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder as( PropertyType type ) {
+            return rhs.comparisonBuilder.is(rhs.operator, factories().getValueFactory(type).create(value));
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#STRING}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asString() {
+            return as(PropertyType.STRING);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#BOOLEAN}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asBoolean() {
+            return as(PropertyType.BOOLEAN);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#LONG}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asLong() {
+            return as(PropertyType.LONG);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#DOUBLE}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asDouble() {
+            return as(PropertyType.DOUBLE);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#DECIMAL}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asDecimal() {
+            return as(PropertyType.DECIMAL);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#DATE}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asDate() {
+            return as(PropertyType.DATE);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#NAME}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asName() {
+            return as(PropertyType.NAME);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#PATH}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asPath() {
+            return as(PropertyType.PATH);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#BINARY}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asBinary() {
+            return as(PropertyType.BINARY);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#REFERENCE}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asReference() {
+            return as(PropertyType.REFERENCE);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#URI}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asUri() {
+            return as(PropertyType.URI);
+        }
+
+        /**
+         * Define the right-hand side literal value cast as a {@link PropertyType#UUID}.
+         * 
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder asUuid() {
+            return as(PropertyType.UUID);
+        }
+    }
+
+    public class RightHandSide {
+        protected final Operator operator;
+        protected final ComparisonBuilder comparisonBuilder;
+
+        protected RightHandSide( ComparisonBuilder comparisonBuilder,
+                                 Operator operator ) {
+            this.operator = operator;
+            this.comparisonBuilder = comparisonBuilder;
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( String literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( int literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( long literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( float literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( double literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( DateTime literal ) {
+            return comparisonBuilder.is(operator, literal.toUtcTimeZone());
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( Path literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( Name literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( URI literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( UUID literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( Binary literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( BigDecimal literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value;
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder literal( boolean literal ) {
+            return comparisonBuilder.is(operator, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param variableName the name of the variable
+         * @return the constraint builder; never null
+         */
+        public ConstraintBuilder variable( String variableName ) {
+            return comparisonBuilder.is(operator, variableName);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( int literal ) {
+            return new CastAs(this, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( String literal ) {
+            return new CastAs(this, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( boolean literal ) {
+            return new CastAs(this, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( long literal ) {
+            return new CastAs(this, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( double literal ) {
+            return new CastAs(this, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( BigDecimal literal ) {
+            return new CastAs(this, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( DateTime literal ) {
+            return new CastAs(this, literal.toUtcTimeZone());
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( Name literal ) {
+            return new CastAs(this, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( Path literal ) {
+            return new CastAs(this, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( UUID literal ) {
+            return new CastAs(this, literal);
+        }
+
+        /**
+         * Define the right-hand side of a comparison.
+         * 
+         * @param literal the literal value that is to be cast
+         * @return the constraint builder; never null
+         */
+        public CastAs cast( URI literal ) {
+            return new CastAs(this, literal);
+        }
+    }
+
     /**
      * An interface used to set the right-hand side of a constraint.
      */
@@ -1192,14 +1693,112 @@
             this.constraintBuilder = constraintBuilder;
         }
 
-        protected ConstraintBuilder isVariable( Operator operator,
-                                                String variableName ) {
-            assert operator != null;
+        /**
+         * Define the operator that will be used in the comparison, returning an interface that can be used to define the
+         * right-hand-side of the comparison.
+         * 
+         * @param operator the operator; may not be null
+         * @return the interface used to define the right-hand-side of the comparison
+         */
+        public RightHandSide is( Operator operator ) {
+            CheckArg.isNotNull(operator, "operator");
+            return new RightHandSide(this, operator);
+        }
+
+        /**
+         * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
+         * the comparison.
+         * 
+         * @return the interface used to define the right-hand-side of the comparison
+         */
+        public RightHandSide isEqualTo() {
+            return is(Operator.EQUAL_TO);
+        }
+
+        /**
+         * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
+         * the comparison.
+         * 
+         * @return the interface used to define the right-hand-side of the comparison
+         */
+        public RightHandSide isNotEqualTo() {
+            return is(Operator.NOT_EQUAL_TO);
+        }
+
+        /**
+         * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
+         * the comparison.
+         * 
+         * @return the interface used to define the right-hand-side of the comparison
+         */
+        public RightHandSide isGreaterThan() {
+            return is(Operator.GREATER_THAN);
+        }
+
+        /**
+         * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
+         * the comparison.
+         * 
+         * @return the interface used to define the right-hand-side of the comparison
+         */
+        public RightHandSide isGreaterThanOrEqualTo() {
+            return is(Operator.GREATER_THAN_OR_EQUAL_TO);
+        }
+
+        /**
+         * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
+         * the comparison.
+         * 
+         * @return the interface used to define the right-hand-side of the comparison
+         */
+        public RightHandSide isLessThan() {
+            return is(Operator.LESS_THAN);
+        }
+
+        /**
+         * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
+         * the comparison.
+         * 
+         * @return the interface used to define the right-hand-side of the comparison
+         */
+        public RightHandSide isLessThanOrEqualTo() {
+            return is(Operator.LESS_THAN_OR_EQUAL_TO);
+        }
+
+        /**
+         * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
+         * the comparison.
+         * 
+         * @return the interface used to define the right-hand-side of the comparison
+         */
+        public RightHandSide isLike() {
+            return is(Operator.LIKE);
+        }
+
+        /**
+         * Define the right-hand-side of the constraint using the supplied operator.
+         * 
+         * @param operator the operator; may not be null
+         * @param variableName the name of the variable
+         * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
+         *         complete already-started clauses; never null
+         */
+        public ConstraintBuilder isVariable( Operator operator,
+                                             String variableName ) {
+            CheckArg.isNotNull(operator, "operator");
             return this.constraintBuilder.setConstraint(new Comparison(left, operator, new BindVariableName(variableName)));
         }
 
-        protected ConstraintBuilder is( Operator operator,
-                                        Object literal ) {
+        /**
+         * Define the right-hand-side of the constraint using the supplied operator.
+         * 
+         * @param operator the operator; may not be null
+         * @param literal the literal value
+         * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
+         *         complete already-started clauses; never null
+         */
+        public ConstraintBuilder is( Operator operator,
+                                     Object literal ) {
             assert operator != null;
             return this.constraintBuilder.setConstraint(new Comparison(left, operator, new Literal(literal)));
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -23,11 +23,18 @@
  */
 package org.jboss.dna.graph.query;
 
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.text.ParsingException;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.query.QueryResults.Statistics;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Constraint;
@@ -37,6 +44,8 @@
 import org.jboss.dna.graph.query.model.Visitors;
 import org.jboss.dna.graph.query.optimize.Optimizer;
 import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.parse.InvalidQueryException;
+import org.jboss.dna.graph.query.parse.QueryParser;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
 import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
@@ -58,11 +67,13 @@
     private final Optimizer optimizer;
     private final Processor processor;
     private final Schemata schemata;
+    private final ConcurrentMap<String, QueryParser> parsers = new ConcurrentHashMap<String, QueryParser>();
 
     public QueryEngine( Planner planner,
                         Optimizer optimizer,
                         Processor processor,
-                        Schemata schemata ) {
+                        Schemata schemata,
+                        QueryParser... parsers ) {
         CheckArg.isNotNull(processor, "processor");
         this.planner = planner != null ? planner : new CanonicalPlanner();
         this.optimizer = optimizer != null ? optimizer : new RuleBasedOptimizer();
@@ -74,14 +85,78 @@
                 return null;
             }
         };
+        for (QueryParser parser : parsers) {
+            if (parser != null) addLanguage(parser);
+        }
     }
 
     /**
+     * Add a language to this engine by supplying its parser.
+     * 
+     * @param languageParser the query parser for the language
+     * @throws IllegalArgumentException if the language parser is null
+     */
+    public void addLanguage( QueryParser languageParser ) {
+        CheckArg.isNotNull(languageParser, "languageParser");
+        this.parsers.put(languageParser.getLanguage().toLowerCase(), languageParser);
+    }
+
+    /**
+     * Remove from this engine the language with the given name.
+     * 
+     * @param language the name of the language, which is to match the {@link QueryParser#getLanguage() language} of the parser
+     * @return the parser for the language, or null if the engine had no support for the named language
+     * @throws IllegalArgumentException if the language is null
+     */
+    public QueryParser removeLanguage( String language ) {
+        CheckArg.isNotNull(language, "language");
+        return this.parsers.remove(language.toLowerCase());
+    }
+
+    /**
+     * Get the set of languages that this engine is capable of parsing.
+     * 
+     * @return the unmodifiable copy of the set of languages; never null but possibly empty
+     */
+    public Set<String> getLanguages() {
+        Set<String> result = new HashSet<String>();
+        for (QueryParser parser : parsers.values()) {
+            result.add(parser.getLanguage());
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    /**
      * Execute the supplied query by planning, optimizing, and then processing it.
      * 
      * @param context the context in which the query should be executed
+     * @param language the language in which the query is expressed; must be one of the supported {@link #getLanguages()
+     *        languages}
      * @param query the query that is to be executed
      * @return the query results; never null
+     * @throws IllegalArgumentException if the language, context or query references are null, or if the language is not know
+     * @throws ParsingException if there is an error parsing the supplied query
+     * @throws InvalidQueryException if the supplied query can be parsed but is invalid
+     */
+    public QueryResults execute( ExecutionContext context,
+                                 String language,
+                                 String query ) {
+        CheckArg.isNotNull(language, "language");
+        CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(query, "query");
+        QueryParser parser = parsers.get(language.toLowerCase());
+        if (parser == null) {
+            throw new IllegalArgumentException(GraphI18n.unknownQueryLanguage.text(language));
+        }
+        return execute(context, parser.parseQuery(query, context));
+    }
+
+    /**
+     * Execute the supplied query by planning, optimizing, and then processing it.
+     * 
+     * @param context the context in which the query should be executed
+     * @param query the query that is to be executed
+     * @return the query results; never null
      * @throws IllegalArgumentException if the context or query references are null
      */
     public QueryResults execute( ExecutionContext context,
@@ -101,6 +176,8 @@
     public QueryResults execute( ExecutionContext context,
                                  QueryCommand query,
                                  PlanHints hints ) {
+        CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(query, "query");
         QueryContext queryContext = new QueryContext(context, hints, schemata);
 
         // Create the canonical plan ...

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -65,9 +65,10 @@
     @Override
     public boolean equals( Object obj ) {
         if (obj == this) return true;
-        if (obj instanceof AllNodes) {
-            AllNodes that = (AllNodes)obj;
-            return ObjectUtil.isEqualWithNulls(this.getAlias(), that.getAlias());
+        if (obj instanceof Selector) {
+            Selector that = (Selector)obj;
+            return ObjectUtil.isEqualWithNulls(this.getName(), that.getName())
+                   && ObjectUtil.isEqualWithNulls(this.getAlias(), that.getAlias());
         }
         return false;
     }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -66,7 +66,7 @@
         if (obj == this) return true;
         if (obj instanceof Literal) {
             Literal that = (Literal)obj;
-            return this.value.equals(that.value);
+            return this.value.equals(that.value) || this.value.toString().equals(that.value.toString());
         }
         return false;
     }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -72,8 +72,8 @@
     @Override
     public boolean equals( Object obj ) {
         if (obj == this) return true;
-        if (obj instanceof NamedSelector) {
-            NamedSelector that = (NamedSelector)obj;
+        if (obj instanceof Selector) {
+            Selector that = (Selector)obj;
             if (!this.getName().equals(that.getName())) return false;
             if (!ObjectUtil.isEqualWithNulls(this.getAlias(), that.getAlias())) return false;
             return true;

Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java (from rev 1285, trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -0,0 +1,90 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.model;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A dynamic operand that evaluates to the depth of a node given by a selector, used in a {@link Comparison} constraint.
+ */
+ at Immutable
+public class NodeDepth extends DynamicOperand {
+    private final SelectorName selectorName;
+
+    /**
+     * Create a dynamic operand that evaluates to the depth of the node identified by the selector.
+     * 
+     * @param selectorName the name of the selector
+     * @throws IllegalArgumentException if the selector name or property name are null
+     */
+    public NodeDepth( SelectorName selectorName ) {
+        CheckArg.isNotNull(selectorName, "selectorName");
+        this.selectorName = selectorName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.DynamicOperand#getSelectorName()
+     */
+    @Override
+    public final SelectorName getSelectorName() {
+        return selectorName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return Visitors.readable(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof NodeDepth) {
+            NodeDepth that = (NodeDepth)obj;
+            return this.selectorName.equals(that.selectorName);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitable#accept(org.jboss.dna.graph.query.model.Visitor)
+     */
+    public void accept( Visitor visitor ) {
+        visitor.visit(this);
+    }
+}

Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java (from rev 1285, trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -0,0 +1,90 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.model;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A dynamic operand that evaluates to the path of a node given by a selector, used in a {@link Comparison} constraint.
+ */
+ at Immutable
+public class NodePath extends DynamicOperand {
+    private final SelectorName selectorName;
+
+    /**
+     * Create a dynamic operand that evaluates to the path of the node identified by the selector.
+     * 
+     * @param selectorName the name of the selector
+     * @throws IllegalArgumentException if the selector name or property name are null
+     */
+    public NodePath( SelectorName selectorName ) {
+        CheckArg.isNotNull(selectorName, "selectorName");
+        this.selectorName = selectorName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.DynamicOperand#getSelectorName()
+     */
+    @Override
+    public final SelectorName getSelectorName() {
+        return selectorName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return Visitors.readable(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof NodePath) {
+            NodePath that = (NodePath)obj;
+            return this.selectorName.equals(that.selectorName);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitable#accept(org.jboss.dna.graph.query.model.Visitor)
+     */
+    public void accept( Visitor visitor ) {
+        visitor.visit(this);
+    }
+}

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -64,6 +64,29 @@
     }
 
     /**
+     * Get the equivalent operator if the operands are to be reversed.
+     * 
+     * @return the reverse operator; never null
+     */
+    public Operator getReverse() {
+        switch (this) {
+            case GREATER_THAN:
+                return LESS_THAN;
+            case GREATER_THAN_OR_EQUAL_TO:
+                return LESS_THAN_OR_EQUAL_TO;
+            case LESS_THAN:
+                return GREATER_THAN;
+            case LESS_THAN_OR_EQUAL_TO:
+                return GREATER_THAN_OR_EQUAL_TO;
+            case EQUAL_TO:
+            case LIKE:
+            case NOT_EQUAL_TO:
+            default:
+                return this;
+        }
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see java.lang.Enum#toString()

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -66,6 +66,10 @@
 
     void visit( NodeName obj );
 
+    void visit( NodePath obj );
+
+    void visit( NodeDepth obj );
+
     void visit( NamedSelector obj );
 
     void visit( Not obj );

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -154,6 +154,16 @@
             }
 
             @Override
+            public void visit( NodeDepth depth ) {
+                super.visit(depth);
+            }
+
+            @Override
+            public void visit( NodePath path ) {
+                super.visit(path);
+            }
+
+            @Override
             public void visit( NodeLocalName node ) {
                 symbols.add(node.getSelectorName());
             }
@@ -348,6 +358,22 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeDepth)
+         */
+        public void visit( NodeDepth obj ) {
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodePath)
+         */
+        public void visit( NodePath obj ) {
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeName)
          */
         public void visit( NodeName obj ) {
@@ -689,6 +715,26 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeDepth)
+         */
+        public void visit( NodeDepth depth ) {
+            strategy.visit(depth);
+            visitNext();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodePath)
+         */
+        public void visit( NodePath path ) {
+            strategy.visit(path);
+            visitNext();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeName)
          */
         public void visit( NodeName nodeName ) {
@@ -1098,8 +1144,15 @@
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Literal)
          */
         public void visit( Literal literal ) {
-            if (context == null) append(literal.getValue().toString());
-            else append(context.getValueFactories().getStringFactory().create(literal.getValue()));
+            Object value = literal.getValue();
+            boolean quote = value instanceof String || value instanceof Path || value instanceof Name;
+            if (quote) append('\'');
+            if (context == null) {
+                append(literal.getValue().toString());
+            } else {
+                append(context.getValueFactories().getStringFactory().create(literal.getValue()));
+            }
+            if (quote) append('\'');
         }
 
         /**
@@ -1116,6 +1169,24 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeDepth)
+         */
+        public void visit( NodeDepth depth ) {
+            append("DEPTH(").append(depth.getSelectorName()).append(')');
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodePath)
+         */
+        public void visit( NodePath path ) {
+            append("PATH(").append(path.getSelectorName()).append(')');
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeLocalName)
          */
         public void visit( NodeLocalName name ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -49,8 +49,10 @@
 import org.jboss.dna.graph.query.model.JoinCondition;
 import org.jboss.dna.graph.query.model.Length;
 import org.jboss.dna.graph.query.model.LowerCase;
+import org.jboss.dna.graph.query.model.NodeDepth;
 import org.jboss.dna.graph.query.model.NodeLocalName;
 import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
 import org.jboss.dna.graph.query.model.Not;
 import org.jboss.dna.graph.query.model.Or;
 import org.jboss.dna.graph.query.model.Ordering;
@@ -338,8 +340,20 @@
             PropertyValue value = (PropertyValue)operand;
             SelectorName replacement = rewrittenSelectors.get(value.getSelectorName());
             if (replacement == null) return operand;
-            return new Length(new PropertyValue(replacement, value.getPropertyName()));
+            return new PropertyValue(replacement, value.getPropertyName());
         }
+        if (operand instanceof NodeDepth) {
+            NodeDepth depth = (NodeDepth)operand;
+            SelectorName replacement = rewrittenSelectors.get(depth.getSelectorName());
+            if (replacement == null) return operand;
+            return new NodeDepth(replacement);
+        }
+        if (operand instanceof NodePath) {
+            NodePath path = (NodePath)operand;
+            SelectorName replacement = rewrittenSelectors.get(path.getSelectorName());
+            if (replacement == null) return operand;
+            return new NodePath(replacement);
+        }
         return operand;
     }
 

Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java (from rev 1285, trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/InvalidQueryException.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -0,0 +1,71 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.parse;
+
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * An exception signalling that an XPath query is invalid (but typically well-formed)
+ */
+public class InvalidQueryException extends RuntimeException {
+
+    /**
+     */
+    private static final long serialVersionUID = 1L;
+
+    private final String xpath;
+
+    /**
+     * Create an exception with the invalid query.
+     * 
+     * @param xpath the XPath query that is invalid
+     */
+    public InvalidQueryException( String xpath ) {
+        super();
+        this.xpath = xpath;
+    }
+
+    /**
+     * Create an exception with the invalid query and a message.
+     * 
+     * @param xpath the XPath query that is invalid
+     * @param message
+     */
+    public InvalidQueryException( String xpath,
+                                  String message ) {
+        super(message);
+        CheckArg.isNotNull(xpath, "xpath");
+        this.xpath = xpath;
+    }
+
+    /**
+     * Get the XPath query that is invalid.
+     * 
+     * @return the query
+     */
+    public String getXPath() {
+        return xpath;
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -23,22 +23,34 @@
  */
 package org.jboss.dna.graph.query.parse;
 
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.text.ParsingException;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
 
 /**
  * The basic interface defining a component that is able to parse a string query into a {@link QueryCommand}.
  */
+ at Immutable
 public interface QueryParser {
 
     /**
+     * Get the name of the language that this parser is able to understand.
+     * 
+     * @return the language name; never null
+     */
+    String getLanguage();
+
+    /**
      * Parse the supplied query from a string representation into a {@link QueryCommand}.
      * 
      * @param query the query in string form; may not be null
      * @param context the context in which the query is being parsed
      * @return the query command
+     * @throws ParsingException if there is an error parsing the supplied query
+     * @throws InvalidQueryException if the supplied query can be parsed but is invalid
      */
     QueryCommand parseQuery( String query,
-                             ExecutionContext context );
+                             ExecutionContext context ) throws InvalidQueryException;
 
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -67,8 +67,10 @@
 import org.jboss.dna.graph.query.model.Literal;
 import org.jboss.dna.graph.query.model.LowerCase;
 import org.jboss.dna.graph.query.model.NamedSelector;
+import org.jboss.dna.graph.query.model.NodeDepth;
 import org.jboss.dna.graph.query.model.NodeLocalName;
 import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
 import org.jboss.dna.graph.query.model.Not;
 import org.jboss.dna.graph.query.model.Operator;
 import org.jboss.dna.graph.query.model.Or;
@@ -102,9 +104,20 @@
  */
 public class SqlQueryParser implements QueryParser {
 
+    public static final String LANGUAGE = "SQL";
+
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.query.parse.QueryParser#getLanguage()
+     */
+    public String getLanguage() {
+        return LANGUAGE;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(String, ExecutionContext)
      */
     public QueryCommand parseQuery( String query,
@@ -188,12 +201,12 @@
         List<ColumnExpression> columns = new ArrayList<ColumnExpression>();
         do {
             Position position = tokens.nextPosition();
-            String propertyName = tokens.consume();
+            String propertyName = removeBracketsAndQuotes(tokens.consume());
             SelectorName selectorName = null;
             if (tokens.canConsume('.')) {
                 // We actually read the selector name, so now read the property name ...
                 selectorName = new SelectorName(propertyName);
-                propertyName = tokens.consume();
+                propertyName = removeBracketsAndQuotes(tokens.consume());
             }
             String alias = propertyName;
             if (tokens.canConsume("AS")) alias = removeBracketsAndQuotes(tokens.consume());
@@ -304,7 +317,7 @@
                 propertyName = parseName(tokens, context);
             } else {
                 if (!(source instanceof Selector)) {
-                    String msg = GraphI18n.functionIsAmbiguous.text("CONTEXT()", pos.getLine(), pos.getColumn());
+                    String msg = GraphI18n.functionIsAmbiguous.text("CONTAINS()", pos.getLine(), pos.getColumn());
                     throw new ParsingException(pos, msg);
                 }
                 selectorName = ((Selector)source).getName();
@@ -681,6 +694,26 @@
             }
             result = new FullTextSearchScore(parseSelectorName(tokens));
             tokens.consume(")");
+        } else if (tokens.canConsume("DEPTH", "(")) {
+            if (tokens.canConsume(")")) {
+                if (source instanceof Selector) {
+                    return new NodeDepth(((Selector)source).getName());
+                }
+                String msg = GraphI18n.functionIsAmbiguous.text("DEPTH()", pos.getLine(), pos.getColumn());
+                throw new ParsingException(pos, msg);
+            }
+            result = new NodeDepth(parseSelectorName(tokens));
+            tokens.consume(")");
+        } else if (tokens.canConsume("PATH", "(")) {
+            if (tokens.canConsume(")")) {
+                if (source instanceof Selector) {
+                    return new NodePath(((Selector)source).getName());
+                }
+                String msg = GraphI18n.functionIsAmbiguous.text("PATH()", pos.getLine(), pos.getColumn());
+                throw new ParsingException(pos, msg);
+            }
+            result = new NodePath(parseSelectorName(tokens));
+            tokens.consume(")");
         } else {
             result = parsePropertyValue(tokens, context, source);
         }
@@ -743,7 +776,19 @@
      *         quotes
      */
     protected String removeBracketsAndQuotes( String text ) {
-        return text.replaceFirst("^['\"\\[]+", "").replaceAll("['\"\\]]+$", "");
+        if (text.length() > 0) {
+            char firstChar = text.charAt(0);
+            switch (firstChar) {
+                case '\'':
+                case '"':
+                    assert text.charAt(text.length() - 1) == firstChar;
+                    return removeBracketsAndQuotes(text.substring(1, text.length() - 1));
+                case '[':
+                    assert text.charAt(text.length() - 1) == ']';
+                    return removeBracketsAndQuotes(text.substring(1, text.length() - 1));
+            }
+        }
+        return text;
     }
 
     protected NamedSelector parseNamedSelector( TokenStream tokens ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -40,8 +40,10 @@
 import org.jboss.dna.graph.query.model.FullTextSearchScore;
 import org.jboss.dna.graph.query.model.Length;
 import org.jboss.dna.graph.query.model.LowerCase;
+import org.jboss.dna.graph.query.model.NodeDepth;
 import org.jboss.dna.graph.query.model.NodeLocalName;
 import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
 import org.jboss.dna.graph.query.model.PropertyValue;
 import org.jboss.dna.graph.query.model.UpperCase;
 import org.jboss.dna.graph.query.validate.Schemata.Column;
@@ -219,6 +221,40 @@
                 }
             };
         }
+        if (operand instanceof NodeDepth) {
+            NodeDepth nodeDepth = (NodeDepth)operand;
+            final int locationIndex = columns.getLocationIndex(nodeDepth.getSelectorName().getName());
+            return new DynamicOperation() {
+                public PropertyType getExpectedType() {
+                    return PropertyType.LONG;
+                }
+
+                public Object evaluate( Object[] tuple ) {
+                    Location location = (Location)tuple[locationIndex];
+                    if (location == null) return null;
+                    Path path = location.getPath();
+                    assert path != null;
+                    return new Long(path.size());
+                }
+            };
+        }
+        if (operand instanceof NodePath) {
+            NodePath nodePath = (NodePath)operand;
+            final int locationIndex = columns.getLocationIndex(nodePath.getSelectorName().getName());
+            return new DynamicOperation() {
+                public PropertyType getExpectedType() {
+                    return PropertyType.PATH;
+                }
+
+                public Object evaluate( Object[] tuple ) {
+                    Location location = (Location)tuple[locationIndex];
+                    if (location == null) return null;
+                    Path path = location.getPath();
+                    assert path != null;
+                    return path;
+                }
+            };
+        }
         if (operand instanceof NodeName) {
             NodeName nodeName = (NodeName)operand;
             final int locationIndex = columns.getLocationIndex(nodeName.getSelectorName().getName());

Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-10-03 04:09:29 UTC (rev 1286)
@@ -116,6 +116,7 @@
 childNotFound = Child "{0}" could not be found under "{1}" in workspace "{2}" 
 
 # Query
+unknownQueryLanguage = '{0}' is not a known query language
 tableDoesNotExist = Table '{0}' does not exist
 columnDoesNotExistOnTable = Column '{0}' does not exist on the table '{1}'
 columnDoesNotExistInQuery = Column '{0}' does not exist in query

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -433,7 +433,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LENGTH(nodes.property) = literal"));
+                                "WHERE LENGTH(nodes.property) = 'literal'"));
     }
 
     @Test
@@ -459,7 +459,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LENGTH(nodes.property) != literal"));
+                                "WHERE LENGTH(nodes.property) != 'literal'"));
     }
 
     @Test
@@ -485,7 +485,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LENGTH(nodes.property) < literal"));
+                                "WHERE LENGTH(nodes.property) < 'literal'"));
     }
 
     @Test
@@ -511,7 +511,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LENGTH(nodes.property) <= literal"));
+                                "WHERE LENGTH(nodes.property) <= 'literal'"));
     }
 
     @Test
@@ -537,7 +537,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LENGTH(nodes.property) > literal"));
+                                "WHERE LENGTH(nodes.property) > 'literal'"));
     }
 
     @Test
@@ -563,7 +563,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LENGTH(nodes.property) >= literal"));
+                                "WHERE LENGTH(nodes.property) >= 'literal'"));
     }
 
     @Test
@@ -583,7 +583,7 @@
     public void shouldBuildQueryWithCriteriaUsingLengthLike() {
         query = builder.selectStar().from("table AS nodes").where().length("nodes", "property").isLike("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LENGTH(nodes.property) LIKE literal"));
+                                "WHERE LENGTH(nodes.property) LIKE 'literal'"));
     }
 
     @Test
@@ -600,10 +600,44 @@
     }
 
     @Test
+    public void shouldBuildQueryWithCriteriaUsingNodeDepthEqualToLiteral() {
+        query = builder.selectStar().from("table AS nodes").where().depth("nodes").isEqualTo(3).end().query();
+        assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+                                "WHERE DEPTH(nodes) = 3"));
+    }
+
+    @Test
+    public void shouldBuildQueryWithCriteriaUsingNodeDepthLessThanOrEqualToLongLiteral() {
+        query = builder.selectStar().from("table AS nodes").where().depth("nodes").isLessThanOrEqualTo(3).end().query();
+        assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+                                "WHERE DEPTH(nodes) <= 3"));
+    }
+
+    @Test
+    public void shouldBuildQueryWithCriteriaUsingNodeDepthLessThanOrEqualToStringLiteral() {
+        query = builder.selectStar().from("table AS nodes").where().depth("nodes").isLessThanOrEqualTo(3).end().query();
+        assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+                                "WHERE DEPTH(nodes) <= 3"));
+    }
+
+    @Test
+    public void shouldBuildQueryWithCriteriaUsingNodeDepthLessThanOrEqualToVariable() {
+        query = builder.selectStar()
+                       .from("table AS nodes")
+                       .where()
+                       .depth("nodes")
+                       .isLessThanOrEqualToVariable("value")
+                       .end()
+                       .query();
+        assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+                                "WHERE DEPTH(nodes) <= $value"));
+    }
+
+    @Test
     public void shouldBuildQueryWithCriteriaUsingNodeNameEqualTo() {
         query = builder.selectStar().from("table AS nodes").where().nodeName("nodes").isEqualTo("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE NAME(nodes) = literal"));
+                                "WHERE NAME(nodes) = 'literal'"));
     }
 
     @Test
@@ -617,7 +651,7 @@
     public void shouldBuildQueryWithCriteriaUsingNodeNameNotEqualTo() {
         query = builder.selectStar().from("table AS nodes").where().nodeName("nodes").isNotEqualTo("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE NAME(nodes) != literal"));
+                                "WHERE NAME(nodes) != 'literal'"));
     }
 
     @Test
@@ -637,7 +671,7 @@
     public void shouldBuildQueryWithCriteriaUsingNodeNameLessThan() {
         query = builder.selectStar().from("table AS nodes").where().nodeName("nodes").isLessThan("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE NAME(nodes) < literal"));
+                                "WHERE NAME(nodes) < 'literal'"));
     }
 
     @Test
@@ -657,7 +691,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE NAME(nodes) <= literal"));
+                                "WHERE NAME(nodes) <= 'literal'"));
     }
 
     @Test
@@ -677,7 +711,7 @@
     public void shouldBuildQueryWithCriteriaUsingNodeNameGreaterThan() {
         query = builder.selectStar().from("table AS nodes").where().nodeName("nodes").isGreaterThan("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE NAME(nodes) > literal"));
+                                "WHERE NAME(nodes) > 'literal'"));
     }
 
     @Test
@@ -703,7 +737,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE NAME(nodes) >= literal"));
+                                "WHERE NAME(nodes) >= 'literal'"));
     }
 
     @Test
@@ -723,7 +757,7 @@
     public void shouldBuildQueryWithCriteriaUsingNodeNameLike() {
         query = builder.selectStar().from("table AS nodes").where().nodeName("nodes").isLike("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE NAME(nodes) LIKE literal"));
+                                "WHERE NAME(nodes) LIKE 'literal'"));
     }
 
     @Test
@@ -737,7 +771,7 @@
     public void shouldBuildQueryWithCriteriaUsingNodeLocalNameEqualTo() {
         query = builder.selectStar().from("table AS nodes").where().nodeLocalName("nodes").isEqualTo("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOCALNAME(nodes) = literal"));
+                                "WHERE LOCALNAME(nodes) = 'literal'"));
     }
 
     @Test
@@ -757,7 +791,7 @@
     public void shouldBuildQueryWithCriteriaUsingNodeLocalNameNotEqualTo() {
         query = builder.selectStar().from("table AS nodes").where().nodeLocalName("nodes").isNotEqualTo("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOCALNAME(nodes) != literal"));
+                                "WHERE LOCALNAME(nodes) != 'literal'"));
     }
 
     @Test
@@ -777,7 +811,7 @@
     public void shouldBuildQueryWithCriteriaUsingNodeLocalNameLessThan() {
         query = builder.selectStar().from("table AS nodes").where().nodeLocalName("nodes").isLessThan("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOCALNAME(nodes) < literal"));
+                                "WHERE LOCALNAME(nodes) < 'literal'"));
     }
 
     @Test
@@ -803,7 +837,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOCALNAME(nodes) <= literal"));
+                                "WHERE LOCALNAME(nodes) <= 'literal'"));
     }
 
     @Test
@@ -823,7 +857,7 @@
     public void shouldBuildQueryWithCriteriaUsingNodeLocalNameGreaterThan() {
         query = builder.selectStar().from("table AS nodes").where().nodeLocalName("nodes").isGreaterThan("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOCALNAME(nodes) > literal"));
+                                "WHERE LOCALNAME(nodes) > 'literal'"));
     }
 
     @Test
@@ -849,7 +883,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOCALNAME(nodes) >= literal"));
+                                "WHERE LOCALNAME(nodes) >= 'literal'"));
     }
 
     @Test
@@ -869,7 +903,7 @@
     public void shouldBuildQueryWithCriteriaUsingNodeLocalNameLike() {
         query = builder.selectStar().from("table AS nodes").where().nodeLocalName("nodes").isLike("literal").end().query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOCALNAME(nodes) LIKE literal"));
+                                "WHERE LOCALNAME(nodes) LIKE 'literal'"));
     }
 
     @Test
@@ -896,7 +930,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE UPPER(NAME(nodes)) = literal"));
+                                "WHERE UPPER(NAME(nodes)) = 'literal'"));
     }
 
     @Test
@@ -924,7 +958,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE UPPER(NAME(nodes)) != literal"));
+                                "WHERE UPPER(NAME(nodes)) != 'literal'"));
     }
 
     @Test
@@ -952,7 +986,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE UPPER(NAME(nodes)) < literal"));
+                                "WHERE UPPER(NAME(nodes)) < 'literal'"));
     }
 
     @Test
@@ -980,7 +1014,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE UPPER(NAME(nodes)) <= literal"));
+                                "WHERE UPPER(NAME(nodes)) <= 'literal'"));
     }
 
     @Test
@@ -1008,7 +1042,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE UPPER(NAME(nodes)) > literal"));
+                                "WHERE UPPER(NAME(nodes)) > 'literal'"));
     }
 
     @Test
@@ -1036,7 +1070,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE UPPER(NAME(nodes)) >= literal"));
+                                "WHERE UPPER(NAME(nodes)) >= 'literal'"));
     }
 
     @Test
@@ -1064,7 +1098,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE UPPER(NAME(nodes)) LIKE literal"));
+                                "WHERE UPPER(NAME(nodes)) LIKE 'literal'"));
     }
 
     @Test
@@ -1092,7 +1126,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOWER(NAME(nodes)) = literal"));
+                                "WHERE LOWER(NAME(nodes)) = 'literal'"));
     }
 
     @Test
@@ -1120,7 +1154,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOWER(NAME(nodes)) != literal"));
+                                "WHERE LOWER(NAME(nodes)) != 'literal'"));
     }
 
     @Test
@@ -1148,7 +1182,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOWER(NAME(nodes)) < literal"));
+                                "WHERE LOWER(NAME(nodes)) < 'literal'"));
     }
 
     @Test
@@ -1176,7 +1210,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOWER(NAME(nodes)) <= literal"));
+                                "WHERE LOWER(NAME(nodes)) <= 'literal'"));
     }
 
     @Test
@@ -1204,7 +1238,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOWER(NAME(nodes)) > literal"));
+                                "WHERE LOWER(NAME(nodes)) > 'literal'"));
     }
 
     @Test
@@ -1232,7 +1266,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOWER(NAME(nodes)) >= literal"));
+                                "WHERE LOWER(NAME(nodes)) >= 'literal'"));
     }
 
     @Test
@@ -1260,7 +1294,7 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOWER(NAME(nodes)) LIKE literal"));
+                                "WHERE LOWER(NAME(nodes)) LIKE 'literal'"));
     }
 
     @Test
@@ -1289,6 +1323,6 @@
                        .end()
                        .query();
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
-                                "WHERE LOWER(UPPER(NAME(nodes))) = literal"));
+                                "WHERE LOWER(UPPER(NAME(nodes))) = 'literal'"));
     }
 }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -52,8 +52,10 @@
 import org.jboss.dna.graph.query.model.Literal;
 import org.jboss.dna.graph.query.model.LowerCase;
 import org.jboss.dna.graph.query.model.NamedSelector;
+import org.jboss.dna.graph.query.model.NodeDepth;
 import org.jboss.dna.graph.query.model.NodeLocalName;
 import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
 import org.jboss.dna.graph.query.model.Not;
 import org.jboss.dna.graph.query.model.Operator;
 import org.jboss.dna.graph.query.model.Or;
@@ -1229,6 +1231,88 @@
     }
 
     // ----------------------------------------------------------------------------------------------------------------
+    // parseDynamicOperand - DEPTH
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseDynamicOperandFromStringContainingDepthOfSelector() {
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("DEPTH(tableA)"), context, mock(Source.class));
+        assertThat(operand, is(instanceOf(NodeDepth.class)));
+        NodeDepth depth = (NodeDepth)operand;
+        assertThat(depth.getSelectorName(), is(selectorName("tableA")));
+    }
+
+    @Test
+    public void shouldParseDynamicOperandFromStringContainingDepthWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
+        Source source = new NamedSelector(selectorName("tableA"));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("DEPTH()"), context, source);
+        assertThat(operand, is(instanceOf(NodeDepth.class)));
+        NodeDepth depth = (NodeDepth)operand;
+        assertThat(depth.getSelectorName(), is(selectorName("tableA")));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseDynamicOperandFromStringContainingDepthWithNoSelectorIfTheSourceIsNotASelector() {
+        parser.parseDynamicOperand(tokens("DEPTH()"), context, mock(Source.class));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseDynamicOperandFromStringContainingDepthWithSelectorNameAndProperty() {
+        parser.parseDynamicOperand(tokens("DEPTH(tableA.property) other"), context, mock(Source.class));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseDynamicOperandFromStringContainingDepthWithoutClosingParenthesis() {
+        parser.parseDynamicOperand(tokens("DEPTH(tableA other"), context, mock(Source.class));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseDynamicOperandFromStringContainingDepthWithoutOpeningParenthesis() {
+        parser.parseDynamicOperand(tokens("Depth  tableA other"), context, mock(Source.class));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // parseDynamicOperand - PATH
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseDynamicOperandFromStringContainingPathOfSelector() {
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("PATH(tableA)"), context, mock(Source.class));
+        assertThat(operand, is(instanceOf(NodePath.class)));
+        NodePath path = (NodePath)operand;
+        assertThat(path.getSelectorName(), is(selectorName("tableA")));
+    }
+
+    @Test
+    public void shouldParseDynamicOperandFromStringContainingPathWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
+        Source source = new NamedSelector(selectorName("tableA"));
+        DynamicOperand operand = parser.parseDynamicOperand(tokens("PATH()"), context, source);
+        assertThat(operand, is(instanceOf(NodePath.class)));
+        NodePath path = (NodePath)operand;
+        assertThat(path.getSelectorName(), is(selectorName("tableA")));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseDynamicOperandFromStringContainingPathWithNoSelectorIfTheSourceIsNotASelector() {
+        parser.parseDynamicOperand(tokens("PATH()"), context, mock(Source.class));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseDynamicOperandFromStringContainingPathWithSelectorNameAndProperty() {
+        parser.parseDynamicOperand(tokens("PATH(tableA.property) other"), context, mock(Source.class));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseDynamicOperandFromStringContainingPathWithoutClosingParenthesis() {
+        parser.parseDynamicOperand(tokens("PATH(tableA other"), context, mock(Source.class));
+    }
+
+    @Test( expected = ParsingException.class )
+    public void shouldFailToParseDynamicOperandFromStringContainingPathWithoutOpeningParenthesis() {
+        parser.parseDynamicOperand(tokens("Path  tableA other"), context, mock(Source.class));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
     // parseDynamicOperand - NAME
     // ----------------------------------------------------------------------------------------------------------------
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -37,13 +37,14 @@
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.FullTextSearchScore;
 import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.NodeDepth;
 import org.jboss.dna.graph.query.model.NodeLocalName;
 import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
 import org.jboss.dna.graph.query.model.Order;
 import org.jboss.dna.graph.query.model.Ordering;
 import org.jboss.dna.graph.query.model.PropertyValue;
 import org.jboss.dna.graph.query.model.SelectorName;
-import org.jboss.dna.graph.query.process.QueryResultColumns;
 import org.jboss.dna.graph.query.validate.ImmutableSchemata;
 import org.jboss.dna.graph.query.validate.Schemata;
 
@@ -209,6 +210,24 @@
         return new Ordering(new Length(new PropertyValue(column.getSelectorName(), column.getPropertyName())), order);
     }
 
+    protected Ordering orderByNodeDepth( String selectorName ) {
+        return orderByNodeDepth(selectorName, Order.ASCENDING);
+    }
+
+    protected Ordering orderByNodeDepth( String selectorName,
+                                         Order order ) {
+        return new Ordering(new NodeDepth(selector(selectorName)), order);
+    }
+
+    protected Ordering orderByNodePath( String selectorName ) {
+        return orderByNodePath(selectorName, Order.ASCENDING);
+    }
+
+    protected Ordering orderByNodePath( String selectorName,
+                                        Order order ) {
+        return new Ordering(new NodePath(selector(selectorName)), order);
+    }
+
     protected Ordering orderByNodeName( String selectorName ) {
         return orderByNodeName(selectorName, Order.ASCENDING);
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -33,8 +33,6 @@
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.Ordering;
 import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-import org.jboss.dna.graph.query.process.SortValuesComponent;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -112,8 +110,40 @@
     }
 
     @Test
+    public void shouldReturnAllResultsOrderedByNodeDepth() {
+        orderings.add(orderByNodeDepth("Selector1"));
+        component = new SortValuesComponent(delegate, orderings);
+        inputTuples.add(tuple(columns, "/a/b1", "v1", 100, "v4"));
+        inputTuples.add(tuple(columns, "/a/b2/c4", "v4", 100, "v3"));
+        inputTuples.add(tuple(columns, "/a", 100, 100, "v2"));
+        inputTuples.add(tuple(columns, "/a/b4/c3", "v3", 100, "v1"));
+        List<Object[]> expected = new ArrayList<Object[]>();
+        expected.add(inputTuples.get(2));
+        expected.add(inputTuples.get(0));
+        expected.add(inputTuples.get(1));
+        expected.add(inputTuples.get(3));
+        assertThat(component.execute(), is(expected));
+    }
+
+    @Test
+    public void shouldReturnAllResultsOrderedByNodePath() {
+        orderings.add(orderByNodePath("Selector1"));
+        component = new SortValuesComponent(delegate, orderings);
+        inputTuples.add(tuple(columns, "/a/b1", "v1", 100, "v4"));
+        inputTuples.add(tuple(columns, "/a/b1/c4[2]", "v4", 100, "v3"));
+        inputTuples.add(tuple(columns, "/a/b1/c2", 100, 100, "v2"));
+        inputTuples.add(tuple(columns, "/a/b1/c4", "v3", 100, "v1"));
+        List<Object[]> expected = new ArrayList<Object[]>();
+        expected.add(inputTuples.get(0));
+        expected.add(inputTuples.get(2));
+        expected.add(inputTuples.get(3));
+        expected.add(inputTuples.get(1));
+        assertThat(component.execute(), is(expected));
+    }
+
+    @Test
     public void shouldReturnAllResultsOrderedByNodeLocalName() {
-        orderings.add(orderByNodeName("Selector1"));
+        orderings.add(orderByNodeLocalName("Selector1"));
         component = new SortValuesComponent(delegate, orderings);
         inputTuples.add(tuple(columns, "/a/b1/c1", "v1", 100, "v4"));
         inputTuples.add(tuple(columns, "/a/b2/c4", "v4", 100, "v3"));

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPath.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -23,13 +23,19 @@
  */
 package org.jboss.dna.jcr.xpath;
 
+import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
 import org.jboss.dna.graph.query.model.Operator;
 
 /**
+ * Abstract syntax components of an XPath query. The supported grammar is defined by JCR 1.0, and is a subset of what is allowed
+ * by the W3C XPath 2.0 specification.
  * 
+ * @see XPathParser#parseXPath(String)
  */
 public class XPath {
 
@@ -39,10 +45,18 @@
         FOLLOWS;
     }
 
-    public static interface Component {
+    public static abstract class Component {
+        /**
+         * Return the collapsable form
+         * 
+         * @return the collapsed form of th is component; never null and possibly the same as this
+         */
+        public Component collapse() {
+            return this;
+        }
     }
 
-    public static abstract class UnaryComponent implements Component {
+    public static abstract class UnaryComponent extends Component {
         protected final Component wrapped;
 
         public UnaryComponent( Component wrapped ) {
@@ -85,7 +99,7 @@
         }
     }
 
-    public static abstract class BinaryComponent implements Component {
+    public static abstract class BinaryComponent extends Component {
         private final Component left;
         private final Component right;
 
@@ -121,8 +135,25 @@
         }
 
         /**
+         * @return operator
+         */
+        public Operator getOperator() {
+            return operator;
+        }
+
+        /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.jcr.xpath.XPath.Component#collapse()
+         */
+        @Override
+        public Component collapse() {
+            return new Comparison(getLeft().collapse(), operator, getRight().collapse());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#toString()
          */
         @Override
@@ -160,6 +191,16 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.jcr.xpath.XPath.Component#collapse()
+         */
+        @Override
+        public Component collapse() {
+            return new NodeComparison(getLeft().collapse(), operator, getRight().collapse());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#toString()
          */
         @Override
@@ -193,6 +234,16 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.jcr.xpath.XPath.Component#collapse()
+         */
+        @Override
+        public Component collapse() {
+            return new Add(getLeft().collapse(), getRight().collapse());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#toString()
          */
         @Override
@@ -225,6 +276,16 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.jcr.xpath.XPath.Component#collapse()
+         */
+        @Override
+        public Component collapse() {
+            return new Subtract(getLeft().collapse(), getRight().collapse());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#toString()
          */
         @Override
@@ -257,6 +318,16 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.jcr.xpath.XPath.Component#collapse()
+         */
+        @Override
+        public Component collapse() {
+            return new And(getLeft().collapse(), getRight().collapse());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#toString()
          */
         @Override
@@ -312,6 +383,70 @@
         }
     }
 
+    public static class Intersect extends BinaryComponent {
+        public Intersect( Component left,
+                          Component right ) {
+            super(left, right);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getLeft() + " intersect " + getRight();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof Intersect) {
+                Intersect that = (Intersect)obj;
+                return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
+            }
+            return false;
+        }
+    }
+
+    public static class Except extends BinaryComponent {
+        public Except( Component left,
+                       Component right ) {
+            super(left, right);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return getLeft() + " except " + getRight();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == this) return true;
+            if (obj instanceof Except) {
+                Except that = (Except)obj;
+                return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
+            }
+            return false;
+        }
+    }
+
     public static class Or extends BinaryComponent {
         public Or( Component left,
                    Component right ) {
@@ -321,6 +456,16 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.jcr.xpath.XPath.Component#collapse()
+         */
+        @Override
+        public Component collapse() {
+            return new Or(getLeft().collapse(), getRight().collapse());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#toString()
          */
         @Override
@@ -344,7 +489,7 @@
         }
     }
 
-    public static class ContextItem implements Component {
+    public static class ContextItem extends Component {
         /**
          * {@inheritDoc}
          * 
@@ -366,7 +511,7 @@
         }
     }
 
-    public static class Literal implements Component {
+    public static class Literal extends Component {
         private final String value;
 
         public Literal( String value ) {
@@ -380,6 +525,19 @@
             return value;
         }
 
+        public boolean isInteger() {
+            try {
+                Integer.parseInt(value);
+                return true;
+            } catch (NumberFormatException e) {
+                return false;
+            }
+        }
+
+        public int getValueAsInteger() {
+            return Integer.parseInt(value);
+        }
+
         /**
          * {@inheritDoc}
          * 
@@ -405,7 +563,7 @@
         }
     }
 
-    public static class FunctionCall implements Component {
+    public static class FunctionCall extends Component {
         private final NameTest name;
         private final List<Component> arguments;
 
@@ -434,6 +592,20 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.jcr.xpath.XPath.Component#collapse()
+         */
+        @Override
+        public Component collapse() {
+            List<Component> args = new ArrayList<Component>(arguments.size());
+            for (Component arg : arguments) {
+                args.add(arg.collapse());
+            }
+            return new FunctionCall(name, args);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#equals(java.lang.Object)
          */
         @Override
@@ -467,7 +639,7 @@
         return sb.toString();
     }
 
-    public static class PathExpression implements Component {
+    public static class PathExpression extends Component implements Iterable<StepExpression> {
         private final List<StepExpression> steps;
         private final boolean relative;
 
@@ -491,9 +663,37 @@
             return steps;
         }
 
+        public StepExpression getLastStep() {
+            return steps.isEmpty() ? null : steps.get(steps.size() - 1);
+        }
+
+        public PathExpression withoutLast() {
+            assert !steps.isEmpty();
+            return new PathExpression(relative, steps.subList(0, steps.size() - 1));
+        }
+
+        public PathExpression withoutFirst() {
+            assert !steps.isEmpty();
+            return new PathExpression(relative, steps.subList(1, steps.size()));
+        }
+
         /**
          * {@inheritDoc}
          * 
+         * @see java.lang.Iterable#iterator()
+         */
+        public Iterator<StepExpression> iterator() {
+            return steps.iterator();
+        }
+
+        @Override
+        public Component collapse() {
+            return steps.size() == 1 ? steps.get(0).collapse() : this;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#equals(java.lang.Object)
          */
         @Override
@@ -517,10 +717,10 @@
         }
     }
 
-    public static interface StepExpression extends Component {
+    public static abstract class StepExpression extends Component {
     }
 
-    public static class FilterStep implements StepExpression {
+    public static class FilterStep extends StepExpression {
         private final Component primaryExpression;
         private final List<Component> predicates;
 
@@ -546,6 +746,11 @@
             return predicates;
         }
 
+        @Override
+        public Component collapse() {
+            return predicates.isEmpty() ? primaryExpression.collapse() : this;
+        }
+
         /**
          * {@inheritDoc}
          * 
@@ -572,7 +777,7 @@
         }
     }
 
-    public static class DescendantOrSelf implements StepExpression {
+    public static class DescendantOrSelf extends StepExpression {
         /**
          * {@inheritDoc}
          * 
@@ -594,7 +799,7 @@
         }
     }
 
-    public static class AxisStep implements StepExpression {
+    public static class AxisStep extends StepExpression {
         private final NodeTest nodeTest;
         private final List<Component> predicates;
 
@@ -620,6 +825,11 @@
             return predicates;
         }
 
+        @Override
+        public Component collapse() {
+            return predicates.isEmpty() ? nodeTest.collapse() : this;
+        }
+
         /**
          * {@inheritDoc}
          * 
@@ -646,7 +856,7 @@
         }
     }
 
-    public static class ParenthesizedExpression implements Component {
+    public static class ParenthesizedExpression extends Component {
         private final Component wrapped;
 
         public ParenthesizedExpression() {
@@ -667,6 +877,16 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.jcr.xpath.XPath.Component#collapse()
+         */
+        @Override
+        public Component collapse() {
+            return wrapped instanceof BinaryComponent ? this : wrapped;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#equals(java.lang.Object)
          */
         @Override
@@ -690,13 +910,13 @@
         }
     }
 
-    public static interface NodeTest extends Component {
+    public static abstract class NodeTest extends Component {
     }
 
-    public static interface KindTest extends NodeTest {
+    public static abstract class KindTest extends NodeTest {
     }
 
-    public static class NameTest implements NodeTest {
+    public static class NameTest extends NodeTest {
         private final String prefixTest;
         private final String localTest;
 
@@ -707,22 +927,55 @@
         }
 
         /**
-         * @return prefixTest
+         * @return the prefix criteria, or null if the prefix criteria is a wildcard
          */
         public String getPrefixTest() {
             return prefixTest;
         }
 
         /**
-         * @return localTest
+         * @return the local name criteria, or null if the local name criteria is a wildcard
          */
         public String getLocalTest() {
             return localTest;
         }
 
         /**
+         * Determine if this name test exactly matches the supplied prefix and local name values.
+         * 
+         * @param prefix the prefix; may be null
+         * @param local the local name; may be null
+         * @return true if this name matches the supplied values, or false otherwise.
+         */
+        public boolean matches( String prefix,
+                                String local ) {
+            if (this.prefixTest != null && !this.prefixTest.equals(prefix)) return false;
+            if (this.localTest != null && !this.localTest.equals(local)) return false;
+            return true;
+        }
+
+        /**
+         * Return whether this represents a wildcard, meaning both {@link #getPrefixTest()} and {@link #getLocalTest()} are null.
+         * 
+         * @return true if this is a wildcard name test.
+         */
+        public boolean isWildcard() {
+            return prefixTest == null && localTest == null;
+        }
+
+        /**
          * {@inheritDoc}
          * 
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return HashCode.compute(this.prefixTest, this.localTest);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see java.lang.Object#equals(java.lang.Object)
          */
         @Override
@@ -748,18 +1001,18 @@
         }
     }
 
-    public static class AttributeNameTest implements NodeTest {
-        private final NodeTest nodeTest;
+    public static class AttributeNameTest extends NodeTest {
+        private final NameTest nameTest;
 
-        public AttributeNameTest( NodeTest nodeTest ) {
-            this.nodeTest = nodeTest;
+        public AttributeNameTest( NameTest nameTest ) {
+            this.nameTest = nameTest;
         }
 
         /**
          * @return nodeTest
          */
-        public NodeTest getNodeTest() {
-            return nodeTest;
+        public NameTest getNameTest() {
+            return nameTest;
         }
 
         /**
@@ -769,7 +1022,7 @@
          */
         @Override
         public String toString() {
-            return "@" + nodeTest;
+            return "@" + nameTest;
         }
 
         /**
@@ -782,14 +1035,14 @@
             if (obj == this) return true;
             if (obj instanceof AttributeNameTest) {
                 AttributeNameTest that = (AttributeNameTest)obj;
-                return this.nodeTest.equals(that.nodeTest);
+                return this.nameTest.equals(that.nameTest);
             }
             return false;
         }
 
     }
 
-    public static class AnyKindTest implements KindTest {
+    public static class AnyKindTest extends KindTest {
         /**
          * {@inheritDoc}
          * 
@@ -811,7 +1064,7 @@
         }
     }
 
-    public static class TextTest implements KindTest {
+    public static class TextTest extends KindTest {
         /**
          * {@inheritDoc}
          * 
@@ -833,7 +1086,7 @@
         }
     }
 
-    public static class CommentTest implements KindTest {
+    public static class CommentTest extends KindTest {
         /**
          * {@inheritDoc}
          * 
@@ -855,7 +1108,7 @@
         }
     }
 
-    public static class ProcessingInstructionTest implements KindTest {
+    public static class ProcessingInstructionTest extends KindTest {
         private final String nameOrStringLiteral;
 
         public ProcessingInstructionTest( String nameOrStringLiteral ) {
@@ -895,7 +1148,7 @@
         }
     }
 
-    public static class DocumentTest implements KindTest {
+    public static class DocumentTest extends KindTest {
         private KindTest elementOrSchemaElementTest;
 
         public DocumentTest( ElementTest elementTest ) {
@@ -948,7 +1201,7 @@
         }
     }
 
-    public static class AttributeTest implements KindTest {
+    public static class AttributeTest extends KindTest {
         private final NameTest attributeNameOrWildcard;
         private final NameTest typeName;
 
@@ -999,7 +1252,7 @@
         }
     }
 
-    public static class ElementTest implements KindTest {
+    public static class ElementTest extends KindTest {
         private final NameTest elementNameOrWildcard;
         private final NameTest typeName;
 
@@ -1050,7 +1303,7 @@
         }
     }
 
-    public static class SchemaElementTest implements KindTest {
+    public static class SchemaElementTest extends KindTest {
         private final NameTest elementDeclarationName;
 
         public SchemaElementTest( NameTest elementDeclarationName ) {
@@ -1090,7 +1343,7 @@
         }
     }
 
-    public static class SchemaAttributeTest implements KindTest {
+    public static class SchemaAttributeTest extends KindTest {
         private final NameTest attributeDeclarationName;
 
         public SchemaAttributeTest( NameTest attributeDeclarationName ) {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathParser.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -39,8 +39,6 @@
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.query.model.Operator;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.parse.QueryParser;
 import org.jboss.dna.jcr.xpath.XPath.Add;
 import org.jboss.dna.jcr.xpath.XPath.And;
 import org.jboss.dna.jcr.xpath.XPath.AnyKindTest;
@@ -54,8 +52,10 @@
 import org.jboss.dna.jcr.xpath.XPath.DescendantOrSelf;
 import org.jboss.dna.jcr.xpath.XPath.DocumentTest;
 import org.jboss.dna.jcr.xpath.XPath.ElementTest;
+import org.jboss.dna.jcr.xpath.XPath.Except;
 import org.jboss.dna.jcr.xpath.XPath.FilterStep;
 import org.jboss.dna.jcr.xpath.XPath.FunctionCall;
+import org.jboss.dna.jcr.xpath.XPath.Intersect;
 import org.jboss.dna.jcr.xpath.XPath.KindTest;
 import org.jboss.dna.jcr.xpath.XPath.Literal;
 import org.jboss.dna.jcr.xpath.XPath.NameTest;
@@ -75,602 +75,606 @@
 import org.jboss.dna.jcr.xpath.XPath.Union;
 
 /**
- * 
+ * A component that parses an XPath query string and creates an abstract syntax tree representation. The supported grammar is
+ * defined by JCR 1.0, and is a subset of what is allowed by the W3C XPath 2.0 specification.
  */
-public class XPathParser implements QueryParser {
+public class XPathParser {
+    private final ExecutionContext context;
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String, org.jboss.dna.graph.ExecutionContext)
-     */
-    public QueryCommand parseQuery( String query,
-                                    ExecutionContext context ) {
+    public XPathParser( ExecutionContext context ) {
+        this.context = context;
+    }
+
+    public Component parseXPath( String xpath ) {
         Tokenizer tokenizer = new XPathTokenizer(false); // skip comments
-        TokenStream tokens = new TokenStream(query, tokenizer, true).start(); // case sensitive!!
-        Component result = new Parser(context).parseXPath(tokens);
-        System.out.println(query);
-        System.out.println(" --> " + result);
-        return null;
+        TokenStream tokens = new TokenStream(xpath, tokenizer, true).start(); // case sensitive!!
+        return parseXPath(tokens);
     }
 
-    protected static class Parser {
-        private final ExecutionContext context;
+    protected Component parseXPath( TokenStream tokens ) {
+        return parseExpr(tokens);
+    }
 
-        protected Parser( ExecutionContext context ) {
-            this.context = context;
+    protected Component parseExpr( TokenStream tokens ) {
+        Component result = parseExprSingle(tokens);
+        if (tokens.matches(',')) {
+            throw new ParsingException(tokens.nextPosition(), "Multiple XPath expressions are not supported");
         }
+        return result;
+    }
 
-        protected Component parseXPath( TokenStream tokens ) {
-            return parseExpr(tokens);
+    protected Component parseExprSingle( TokenStream tokens ) {
+        if (tokens.matches("for", "$", ANY_VALUE, "IN")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath 'for' expressions are not supported");
         }
+        if (tokens.matches("some", "$", ANY_VALUE, "IN")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath 'some' expressions are not supported");
+        }
+        if (tokens.matches("every", "$", ANY_VALUE, "IN")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath 'every' expressions are not supported");
+        }
+        if (tokens.matches("if", "(", ANY_VALUE, "IN")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath if-then-else expressions are not supported");
+        }
+        return parseOrExpr(tokens);
+    }
 
-        protected Component parseExpr( TokenStream tokens ) {
-            Component result = parseExprSingle(tokens);
-            if (tokens.matches(',')) {
-                throw new ParsingException(tokens.nextPosition(), "Multiple XPath expressions are not supported");
-            }
-            return result;
+    protected Component parseOrExpr( TokenStream tokens ) {
+        Component result = parseAndExpr(tokens);
+        while (tokens.canConsume("or")) {
+            result = new Or(result, parseInstanceofExpr(tokens));
         }
+        return result;
+    }
 
-        protected Component parseExprSingle( TokenStream tokens ) {
-            if (tokens.matches("for", "$", ANY_VALUE, "IN")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath 'for' expressions are not supported");
-            }
-            if (tokens.matches("some", "$", ANY_VALUE, "IN")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath 'some' expressions are not supported");
-            }
-            if (tokens.matches("every", "$", ANY_VALUE, "IN")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath 'every' expressions are not supported");
-            }
-            if (tokens.matches("if", "(", ANY_VALUE, "IN")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath if-then-else expressions are not supported");
-            }
-            return parseOrExpr(tokens);
+    protected Component parseAndExpr( TokenStream tokens ) {
+        Component result = parseInstanceofExpr(tokens);
+        while (tokens.canConsume("and")) {
+            result = new And(result, parseInstanceofExpr(tokens));
         }
+        return result;
+    }
 
-        protected Component parseOrExpr( TokenStream tokens ) {
-            Component result = parseAndExpr(tokens);
-            while (tokens.canConsume("or")) {
-                result = new Or(result, parseInstanceofExpr(tokens));
-            }
-            return result;
+    protected Component parseInstanceofExpr( TokenStream tokens ) {
+        Component result = parseTreatExpr(tokens);
+        if (tokens.matches("instance", "of")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath 'instance of' expressions are not supported");
         }
+        return result;
+    }
 
-        protected Component parseAndExpr( TokenStream tokens ) {
-            Component result = parseInstanceofExpr(tokens);
-            while (tokens.canConsume("and")) {
-                result = new And(result, parseInstanceofExpr(tokens));
-            }
-            return result;
+    protected Component parseTreatExpr( TokenStream tokens ) {
+        Component result = parseCastableExpr(tokens);
+        if (tokens.matches("treat", "as")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath 'treat as' expressions are not supported");
         }
+        return result;
+    }
 
-        protected Component parseInstanceofExpr( TokenStream tokens ) {
-            Component result = parseTreatExpr(tokens);
-            if (tokens.matches("instance", "of")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath 'instance of' expressions are not supported");
-            }
-            return result;
+    protected Component parseCastableExpr( TokenStream tokens ) {
+        Component result = parseCastExpr(tokens);
+        if (tokens.matches("castable", "as")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath 'castable as' expressions are not supported");
         }
+        return result;
+    }
 
-        protected Component parseTreatExpr( TokenStream tokens ) {
-            Component result = parseCastableExpr(tokens);
-            if (tokens.matches("treat", "as")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath 'treat as' expressions are not supported");
-            }
-            return result;
+    protected Component parseCastExpr( TokenStream tokens ) {
+        Component result = parseComparisonExpr(tokens);
+        if (tokens.matches("cast", "as")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath 'cast as' expressions are not supported");
         }
+        return result;
+    }
 
-        protected Component parseCastableExpr( TokenStream tokens ) {
-            Component result = parseCastExpr(tokens);
-            if (tokens.matches("castable", "as")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath 'castable as' expressions are not supported");
-            }
-            return result;
+    protected Component parseComparisonExpr( TokenStream tokens ) {
+        Component result = parseRangeExpr(tokens);
+        // General comparison is optional ...
+        Operator operator = parseGeneralComp(tokens);
+        if (operator == null) parseValueComp(tokens);
+        if (operator != null) {
+            return new Comparison(result, operator, parseRangeExpr(tokens));
         }
+        NodeComparisonOperator nodeComp = parseNodeComp(tokens);
+        if (nodeComp != null) {
+            return new NodeComparison(result, nodeComp, parseRangeExpr(tokens));
+        }
+        return result;
+    }
 
-        protected Component parseCastExpr( TokenStream tokens ) {
-            Component result = parseComparisonExpr(tokens);
-            if (tokens.matches("cast", "as")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath 'cast as' expressions are not supported");
-            }
-            return result;
+    protected Component parseValueComp( TokenStream tokens ) {
+        if (tokens.matchesAnyOf("eq", "ne", "lt", "le", "gt")) {
+            throw new ParsingException(tokens.nextPosition(),
+                                       "XPath value comparisons using 'eq', 'ne', 'lt', 'le', or 'gt' are not supported");
         }
+        return null;
+    }
 
-        protected Component parseComparisonExpr( TokenStream tokens ) {
-            Component result = parseRangeExpr(tokens);
-            // General comparison is optional ...
-            Operator operator = parseGeneralComp(tokens);
-            if (operator == null) parseValueComp(tokens);
-            if (operator != null) {
-                return new Comparison(result, operator, parseRangeExpr(tokens));
-            }
-            NodeComparisonOperator nodeComp = parseNodeComp(tokens);
-            if (nodeComp != null) {
-                return new NodeComparison(result, nodeComp, parseRangeExpr(tokens));
-            }
-            return result;
+    protected NodeComparisonOperator parseNodeComp( TokenStream tokens ) {
+        if (tokens.matches("is") || tokens.matches("<", "<") || tokens.matches(">", ">")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath 'is', '<<' and '>>' expressions are not supported");
         }
+        return null;
+    }
 
-        protected Component parseValueComp( TokenStream tokens ) {
-            if (tokens.matchesAnyOf("eq", "ne", "lt", "le", "gt")) {
-                throw new ParsingException(tokens.nextPosition(),
-                                           "XPath value comparisons using 'eq', 'ne', 'lt', 'le', or 'gt' are not supported");
-            }
-            return null;
+    protected Component parseRangeExpr( TokenStream tokens ) {
+        Component result = parseAdditiveExpr(tokens);
+        if (tokens.matches("to")) {
+            throw new ParsingException(tokens.nextPosition(), "XPath range expressions with 'to' are not supported");
         }
+        return result;
+    }
 
-        protected NodeComparisonOperator parseNodeComp( TokenStream tokens ) {
-            if (tokens.matches("is") || tokens.matches("<", "<") || tokens.matches(">", ">")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath 'is', '<<' and '>>' expressions are not supported");
+    protected Component parseAdditiveExpr( TokenStream tokens ) {
+        Component result = parseMultiplicativeExpr(tokens);
+        while (true) {
+            if (tokens.canConsume("+")) {
+                result = new Add(result, parseMultiplicativeExpr(tokens));
+            } else if (tokens.canConsume("-")) {
+                result = new Subtract(result, parseMultiplicativeExpr(tokens));
+            } else {
+                break; // no more additions
             }
-            return null;
         }
+        return result;
+    }
 
-        protected Component parseRangeExpr( TokenStream tokens ) {
-            Component result = parseAdditiveExpr(tokens);
-            if (tokens.matches("to")) {
-                throw new ParsingException(tokens.nextPosition(), "XPath range expressions with 'to' are not supported");
-            }
-            return result;
+    protected Component parseMultiplicativeExpr( TokenStream tokens ) {
+        Component result = parseUnaryExpr(tokens);
+        if (tokens.matchesAnyOf("+", "div", "idiv", "mod")) {
+            throw new ParsingException(tokens.nextPosition(),
+                                       "XPath multiplicative expressions using '+', 'div', 'idiv', or 'mod' are not supported");
         }
+        return result;
+    }
 
-        protected Component parseAdditiveExpr( TokenStream tokens ) {
-            Component result = parseMultiplicativeExpr(tokens);
-            while (true) {
-                if (tokens.canConsume("+")) {
-                    result = new Add(result, parseMultiplicativeExpr(tokens));
-                } else if (tokens.canConsume("-")) {
-                    result = new Subtract(result, parseMultiplicativeExpr(tokens));
-                } else {
-                    break; // no more additions
-                }
-            }
-            return result;
+    protected Component parseUnaryExpr( TokenStream tokens ) {
+        boolean negative = false;
+        // Technically more than one +/- are allowed by the spec
+        while (tokens.matchesAnyOf("+", "-")) {
+            if (tokens.canConsume("-")) negative = true;
+            tokens.canConsume("+");
         }
+        Component result = parseUnionExpr(tokens);
+        return negative ? new Negation(result) : result;
+    }
 
-        protected Component parseMultiplicativeExpr( TokenStream tokens ) {
-            Component result = parseUnaryExpr(tokens);
-            if (tokens.matchesAnyOf("+", "div", "idiv", "mod")) {
-                throw new ParsingException(tokens.nextPosition(),
-                                           "XPath multiplicative expressions using '+', 'div', 'idiv', or 'mod' are not supported");
+    protected Component parseUnionExpr( TokenStream tokens ) {
+        Component result = parseIntersectExceptExpr(tokens);
+        while (true) {
+            if (tokens.canConsumeAnyOf("union", "|")) {
+                result = new Union(result, parseIntersectExceptExpr(tokens));
+            } else {
+                break; // no more
             }
-            return result;
         }
+        return result;
+    }
 
-        protected Component parseUnaryExpr( TokenStream tokens ) {
-            boolean negative = false;
-            // Technically more than one +/- are allowed by the spec
-            while (tokens.matchesAnyOf("+", "-")) {
-                if (tokens.canConsume("-")) negative = true;
-                tokens.canConsume("+");
+    protected Component parseIntersectExceptExpr( TokenStream tokens ) {
+        Component result = parseValueExpr(tokens);
+        while (true) {
+            if (tokens.canConsumeAnyOf("intersect")) {
+                result = new Intersect(result, parseValueExpr(tokens));
+            } else if (tokens.canConsumeAnyOf("except")) {
+                result = new Except(result, parseValueExpr(tokens));
+            } else {
+                break; // no more
             }
-            Component result = parseUnionExpr(tokens);
-            return negative ? new Negation(result) : result;
         }
+        return result;
+    }
 
-        protected Component parseUnionExpr( TokenStream tokens ) {
-            Component result = parseIntersectExceptExpr(tokens);
-            while (true) {
-                if (tokens.canConsumeAnyOf("union", "|")) {
-                    result = new Union(result, parseIntersectExceptExpr(tokens));
-                } else {
-                    break; // no more
+    protected Component parseValueExpr( TokenStream tokens ) {
+        return parsePathExpr(tokens);
+    }
+
+    protected PathExpression parsePathExpr( TokenStream tokens ) {
+        boolean relative = true;
+        boolean prependDependentOrSelf = false;
+        if (tokens.canConsume('/')) {
+            if (tokens.canConsume('/')) {
+                if (!tokens.hasNext()) {
+                    // See http://www.w3.org/XML/2007/qt-errata/xpath20-errata.html#E3
+                    throw new ParsingException(tokens.previousPosition(), "'//' is not a valid XPath expression");
                 }
+                prependDependentOrSelf = true;
             }
-            return result;
+            relative = false;
         }
-
-        protected Component parseIntersectExceptExpr( TokenStream tokens ) {
-            Component result = parseValueExpr(tokens);
-            if (tokens.matchesAnyOf("intersect", "except")) {
-                throw new ParsingException(tokens.nextPosition(),
-                                           "XPath multiplicative expressions using '+', 'div', 'idiv', or 'mod' are not supported");
-            }
-            return result;
+        PathExpression result = new PathExpression(relative, parseRelativePathExpr(tokens).getSteps());
+        if (prependDependentOrSelf) {
+            result.getSteps().add(0, new DescendantOrSelf());
         }
+        return result;
+    }
 
-        protected Component parseValueExpr( TokenStream tokens ) {
-            return parsePathExpr(tokens);
-        }
-
-        protected PathExpression parsePathExpr( TokenStream tokens ) {
-            boolean relative = true;
-            boolean prependDependentOrSelf = false;
+    protected PathExpression parseRelativePathExpr( TokenStream tokens ) {
+        List<StepExpression> steps = new ArrayList<StepExpression>();
+        steps.add(parseStepExpr(tokens));
+        while (tokens.canConsume('/')) {
             if (tokens.canConsume('/')) {
-                if (tokens.canConsume('/')) {
-                    if (!tokens.hasNext()) {
-                        // See http://www.w3.org/XML/2007/qt-errata/xpath20-errata.html#E3
-                        throw new ParsingException(tokens.previousPosition(), "'//' is not a valid XPath expression");
-                    }
-                    prependDependentOrSelf = true;
-                }
-                relative = false;
+                steps.add(new DescendantOrSelf());
             }
-            PathExpression result = new PathExpression(relative, parseRelativePathExpr(tokens).getSteps());
-            if (prependDependentOrSelf) {
-                result.getSteps().add(0, new DescendantOrSelf());
+            if (tokens.hasNext()) {
+                steps.add(parseStepExpr(tokens));
             }
-            return result;
         }
+        return new PathExpression(true, steps);
+    }
 
-        protected PathExpression parseRelativePathExpr( TokenStream tokens ) {
-            List<StepExpression> steps = new ArrayList<StepExpression>();
-            steps.add(parseStepExpr(tokens));
-            while (tokens.canConsume('/')) {
-                if (tokens.canConsume('/')) {
-                    steps.add(new DescendantOrSelf());
-                }
-                if (tokens.hasNext()) {
-                    steps.add(parseStepExpr(tokens));
-                }
-            }
-            return new PathExpression(true, steps);
+    protected StepExpression parseStepExpr( TokenStream tokens ) {
+        KindTest kindTest = parseKindTest(tokens);
+        if (kindTest != null) {
+            // Now parse the predicates ...
+            List<Component> predicates = parsePredicates(tokens);
+            return new AxisStep(kindTest, predicates);
         }
-
-        protected StepExpression parseStepExpr( TokenStream tokens ) {
-            if (tokens.matches('(') || tokens.matches('.') || tokens.matches(XPathTokenizer.QUOTED_STRING)
-                || tokens.matches(ANY_VALUE, "(") || tokens.matches(ANY_VALUE, ":", ANY_VALUE, "(")) {
-                // We know its a filter expression (though literals don't fit this pattern) ...
-                return parseFilterExpr(tokens);
-            }
-            AxisStep result = parseAxisStep(tokens);
-            if (result != null) return result;
-            // It must be the remaining kind of filter expression ...
+        if (tokens.matches('(') || tokens.matches('.') || tokens.matches(XPathTokenizer.QUOTED_STRING)
+            || tokens.matches(ANY_VALUE, "(") || tokens.matches(ANY_VALUE, ":", ANY_VALUE, "(")) {
+            // We know its a filter expression (though literals don't fit this pattern) ...
             return parseFilterExpr(tokens);
         }
+        AxisStep result = parseAxisStep(tokens);
+        if (result != null) return result;
+        // It must be the remaining kind of filter expression ...
+        return parseFilterExpr(tokens);
+    }
 
-        protected AxisStep parseAxisStep( TokenStream tokens ) {
-            NodeTest nodeTest = null;
-            if (tokens.canConsume('@')) {
-                // Abbreviated forward step with an attribute...
-                nodeTest = new AttributeNameTest(parseNodeTest(tokens));
-            } else if (tokens.matches('*')) {
-                // Abbreviated forward step with an wildcard element ...
-                nodeTest = parseNodeTest(tokens);
+    protected AxisStep parseAxisStep( TokenStream tokens ) {
+        NodeTest nodeTest = null;
+        if (tokens.canConsume('@')) {
+            // Abbreviated forward step with an attribute...
+            nodeTest = new AttributeNameTest(parseNameTest(tokens));
+        } else if (tokens.matches('*')) {
+            // Abbreviated forward step with an wildcard element ...
+            nodeTest = parseNodeTest(tokens);
 
-            } else if (tokens.matches("child", ":", ":") || tokens.matches("attribute", ":", ":")
-                       || tokens.matches("self", ":", ":") || tokens.matches("descendant", ":", ":")
-                       || tokens.matches("descendant-or-self", ":", ":") || tokens.matches("following-sibling", ":", ":")
-                       || tokens.matches("following", ":", ":") || tokens.matches("namespace", ":", ":")) {
-                // No non-abbreviated forward steps allowed
-                throw new ParsingException(
-                                           tokens.nextPosition(),
-                                           "XPath non-abbreviated forward steps (e.g., 'child::', 'attribute::', 'self::', 'descendant::', 'descendant-or-self::', 'following-sibling::', 'following::', or 'namespace::') are not supported");
-            } else if (tokens.matches("..")) {
-                // No abbreviated reverse steps allowed ...
-                throw new ParsingException(tokens.nextPosition(),
-                                           "XPath abbreviated reverse steps (e.g., '..') are not supported");
-            } else if (tokens.matches("parent", ":", ":") || tokens.matches("ancestor-or-self", ":", ":")
-                       || tokens.matches("preceding-sibling", ":", ":") || tokens.matches("preceding", ":", ":")
-                       || tokens.matches("ancestor", ":", ":")) {
-                // No non-abbreviated reverse steps allowed ...
-                throw new ParsingException(
-                                           tokens.nextPosition(),
-                                           "XPath non-abbreviated reverse steps (e.g., 'parent::', 'ancestor::', 'ancestor-or-self::', 'preceding-or-sibling::', or 'preceding::') are not supported");
-            } else if (tokens.matches(ANY_VALUE, ":", ANY_VALUE)
-                       && tokens.matches(XPathTokenizer.NAME, XPathTokenizer.SYMBOL, XPathTokenizer.NAME)) {
-                // This is probably a forward step with a (qualified) name test ...
-                nodeTest = parseQName(tokens);
-            } else if (tokens.matches(XPathTokenizer.NAME)) {
-                // This is probably a forward step with an unqualified name test ...
-                nodeTest = parseNodeTest(tokens);
-            } else {
-                return null;
-            }
-
-            // Parse the predicates
-            List<Component> predicates = parsePredicates(tokens);
-            return new AxisStep(nodeTest, predicates);
+        } else if (tokens.matches("child", ":", ":") || tokens.matches("attribute", ":", ":") || tokens.matches("self", ":", ":")
+                   || tokens.matches("descendant", ":", ":") || tokens.matches("descendant-or-self", ":", ":")
+                   || tokens.matches("following-sibling", ":", ":") || tokens.matches("following", ":", ":")
+                   || tokens.matches("namespace", ":", ":")) {
+            // No non-abbreviated forward steps allowed
+            throw new ParsingException(
+                                       tokens.nextPosition(),
+                                       "XPath non-abbreviated forward steps (e.g., 'child::', 'attribute::', 'self::', 'descendant::', 'descendant-or-self::', 'following-sibling::', 'following::', or 'namespace::') are not supported");
+        } else if (tokens.matches("..")) {
+            // No abbreviated reverse steps allowed ...
+            throw new ParsingException(tokens.nextPosition(), "XPath abbreviated reverse steps (e.g., '..') are not supported");
+        } else if (tokens.matches("parent", ":", ":") || tokens.matches("ancestor-or-self", ":", ":")
+                   || tokens.matches("preceding-sibling", ":", ":") || tokens.matches("preceding", ":", ":")
+                   || tokens.matches("ancestor", ":", ":")) {
+            // No non-abbreviated reverse steps allowed ...
+            throw new ParsingException(
+                                       tokens.nextPosition(),
+                                       "XPath non-abbreviated reverse steps (e.g., 'parent::', 'ancestor::', 'ancestor-or-self::', 'preceding-or-sibling::', or 'preceding::') are not supported");
+        } else if (tokens.matches(ANY_VALUE, ":", ANY_VALUE)
+                   && tokens.matches(XPathTokenizer.NAME, XPathTokenizer.SYMBOL, XPathTokenizer.NAME)) {
+            // This is probably a forward step with a (qualified) name test ...
+            nodeTest = parseQName(tokens);
+        } else if (tokens.matches(XPathTokenizer.NAME)) {
+            // This is probably a forward step with an unqualified name test ...
+            nodeTest = parseNodeTest(tokens);
+        } else {
+            return null;
         }
 
-        protected List<Component> parsePredicates( TokenStream tokens ) {
-            List<Component> predicates = new ArrayList<Component>();
-            while (tokens.canConsume('[')) {
-                predicates.add(parseExpr(tokens));
-                tokens.consume(']');
-            }
-            return predicates;
-        }
+        // Parse the predicates
+        List<Component> predicates = parsePredicates(tokens);
+        return new AxisStep(nodeTest, predicates);
+    }
 
-        protected FilterStep parseFilterExpr( TokenStream tokens ) {
-            Component primaryExpr = parsePrimaryExpr(tokens);
-            List<Component> predicates = parsePredicates(tokens);
-            return new FilterStep(primaryExpr, predicates);
+    protected List<Component> parsePredicates( TokenStream tokens ) {
+        List<Component> predicates = new ArrayList<Component>();
+        while (tokens.canConsume('[')) {
+            predicates.add(collapse(parseExpr(tokens)));
+            tokens.consume(']');
         }
+        return predicates;
+    }
 
-        protected Component parsePrimaryExpr( TokenStream tokens ) {
-            if (tokens.matches('(')) {
-                return parseParenthesizedExpr(tokens);
-            }
-            if (tokens.matches('.')) {
-                return parseContextItemExpr(tokens);
-            }
-            if (tokens.matches(XPathTokenizer.QUOTED_STRING)) {
-                return parseStringLiteral(tokens);
-            }
-            if (tokens.matches(ANY_VALUE, "(") || tokens.matches(ANY_VALUE, ":", ANY_VALUE, "(")) {
-                return parseFunctionCall(tokens);
-            }
-            return parseNumericLiteral(tokens);
-        }
+    protected FilterStep parseFilterExpr( TokenStream tokens ) {
+        Component primaryExpr = parsePrimaryExpr(tokens);
+        List<Component> predicates = parsePredicates(tokens);
+        return new FilterStep(primaryExpr, predicates);
+    }
 
-        protected ContextItem parseContextItemExpr( TokenStream tokens ) {
-            tokens.consume('.');
-            return new ContextItem();
+    protected Component parsePrimaryExpr( TokenStream tokens ) {
+        if (tokens.matches('(')) {
+            return parseParenthesizedExpr(tokens);
         }
+        if (tokens.matches('.')) {
+            return parseContextItemExpr(tokens);
+        }
+        if (tokens.matches(XPathTokenizer.QUOTED_STRING)) {
+            return parseStringLiteral(tokens);
+        }
+        if (tokens.matches(ANY_VALUE, "(") || tokens.matches(ANY_VALUE, ":", ANY_VALUE, "(")) {
+            return parseFunctionCall(tokens);
+        }
+        return parseNumericLiteral(tokens);
+    }
 
-        protected ParenthesizedExpression parseParenthesizedExpr( TokenStream tokens ) {
-            tokens.consume('(');
-            if (tokens.canConsume(')')) {
-                return new ParenthesizedExpression();
-            }
-            Component expr = parseExpr(tokens);
-            tokens.consume(')');
-            return new ParenthesizedExpression(expr);
+    protected ContextItem parseContextItemExpr( TokenStream tokens ) {
+        tokens.consume('.');
+        return new ContextItem();
+    }
+
+    protected ParenthesizedExpression parseParenthesizedExpr( TokenStream tokens ) {
+        tokens.consume('(');
+        if (tokens.canConsume(')')) {
+            return new ParenthesizedExpression();
         }
+        Component expr = collapse(parseExpr(tokens));
+        tokens.consume(')');
+        return new ParenthesizedExpression(expr);
+    }
 
-        protected Literal parseNumericLiteral( TokenStream tokens ) {
-            Position pos = tokens.nextPosition();
-            String sign = "";
-            if (tokens.canConsume('-')) sign = "-";
-            else if (tokens.canConsume('+')) sign = "";
+    protected Literal parseNumericLiteral( TokenStream tokens ) {
+        Position pos = tokens.nextPosition();
+        String sign = "";
+        if (tokens.canConsume('-')) sign = "-";
+        else if (tokens.canConsume('+')) sign = "";
 
-            // Try to parse this value as a number ...
-            ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
-            String number = tokens.consume();
-            if (number.indexOf(".") != -1) {
-                String value = sign + number;
-                if (value.endsWith("e") && (tokens.matches('+') || tokens.matches('-'))) {
-                    // There's more to the number ...
-                    value = value + tokens.consume() + tokens.consume(); // +/-EXP
-                }
-                try {
-                    // Convert to a double and then back to a string to get canonical form ...
-                    String canonical = stringFactory.create(context.getValueFactories().getDoubleFactory().create(value));
-                    return new Literal(canonical);
-                } catch (ValueFormatException e) {
-                    String msg = GraphI18n.expectingLiteralAndUnableToParseAsDouble.text(value, pos.getLine(), pos.getColumn());
-                    throw new ParsingException(pos, msg);
-                }
+        // Try to parse this value as a number ...
+        ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
+        String number = tokens.consume();
+        if (number.indexOf(".") != -1) {
+            String value = sign + number;
+            if (value.endsWith("e") && (tokens.matches('+') || tokens.matches('-'))) {
+                // There's more to the number ...
+                value = value + tokens.consume() + tokens.consume(); // +/-EXP
             }
-            // try to parse an a long ...
-            String value = sign + number;
             try {
-                // Convert to a long and then back to a string to get canonical form ...
-                String canonical = stringFactory.create(context.getValueFactories().getLongFactory().create(value));
+                // Convert to a double and then back to a string to get canonical form ...
+                String canonical = stringFactory.create(context.getValueFactories().getDoubleFactory().create(value));
                 return new Literal(canonical);
             } catch (ValueFormatException e) {
-                String msg = GraphI18n.expectingLiteralAndUnableToParseAsLong.text(value, pos.getLine(), pos.getColumn());
+                String msg = GraphI18n.expectingLiteralAndUnableToParseAsDouble.text(value, pos.getLine(), pos.getColumn());
                 throw new ParsingException(pos, msg);
             }
         }
-
-        protected Literal parseStringLiteral( TokenStream tokens ) {
-            boolean removeQuotes = tokens.matches(XPathTokenizer.QUOTED_STRING);
-            String value = tokens.consume();
-            if (removeQuotes) value = removeQuotes(value);
-            return new Literal(value);
+        // try to parse an a long ...
+        String value = sign + number;
+        try {
+            // Convert to a long and then back to a string to get canonical form ...
+            String canonical = stringFactory.create(context.getValueFactories().getLongFactory().create(value));
+            return new Literal(canonical);
+        } catch (ValueFormatException e) {
+            String msg = GraphI18n.expectingLiteralAndUnableToParseAsLong.text(value, pos.getLine(), pos.getColumn());
+            throw new ParsingException(pos, msg);
         }
+    }
 
-        protected FunctionCall parseFunctionCall( TokenStream tokens ) {
-            NameTest name = parseQName(tokens);
-            tokens.consume("(");
-            List<Component> args = new ArrayList<Component>();
-            if (!tokens.matches(')')) {
-                do {
-                    args.add(parseExprSingle(tokens));
-                } while (tokens.canConsume(","));
-                tokens.consume(")");
-            }
-            return new FunctionCall(name, args);
-        }
+    protected Literal parseStringLiteral( TokenStream tokens ) {
+        boolean removeQuotes = tokens.matches(XPathTokenizer.QUOTED_STRING);
+        String value = tokens.consume();
+        if (removeQuotes) value = removeQuotes(value);
+        return new Literal(value);
+    }
 
-        protected Operator parseGeneralComp( TokenStream tokens ) {
-            if (tokens.canConsume("!", "=")) return Operator.NOT_EQUAL_TO;
-            if (tokens.canConsume("=")) return Operator.EQUAL_TO;
-            if (tokens.canConsume("<", "=")) return Operator.LESS_THAN_OR_EQUAL_TO;
-            if (tokens.canConsume(">", "=")) return Operator.GREATER_THAN_OR_EQUAL_TO;
-            if (tokens.canConsume("<")) return Operator.LESS_THAN;
-            if (tokens.canConsume(">")) return Operator.GREATER_THAN;
-            return null;
+    protected FunctionCall parseFunctionCall( TokenStream tokens ) {
+        NameTest name = parseQName(tokens);
+        tokens.consume("(");
+        List<Component> args = new ArrayList<Component>();
+        if (!tokens.matches(')')) {
+            do {
+                args.add(collapse(parseExprSingle(tokens)));
+            } while (tokens.canConsume(","));
+            tokens.consume(")");
         }
+        return new FunctionCall(name, args);
+    }
 
-        protected NodeTest parseNodeTest( TokenStream tokens ) {
-            KindTest kind = parseKindTest(tokens);
-            if (kind != null) return kind;
-            return parseNameTest(tokens);
-        }
+    protected Operator parseGeneralComp( TokenStream tokens ) {
+        if (tokens.canConsume("!", "=")) return Operator.NOT_EQUAL_TO;
+        if (tokens.canConsume("=")) return Operator.EQUAL_TO;
+        if (tokens.canConsume("<", "=")) return Operator.LESS_THAN_OR_EQUAL_TO;
+        if (tokens.canConsume(">", "=")) return Operator.GREATER_THAN_OR_EQUAL_TO;
+        if (tokens.canConsume("<")) return Operator.LESS_THAN;
+        if (tokens.canConsume(">")) return Operator.GREATER_THAN;
+        return null;
+    }
 
-        protected NameTest parseNameTest( TokenStream tokens ) {
-            NameTest wildcard = parseWildcard(tokens);
-            if (wildcard != null) return wildcard;
-            return parseQName(tokens);
-        }
+    protected NodeTest parseNodeTest( TokenStream tokens ) {
+        KindTest kind = parseKindTest(tokens);
+        if (kind != null) return kind;
+        return parseNameTest(tokens);
+    }
 
-        protected NameTest parseQName( TokenStream tokens ) {
-            String firstPart = parseNCName(tokens);
-            if (tokens.canConsume(':')) {
-                String secondPart = tokens.consume();
-                return new NameTest(firstPart, secondPart);
-            }
-            return new NameTest(null, firstPart);
+    protected NameTest parseNameTest( TokenStream tokens ) {
+        NameTest wildcard = parseWildcard(tokens);
+        if (wildcard != null) return wildcard;
+        return parseQName(tokens);
+    }
+
+    protected NameTest parseQName( TokenStream tokens ) {
+        String firstPart = parseNCName(tokens);
+        if (tokens.canConsume(':')) {
+            String secondPart = tokens.consume();
+            return new NameTest(firstPart, secondPart);
         }
+        return new NameTest(null, firstPart);
+    }
 
-        protected String parseNCName( TokenStream tokens ) {
-            String name = tokens.consume();
-            if (!XmlCharacters.isValidNcName(name)) {
-                throw new ParsingException(tokens.previousPosition(), "Expected valid NCName but found " + name);
-            }
-            return name;
+    protected String parseNCName( TokenStream tokens ) {
+        String name = tokens.consume();
+        if (!XmlCharacters.isValidNcName(name)) {
+            throw new ParsingException(tokens.previousPosition(), "Expected valid NCName but found " + name);
         }
+        return name;
+    }
 
-        protected NameTest parseWildcard( TokenStream tokens ) {
-            if (tokens.canConsume('*')) {
-                if (tokens.canConsume(':')) {
-                    if (tokens.canConsume('*')) {
-                        return new NameTest(null, null);
-                    }
-                    String localName = tokens.consume();
-                    return new NameTest(null, localName);
+    protected NameTest parseWildcard( TokenStream tokens ) {
+        if (tokens.canConsume('*')) {
+            if (tokens.canConsume(':')) {
+                if (tokens.canConsume('*')) {
+                    return new NameTest(null, null);
                 }
-                return new NameTest(null, null);
+                String localName = tokens.consume();
+                return new NameTest(null, localName);
             }
-            if (tokens.matches(XPathTokenizer.NAME, XPathTokenizer.SYMBOL, XPathTokenizer.SYMBOL)
-                && tokens.matches(TokenStream.ANY_VALUE, ":", "*")) {
-                String prefix = tokens.consume();
-                tokens.consume(':');
-                tokens.consume('*');
-                return new NameTest(prefix, null);
-            }
-            return null;
+            return new NameTest(null, null);
         }
-
-        protected NameTest parseItemType( TokenStream tokens ) {
-            return parseQName(tokens);
+        if (tokens.matches(XPathTokenizer.NAME, XPathTokenizer.SYMBOL, XPathTokenizer.SYMBOL)
+            && tokens.matches(TokenStream.ANY_VALUE, ":", "*")) {
+            String prefix = tokens.consume();
+            tokens.consume(':');
+            tokens.consume('*');
+            return new NameTest(prefix, null);
         }
+        return null;
+    }
 
-        protected NameTest parseAtomicType( TokenStream tokens ) {
-            return parseQName(tokens);
-        }
+    protected NameTest parseItemType( TokenStream tokens ) {
+        return parseQName(tokens);
+    }
 
-        protected KindTest parseKindTest( TokenStream tokens ) {
-            KindTest result = parseAnyKindTest(tokens);
-            if (result == null) result = parseDocumentTest(tokens);
-            if (result == null) result = parseElementTest(tokens);
-            if (result == null) result = parseAttributeTest(tokens);
-            if (result == null) result = parseSchemaElementTest(tokens);
-            if (result == null) result = parseSchemaAttributeTest(tokens);
-            if (result == null) result = parsePITest(tokens);
-            if (result == null) result = parseCommentTest(tokens);
-            if (result == null) result = parseTextTest(tokens);
-            return result;
-        }
+    protected NameTest parseAtomicType( TokenStream tokens ) {
+        return parseQName(tokens);
+    }
 
-        protected AnyKindTest parseAnyKindTest( TokenStream tokens ) {
-            if (tokens.canConsume("node", "(", ")")) {
-                return new AnyKindTest();
-            }
-            return null;
-        }
+    protected KindTest parseKindTest( TokenStream tokens ) {
+        KindTest result = parseAnyKindTest(tokens);
+        if (result == null) result = parseDocumentTest(tokens);
+        if (result == null) result = parseElementTest(tokens);
+        if (result == null) result = parseAttributeTest(tokens);
+        if (result == null) result = parseSchemaElementTest(tokens);
+        if (result == null) result = parseSchemaAttributeTest(tokens);
+        if (result == null) result = parsePITest(tokens);
+        if (result == null) result = parseCommentTest(tokens);
+        if (result == null) result = parseTextTest(tokens);
+        return result;
+    }
 
-        protected ProcessingInstructionTest parsePITest( TokenStream tokens ) {
-            if (tokens.canConsume("processing-instruction", "(")) {
-                if (tokens.canConsume(")")) return new ProcessingInstructionTest(null);
-                String nameOrStringLiteral = tokens.consume();
-                tokens.consume(")");
-                return new ProcessingInstructionTest(nameOrStringLiteral);
-            }
-            return null;
+    protected AnyKindTest parseAnyKindTest( TokenStream tokens ) {
+        if (tokens.canConsume("node", "(", ")")) {
+            return new AnyKindTest();
         }
+        return null;
+    }
 
-        protected CommentTest parseCommentTest( TokenStream tokens ) {
-            if (tokens.canConsume("comment", "(", ")")) {
-                return new CommentTest();
-            }
-            return null;
+    protected ProcessingInstructionTest parsePITest( TokenStream tokens ) {
+        if (tokens.canConsume("processing-instruction", "(")) {
+            if (tokens.canConsume(")")) return new ProcessingInstructionTest(null);
+            String nameOrStringLiteral = tokens.consume();
+            tokens.consume(")");
+            return new ProcessingInstructionTest(nameOrStringLiteral);
         }
+        return null;
+    }
 
-        protected TextTest parseTextTest( TokenStream tokens ) {
-            if (tokens.canConsume("text", "(", ")")) {
-                return new TextTest();
-            }
-            return null;
+    protected CommentTest parseCommentTest( TokenStream tokens ) {
+        if (tokens.canConsume("comment", "(", ")")) {
+            return new CommentTest();
         }
+        return null;
+    }
 
-        protected DocumentTest parseDocumentTest( TokenStream tokens ) {
-            if (tokens.canConsume("document-node", "(")) {
-                // Document test ...
-                ElementTest elementTest = parseElementTest(tokens);
-                DocumentTest result = null;
-                if (elementTest != null) {
-                    result = new DocumentTest(elementTest);
-                } else {
-                    SchemaElementTest schemaTest = parseSchemaElementTest(tokens);
-                    result = schemaTest != null ? new DocumentTest(schemaTest) : null;
-                }
-                tokens.consume(")");
-                return result;
-            }
-            return null;
+    protected TextTest parseTextTest( TokenStream tokens ) {
+        if (tokens.canConsume("text", "(", ")")) {
+            return new TextTest();
         }
+        return null;
+    }
 
-        protected ElementTest parseElementTest( TokenStream tokens ) {
-            if (tokens.canConsume("element", "(")) {
-                if (tokens.canConsume(")") || tokens.canConsume("*", ")")) {
-                    return new ElementTest(new NameTest(null, null), new NameTest(null, null));
-                }
-                ElementTest result = null;
-                NameTest elementName = parseNameTest(tokens);
-                if (tokens.canConsume(",")) {
-                    NameTest typeName = parseNameTest(tokens);
-                    result = new ElementTest(elementName, typeName);
-                } else {
-                    result = new ElementTest(elementName, new NameTest(null, null));
-                }
-                tokens.consume(")");
-                return result;
+    protected DocumentTest parseDocumentTest( TokenStream tokens ) {
+        if (tokens.canConsume("document-node", "(")) {
+            // Document test ...
+            ElementTest elementTest = parseElementTest(tokens);
+            DocumentTest result = null;
+            if (elementTest != null) {
+                result = new DocumentTest(elementTest);
+            } else {
+                SchemaElementTest schemaTest = parseSchemaElementTest(tokens);
+                result = schemaTest != null ? new DocumentTest(schemaTest) : null;
             }
-            return null;
+            tokens.consume(")");
+            return result;
         }
+        return null;
+    }
 
-        protected SchemaElementTest parseSchemaElementTest( TokenStream tokens ) {
-            if (tokens.canConsume("schema-element", "(")) {
-                NameTest elementDeclarationName = parseNameTest(tokens);
-                SchemaElementTest result = new SchemaElementTest(elementDeclarationName);
-                tokens.consume(")");
-                return result;
+    protected ElementTest parseElementTest( TokenStream tokens ) {
+        if (tokens.canConsume("element", "(")) {
+            if (tokens.canConsume(")") || tokens.canConsume("*", ")")) {
+                return new ElementTest(new NameTest(null, null), new NameTest(null, null));
             }
-            return null;
+            ElementTest result = null;
+            NameTest elementName = parseNameTest(tokens);
+            if (tokens.canConsume(",")) {
+                NameTest typeName = parseNameTest(tokens);
+                result = new ElementTest(elementName, typeName);
+                tokens.canConsume('?'); // just eat this
+            } else {
+                result = new ElementTest(elementName, new NameTest(null, null));
+            }
+            tokens.consume(")");
+            return result;
         }
+        return null;
+    }
 
-        protected AttributeTest parseAttributeTest( TokenStream tokens ) {
-            if (tokens.canConsume("attribute", "(")) {
-                if (tokens.canConsume(")") || tokens.canConsume("*", ")")) {
-                    return new AttributeTest(new NameTest(null, null), new NameTest(null, null));
-                }
-                AttributeTest result = null;
-                NameTest attributeName = parseNameTest(tokens);
-                if (tokens.canConsume(",")) {
-                    NameTest typeName = parseNameTest(tokens);
-                    result = new AttributeTest(attributeName, typeName);
-                } else {
-                    result = new AttributeTest(attributeName, new NameTest(null, null));
-                }
-                tokens.consume(")");
-                return result;
-            }
-            return null;
+    protected SchemaElementTest parseSchemaElementTest( TokenStream tokens ) {
+        if (tokens.canConsume("schema-element", "(")) {
+            NameTest elementDeclarationName = parseNameTest(tokens);
+            SchemaElementTest result = new SchemaElementTest(elementDeclarationName);
+            tokens.consume(")");
+            return result;
         }
+        return null;
+    }
 
-        protected SchemaAttributeTest parseSchemaAttributeTest( TokenStream tokens ) {
-            if (tokens.canConsume("schema-attribute", "(")) {
-                NameTest attributeDeclarationName = parseNameTest(tokens);
-                SchemaAttributeTest result = new SchemaAttributeTest(attributeDeclarationName);
-                tokens.consume(")");
-                return result;
+    protected AttributeTest parseAttributeTest( TokenStream tokens ) {
+        if (tokens.canConsume("attribute", "(")) {
+            if (tokens.canConsume(")") || tokens.canConsume("*", ")")) {
+                return new AttributeTest(new NameTest(null, null), new NameTest(null, null));
             }
-            return null;
+            AttributeTest result = null;
+            NameTest attributeName = parseNameTest(tokens);
+            if (tokens.canConsume(",")) {
+                NameTest typeName = parseNameTest(tokens);
+                result = new AttributeTest(attributeName, typeName);
+            } else {
+                result = new AttributeTest(attributeName, new NameTest(null, null));
+            }
+            tokens.consume(")");
+            return result;
         }
+        return null;
+    }
 
-        protected void parseSingleType( TokenStream tokens ) {
+    protected SchemaAttributeTest parseSchemaAttributeTest( TokenStream tokens ) {
+        if (tokens.canConsume("schema-attribute", "(")) {
+            NameTest attributeDeclarationName = parseNameTest(tokens);
+            SchemaAttributeTest result = new SchemaAttributeTest(attributeDeclarationName);
+            tokens.consume(")");
+            return result;
         }
+        return null;
+    }
 
-        protected void parseSequenceType( TokenStream tokens ) {
-        }
+    protected void parseSingleType( TokenStream tokens ) {
+    }
 
-        /**
-         * Remove any leading and trailing single-quotes or double-quotes from the supplied text.
-         * 
-         * @param text the input text; may not be null
-         * @return the text without leading and trailing quotes, or <code>text</code> if there were no square brackets or quotes
-         */
-        protected String removeQuotes( String text ) {
-            return text.replaceFirst("^['\"]+", "").replaceAll("['\"]+$", "");
-        }
+    protected void parseSequenceType( TokenStream tokens ) {
     }
 
     /**
+     * Remove any leading and trailing single-quotes or double-quotes from the supplied text.
+     * 
+     * @param text the input text; may not be null
+     * @return the text without leading and trailing quotes, or <code>text</code> if there were no square brackets or quotes
+     */
+    protected String removeQuotes( String text ) {
+        return text.replaceFirst("^['\"]+", "").replaceAll("['\"]+$", "");
+    }
+
+    protected Component collapse( Component component ) {
+        return XPathQueryParser.COLLAPSE_INNER_COMPONENTS ? component.collapse() : component;
+    }
+
+    /**
      * A {@link TokenStream.Tokenizer} implementation that parses single- and double-quoted strings, symbols, words consisting of
      * {@link TokenStream.CharacterStream#isNextValidXmlNcNameCharacter() NCName}s (as defined by the <a
      * href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">Namespaces in XML 1.0</a> specification), XPath comments,and

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -0,0 +1,65 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.xpath;
+
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.parse.InvalidQueryException;
+import org.jboss.dna.graph.query.parse.QueryParser;
+import org.jboss.dna.jcr.xpath.XPath.Component;
+
+/**
+ * A {@link QueryParser} implementation that accepts XPath expressions and converts them to a {@link QueryCommand DNA Abstract
+ * Query Model} representation.
+ */
+public class XPathQueryParser implements QueryParser {
+
+    static final boolean COLLAPSE_INNER_COMPONENTS = true;
+    private static final String LANGUAGE = "XPath";
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.parse.QueryParser#getLanguage()
+     */
+    public String getLanguage() {
+        return LANGUAGE;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String, org.jboss.dna.graph.ExecutionContext)
+     */
+    public QueryCommand parseQuery( String query,
+                                    ExecutionContext context ) throws InvalidQueryException, ParsingException {
+        Component xpath = new XPathParser(context).parseXPath(query);
+        System.out.println(query);
+        System.out.println(" --> " + xpath);
+        // Convert the result into a QueryCommand ...
+        QueryCommand command = new XPathToQueryTranslator(context, query).createQuery(xpath);
+        return command;
+    }
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslator.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslator.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -0,0 +1,807 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.xpath;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.QueryBuilder;
+import org.jboss.dna.graph.query.QueryBuilder.ConstraintBuilder;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.parse.InvalidQueryException;
+import org.jboss.dna.jcr.xpath.XPath.And;
+import org.jboss.dna.jcr.xpath.XPath.AttributeNameTest;
+import org.jboss.dna.jcr.xpath.XPath.AxisStep;
+import org.jboss.dna.jcr.xpath.XPath.BinaryComponent;
+import org.jboss.dna.jcr.xpath.XPath.Comparison;
+import org.jboss.dna.jcr.xpath.XPath.Component;
+import org.jboss.dna.jcr.xpath.XPath.ContextItem;
+import org.jboss.dna.jcr.xpath.XPath.DescendantOrSelf;
+import org.jboss.dna.jcr.xpath.XPath.ElementTest;
+import org.jboss.dna.jcr.xpath.XPath.Except;
+import org.jboss.dna.jcr.xpath.XPath.FilterStep;
+import org.jboss.dna.jcr.xpath.XPath.FunctionCall;
+import org.jboss.dna.jcr.xpath.XPath.Intersect;
+import org.jboss.dna.jcr.xpath.XPath.Literal;
+import org.jboss.dna.jcr.xpath.XPath.NameTest;
+import org.jboss.dna.jcr.xpath.XPath.NodeTest;
+import org.jboss.dna.jcr.xpath.XPath.Or;
+import org.jboss.dna.jcr.xpath.XPath.ParenthesizedExpression;
+import org.jboss.dna.jcr.xpath.XPath.PathExpression;
+import org.jboss.dna.jcr.xpath.XPath.StepExpression;
+import org.jboss.dna.jcr.xpath.XPath.Union;
+
+/**
+ * A component that translates an {@link XPath} abstract syntax model representation into a {@link QueryCommand DNA abstract query
+ * model}.
+ */
+public class XPathToQueryTranslator {
+
+    protected static final Map<NameTest, PropertyType> CAST_FUNCTION_NAME_TO_TYPE;
+
+    static {
+        Map<NameTest, PropertyType> map = new HashMap<NameTest, PropertyType>();
+        map.put(new NameTest("fn", "string"), PropertyType.STRING);
+        map.put(new NameTest("xs", "string"), PropertyType.STRING);
+        map.put(new NameTest("xs", "base64Binary"), PropertyType.BINARY);
+        map.put(new NameTest("xs", "double"), PropertyType.DOUBLE);
+        map.put(new NameTest("xs", "long"), PropertyType.LONG);
+        map.put(new NameTest("xs", "boolean"), PropertyType.BOOLEAN);
+        map.put(new NameTest("xs", "dateTime"), PropertyType.DATE);
+        map.put(new NameTest("xs", "string"), PropertyType.PATH);
+        map.put(new NameTest("xs", "string"), PropertyType.NAME);
+        map.put(new NameTest("xs", "IDREF"), PropertyType.REFERENCE);
+        CAST_FUNCTION_NAME_TO_TYPE = Collections.unmodifiableMap(map);
+    }
+
+    private final String query;
+    private final ExecutionContext context;
+    private final QueryBuilder builder;
+    private final Set<String> aliases = new HashSet<String>();
+
+    public XPathToQueryTranslator( ExecutionContext context,
+                                   String query ) {
+        this.query = query;
+        this.context = context;
+        this.builder = new QueryBuilder(this.context);
+    }
+
+    public QueryCommand createQuery( Component xpath ) {
+        if (xpath instanceof BinaryComponent) {
+            BinaryComponent binary = (BinaryComponent)xpath;
+            if (binary instanceof Union) {
+                createQuery(binary.getLeft());
+                builder.union();
+                createQuery(binary.getRight());
+                return builder.query();
+            } else if (binary instanceof Intersect) {
+                createQuery(binary.getLeft());
+                builder.intersect();
+                createQuery(binary.getRight());
+                return builder.query();
+            } else if (binary instanceof Except) {
+                createQuery(binary.getLeft());
+                builder.except();
+                createQuery(binary.getRight());
+                return builder.query();
+            }
+        } else if (xpath instanceof PathExpression) {
+            translate((PathExpression)xpath);
+            return builder.query();
+        }
+        // unexpected component ...
+        throw new InvalidQueryException(query,
+                                        "Acceptable XPath queries must lead with a path expression or must be a union, intersect or except");
+    }
+
+    protected void translate( PathExpression pathExpression ) {
+        List<StepExpression> steps = pathExpression.getSteps();
+        assert !steps.isEmpty();
+        if (!pathExpression.isRelative()) {
+            // Absolute path must start with "/jcr:root/" or "//" ...
+            Component first = steps.get(0).collapse();
+            // Result will be NameTest("jcr","root") or DescendantOrSelf ...
+            if (first instanceof DescendantOrSelf) {
+                // do nothing ...
+            } else if (first instanceof NameTest && steps.size() > 1 && ((NameTest)first).matches("jcr", "root")) {
+                // We can actually remove this first step, since relative paths are relative to the root ...
+                steps = steps.subList(1, steps.size());
+            } else {
+                throw new InvalidQueryException(query, "An absolute path expression must start with '//' or '/jcr:root/...'");
+            }
+        }
+
+        // Walk the steps along the path expression ...
+        ConstraintBuilder where = builder.where();
+        List<StepExpression> path = new ArrayList<StepExpression>();
+        String tableName = null;
+        for (StepExpression step : steps) {
+            if (step instanceof AxisStep) {
+                AxisStep axis = (AxisStep)step;
+                NodeTest nodeTest = axis.getNodeTest();
+                if (nodeTest instanceof NameTest) {
+                    if (appliesToPathConstraint(axis.getPredicates())) {
+                        // Can go into the path constraint ...
+                        path.add(step);
+                    } else {
+                        // The constraints are more complicated, so we need to define a new source/table ...
+                        // path.add(step);
+                        tableName = translateSource(tableName, path, where);
+                        translatePredicates(axis.getPredicates(), tableName, where);
+                        path.clear();
+                    }
+                } else if (nodeTest instanceof ElementTest) {
+                    // We need to build a new source with the partial path we have so far ...
+                    tableName = translateElementTest((ElementTest)nodeTest, path, where);
+                    translatePredicates(axis.getPredicates(), tableName, where);
+                    path.clear();
+                } else if (nodeTest instanceof AttributeNameTest) {
+                    AttributeNameTest attributeName = (AttributeNameTest)nodeTest;
+                    builder.select(nameFrom(attributeName.getNameTest()));
+                } else {
+                    throw new InvalidQueryException(query, "The '" + step + "' step is not supported");
+                }
+            } else if (step instanceof FilterStep) {
+                FilterStep filter = (FilterStep)step;
+                Component primary = filter.getPrimaryExpression();
+                List<Component> predicates = filter.getPredicates();
+                if (primary instanceof ContextItem) {
+                    if (appliesToPathConstraint(predicates)) {
+                        // Can ignore the '.' ...
+                    } else {
+                        // The constraints are more complicated, so we need to define a new source/table ...
+                        path.add(step);
+                        tableName = translateSource(tableName, path, where);
+                        translatePredicates(predicates, tableName, where);
+                        path.clear();
+                    }
+                } else if (primary instanceof Literal) {
+                    throw new InvalidQueryException(query,
+                                                    "A literal is not supported in the primary path expression; therefore '"
+                                                    + primary + "' is not valid");
+                } else if (primary instanceof FunctionCall) {
+                    throw new InvalidQueryException(query,
+                                                    "A function call is not supported in the primary path expression; therefore '"
+                                                    + primary + "' is not valid");
+                } else if (primary instanceof ParenthesizedExpression) {
+                    // This can be used to define an OR-ed set of expressions defining select columns ...
+                    ParenthesizedExpression paren = (ParenthesizedExpression)primary;
+                    Component wrapped = paren.getWrapped().collapse();
+                    if (wrapped instanceof AttributeNameTest) {
+                        AttributeNameTest attributeName = (AttributeNameTest)wrapped;
+                        builder.select(nameFrom(attributeName.getNameTest()));
+                    } else if (wrapped instanceof BinaryComponent) {
+                        for (AttributeNameTest attributeName : extractAttributeNames((BinaryComponent)wrapped)) {
+                            builder.select(nameFrom(attributeName.getNameTest()));
+                        }
+                        path.add(filter); // in case any element names are there
+                    } else {
+                        throw new InvalidQueryException(query,
+                                                        "A parenthesized expression of this type is not supported in the primary path expression; therefore '"
+                                                        + primary + "' is not valid");
+                    }
+                }
+
+            } else {
+                path.add(step);
+            }
+        }
+        if (!path.isEmpty()) {
+            translateSource(tableName, path, where);
+        }
+        where.end();
+    }
+
+    /**
+     * Find any {@link AttributeNameTest attribute names} that have been unioned together (with '|'). Any other combination of
+     * objects results in an error.
+     * 
+     * @param binary the binary component
+     * @return the list of attribute names, if that's all that's in the supplied component; may be empty
+     */
+    protected List<AttributeNameTest> extractAttributeNames( BinaryComponent binary ) {
+        List<AttributeNameTest> results = new ArrayList<AttributeNameTest>();
+        boolean failed = false;
+        if (binary instanceof Union) {
+            for (int i = 0; i != 2; ++i) {
+                Component comp = i == 0 ? binary.getLeft() : binary.getRight();
+                comp = comp.collapse();
+                if (comp instanceof Union) {
+                    results.addAll(extractAttributeNames((BinaryComponent)comp));
+                } else if (comp instanceof AttributeNameTest) {
+                    results.add((AttributeNameTest)comp);
+                } else if (comp instanceof NameTest) {
+                    // Element names, which are fine but we'll ignore
+                } else {
+                    failed = true;
+                    break;
+                }
+            }
+        } else {
+            failed = true;
+        }
+        if (failed) {
+            throw new InvalidQueryException(query,
+                                            "A parenthesized expression in a path step may only contain ORed and ANDed attribute names or element names; therefore '"
+                                            + binary + "' is not valid");
+        }
+        return results;
+    }
+
+    /**
+     * Find any {@link NameTest element names} that have been unioned together (with '|'). Any other combination of objects
+     * results in an error.
+     * 
+     * @param binary the binary component
+     * @return the list of attribute names, if that's all that's in the supplied component; may be empty
+     */
+    protected List<NameTest> extractElementNames( BinaryComponent binary ) {
+        List<NameTest> results = new ArrayList<NameTest>();
+        boolean failed = false;
+        if (binary instanceof Union) {
+            for (int i = 0; i != 2; ++i) {
+                Component comp = i == 0 ? binary.getLeft() : binary.getRight();
+                comp = comp.collapse();
+                if (comp instanceof Union) {
+                    results.addAll(extractElementNames((BinaryComponent)comp));
+                } else if (comp instanceof AttributeNameTest) {
+                    // ignore these ...
+                } else if (comp instanceof NameTest) {
+                    results.add((NameTest)comp);
+                } else {
+                    failed = true;
+                    break;
+                }
+            }
+        } else {
+            failed = true;
+        }
+        if (failed) {
+            throw new InvalidQueryException(query,
+                                            "A parenthesized expression in a path step may only contain ORed element names; therefore '"
+                                            + binary + "' is not valid");
+        }
+        return results;
+    }
+
+    protected String translateSource( String tableName,
+                                      List<StepExpression> path,
+                                      ConstraintBuilder where ) {
+        String alias = newAlias();
+        if (tableName != null) {
+            // This is after some element(...) steps, so we need to join ...
+            builder.joinAllNodesAs(alias);
+        } else {
+            // This is the only part of the query ...
+            builder.fromAllNodesAs(alias);
+        }
+        tableName = alias;
+        if (path.size() == 1 && path.get(0).collapse() instanceof NameTest) {
+            // Node immediately below root ...
+            NameTest nodeName = (NameTest)path.get(0).collapse();
+            where.nodeName(alias).isEqualTo(nameFrom(nodeName)).and().depth(alias).isEqualTo(1);
+        } else if (path.size() == 2 && path.get(0) instanceof DescendantOrSelf && path.get(1).collapse() instanceof NameTest) {
+            // Node anywhere ...
+            NameTest nodeName = (NameTest)path.get(1).collapse();
+            if (!nodeName.isWildcard()) {
+                where.nodeName(alias).isEqualTo(nameFrom(nodeName));
+            }
+        } else {
+            // Must be just a bunch of descendant-or-self, axis and filter steps ...
+            translatePathExpressionConstraint(new PathExpression(true, path), where, alias);
+        }
+        return tableName;
+    }
+
+    protected String translateElementTest( ElementTest elementTest,
+                                           List<StepExpression> pathConstraint,
+                                           ConstraintBuilder where ) {
+        String tableName = null;
+        NameTest typeName = elementTest.getTypeName();
+        if (typeName.isWildcard()) {
+            tableName = newAlias();
+            builder.fromAllNodesAs(tableName);
+        } else {
+            if (typeName.getLocalTest() == null) {
+                throw new InvalidQueryException(
+                                                query,
+                                                "The '"
+                                                + elementTest
+                                                + "' clause uses a partial wildcard in the type name, but only a wildcard on the whole name is supported");
+            }
+            tableName = nameFrom(typeName);
+            builder.from(tableName);
+        }
+        if (elementTest.getElementName() != null) {
+            NameTest nodeName = elementTest.getElementName();
+            if (!nodeName.isWildcard()) {
+                where.nodeName(tableName).isEqualTo(nameFrom(nodeName));
+            }
+        }
+        if (pathConstraint.isEmpty()) {
+            where.depth(tableName).isEqualTo(1);
+        }
+        return tableName;
+    }
+
+    protected void translatePredicates( List<Component> predicates,
+                                        String tableName,
+                                        ConstraintBuilder where ) {
+        assert tableName != null;
+        for (Component predicate : predicates) {
+            translatePredicate(predicate, tableName, where);
+        }
+    }
+
+    protected String translatePredicate( Component predicate,
+                                         String tableName,
+                                         ConstraintBuilder where ) {
+        predicate = predicate.collapse();
+        assert tableName != null;
+        if (predicate instanceof ParenthesizedExpression) {
+            ParenthesizedExpression paren = (ParenthesizedExpression)predicate;
+            where = where.openParen();
+            translatePredicate(paren.getWrapped(), tableName, where);
+            where.closeParen();
+        } else if (predicate instanceof And) {
+            And and = (And)predicate;
+            where = where.openParen();
+            translatePredicate(and.getLeft(), tableName, where);
+            where.and();
+            translatePredicate(and.getRight(), tableName, where);
+            where.closeParen();
+        } else if (predicate instanceof Or) {
+            Or or = (Or)predicate;
+            where = where.openParen();
+            translatePredicate(or.getLeft(), tableName, where);
+            where.or();
+            translatePredicate(or.getRight(), tableName, where);
+            where.closeParen();
+        } else if (predicate instanceof Union) {
+            Union union = (Union)predicate;
+            where = where.openParen();
+            translatePredicate(union.getLeft(), tableName, where);
+            where.or();
+            translatePredicate(union.getRight(), tableName, where);
+            where.closeParen();
+        } else if (predicate instanceof Literal) {
+            Literal literal = (Literal)predicate;
+            if (literal.isInteger()) return tableName; // do nothing, since this is a path constraint and is handled elsewhere
+        } else if (predicate instanceof AttributeNameTest) {
+            // This adds the criteria that the attribute exists, and adds it to the select ...
+            AttributeNameTest attribute = (AttributeNameTest)predicate;
+            String propertyName = nameFrom(attribute.getNameTest());
+            where.hasProperty(tableName, propertyName);
+        } else if (predicate instanceof NameTest) {
+            // This adds the criteria that the child node exists ...
+            NameTest childName = (NameTest)predicate;
+            String alias = newAlias();
+            builder.joinAllNodesAs(alias).onChildNode(tableName, alias);
+            if (!childName.isWildcard()) where.nodeName(alias).isEqualTo(nameFrom(childName));
+            tableName = alias;
+        } else if (predicate instanceof Comparison) {
+            Comparison comparison = (Comparison)predicate;
+            Component left = comparison.getLeft();
+            Component right = comparison.getRight();
+            Operator operator = comparison.getOperator();
+            if (left instanceof Literal) {
+                Component temp = left;
+                left = right;
+                right = temp;
+                operator = operator.getReverse();
+            }
+            if (left instanceof AttributeNameTest) {
+                AttributeNameTest attribute = (AttributeNameTest)left;
+                String propertyName = nameFrom(attribute.getNameTest());
+                if (right instanceof Literal) {
+                    String value = ((Literal)right).getValue();
+                    where.propertyValue(tableName, propertyName).is(operator, value);
+                } else if (right instanceof FunctionCall) {
+                    FunctionCall call = (FunctionCall)right;
+                    NameTest functionName = call.getName();
+                    List<Component> parameters = call.getParameters();
+                    // Is this a cast ...
+                    PropertyType castType = CAST_FUNCTION_NAME_TO_TYPE.get(functionName);
+                    if (castType != null) {
+                        if (parameters.size() == 1 && parameters.get(0).collapse() instanceof Literal) {
+                            // The first parameter can be the type name (or table name) ...
+                            Literal value = (Literal)parameters.get(0).collapse();
+                            where.propertyValue(tableName, propertyName).is(operator).cast(value.getValue()).as(castType);
+                        } else {
+                            throw new InvalidQueryException(query, "A cast function requires one literal parameter; therefore '"
+                                                                   + comparison + "' is not valid");
+                        }
+                    } else {
+                        throw new InvalidQueryException(query,
+                                                        "Only the 'jcr:score' function is allowed in a comparison predicate; therefore '"
+                                                        + comparison + "' is not valid");
+                    }
+                }
+            } else if (left instanceof FunctionCall && right instanceof Literal) {
+                FunctionCall call = (FunctionCall)left;
+                NameTest functionName = call.getName();
+                List<Component> parameters = call.getParameters();
+                String value = ((Literal)right).getValue();
+                if (functionName.matches("jcr", "score")) {
+                    String scoreTableName = tableName;
+                    if (parameters.isEmpty()) {
+                        scoreTableName = tableName;
+                    } else if (parameters.size() == 1 && parameters.get(0) instanceof NameTest) {
+                        // The first parameter can be the type name (or table name) ...
+                        NameTest name = (NameTest)parameters.get(0);
+                        if (!name.isWildcard()) scoreTableName = nameFrom(name);
+                    } else {
+                        throw new InvalidQueryException(query,
+                                                        "The 'jcr:score' function may have no parameters or the type name as the only parameter.");
+
+                    }
+                    where.fullTextSearchScore(scoreTableName).is(operator, value);
+                } else {
+                    throw new InvalidQueryException(query,
+                                                    "Only the 'jcr:score' function is allowed in a comparison predicate; therefore '"
+                                                    + comparison + "' is not valid");
+                }
+            }
+        } else if (predicate instanceof FunctionCall) {
+            FunctionCall call = (FunctionCall)predicate;
+            NameTest functionName = call.getName();
+            List<Component> parameters = call.getParameters();
+            Component param1 = parameters.size() > 0 ? parameters.get(0) : null;
+            Component param2 = parameters.size() > 1 ? parameters.get(1) : null;
+            if (functionName.matches(null, "not")) {
+                if (parameters.size() != 1) {
+                    throw new InvalidQueryException(query, "The 'not' function requires one parameter; therefore '" + predicate
+                                                           + "' is not valid");
+                }
+                where = where.not().openParen();
+                translatePredicate(param1, tableName, where);
+                where.closeParen();
+            } else if (functionName.matches("jcr", "like")) {
+                if (parameters.size() != 2) {
+                    throw new InvalidQueryException(query, "The 'jcr:like' function requires two parameters; therefore '"
+                                                           + predicate + "' is not valid");
+                }
+                if (!(param1 instanceof AttributeNameTest)) {
+                    throw new InvalidQueryException(query,
+                                                    "The first parameter of 'jcr:like' must be an property reference with the '@' symbol; therefore '"
+                                                    + predicate + "' is not valid");
+                }
+                if (!(param2 instanceof Literal)) {
+                    throw new InvalidQueryException(query, "The second parameter of 'jcr:like' must be a literal; therefore '"
+                                                           + predicate + "' is not valid");
+                }
+                NameTest attributeName = ((AttributeNameTest)param1).getNameTest();
+                String value = ((Literal)param2).getValue();
+                where.propertyValue(tableName, nameFrom(attributeName)).isLike(value);
+            } else if (functionName.matches("jcr", "contains")) {
+                if (parameters.size() != 2) {
+                    throw new InvalidQueryException(query, "The 'jcr:contains' function requires two parameters; therefore '"
+                                                           + predicate + "' is not valid");
+                }
+                if (!(param2 instanceof Literal)) {
+                    throw new InvalidQueryException(query,
+                                                    "The second parameter of 'jcr:contains' must be a literal; therefore '"
+                                                    + predicate + "' is not valid");
+                }
+                String value = ((Literal)param2).getValue();
+                if (param1 instanceof ContextItem) {
+                    // refers to the current node (or table) ...
+                    where.search(tableName, value);
+                } else if (param1 instanceof AttributeNameTest) {
+                    // refers to an attribute on the current node (or table) ...
+                    NameTest attributeName = ((AttributeNameTest)param1).getNameTest();
+                    where.search(tableName, nameFrom(attributeName), value);
+                } else if (param1 instanceof NameTest) {
+                    // refers to child node, so we need to add a join ...
+                    String alias = newAlias();
+                    builder.joinAllNodesAs(alias).onChildNode(tableName, alias);
+                    // Now add the criteria ...
+                    where.search(alias, value);
+                    tableName = alias;
+                } else if (param1 instanceof PathExpression) {
+                    // refers to a descendant node ...
+                    PathExpression pathExpr = (PathExpression)param1;
+                    if (pathExpr.getLastStep().collapse() instanceof AttributeNameTest) {
+                        AttributeNameTest attributeName = (AttributeNameTest)pathExpr.getLastStep().collapse();
+                        pathExpr = pathExpr.withoutLast();
+                        String searchTable = translatePredicate(pathExpr, tableName, where);
+                        if (attributeName.getNameTest().isWildcard()) {
+                            where.search(searchTable, value);
+                        } else {
+                            where.search(searchTable, nameFrom(attributeName.getNameTest()), value);
+                        }
+                    } else {
+                        String searchTable = translatePredicate(param1, tableName, where);
+                        where.search(searchTable, value);
+                    }
+                } else {
+                    throw new InvalidQueryException(query,
+                                                    "The first parameter of 'jcr:contains' must be a relative path (e.g., '.', an attribute name, a child name, etc.); therefore '"
+                                                    + predicate + "' is not valid");
+                }
+            } else if (functionName.matches("jcr", "deref")) {
+                throw new InvalidQueryException(query,
+                                                "The 'jcr:deref' function is not required by JCR and is not currently supported; therefore '"
+                                                + predicate + "' is not valid");
+            } else {
+                throw new InvalidQueryException(query,
+                                                "Only the 'jcr:like' and 'jcr:contains' functions are allowed in a predicate; therefore '"
+                                                + predicate + "' is not valid");
+            }
+        } else if (predicate instanceof PathExpression) {
+            // Requires that the descendant node with the relative path does exist ...
+            PathExpression pathExpr = (PathExpression)predicate;
+            List<StepExpression> steps = pathExpr.getSteps();
+            assert steps.size() > 1; // 1 or 0 would have been collapsed ...
+            Component firstStep = steps.get(0).collapse();
+            if (firstStep instanceof ContextItem) {
+                // Remove the context and retry ...
+                return translatePredicate(new PathExpression(true, steps.subList(1, steps.size())), tableName, where);
+            }
+            if (firstStep instanceof NameTest) {
+                // Special case where this is similar to '[a/@id]'
+                NameTest childName = (NameTest)firstStep;
+                String alias = newAlias();
+                builder.joinAllNodesAs(alias).onChildNode(tableName, alias);
+                if (!childName.isWildcard()) {
+                    where.nodeName(alias).isEqualTo(nameFrom(childName));
+                }
+                return translatePredicate(new PathExpression(true, steps.subList(1, steps.size())), alias, where);
+            }
+            if (firstStep instanceof DescendantOrSelf) {
+                // Special case where this is similar to '[a/@id]'
+                String alias = newAlias();
+                builder.joinAllNodesAs(alias).onDescendant(tableName, alias);
+                return translatePredicate(new PathExpression(true, steps.subList(1, steps.size())), alias, where);
+            }
+            // Add the join ...
+            String alias = newAlias();
+            builder.joinAllNodesAs(alias).onDescendant(tableName, alias);
+            // Now add the criteria ...
+            translatePathExpressionConstraint(pathExpr, where, alias);
+        } else {
+            throw new InvalidQueryException(query, "Unsupported criteria '" + predicate + "'");
+        }
+        return tableName;
+    }
+
+    /**
+     * Determine if the predicates contain any expressions that cannot be put into a LIKE constraint on the path.
+     * 
+     * @param predicates the predicates
+     * @return true if the supplied predicates can be handled entirely in the LIKE constraint on the path, or false if they have
+     *         to be handled as other criteria
+     */
+    protected boolean appliesToPathConstraint( List<Component> predicates ) {
+        if (predicates.isEmpty()) return true;
+        if (predicates.size() > 1) return false;
+        assert predicates.size() == 1;
+        Component predicate = predicates.get(0);
+        if (predicate instanceof Literal && ((Literal)predicate).isInteger()) return true;
+        if (predicate instanceof NameTest && ((NameTest)predicate).isWildcard()) return true;
+        return false;
+    }
+
+    protected boolean translatePathExpressionConstraint( PathExpression pathExrp,
+                                                         ConstraintBuilder where,
+                                                         String tableName ) {
+        String[] paths = relativePathLikeExpressions(pathExrp);
+        if (paths == null || paths.length == 0) return false;
+        where = where.openParen();
+        boolean first = true;
+        int number = 0;
+        for (String path : paths) {
+            if (path == null || path.length() == 0 || path.equals("%/")) continue;
+            if (first) first = false;
+            else where.or();
+            if (path.indexOf('%') != -1) {
+                where.path(tableName).isLike(path);
+            } else {
+                where.path(tableName).isEqualTo(path);
+            }
+            ++number;
+        }
+        if (number > 0) where.closeParen();
+        return true;
+    }
+
+    protected String[] relativePathLikeExpressions( PathExpression pathExpression ) {
+        List<StepExpression> steps = pathExpression.getSteps();
+        if (steps.isEmpty()) return new String[] {};
+        if (steps.size() == 1 && steps.get(0) instanceof DescendantOrSelf) return new String[] {};
+        PathLikeBuilder builder = new SinglePathLikeBuilder();
+        for (StepExpression step : steps) {
+            if (step instanceof DescendantOrSelf) {
+                if (builder.isEmpty()) {
+                    builder.append("%/");
+                } else {
+                    builder = new DualPathLikeBuilder(builder.clone(), builder.append("/%"));
+                }
+            } else if (step instanceof AxisStep) {
+                AxisStep axis = (AxisStep)step;
+                NodeTest nodeTest = axis.getNodeTest();
+                assert !(nodeTest instanceof ElementTest);
+                if (nodeTest instanceof NameTest) {
+                    NameTest nameTest = (NameTest)nodeTest;
+                    builder.append('/');
+                    if (nameTest.getPrefixTest() != null) {
+                        builder.append(nameTest.getPrefixTest()).append(':');
+                    }
+                    if (nameTest.getLocalTest() != null) {
+                        builder.append(nameTest.getLocalTest());
+                    } else {
+                        builder.append('%');
+                    }
+                    List<Component> predicates = axis.getPredicates();
+                    if (!predicates.isEmpty()) {
+                        assert predicates.size() == 1;
+                        Component predicate = predicates.get(0);
+                        if (predicate instanceof Literal && ((Literal)predicate).isInteger()) {
+                            builder.append('[').append(((Literal)predicate).getValue()).append(']');
+                        }
+                    }
+                }
+            } else if (step instanceof FilterStep) {
+                FilterStep filter = (FilterStep)step;
+                Component primary = filter.getPrimaryExpression();
+                if (primary instanceof ContextItem) {
+                    continue; // ignore this '.'
+                } else if (primary instanceof ParenthesizedExpression) {
+                    ParenthesizedExpression paren = (ParenthesizedExpression)primary;
+                    Component wrapped = paren.getWrapped().collapse();
+                    if (wrapped instanceof AttributeNameTest) {
+                        // ignore this; handled earlier ...
+                    } else if (wrapped instanceof BinaryComponent) {
+                        List<NameTest> names = extractElementNames((BinaryComponent)wrapped);
+                        if (names.size() >= 1) {
+                            PathLikeBuilder orig = builder.clone();
+                            builder.append('/').append(nameFrom(names.get(0)));
+                            if (names.size() > 1) {
+                                for (NameTest name : names.subList(1, names.size())) {
+                                    builder = new DualPathLikeBuilder(orig.clone().append('/').append(nameFrom(name)), builder);
+                                }
+                            }
+                        }
+                    } else {
+                        throw new InvalidQueryException(query,
+                                                        "A parenthesized expression of this type is not supported in the primary path expression; therefore '"
+                                                        + primary + "' is not valid");
+                    }
+                }
+            }
+        }
+        return builder.getPaths();
+    }
+
+    protected static interface PathLikeBuilder {
+        PathLikeBuilder append( String string );
+
+        PathLikeBuilder append( char c );
+
+        boolean isEmpty();
+
+        PathLikeBuilder clone();
+
+        String[] getPaths();
+    }
+
+    protected static class SinglePathLikeBuilder implements PathLikeBuilder {
+        private final StringBuilder builder = new StringBuilder();
+
+        public SinglePathLikeBuilder append( String string ) {
+            builder.append(string);
+            return this;
+        }
+
+        public SinglePathLikeBuilder append( char c ) {
+            builder.append(c);
+            return this;
+        }
+
+        public boolean isEmpty() {
+            return builder.length() == 0;
+        }
+
+        @Override
+        public SinglePathLikeBuilder clone() {
+            return new SinglePathLikeBuilder().append(builder.toString());
+        }
+
+        @Override
+        public String toString() {
+            return builder.toString();
+        }
+
+        public String[] getPaths() {
+            return isEmpty() ? new String[] {} : new String[] {builder.toString()};
+        }
+    }
+
+    protected static class DualPathLikeBuilder implements PathLikeBuilder {
+        private final PathLikeBuilder builder1;
+        private final PathLikeBuilder builder2;
+
+        protected DualPathLikeBuilder( PathLikeBuilder builder1,
+                                       PathLikeBuilder builder2 ) {
+            this.builder1 = builder1;
+            this.builder2 = builder2;
+        }
+
+        public DualPathLikeBuilder append( String string ) {
+            builder1.append(string);
+            builder2.append(string);
+            return this;
+        }
+
+        public DualPathLikeBuilder append( char c ) {
+            builder1.append(c);
+            builder2.append(c);
+            return this;
+        }
+
+        public boolean isEmpty() {
+            return false;
+        }
+
+        @Override
+        public DualPathLikeBuilder clone() {
+            return new DualPathLikeBuilder(builder1.clone(), builder2.clone());
+        }
+
+        public String[] getPaths() {
+            String[] paths1 = builder1.getPaths();
+            String[] paths2 = builder2.getPaths();
+            String[] result = new String[paths1.length + paths2.length];
+            System.arraycopy(paths1, 0, result, 0, paths1.length);
+            System.arraycopy(paths2, 0, result, paths1.length, paths2.length);
+            return result;
+        }
+    }
+
+    protected String nameFrom( NameTest name ) {
+        String prefix = name.getPrefixTest();
+        String local = name.getLocalTest();
+        assert local != null;
+        return (prefix != null ? prefix + ":" : "") + local;
+    }
+
+    protected String newAlias() {
+        String root = "nodeSet";
+        int num = 1;
+        String alias = root + num;
+        while (aliases.contains(alias)) {
+            num += 1;
+            alias = root + num;
+        }
+        aliases.add(alias);
+        return alias;
+    }
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslator.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathParserTest.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -36,11 +36,13 @@
 import org.jboss.dna.common.text.TokenStream.Tokenizer;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.jcr.xpath.XPath.And;
 import org.jboss.dna.jcr.xpath.XPath.AnyKindTest;
 import org.jboss.dna.jcr.xpath.XPath.AttributeNameTest;
 import org.jboss.dna.jcr.xpath.XPath.AttributeTest;
 import org.jboss.dna.jcr.xpath.XPath.AxisStep;
 import org.jboss.dna.jcr.xpath.XPath.CommentTest;
+import org.jboss.dna.jcr.xpath.XPath.Comparison;
 import org.jboss.dna.jcr.xpath.XPath.Component;
 import org.jboss.dna.jcr.xpath.XPath.ContextItem;
 import org.jboss.dna.jcr.xpath.XPath.DescendantOrSelf;
@@ -51,15 +53,16 @@
 import org.jboss.dna.jcr.xpath.XPath.Literal;
 import org.jboss.dna.jcr.xpath.XPath.NameTest;
 import org.jboss.dna.jcr.xpath.XPath.NodeTest;
+import org.jboss.dna.jcr.xpath.XPath.Or;
 import org.jboss.dna.jcr.xpath.XPath.ParenthesizedExpression;
 import org.jboss.dna.jcr.xpath.XPath.PathExpression;
 import org.jboss.dna.jcr.xpath.XPath.SchemaAttributeTest;
 import org.jboss.dna.jcr.xpath.XPath.SchemaElementTest;
 import org.jboss.dna.jcr.xpath.XPath.StepExpression;
 import org.jboss.dna.jcr.xpath.XPath.TextTest;
-import org.jboss.dna.jcr.xpath.XPathParser.XPathTokenizer;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -68,12 +71,12 @@
 public class XPathParserTest {
 
     private ExecutionContext context;
-    private XPathParser.Parser parser;
+    private XPathParser parser;
 
     @Before
     public void beforeEach() {
         context = new ExecutionContext();
-        parser = new XPathParser.Parser(context);
+        parser = new XPathParser(context);
     }
 
     @After
@@ -85,19 +88,59 @@
     public void shouldParseXPathExpressions() {
         assertParsable("/jcr:root/a/b/c");
         assertParsable("/jcr:root/a/b/c[*]");
-        assertParsable("/jcr:root/some[1]/element(nodes, my:type)[1]");
+        assertParsable("/jcr:root/some[1]/element(nodes, my:type)[2]");
         assertParsable("//element(*,my:type)");
         assertParsable("//element(*,my:type)[@jcr:title='something' and @globalProperty='something else']");
         assertParsable("//element(*,my:type)[@jcr:title | @globalProperty]");
+        assertParsable("//element(*,my:type)/(@jcr:title | @globalProperty)");
         assertParsable("//element(*, my:type) order by @my:title");
         assertParsable("//element(*, my:type) [jcr:contains(., 'jcr')] order by jcr:score() descending");
         assertParsable("//element(*, employee)[@secretary and @assistant]");
+        assertParsable("//element(*, employee)[@secretary or @assistant]");
     }
 
     @Test
+    public void shouldParseXPathExpressions2() {
+        assertParsable("/jcr:root/a/b/c", pathExpr(axisStep(nameTest("jcr", "root")),
+                                                   axisStep(nameTest("a")),
+                                                   axisStep(nameTest("b")),
+                                                   axisStep(nameTest("c"))));
+        assertParsable("/jcr:root/a/b/c[*]", pathExpr(axisStep(nameTest("jcr", "root")),
+                                                      axisStep(nameTest("a")),
+                                                      axisStep(nameTest("b")),
+                                                      axisStep(nameTest("c"), wildcard())));
+        assertParsable("/jcr:root/some[1]", pathExpr(axisStep(nameTest("jcr", "root")), axisStep(nameTest("some"), literal("1"))));
+        assertParsable("/jcr:root/element(*)", pathExpr(axisStep(nameTest("jcr", "root")), axisStep(element(wildcard(),
+                                                                                                            wildcard()))));
+        assertParsable("/jcr:root/element(name)", pathExpr(axisStep(nameTest("jcr", "root")), axisStep(element(nameTest("name"),
+                                                                                                               wildcard()))));
+        assertParsable("/jcr:root/element(*, *)", pathExpr(axisStep(nameTest("jcr", "root")), axisStep(element(wildcard(),
+                                                                                                               wildcard()))));
+        assertParsable("/jcr:root/element(*, my:type)", pathExpr(axisStep(nameTest("jcr", "root")),
+                                                                 axisStep(element(wildcard(), nameTest("my", "type")))));
+        assertParsable("/jcr:root/element(ex:name, my:type)",
+                       pathExpr(axisStep(nameTest("jcr", "root")), axisStep(element(nameTest("ex", "name"),
+                                                                                    nameTest("my", "type")))));
+        assertParsable("/jcr:root/element(name, my:type)", pathExpr(axisStep(nameTest("jcr", "root")),
+                                                                    axisStep(element(nameTest("name"), nameTest("my", "type")))));
+        assertParsable("/jcr:root/element(name, type)", pathExpr(axisStep(nameTest("jcr", "root")),
+                                                                 axisStep(element(nameTest("name"), nameTest("type")))));
+        assertParsable("/jcr:root/some[1]/element(nodes, my:type)[1]", pathExpr(axisStep(nameTest("jcr", "root")),
+                                                                                axisStep(nameTest("some"), literal("1")),
+                                                                                axisStep(element(nameTest("nodes"),
+                                                                                                 nameTest("my", "type")),
+                                                                                         literal("1"))));
+        assertParsable("/jcr:root/some[1]/element(*, my:type)[1]", pathExpr(axisStep(nameTest("jcr", "root")),
+                                                                            axisStep(nameTest("some"), literal("1")),
+                                                                            axisStep(element(wildcard(), nameTest("my", "type")),
+                                                                                     literal("1"))));
+    }
+
+    @Ignore
+    @Test
     public void shouldParseXPathExpressionsThatCombineSeparateExpressions() {
-        assertParsable("/jcr:root/a/b/c and /jcr:root/c/d/e");
-        assertParsable("/jcr:root/a/b/c and /jcr:root/c/d/e or /jcr:root/f/g/h");
+        assertParsable("/jcr:root/a/b/c union /jcr:root/c/d/e");
+        assertParsable("/jcr:root/a/b/c union /jcr:root/c/d/e intersect /jcr:root/f/g/h");
     }
 
     @Test
@@ -116,16 +159,14 @@
 
     @Test
     public void shouldParsePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNamePathPredicate() {
-        assertThat(parser.parsePathExpr(tokenize("//.[c]")), is(pathExpr(descendantOrSelf(),
-                                                                         filterStep(contextItem(),
-                                                                                    relativePathExpr(axisStep(nameTest("c")))))));
+        assertThat(parser.parsePathExpr(tokenize("//.[c]")), is(pathExpr(descendantOrSelf(), filterStep(contextItem(),
+                                                                                                        nameTest("c")))));
     }
 
     @Test
     public void shouldParsePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNumericLiteralPredicate() {
-        assertThat(parser.parsePathExpr(tokenize("//.[3]")), is(pathExpr(descendantOrSelf(),
-                                                                         filterStep(contextItem(),
-                                                                                    relativePathExpr(filterStep(literal("3")))))));
+        assertThat(parser.parsePathExpr(tokenize("//.[3]")), is(pathExpr(descendantOrSelf(), filterStep(contextItem(),
+                                                                                                        literal("3")))));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -148,17 +189,13 @@
     @Test
     public void shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNamePathPredicate() {
         assertThat(parser.parseRelativePathExpr(tokenize("a//.[c]")),
-                   is(relativePathExpr(axisStep(nameTest("a")),
-                                       descendantOrSelf(),
-                                       filterStep(contextItem(), relativePathExpr(axisStep(nameTest("c")))))));
+                   is(relativePathExpr(axisStep(nameTest("a")), descendantOrSelf(), filterStep(contextItem(), nameTest("c")))));
     }
 
     @Test
     public void shouldParseRelativePathExpressionWithAbbreviatedDescendantOrSelfWithRelativeNumericLiteralPredicate() {
         assertThat(parser.parseRelativePathExpr(tokenize("a//.[3]")),
-                   is(relativePathExpr(axisStep(nameTest("a")),
-                                       descendantOrSelf(),
-                                       filterStep(contextItem(), relativePathExpr(filterStep(literal("3")))))));
+                   is(relativePathExpr(axisStep(nameTest("a")), descendantOrSelf(), filterStep(contextItem(), literal("3")))));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -167,8 +204,7 @@
 
     @Test
     public void shouldParseStepExpressionFromParenthesizedLiteral() {
-        assertThat(parser.parseStepExpr(tokenize("('foo')")),
-                   is((StepExpression)filterStep(paren(relativePathExpr(filterStep(literal("foo")))))));
+        assertThat(parser.parseStepExpr(tokenize("('foo')")), is((StepExpression)filterStep(paren(literal("foo")))));
     }
 
     @Test
@@ -178,22 +214,20 @@
 
     @Test
     public void shouldParseStepExpressionFromFunctionCallWithUnqualifiedName() {
-        assertThat(parser.parseStepExpr(tokenize("element(*,*)")),
-                   is((StepExpression)filterStep(functionCall(nameTest("element"),
-                                                              relativePathExpr(axisStep(wildcard())),
-                                                              relativePathExpr(axisStep(wildcard()))))));
+        assertThat(parser.parseStepExpr(tokenize("element2(*,*)")),
+                   is((StepExpression)filterStep(functionCall(nameTest("element2"), wildcard(), wildcard()))));
     }
 
     @Test
     public void shouldParseStepExpressionFromFunctionCallWithQualifiedName() {
         assertThat(parser.parseStepExpr(tokenize("foo:bar(*)")),
-                   is((StepExpression)filterStep(functionCall(nameTest("foo", "bar"), relativePathExpr(axisStep(wildcard()))))));
+                   is((StepExpression)filterStep(functionCall(nameTest("foo", "bar"), wildcard()))));
     }
 
     @Test
     public void shouldParseStepExpressionFromQualifiedNameWithPredicate() {
         assertThat(parser.parseStepExpr(tokenize("foo:bar[3]")),
-                   is((StepExpression)axisStep(nameTest("foo", "bar"), relativePathExpr(filterStep(literal("3"))))));
+                   is((StepExpression)axisStep(nameTest("foo", "bar"), literal("3"))));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -255,18 +289,17 @@
 
     @Test
     public void shouldParseAxisStepFromWildcardPrefixAndNonWildcardLocalNameWithPredicates() {
-        assertThat(parser.parseAxisStep(tokenize("*:name[3]")), is(axisStep(nameTest(null, "name"),
-                                                                            relativePathExpr(filterStep(literal("3"))))));
+        assertThat(parser.parseAxisStep(tokenize("*:name[3]")), is(axisStep(nameTest(null, "name"), literal("3"))));
     }
 
     @Test
     public void shouldParseAxisStepFromWildcardLocalNameWithPredicates() {
-        assertThat(parser.parseAxisStep(tokenize("*[3]")), is(axisStep(wildcard(), relativePathExpr(filterStep(literal("3"))))));
+        assertThat(parser.parseAxisStep(tokenize("*[3]")), is(axisStep(wildcard(), literal("3"))));
     }
 
     @Test
     public void shouldParseAxisStepFromWildcardPrefixAndLocalNameWithPredicates() {
-        assertThat(parser.parseAxisStep(tokenize("*:*[3]")), is(axisStep(wildcard(), relativePathExpr(filterStep(literal("3"))))));
+        assertThat(parser.parseAxisStep(tokenize("*:*[3]")), is(axisStep(wildcard(), literal("3"))));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -342,16 +375,63 @@
     // ----------------------------------------------------------------------------------------------------------------
 
     @Test
+    public void shouldParsePredicatesWithAttributeEqualsStringLiteral() {
+        assertThat(parser.parsePredicates(tokenize("[@jcr:title='something']")),
+                   is(predicates(comparison(attributeNameTest(nameTest("jcr", "title")), Operator.EQUAL_TO, literal("something")))));
+    }
+
+    @Test
+    public void shouldParsePredicatesWithAttributeLessThanIntegerLiteral() {
+        assertThat(parser.parsePredicates(tokenize("[@ex:age<3]")), is(predicates(comparison(attributeNameTest(nameTest("ex",
+                                                                                                                        "age")),
+                                                                                             Operator.LESS_THAN,
+                                                                                             literal("3")))));
+    }
+
+    @Test
+    public void shouldParsePredicatesWithAttributeLikeStringLiteral() {
+        assertThat(parser.parsePredicates(tokenize("[jcr:like(@jcr:title,'%something%')]")),
+                   is(predicates(functionCall(nameTest("jcr", "like"),
+                                              attributeNameTest(nameTest("jcr", "title")),
+                                              literal("%something%")))));
+    }
+
+    @Test
+    public void shouldParsePredicatesWithAndedExpressions() {
+        assertThat(parser.parsePredicates(tokenize("[@ex:age<3 and jcr:like(@jcr:title,'%something%')]")),
+                   is(predicates(and(comparison(attributeNameTest(nameTest("ex", "age")), Operator.LESS_THAN, literal("3")),
+                                     functionCall(nameTest("jcr", "like"),
+                                                  attributeNameTest(nameTest("jcr", "title")),
+                                                  literal("%something%"))))));
+    }
+
+    @Test
+    public void shouldParsePredicatesWithOredExpressions() {
+        assertThat(parser.parsePredicates(tokenize("[@ex:age<3 or jcr:like(@jcr:title,'%something%')]")),
+                   is(predicates(or(comparison(attributeNameTest(nameTest("ex", "age")), Operator.LESS_THAN, literal("3")),
+                                    functionCall(nameTest("jcr", "like"),
+                                                 attributeNameTest(nameTest("jcr", "title")),
+                                                 literal("%something%"))))));
+    }
+
+    @Test
+    public void shouldParsePredicatesWithMultipleSeparatePredicates() {
+        assertThat(parser.parsePredicates(tokenize("[@ex:age<3][jcr:like(@jcr:title,'%something%')]")),
+                   is(predicates(comparison(attributeNameTest(nameTest("ex", "age")), Operator.LESS_THAN, literal("3")),
+                                 functionCall(nameTest("jcr", "like"),
+                                              attributeNameTest(nameTest("jcr", "title")),
+                                              literal("%something%")))));
+    }
+
+    @Test
     public void shouldParsePredicatesWhenThereIsOnlyOnePredicate() {
-        assertThat(parser.parsePredicates(tokenize("[foo]")), is(predicates(relativePathExpr(axisStep(nameTest("foo"))))));
+        assertThat(parser.parsePredicates(tokenize("[foo]")), is(predicates(nameTest("foo"))));
     }
 
     @Test
     public void shouldParsePredicatesWhenThereAreMultiplePredicates() {
-        assertThat(parser.parsePredicates(tokenize("['foo']['bar']")),
-                   is(predicates(relativePathExpr(filterStep(literal("foo"))), relativePathExpr(filterStep(literal("bar"))))));
-        assertThat(parser.parsePredicates(tokenize("[foo][bar]")), is(predicates(relativePathExpr(axisStep(nameTest("foo"))),
-                                                                                 relativePathExpr(axisStep(nameTest("bar"))))));
+        assertThat(parser.parsePredicates(tokenize("['foo']['bar']")), is(predicates(literal("foo"), literal("bar"))));
+        assertThat(parser.parsePredicates(tokenize("[foo][bar]")), is(predicates(nameTest("foo"), nameTest("bar"))));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -369,7 +449,7 @@
 
     @Test
     public void shouldParseParenthesizedExpression() {
-        assertThat(parser.parseParenthesizedExpr(tokenize("('foo')")), is(paren(relativePathExpr(filterStep(literal("foo"))))));
+        assertThat(parser.parseParenthesizedExpr(tokenize("('foo')")), is(paren(literal("foo"))));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -430,7 +510,7 @@
         assertThat(func, is(notNullValue()));
         assertThat(func.getName(), is(nameTest("a")));
         assertThat(func.getParameters().size(), is(1));
-        assertThat(func.getParameters().get(0), is((Component)relativePathExpr(filterStep(literal("foo")))));
+        assertThat(func.getParameters().get(0), is((Component)literal("foo")));
     }
 
     @Test
@@ -439,7 +519,7 @@
         assertThat(func, is(notNullValue()));
         assertThat(func.getName(), is(nameTest("a")));
         assertThat(func.getParameters().size(), is(1));
-        assertThat(func.getParameters().get(0), is((Component)relativePathExpr(axisStep(nameTest("foo")))));
+        assertThat(func.getParameters().get(0), is((Component)nameTest("foo")));
     }
 
     // ----------------------------------------------------------------------------------------------------------------
@@ -557,7 +637,7 @@
 
     @Test
     public void shouldParseAnyKindTest() {
-        assertThat(parser.parseAnyKindTest(tokenize("node()")), is(notNullValue()));
+        assertThat(parser.parseAnyKindTest(tokenize("node()")), is(instanceOf(AnyKindTest.class)));
     }
 
     @Test
@@ -573,7 +653,7 @@
 
     @Test
     public void shouldParseCommentTest() {
-        assertThat(parser.parseCommentTest(tokenize("comment()")), is(notNullValue()));
+        assertThat(parser.parseCommentTest(tokenize("comment()")), is(instanceOf(CommentTest.class)));
     }
 
     @Test
@@ -589,7 +669,7 @@
 
     @Test
     public void shouldParseTextTest() {
-        assertThat(parser.parseTextTest(tokenize("text()")), is(notNullValue()));
+        assertThat(parser.parseTextTest(tokenize("text()")), is(instanceOf(TextTest.class)));
     }
 
     @Test
@@ -769,8 +849,12 @@
         assertThat(result, is(nullValue()));
     }
 
+    // ----------------------------------------------------------------------------------------------------------------
+    // utility methods
+    // ----------------------------------------------------------------------------------------------------------------
+
     protected TokenStream tokenize( String xpath ) {
-        Tokenizer tokenizer = new XPathTokenizer(false); // skip comments
+        Tokenizer tokenizer = new XPathParser.XPathTokenizer(false); // skip comments
         return new TokenStream(xpath, tokenizer, true).start(); // case sensitive!!
     }
 
@@ -812,6 +896,22 @@
         return new ContextItem();
     }
 
+    protected And and( Component left,
+                       Component right ) {
+        return new And(left, right);
+    }
+
+    protected Or or( Component left,
+                     Component right ) {
+        return new Or(left, right);
+    }
+
+    protected Comparison comparison( Component left,
+                                     Operator operator,
+                                     Component right ) {
+        return new Comparison(left, operator, right);
+    }
+
     protected List<Component> predicates( Component... predicates ) {
         return Arrays.asList(predicates);
     }
@@ -833,6 +933,15 @@
         return new AttributeTest(name, type);
     }
 
+    protected ElementTest element( NameTest name ) {
+        return new ElementTest(name, null);
+    }
+
+    protected ElementTest element( NameTest name,
+                                   NameTest type ) {
+        return new ElementTest(name, type);
+    }
+
     protected FunctionCall functionCall( NameTest name,
                                          Component... parameters ) {
         return new FunctionCall(name, Arrays.asList(parameters));
@@ -852,12 +961,22 @@
     }
 
     protected void assertParsable( String xpath ) {
-        new XPathParser().parseQuery(xpath, context);
+        new XPathQueryParser().parseQuery(xpath, context);
     }
 
+    protected void assertParsable( String xpath,
+                                   Component component ) {
+        Component actual = parser.parseExpr(tokenize(xpath));
+        if (component != null) {
+            assertThat(actual, is(component));
+        } else {
+            assertThat(actual, is(nullValue()));
+        }
+    }
+
     protected void assertNotParsable( String xpath ) {
         try {
-            new XPathParser().parseQuery(xpath, context);
+            new XPathQueryParser().parseQuery(xpath, context);
             fail("Expected an invalid XPath:  " + xpath);
         } catch (ParsingException e) {
             // expected

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -0,0 +1,297 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.xpath;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.hamcrest.Matcher;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.parse.SqlQueryParser;
+import org.jboss.dna.jcr.xpath.XPath.Component;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class XPathToQueryTranslatorTest {
+
+    private ExecutionContext context;
+    private XPathParser parser;
+
+    @Before
+    public void beforeEach() {
+        context = new ExecutionContext();
+        context.getNamespaceRegistry().register("x", "http://example.com");
+        parser = new XPathParser(context);
+    }
+
+    @After
+    public void afterEach() {
+        parser = null;
+    }
+
+    @Test
+    public void shouldTranslateXPathExpressionsToSql() {
+        printSqlFor("//element(*,my:type)");
+        printSqlFor("//element(nodeName,my:type)");
+    }
+
+    @Test
+    public void shouldTranslateFromXPathOfAnyNode() {
+        assertThat(xpath("//element(*)"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1"));
+        assertThat(xpath("/jcr:root//element(*)"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1"));
+        assertThat(xpath("//*"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1"));
+        assertThat(xpath("/jcr:root//*"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1"));
+        assertThat(xpath("//."), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1"));
+        assertThat(xpath("/jcr:root//."), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingExplicitPath() {
+        assertThat(xpath("/jcr:root/a"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'a' AND DEPTH(nodeSet1) = 1"));
+        assertThat(xpath("/jcr:root/a/b"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b'"));
+        assertThat(xpath("/jcr:root/a/b/c"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b/c'"));
+        assertThat(xpath("/jcr:root/a/b/c/d"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b/c/d'"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingExplicitPathWithChildNumbers() {
+        assertThat(xpath("/jcr:root/a[2]/b"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a[2]/b'"));
+        assertThat(xpath("/jcr:root/a/b[3]"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b[3]'"));
+        assertThat(xpath("/jcr:root/a[2]/b[3]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a[2]/b[3]'"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingExplicitPathWithWildcardChildNumbers() {
+        assertThat(xpath("/jcr:root/a[*]/b"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b'"));
+        assertThat(xpath("/jcr:root/a/b[*]"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b'"));
+        assertThat(xpath("/jcr:root/a[*]/b[*]"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b'"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingPathWithDescendantOrSelf() {
+        assertThat(xpath("/jcr:root/a/b//c"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b/c' OR PATH(nodeSet1) LIKE '/a/b/%/c'"));
+        assertThat(xpath("/jcr:root/a/b[2]//c"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b[2]/c' OR PATH(nodeSet1) LIKE '/a/b[2]/%/c'"));
+        assertThat(xpath("/jcr:root/a/b//c[4]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b/c[4]' OR PATH(nodeSet1) LIKE '/a/b/%/c[4]'"));
+        assertThat(xpath("/jcr:root/a/b[2]//c[4]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b[2]/c[4]' OR PATH(nodeSet1) LIKE '/a/b[2]/%/c[4]'"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingPathWithMultipleDescendantOrSelf() {
+        assertThat(xpath("/jcr:root/a/b//c//d"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE (((PATH(nodeSet1) = '/a/b/c/d' OR PATH(nodeSet1) LIKE '/a/b/%/c/d') OR PATH(nodeSet1) LIKE '/a/b/c/%/d') OR PATH(nodeSet1) LIKE '/a/b/%/c/%/d')"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingPredicatesUsingRelativePaths() {
+        assertThat(xpath("//element(*,my:type)[a/@id]"),
+                   isSql("SELECT * FROM [my:type] JOIN __ALLNODES__ as nodeSet1 ON ISCHILDNODE(nodeSet1,[my:type]) WHERE NAME(nodeSet1) = 'a' AND nodeSet1.id IS NOT NULL"));
+        assertThat(xpath("//element(*,my:type)[a/b/@id]"),
+                   isSql("SELECT * FROM [my:type] JOIN __ALLNODES__ as nodeSet1 ON ISCHILDNODE(nodeSet1,[my:type]) JOIN __ALLNODES__ as nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE (NAME(nodeSet1) = 'a' AND NAME(nodeSet2) = 'b') AND nodeSet2.id IS NOT NULL"));
+        assertThat(xpath("//element(*,my:type)[a/b/((@id and @name) or not(@address))]"),
+                   isSql("SELECT * FROM [my:type] JOIN __ALLNODES__ as nodeSet1 ON ISCHILDNODE(nodeSet1,[my:type]) JOIN __ALLNODES__ as nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE (NAME(nodeSet1) = 'a' AND NAME(nodeSet2) = 'b') AND ((nodeSet2.id IS NOT NULL and nodeSet2.name IS NOT NULL) OR (NOT(nodeSet2.address IS NOT NULL)))"));
+        assertThat(xpath("//element(*,my:type)[./a/b/((@id and @name) or not(@address))]"),
+                   isSql("SELECT * FROM [my:type] JOIN __ALLNODES__ as nodeSet1 ON ISCHILDNODE(nodeSet1,[my:type]) JOIN __ALLNODES__ as nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE (NAME(nodeSet1) = 'a' AND NAME(nodeSet2) = 'b') AND ((nodeSet2.id IS NOT NULL and nodeSet2.name IS NOT NULL) OR (NOT(nodeSet2.address IS NOT NULL)))"));
+        assertThat(xpath("//element(*,my:type)[a/b/((@id and @name) or not(jcr:contains(@desc,'rock star')))]"),
+                   isSql("SELECT * FROM [my:type] JOIN __ALLNODES__ as nodeSet1 ON ISCHILDNODE(nodeSet1,[my:type]) JOIN __ALLNODES__ as nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE (NAME(nodeSet1) = 'a' AND NAME(nodeSet2) = 'b') AND ((nodeSet2.id IS NOT NULL and nodeSet2.name IS NOT NULL) OR (NOT(CONTAINS(nodeSet2.desc,'rock star'))))"));
+        assertThat(xpath("//element(*,my:type)[*/@id]"),
+                   isSql("SELECT * FROM [my:type] JOIN __ALLNODES__ as nodeSet1 ON ISCHILDNODE(nodeSet1,[my:type]) WHERE nodeSet1.id IS NOT NULL"));
+        assertThat(xpath("//element(*,my:type)[*/*/@id]"),
+                   isSql("SELECT * FROM [my:type] JOIN __ALLNODES__ as nodeSet1 ON ISCHILDNODE(nodeSet1,[my:type]) JOIN __ALLNODES__ as nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE nodeSet2.id IS NOT NULL"));
+        assertThat(xpath("//element(*,my:type)[./*/*/@id]"),
+                   isSql("SELECT * FROM [my:type] JOIN __ALLNODES__ as nodeSet1 ON ISCHILDNODE(nodeSet1,[my:type]) JOIN __ALLNODES__ as nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE nodeSet2.id IS NOT NULL"));
+        assertThat(xpath("//element(*,my:type)[.//@id]"),
+                   isSql("SELECT * FROM [my:type] JOIN __ALLNODES__ as nodeSet1 ON ISDESCENDANTNODE(nodeSet1,[my:type]) WHERE nodeSet1.id IS NOT NULL"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingPredicatesIdentifyingPropertiesThatMustHaveValues() {
+        assertThat(xpath("//element(*,my:type)[@id]"), isSql("SELECT * FROM [my:type] WHERE id IS NOT NULL"));
+        assertThat(xpath("//element(*,my:type)[@id][@name]"),
+                   isSql("SELECT * FROM [my:type] WHERE id IS NOT NULL AND name IS NOT NULL"));
+        assertThat(xpath("//element(*,my:type)[@id | @name]"),
+                   isSql("SELECT * FROM [my:type] WHERE id IS NOT NULL OR name IS NOT NULL"));
+        assertThat(xpath("//element(*,my:type)[@id | (@name and @address)]"),
+                   isSql("SELECT * FROM [my:type] WHERE id IS NOT NULL OR (name IS NOT NULL AND address IS NOT NULL)"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingPredicatesUsingNot() {
+        assertThat(xpath("//element(*,my:type)[not(@id)]"), isSql("SELECT * FROM [my:type] WHERE NOT(id IS NOT NULL)"));
+        assertThat(xpath("//element(*,my:type)[not(jcr:contains(@desc,'rock star'))]"),
+                   isSql("SELECT * FROM [my:type] WHERE NOT(CONTAINS(desc,'rock star'))"));
+        assertThat(xpath("//element(*,my:type)[not(@id < 1 and jcr:contains(@desc,'rock star'))]"),
+                   isSql("SELECT * FROM [my:type] WHERE NOT(id < 1 AND CONTAINS(desc,'rock star'))"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingPredicatesIdentifyingPropertyCriteria() {
+        assertThat(xpath("//element(*,my:type)[@id = 1]"), isSql("SELECT * FROM [my:type] WHERE id = 1"));
+        assertThat(xpath("//element(*,my:type)[@id < 1 and @name = 'john']"),
+                   isSql("SELECT * FROM [my:type] WHERE id < 1 AND name = 'john'"));
+        assertThat(xpath("//element(*,my:type)[@id < 1 and ( @name = 'john' or @name = 'mary')]"),
+                   isSql("SELECT * FROM [my:type] WHERE id < 1 AND (name = 'john' OR name = 'mary')"));
+        assertThat(xpath("//element(*,my:type)[@id < 1 and ( jcr:like(@name,'%john') or @name = 'mary')]"),
+                   isSql("SELECT * FROM [my:type] WHERE id < 1 AND (name like '%john' OR name = 'mary')"));
+        assertThat(xpath("//element(*,my:type)[@id < 1 and jcr:contains(@desc,'rock star')]"),
+                   isSql("SELECT * FROM [my:type] WHERE id < 1 AND CONTAINS(desc,'rock star')"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingPredicatesIdentifyingPropertyCriteriaWithTypeCasts() {
+        assertThat(xpath("//element(*,my:type)[@datestart<=xs:dateTime('2009-09-24T11:53:23.293-05:00')]"),
+                   isSql("SELECT * FROM [my:type] WHERE datestart <= CAST('2009-09-24T11:53:23.293-05:00' AS DATE)"));
+        assertThat(xpath("//element(*,my:type)[@prop<=xs:boolean('true')]"),
+                   isSql("SELECT * FROM [my:type] WHERE prop <= CAST('true' AS BOOLEAN)"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathContainingAttributesInPathIdentifyingPropertiesToBeSelected() {
+        assertThat(xpath("//element(*,my:type)/@id"), isSql("SELECT id FROM [my:type]"));
+        assertThat(xpath("//element(*,my:type)/(@id|@name)"), isSql("SELECT id, name FROM [my:type]"));
+        assertThat(xpath("//element(*,my:type)/(@id|@x:address)"), isSql("SELECT id, [x:address] FROM [my:type]"));
+        assertThat(xpath("//element(*,my:type)/(@id|@name|@x:address)"), isSql("SELECT id, name, [x:address] FROM [my:type]"));
+        assertThat(xpath("//element(*,my:type)/(@id union @name)"), isSql("SELECT id, name FROM [my:type]"));
+        assertThat(xpath("//element(*,my:type)/(@id union @name union @x:address)"),
+                   isSql("SELECT id, name, [x:address] FROM [my:type]"));
+        assertThat(xpath("//(@id|@name)"), isSql("SELECT nodeSet1.id, nodeSet1.name FROM __ALLNODES__ AS nodeSet1"));
+        assertThat(xpath("//./(@id|@name)"), isSql("SELECT nodeSet1.id, nodeSet1.name FROM __ALLNODES__ AS nodeSet1"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathOfAnyNodeOfSpecificType() {
+        assertThat(xpath("//element(*,my:type)"), isSql("SELECT * FROM [my:type]"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathOfAnyNodeOfSpecificTypeAndWithSpecificName() {
+        assertThat(xpath("//element(nodeName,my:type)"), isSql("SELECT * FROM [my:type] WHERE NAME([my:type]) = 'nodeName'"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathOfAnyNodeWithName() {
+        assertThat(xpath("//element(nodeName,*)"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName'"));
+
+        assertThat(xpath("//element(nodeName,*)"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName'"));
+
+        assertThat(xpath("//nodeName"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName'"));
+
+        assertThat(xpath("/jcr:root//element(nodeName,*)"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName'"));
+
+        assertThat(xpath("/jcr:root//nodeName"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName'"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathOfNodeWithNameUnderRoot() {
+        assertThat(xpath("/jcr:root/element(nodeName,*)"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = 1"));
+
+        assertThat(xpath("/jcr:root/nodeName"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = 1"));
+
+        assertThat(xpath("nodeName"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = 1"));
+    }
+
+    @Test
+    public void shouldTranslateFromXPathOfAnyNodeUsingPredicate() {
+        assertThat(xpath("//.[jcr:contains(.,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE CONTAINS(nodeSet1.*,'bar')"));
+        assertThat(xpath("//.[jcr:contains(a,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 JOIN __ALLNODES__ AS nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE CONTAINS(nodeSet2.*,'bar')"));
+        assertThat(xpath("//*[jcr:contains(.,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE CONTAINS(nodeSet1.*,'bar')"));
+        assertThat(xpath("//*[jcr:contains(a,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 JOIN __ALLNODES__ AS nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE CONTAINS(nodeSet2.*,'bar')"));
+        assertThat(xpath("//*[jcr:contains(a/@b,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 JOIN __ALLNODES__ AS nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE NAME(nodeSet2) = 'a' AND CONTAINS(nodeSet2.b,'bar')"));
+        assertThat(xpath("//*[jcr:contains(a/*/@b,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 JOIN __ALLNODES__ AS nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) JOIN __ALLNODES__ AS nodeSet3 ON ISCHILDNODE(nodeSet3,nodeSet2) WHERE NAME(nodeSet2) = 'a' AND CONTAINS(nodeSet3.b,'bar')"));
+        assertThat(xpath("/jcr:root//element(*)[jcr:contains(a/@b,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 JOIN __ALLNODES__ AS nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE NAME(nodeSet2) = 'a' AND CONTAINS(nodeSet2.b,'bar')"));
+        assertThat(xpath("/jcr:root//element(*)[jcr:contains(a/*/@b,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 JOIN __ALLNODES__ AS nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) JOIN __ALLNODES__ AS nodeSet3 ON ISCHILDNODE(nodeSet3,nodeSet2) WHERE NAME(nodeSet2) = 'a' AND CONTAINS(nodeSet3.b,'bar')"));
+        assertThat(xpath("/jcr:root//*[jcr:contains(a/@b,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 JOIN __ALLNODES__ AS nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) WHERE NAME(nodeSet2) = 'a' AND CONTAINS(nodeSet2.b,'bar')"));
+        assertThat(xpath("/jcr:root//*[jcr:contains(a/*/@b,'bar')]"),
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 JOIN __ALLNODES__ AS nodeSet2 ON ISCHILDNODE(nodeSet2,nodeSet1) JOIN __ALLNODES__ AS nodeSet3 ON ISCHILDNODE(nodeSet3,nodeSet2) WHERE NAME(nodeSet2) = 'a' AND CONTAINS(nodeSet3.b,'bar')"));
+    }
+
+    // @Test
+    // public void shouldParseXPathExpressions() {
+    // assertParsable("/jcr:root/a/b/c");
+    // assertParsable("/jcr:root/a/b/c[*]");
+    // assertParsable("/jcr:root/some[1]/element(nodes, my:type)[1]");
+    // assertParsable("//element(*,my:type)");
+    // assertParsable("//element(*,my:type)[@jcr:title='something' and @globalProperty='something else']");
+    // assertParsable("//element(*,my:type)[@jcr:title | @globalProperty]");
+    // assertParsable("//element(*, my:type) order by @my:title");
+    // assertParsable("//element(*, my:type) [jcr:contains(., 'jcr')] order by jcr:score() descending");
+    // assertParsable("//element(*, employee)[@secretary and @assistant]");
+    // }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // utility methods
+    // ----------------------------------------------------------------------------------------------------------------
+
+    protected void printSqlFor( String xpath ) {
+        System.out.println("XPath: " + xpath);
+        System.out.println("SQL:   " + translateToSql(xpath));
+        System.out.println();
+    }
+
+    private QueryCommand translateToSql( String xpath ) {
+        Component component = parser.parseXPath(xpath);
+        XPathToQueryTranslator translator = new XPathToQueryTranslator(context, xpath);
+        return translator.createQuery(component);
+    }
+
+    private QueryCommand xpath( String xpath ) {
+        Component component = parser.parseXPath(xpath);
+        XPathToQueryTranslator translator = new XPathToQueryTranslator(context, xpath);
+        return translator.createQuery(component);
+    }
+
+    protected QueryCommand sql( String sql ) {
+        return new SqlQueryParser().parseQuery(sql, context);
+    }
+
+    protected Matcher<QueryCommand> isSql( String sql ) {
+        return is(sql(sql));
+    }
+
+}


Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-10-03 04:07:41 UTC (rev 1285)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-10-03 04:09:29 UTC (rev 1286)
@@ -25,7 +25,10 @@
 
 import java.io.IOException;
 import java.util.LinkedList;
+import java.util.Set;
 import org.apache.lucene.queryParser.ParseException;
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryEngine;
 import org.jboss.dna.graph.query.QueryResults;
@@ -34,6 +37,8 @@
 import org.jboss.dna.graph.query.optimize.Optimizer;
 import org.jboss.dna.graph.query.optimize.OptimizerRule;
 import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.parse.InvalidQueryException;
+import org.jboss.dna.graph.query.parse.QueryParser;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
 import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
@@ -53,10 +58,66 @@
         engine = new QueryEngine(new CanonicalPlanner(), new LuceneOptimizer(), new LuceneProcessor(), schemata);
     }
 
+    public LuceneQueryEngine( Schemata schemata,
+                              QueryParser... languages ) {
+        engine = new QueryEngine(new CanonicalPlanner(), new LuceneOptimizer(), new LuceneProcessor(), schemata, languages);
+    }
+
     /**
+     * Add a language to this engine by supplying its parser.
+     * 
+     * @param languageParser the query parser for the language
+     * @throws IllegalArgumentException if the language parser is null
+     */
+    public void addLanguage( QueryParser languageParser ) {
+        this.engine.addLanguage(languageParser);
+    }
+
+    /**
+     * Remove from this engine the language with the given name.
+     * 
+     * @param language the name of the language, which is to match the {@link QueryParser#getLanguage() language} of the parser
+     * @return the parser for the language, or null if the engine had no support for the named language
+     * @throws IllegalArgumentException if the language is null
+     */
+    public QueryParser removeLanguage( String language ) {
+        return this.engine.removeLanguage(language);
+    }
+
+    /**
+     * Get the set of languages that this engine is capable of parsing.
+     * 
+     * @return the unmodifiable copy of the set of languages; never null but possibly empty
+     */
+    public Set<String> getLanguages() {
+        return this.engine.getLanguages();
+    }
+
+    /**
      * Execute the supplied query by planning, optimizing, and then processing it.
      * 
+     * @param indexes the context in which the query should be executed
+     * @param language the language in which the query is expressed; must be one of the supported {@link #getLanguages()
+     *        languages}
      * @param query the query that is to be executed
+     * @return the query results; never null
+     * @throws IllegalArgumentException if the language, context or query references are null, or if the language is not know
+     * @throws ParsingException if there is an error parsing the supplied query
+     * @throws InvalidQueryException if the supplied query can be parsed but is invalid
+     */
+    public QueryResults execute( IndexContext indexes,
+                                 String language,
+                                 String query ) {
+        CheckArg.isNotNull(language, "language");
+        CheckArg.isNotNull(indexes, "indexes");
+        CheckArg.isNotNull(query, "query");
+        return engine.execute(indexes.context(), language, query);
+    }
+
+    /**
+     * Execute the supplied query by planning, optimizing, and then processing it.
+     * 
+     * @param query the query that is to be executed
      * @param indexes the indexes that should be used to execute the query; never null
      * @return the query results; never null
      * @throws IllegalArgumentException if the context or query references are null


From dna-commits at lists.jboss.org  Mon Oct  5 15:00:28 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 5 Oct 2009 15:00:28 -0400
Subject: [dna-commits] DNA SVN: r1287 - in
	trunk/web/dna-web-jcr-rest-client/src:
	test/java/org/jboss/dna/web/jcr/rest/client/json and 3 other
	directories.
Message-ID: <200910051900.n95J0Sn5022777@svn01.web.mwc.hst.phx2.redhat.com>

Author: elvisisking
Date: 2009-10-05 15:00:27 -0400 (Mon, 05 Oct 2009)
New Revision: 1287

Added:
   trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-().txt
Removed:
   trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-*().txt
Modified:
   trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClient.java
   trunk/web/dna-web-jcr-rest-client/src/test/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClientTest.java
Log:
Changed file name of a test file which was causing problems on MS Windows.

Modified: trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClient.java
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClient.java	2009-10-03 04:09:29 UTC (rev 1286)
+++ trunk/web/dna-web-jcr-rest-client/src/main/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClient.java	2009-10-05 19:00:27 UTC (rev 1287)
@@ -92,13 +92,12 @@
             connection.write(fileNode.getContent());
 
             // make sure node was created
-            if (connection.getResponseCode() != HttpURLConnection.HTTP_CREATED) {
+            int responseCode = connection.getResponseCode();
+
+            if (responseCode != HttpURLConnection.HTTP_CREATED) {
                 // node was not created
-                this.logger.error(RestClientI18n.connectionErrorMsg, connection.getResponseCode(), "createFileNode"); //$NON-NLS-1$
-                String msg = RestClientI18n.createFileFailedMsg.text(file.getName(),
-                                                                     path,
-                                                                     workspace.getName(),
-                                                                     connection.getResponseCode());
+                this.logger.error(RestClientI18n.connectionErrorMsg, responseCode, "createFileNode"); //$NON-NLS-1$
+                String msg = RestClientI18n.createFileFailedMsg.text(file.getName(), path, workspace.getName(), responseCode);
                 throw new RuntimeException(msg);
             }
         } finally {
@@ -127,10 +126,12 @@
             connection.write(folderNode.getContent());
 
             // make sure node was created
-            if (connection.getResponseCode() != HttpURLConnection.HTTP_CREATED) {
+            int responseCode = connection.getResponseCode();
+
+            if (responseCode != HttpURLConnection.HTTP_CREATED) {
                 // node was not created
-                this.logger.error(RestClientI18n.connectionErrorMsg, connection.getResponseCode(), "createFolderNode"); //$NON-NLS-1$
-                String msg = RestClientI18n.createFolderFailedMsg.text(path, workspace.getName(), connection.getResponseCode());
+                this.logger.error(RestClientI18n.connectionErrorMsg, responseCode, "createFolderNode"); //$NON-NLS-1$
+                String msg = RestClientI18n.createFolderFailedMsg.text(path, workspace.getName(), responseCode);
                 throw new RuntimeException(msg);
             }
         } finally {
@@ -195,13 +196,15 @@
         HttpClientConnection connection = connect(server, serverNode.getFindRepositoriesUrl(), RequestMethod.GET);
 
         try {
-            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
+            int responseCode = connection.getResponseCode();
+
+            if (responseCode == HttpURLConnection.HTTP_OK) {
                 return serverNode.getRepositories(connection.read());
             }
 
             // not a good response code
-            this.logger.error(RestClientI18n.connectionErrorMsg, connection.getResponseCode(), "getRepositories"); //$NON-NLS-1$
-            String msg = RestClientI18n.getRepositoriesFailedMsg.text(server.getName(), connection.getResponseCode());
+            this.logger.error(RestClientI18n.connectionErrorMsg, responseCode, "getRepositories"); //$NON-NLS-1$
+            String msg = RestClientI18n.getRepositoriesFailedMsg.text(server.getName(), responseCode);
             throw new RuntimeException(msg);
         } finally {
             if (connection != null) {
@@ -245,15 +248,17 @@
         HttpClientConnection connection = connect(repository.getServer(), repositoryNode.getUrl(), RequestMethod.GET);
 
         try {
-            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
+            int responseCode = connection.getResponseCode();
+
+            if (responseCode == HttpURLConnection.HTTP_OK) {
                 return repositoryNode.getWorkspaces(connection.read());
             }
 
             // not a good response code
-            this.logger.error(RestClientI18n.connectionErrorMsg, connection.getResponseCode(), "getWorkspaces"); //$NON-NLS-1$
+            this.logger.error(RestClientI18n.connectionErrorMsg, responseCode, "getWorkspaces"); //$NON-NLS-1$
             String msg = RestClientI18n.getWorkspacesFailedMsg.text(repository.getName(),
                                                                     repository.getServer().getName(),
-                                                                    connection.getResponseCode());
+                                                                    responseCode);
             throw new RuntimeException(msg);
         } finally {
             if (connection != null) {

Modified: trunk/web/dna-web-jcr-rest-client/src/test/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClientTest.java
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/test/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClientTest.java	2009-10-03 04:09:29 UTC (rev 1286)
+++ trunk/web/dna-web-jcr-rest-client/src/test/java/org/jboss/dna/web/jcr/rest/client/json/JsonRestClientTest.java	2009-10-05 19:00:27 UTC (rev 1287)
@@ -66,7 +66,7 @@
     private static final String BINARY_FILE_PATH = WORKSPACE_PATH + "picture.jpg"; //$NON-NLS-1$
 
     private static final String WORKSPACE_UNUSUALPATH = "/myproject/My.Test - Folder/"; //$NON-NLS-1$
-    private static final String FILE_UNUSUALPATH = WORKSPACE_UNUSUALPATH + "Test File_.a-*().txt"; //$NON-NLS-1$
+    private static final String FILE_UNUSUALPATH = WORKSPACE_UNUSUALPATH + "Test File_.a-().txt"; //$NON-NLS-1$
 
     // ===========================================================================================================================
     // Fields

Copied: trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-().txt (from rev 1284, trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-*().txt)
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-().txt	                        (rev 0)
+++ trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-().txt	2009-10-05 19:00:27 UTC (rev 1287)
@@ -0,0 +1,174 @@
+Help us improve Wikipedia by supporting it financially.
+Registration for Wikimania 2009 is now open. Learn more.
+    
+[Hide] [Help us with translations!]
+Percent-encoding
+From Wikipedia, the free encyclopedia
+Jump to: navigation, search
+    This article includes a list of references, related reading or external links, but its sources remain unclear because it lacks inline citations. Please improve this article by introducing more precise citations where appropriate. (April 2008)
+
+    For the urlencode in mediawiki, see Help:Magic words
+
+Percent-encoding, also known as URL encoding, is a mechanism for encoding information in a Uniform Resource Identifier (URI) under certain circumstances. Although it is known as URL encoding it is, in fact, used more generally within the main Uniform Resource Identifier (URI) set, which includes both Uniform Resource Locator (URL) and Uniform Resource Name (URN). As such it is also used in the preparation of data of the "application/x-www-form-urlencoded" media type, as is often used in email messages and the submission of HTML form data in HTTP requests.
+Contents
+[hide]
+
+    * 1 Percent-encoding in a URI
+          o 1.1 Types of URI characters
+          o 1.2 Percent-encoding reserved characters
+          o 1.3 Percent-encoding unreserved characters
+          o 1.4 Percent-encoding arbitrary data
+                + 1.4.1 Binary data
+                + 1.4.2 Character data
+          o 1.5 Current standard
+          o 1.6 Non-standard implementations
+    * 2 The application/x-www-form-urlencoded type
+    * 3 See also
+    * 4 References
+    * 5 External links
+
+[edit] Percent-encoding in a URI
+
+[edit] Types of URI characters
+
+The characters allowed in a URI are either reserved or unreserved. Reserved characters are those characters that sometimes have special meaning, while unreserved characters have no such meaning. Using percent-encoding, characters which otherwise would not be allowed are represented using allowed characters. The sets of reserved and unreserved characters and the circumstances under which certain reserved characters have special meaning have changed slightly with each revision of specifications that govern URIs and URI schemes.
+RFC 3986 section 2.2 Reserved Characters (January 2005) !   *   '   (   )   ;   :   @   &   =   +   $   ,   /   ?   %   #   [   ]
+RFC 3986 section 2.3 Unreserved Characters (January 2005) A     B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y   Z
+a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   p   q   r   s   t   u   v   w   x   y   z
+0   1   2   3   4   5   6   7   8   9   -   _   .   ~   
+
+No other characters are allowed in a URI.
+
+[edit] Percent-encoding reserved characters
+
+When a character from the reserved set (a "reserved character") has special meaning (a "reserved purpose") in a certain context, and a URI scheme says that it is necessary to use that character for some other purpose, then the character must be percent-encoded. Percent-encoding a reserved character involves converting the character to its corresponding byte value in ASCII and then representing that value as a pair of hexadecimal digits. The digits, preceded by a percent sign ("%"), are then used in the URI in place of the reserved character. (For a non-ASCII character, it is typically converted to its byte sequence in UTF-8, and then each byte value is represented as above.)
+
+The reserved character "/", for example, if used in the "path" component of a URI, has the special meaning of being a delimiter between path segments. If, according to a given URI scheme, "/" needs to be in a path segment, then the three characters "%2F" or "%2f" must be used in the segment instead of a raw "/".
+Reserved characters after percent-encoding !    *   "   '   (   )   ;   :   @   &   =   +   $   ,   /   ?   %   #   [   ]
+%21     %2A     %22     %27     %28     %29     %3B     %3A     %40     %26     %3D     %2B     %24     %2C     %2F     %3F     %25     %23     %5B     %5D
+
+Reserved characters that have no reserved purpose in a particular context may also be percent-encoded but are not semantically different from those that are not.
+
+In the "query" component of a URI, for example, "/" is still considered a reserved character but it normally has no reserved purpose, unless a particular URI scheme says otherwise. The character does not need to be percent-encoded when it has no reserved purpose.
+
+URIs that differ only by whether a reserved character is percent-encoded or appears literally are normally considered not equivalent (denoting the same resource) unless it can be determined that the reserved characters in question have no reserved purpose. This determination is dependent upon the rules established for reserved characters by individual URI schemes.
+
+[edit] Percent-encoding unreserved characters
+
+Characters from the unreserved set can be percent-encoded in the same way as reserved characters. That is, if a scheme calls for an unreserved character to be used in a URI, either the raw character or its percent-encoded equivalent may be used interchangeably.
+
+URIs that differ only by whether an unreserved character is percent-encoded or appears literally are equivalent by definition, but URI processors, in practice, may not always recognize this equivalence. For example, URI consumers shouldn't treat "%41" differently from "A" or "%7E" differently from "~", but some do. For maximum interoperability, URI producers are discouraged from percent-encoding unreserved characters.
+
+[edit] Percent-encoding arbitrary data
+
+Most URI schemes involve the representation of arbitrary data, such as an IP address or file system path, as components of a URI. URI scheme specifications should, but often don't, provide an explicit mapping between URI characters and all possible data values being represented by those characters.
+
+[edit] Binary data
+
+Since the publication of RFC 1738 in 1994 it has been specified that schemes that provide for the representation of binary data in a URI must divide the data into 8-bit bytes and percent-encode each byte in the same manner as above. Byte value 0F (hexadecimal), for example, should be represented by "%0F", but byte value 41 (hexadecimal) can be represented by "A", or "%41". The use of unencoded characters for alphanumeric and unreserved characters is typically preferred as it results in shorter URLs.
+
+[edit] Character data
+
+The procedure for percent-encoding binary data has often been extrapolated, sometimes inappropriately or without being fully specified, to apply to character-based data. In the World Wide Web's formative years, when dealing with data characters in the ASCII repertoire and using their corresponding bytes in ASCII as the basis for determining percent-encoded sequences, this practice was relatively harmless; it was just assumed that characters and bytes mapped one-to-one and were interchangeable. The need to represent characters outside the ASCII range, however, grew quickly and URI schemes and protocols often failed to provide standard rules for preparing character data for inclusion in a URI. Web applications consequently began using different multi-byte, stateful, and other non-ASCII-compatible encodings as the basis for percent-encoding, leading to ambiguities and difficulty interpreting URIs reliably.
+
+For example, many URI schemes and protocols based on RFCs 1738 and 2396 presume that the data characters will be converted to bytes according to some unspecified character encoding before being represented in a URI by unreserved characters or percent-encoded bytes. If the scheme does not allow the URI to provide a hint as to what encoding was used, or if the encoding conflicts with the use of ASCII to percent-encode reserved and unreserved characters, then the URI cannot be reliably interpreted. Some schemes fail to account for encoding at all, and instead just suggest that data characters map directly to URI characters, which leaves it up to implementations to decide whether and how to percent-encode data characters that are in neither the reserved nor unreserved sets.
+
+[edit] Current standard
+
+The generic URI syntax mandates that new URI schemes that provide for the representation of character data in a URI must, in effect, represent characters from the unreserved set without translation, and should convert all other characters to bytes according to UTF-8, and then percent-encode those values. This requirement was introduced in January 2005 with the publication of RFC 3986. URI schemes introduced before this date are not affected.
+
+Not addressed by the current specification is what to do with encoded character data. For example, in computers, character data manifests in encoded form, at some level, and thus could be treated as either binary data or as character data when being mapped to URI characters. Presumably, it is up to the URI scheme specifications to account for this possibility and require one or the other, but in practice, few, if any, actually do.
+
+[edit] Non-standard implementations
+
+There exists a non-standard encoding for Unicode characters: %uxxxx, where xxxx is a Unicode value represented as four hexadecimal digits. This behavior is not specified by any RFC and has been rejected by the W3C. The third edition of ECMA-262 still includes an escape(string) function that uses this syntax, but also an encodeURI(uri) function that converts to UTF-8 and percent-encodes each octet.
+
+[edit] The application/x-www-form-urlencoded type
+
+When data that has been entered into HTML forms is submitted, the form field names and values are encoded and sent to the server in an HTTP request message using method GET or POST, or, historically, via email.[1] The encoding used by default is based on a very early version of the general URI percent-encoding rules, with a number of modifications such as newline normalization and replacing spaces with "+" instead of "%20". The MIME type of data encoded this way is application/x-www-form-urlencoded, and it is currently defined (still in a very outdated manner) in the HTML and XForms specifications. In addition, the CGI specification contains rules for how web servers decode data of this type and make it available to applications.
+
+When sent in an HTTP GET request, application/x-www-form-urlencoded data is included in the query component of the request URI. When sent in an HTTP POST request or via email, the data is placed in the body of the message, and the name of the media type is included in the message's Content-Type header.
+
+[edit] See also
+
+    * Internationalized Resource Identifier
+    * Punycode
+
+[edit] References
+
+   1. ^ User-agent support for email based HTML form submission, using a 'mailto' URL as the form action, was proposed in RFC 1867 section 5.6, during the HTML 3.2 era. Various web browsers implemented it by invoking a separate email program or using their own rudimentary SMTP capabilities. Although sometimes unreliable, it was briefly popular as a simple way to transmit form data without involving a web server or CGI scripts.
+
+[edit] External links
+
+The following specifications all discuss and define reserved characters, unreserved characters, and percent-encoding, in some form or other:
+
+    * RFC 3986 / STD 66, the current generic URI syntax specification.
+    * RFC 2396 (obsolete) and RFC 2732 together comprised the previous version of the generic URI syntax specification.
+    * RFC 1738 (mostly obsolete) and RFC 1808 (obsolete), which define URLs.
+    * RFC 1630 (obsolete), the first generic URI syntax specification.
+    * W3C Guidelines on Naming and Addressing: URIs, URLs, ...
+    * W3C explanation of UTF-8 in URIs
+    * W3C HTML form content types
+
+Retrieved from "http://en.wikipedia.org/wiki/Percent-encoding"
+Categories: URI scheme | Internet standards | Binary-to-text encoding formats
+Hidden categories: Articles lacking in-text citations from April 2008
+Views
+
+    * Article
+    * Discussion
+    * Edit this page
+    * History
+
+Personal tools
+
+    * Try Beta
+    * Log in / create account
+
+Navigation
+
+    * Main page
+    * Contents
+    * Featured content
+    * Current events
+    * Random article
+
+Search
+ 
+Interaction
+
+    * About Wikipedia
+    * Community portal
+    * Recent changes
+    * Contact Wikipedia
+    * Donate to Wikipedia
+    * Help
+
+Toolbox
+
+    * What links here
+    * Related changes
+    * Upload file
+    * Special pages
+    * Printable version
+    * Permanent link
+    * Cite this page
+
+Languages
+
+    * ?esky
+    * Deutsch
+    * ???
+    * Magyar
+    * ???
+
+Powered by MediaWiki
+Wikimedia Foundation
+
+    * This page was last modified on 5 August 2009 at 16:59.
+    * Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. See Terms of Use for details.
+      Wikipedia? is a registered trademark of the Wikimedia Foundation, Inc., a non-profit organization.
+    * Privacy policy
+    * About Wikipedia
+    * Disclaimers
+


Property changes on: trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-().txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Deleted: trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-*().txt
===================================================================
--- trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-*().txt	2009-10-03 04:09:29 UTC (rev 1286)
+++ trunk/web/dna-web-jcr-rest-client/src/test/resources/myproject/My.Test - Folder/Test File_.a-*().txt	2009-10-05 19:00:27 UTC (rev 1287)
@@ -1,174 +0,0 @@
-Help us improve Wikipedia by supporting it financially.
-Registration for Wikimania 2009 is now open. Learn more.
-    
-[Hide] [Help us with translations!]
-Percent-encoding
-From Wikipedia, the free encyclopedia
-Jump to: navigation, search
-    This article includes a list of references, related reading or external links, but its sources remain unclear because it lacks inline citations. Please improve this article by introducing more precise citations where appropriate. (April 2008)
-
-    For the urlencode in mediawiki, see Help:Magic words
-
-Percent-encoding, also known as URL encoding, is a mechanism for encoding information in a Uniform Resource Identifier (URI) under certain circumstances. Although it is known as URL encoding it is, in fact, used more generally within the main Uniform Resource Identifier (URI) set, which includes both Uniform Resource Locator (URL) and Uniform Resource Name (URN). As such it is also used in the preparation of data of the "application/x-www-form-urlencoded" media type, as is often used in email messages and the submission of HTML form data in HTTP requests.
-Contents
-[hide]
-
-    * 1 Percent-encoding in a URI
-          o 1.1 Types of URI characters
-          o 1.2 Percent-encoding reserved characters
-          o 1.3 Percent-encoding unreserved characters
-          o 1.4 Percent-encoding arbitrary data
-                + 1.4.1 Binary data
-                + 1.4.2 Character data
-          o 1.5 Current standard
-          o 1.6 Non-standard implementations
-    * 2 The application/x-www-form-urlencoded type
-    * 3 See also
-    * 4 References
-    * 5 External links
-
-[edit] Percent-encoding in a URI
-
-[edit] Types of URI characters
-
-The characters allowed in a URI are either reserved or unreserved. Reserved characters are those characters that sometimes have special meaning, while unreserved characters have no such meaning. Using percent-encoding, characters which otherwise would not be allowed are represented using allowed characters. The sets of reserved and unreserved characters and the circumstances under which certain reserved characters have special meaning have changed slightly with each revision of specifications that govern URIs and URI schemes.
-RFC 3986 section 2.2 Reserved Characters (January 2005) !   *   '   (   )   ;   :   @   &   =   +   $   ,   /   ?   %   #   [   ]
-RFC 3986 section 2.3 Unreserved Characters (January 2005) A     B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y   Z
-a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   p   q   r   s   t   u   v   w   x   y   z
-0   1   2   3   4   5   6   7   8   9   -   _   .   ~   
-
-No other characters are allowed in a URI.
-
-[edit] Percent-encoding reserved characters
-
-When a character from the reserved set (a "reserved character") has special meaning (a "reserved purpose") in a certain context, and a URI scheme says that it is necessary to use that character for some other purpose, then the character must be percent-encoded. Percent-encoding a reserved character involves converting the character to its corresponding byte value in ASCII and then representing that value as a pair of hexadecimal digits. The digits, preceded by a percent sign ("%"), are then used in the URI in place of the reserved character. (For a non-ASCII character, it is typically converted to its byte sequence in UTF-8, and then each byte value is represented as above.)
-
-The reserved character "/", for example, if used in the "path" component of a URI, has the special meaning of being a delimiter between path segments. If, according to a given URI scheme, "/" needs to be in a path segment, then the three characters "%2F" or "%2f" must be used in the segment instead of a raw "/".
-Reserved characters after percent-encoding !    *   "   '   (   )   ;   :   @   &   =   +   $   ,   /   ?   %   #   [   ]
-%21     %2A     %22     %27     %28     %29     %3B     %3A     %40     %26     %3D     %2B     %24     %2C     %2F     %3F     %25     %23     %5B     %5D
-
-Reserved characters that have no reserved purpose in a particular context may also be percent-encoded but are not semantically different from those that are not.
-
-In the "query" component of a URI, for example, "/" is still considered a reserved character but it normally has no reserved purpose, unless a particular URI scheme says otherwise. The character does not need to be percent-encoded when it has no reserved purpose.
-
-URIs that differ only by whether a reserved character is percent-encoded or appears literally are normally considered not equivalent (denoting the same resource) unless it can be determined that the reserved characters in question have no reserved purpose. This determination is dependent upon the rules established for reserved characters by individual URI schemes.
-
-[edit] Percent-encoding unreserved characters
-
-Characters from the unreserved set can be percent-encoded in the same way as reserved characters. That is, if a scheme calls for an unreserved character to be used in a URI, either the raw character or its percent-encoded equivalent may be used interchangeably.
-
-URIs that differ only by whether an unreserved character is percent-encoded or appears literally are equivalent by definition, but URI processors, in practice, may not always recognize this equivalence. For example, URI consumers shouldn't treat "%41" differently from "A" or "%7E" differently from "~", but some do. For maximum interoperability, URI producers are discouraged from percent-encoding unreserved characters.
-
-[edit] Percent-encoding arbitrary data
-
-Most URI schemes involve the representation of arbitrary data, such as an IP address or file system path, as components of a URI. URI scheme specifications should, but often don't, provide an explicit mapping between URI characters and all possible data values being represented by those characters.
-
-[edit] Binary data
-
-Since the publication of RFC 1738 in 1994 it has been specified that schemes that provide for the representation of binary data in a URI must divide the data into 8-bit bytes and percent-encode each byte in the same manner as above. Byte value 0F (hexadecimal), for example, should be represented by "%0F", but byte value 41 (hexadecimal) can be represented by "A", or "%41". The use of unencoded characters for alphanumeric and unreserved characters is typically preferred as it results in shorter URLs.
-
-[edit] Character data
-
-The procedure for percent-encoding binary data has often been extrapolated, sometimes inappropriately or without being fully specified, to apply to character-based data. In the World Wide Web's formative years, when dealing with data characters in the ASCII repertoire and using their corresponding bytes in ASCII as the basis for determining percent-encoded sequences, this practice was relatively harmless; it was just assumed that characters and bytes mapped one-to-one and were interchangeable. The need to represent characters outside the ASCII range, however, grew quickly and URI schemes and protocols often failed to provide standard rules for preparing character data for inclusion in a URI. Web applications consequently began using different multi-byte, stateful, and other non-ASCII-compatible encodings as the basis for percent-encoding, leading to ambiguities and difficulty interpreting URIs reliably.
-
-For example, many URI schemes and protocols based on RFCs 1738 and 2396 presume that the data characters will be converted to bytes according to some unspecified character encoding before being represented in a URI by unreserved characters or percent-encoded bytes. If the scheme does not allow the URI to provide a hint as to what encoding was used, or if the encoding conflicts with the use of ASCII to percent-encode reserved and unreserved characters, then the URI cannot be reliably interpreted. Some schemes fail to account for encoding at all, and instead just suggest that data characters map directly to URI characters, which leaves it up to implementations to decide whether and how to percent-encode data characters that are in neither the reserved nor unreserved sets.
-
-[edit] Current standard
-
-The generic URI syntax mandates that new URI schemes that provide for the representation of character data in a URI must, in effect, represent characters from the unreserved set without translation, and should convert all other characters to bytes according to UTF-8, and then percent-encode those values. This requirement was introduced in January 2005 with the publication of RFC 3986. URI schemes introduced before this date are not affected.
-
-Not addressed by the current specification is what to do with encoded character data. For example, in computers, character data manifests in encoded form, at some level, and thus could be treated as either binary data or as character data when being mapped to URI characters. Presumably, it is up to the URI scheme specifications to account for this possibility and require one or the other, but in practice, few, if any, actually do.
-
-[edit] Non-standard implementations
-
-There exists a non-standard encoding for Unicode characters: %uxxxx, where xxxx is a Unicode value represented as four hexadecimal digits. This behavior is not specified by any RFC and has been rejected by the W3C. The third edition of ECMA-262 still includes an escape(string) function that uses this syntax, but also an encodeURI(uri) function that converts to UTF-8 and percent-encodes each octet.
-
-[edit] The application/x-www-form-urlencoded type
-
-When data that has been entered into HTML forms is submitted, the form field names and values are encoded and sent to the server in an HTTP request message using method GET or POST, or, historically, via email.[1] The encoding used by default is based on a very early version of the general URI percent-encoding rules, with a number of modifications such as newline normalization and replacing spaces with "+" instead of "%20". The MIME type of data encoded this way is application/x-www-form-urlencoded, and it is currently defined (still in a very outdated manner) in the HTML and XForms specifications. In addition, the CGI specification contains rules for how web servers decode data of this type and make it available to applications.
-
-When sent in an HTTP GET request, application/x-www-form-urlencoded data is included in the query component of the request URI. When sent in an HTTP POST request or via email, the data is placed in the body of the message, and the name of the media type is included in the message's Content-Type header.
-
-[edit] See also
-
-    * Internationalized Resource Identifier
-    * Punycode
-
-[edit] References
-
-   1. ^ User-agent support for email based HTML form submission, using a 'mailto' URL as the form action, was proposed in RFC 1867 section 5.6, during the HTML 3.2 era. Various web browsers implemented it by invoking a separate email program or using their own rudimentary SMTP capabilities. Although sometimes unreliable, it was briefly popular as a simple way to transmit form data without involving a web server or CGI scripts.
-
-[edit] External links
-
-The following specifications all discuss and define reserved characters, unreserved characters, and percent-encoding, in some form or other:
-
-    * RFC 3986 / STD 66, the current generic URI syntax specification.
-    * RFC 2396 (obsolete) and RFC 2732 together comprised the previous version of the generic URI syntax specification.
-    * RFC 1738 (mostly obsolete) and RFC 1808 (obsolete), which define URLs.
-    * RFC 1630 (obsolete), the first generic URI syntax specification.
-    * W3C Guidelines on Naming and Addressing: URIs, URLs, ...
-    * W3C explanation of UTF-8 in URIs
-    * W3C HTML form content types
-
-Retrieved from "http://en.wikipedia.org/wiki/Percent-encoding"
-Categories: URI scheme | Internet standards | Binary-to-text encoding formats
-Hidden categories: Articles lacking in-text citations from April 2008
-Views
-
-    * Article
-    * Discussion
-    * Edit this page
-    * History
-
-Personal tools
-
-    * Try Beta
-    * Log in / create account
-
-Navigation
-
-    * Main page
-    * Contents
-    * Featured content
-    * Current events
-    * Random article
-
-Search
- 
-Interaction
-
-    * About Wikipedia
-    * Community portal
-    * Recent changes
-    * Contact Wikipedia
-    * Donate to Wikipedia
-    * Help
-
-Toolbox
-
-    * What links here
-    * Related changes
-    * Upload file
-    * Special pages
-    * Printable version
-    * Permanent link
-    * Cite this page
-
-Languages
-
-    * ?esky
-    * Deutsch
-    * ???
-    * Magyar
-    * ???
-
-Powered by MediaWiki
-Wikimedia Foundation
-
-    * This page was last modified on 5 August 2009 at 16:59.
-    * Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. See Terms of Use for details.
-      Wikipedia? is a registered trademark of the Wikimedia Foundation, Inc., a non-profit organization.
-    * Privacy policy
-    * About Wikipedia
-    * Disclaimers
-


From dna-commits at lists.jboss.org  Wed Oct  7 11:43:06 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 7 Oct 2009 11:43:06 -0400
Subject: [dna-commits] DNA SVN: r1288 -
	trunk/tools/org.jboss.dna.eclipse.jcr.rest.client.updatesite.
Message-ID: <200910071543.n97Fh6tb002964@svn01.web.mwc.hst.phx2.redhat.com>

Author: elvisisking
Date: 2009-10-07 11:43:05 -0400 (Wed, 07 Oct 2009)
New Revision: 1288

Added:
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client.updatesite/readme.txt
Log:
Added a "readme" with steps to create an update site archive. Also include information on how to install archive into Eclipse and to how to test using JBoss.

Added: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client.updatesite/readme.txt
===================================================================
--- trunk/tools/org.jboss.dna.eclipse.jcr.rest.client.updatesite/readme.txt	                        (rev 0)
+++ trunk/tools/org.jboss.dna.eclipse.jcr.rest.client.updatesite/readme.txt	2009-10-07 15:43:05 UTC (rev 1288)
@@ -0,0 +1,29 @@
+***** Steps to create an update site archive *****
+
+1. Make sure you get a clean Maven build by running "mvn clean install" in the "trunk/" directory.
+
+2. In the "web/dna-web-jcr-rest-client/" directory run "mvn assembly:assembly" (which generates a dependencies jar)
+
+3. Copy these 2 files from "web/dna-web-jcr-rest-client/target/" to "tools/org.jboss.dna.eclipse.jcr.rest.client" project:
+    - dna-web-jcr-rest-client-[release]-jar-with-dependencies.jar
+    - dna-web-jcr-rest-client-[release]-sources.jar
+
+4. Update the feature version number by editing "feature.xml" in the "tools/org.jboss.dna.eclipse.jcr.rest.client.feature" project.
+
+5. Open "site.xml" in the tools/org.jboss.dna.eclipse.jcr.rest.client.updatesite" project. If the feature on the "Site Map" tab
+   is not right, delete the existing one and then click "Add Feature" to add the correct version of the feature plugin.
+   
+6. Once the feature is added click "Build All." This will create 2 folders (features, plugins) and 2 files (artifacts.xml, content.xml).
+
+7. At the top of both the artifacts.xml and content.xml, change the name to "JBoss DNA JCR REST Eclipse Client."
+
+8. Create a zip archive containing the following: features/, plugins/, site.xml, artifacts.xml, & contents.xml.
+
+***** Additional Information *****
+
+- To install the archive download and install a new copy of Eclipse. Use Help -> Software Updates to add the local archive site.
+
+- In order to test you need a DNA REST server. Copy the "resources.war" file found in the "targets/" directory of the
+  "web/dna-web-jcr-rest-war" project. For JBoss copy it into the "[JBOSS-INSTALL-DIR]/server/[CONFIG-NAME]/deploy/" directory. Then
+  in Eclipse using the DNA Servers View create a new server with the appropriate URL (for example: http://localhost:8080).
+  The user is "dnauser" and the passowrd is "password".
\ No newline at end of file


Property changes on: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client.updatesite/readme.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain


From dna-commits at lists.jboss.org  Wed Oct  7 12:18:30 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 7 Oct 2009 12:18:30 -0400
Subject: [dna-commits] DNA SVN: r1289 - in
	trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client:
	jobs and 1 other directories.
Message-ID: <200910071618.n97GIUUZ011767@svn01.web.mwc.hst.phx2.redhat.com>

Author: elvisisking
Date: 2009-10-07 12:18:30 -0400 (Wed, 07 Oct 2009)
New Revision: 1289

Modified:
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/RestClientI18n.properties
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/jobs/PublishJob.java
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/DnaContentProvider.java
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/ServerView.java
Log:
DNA 533 Decorator Does Not Add/Remove Overaly Image Immediately After The Publishing Operation: DnaContentProvider had some left over overrided, unimplemented interface methods that I just deleted. Everything started working.
DNA 538 Duplicate Error Messages Generated When A Connection To A Server Cannot Be Established: If a connection to a server fails, we won't try connecting again until a certain amount of time has gone by (currently set to 10 seconds).

Modified: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/RestClientI18n.properties
===================================================================
--- trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/RestClientI18n.properties	2009-10-07 15:43:05 UTC (rev 1288)
+++ trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/RestClientI18n.properties	2009-10-07 16:18:30 UTC (rev 1289)
@@ -139,7 +139,7 @@
 serverReconnectActionToolTip = Reconnect to the selected server
 
 serverManagerGetRepositoriesExceptionMsg = There was a problem obtaining repositories for the server "{0}."
-serverManagerGetWorkspacesExceptionMsg = There was a problem obtaining workspaces for repository "{0}."
+serverManagerGetWorkspacesExceptionMsg = There was a problem obtaining workspaces for "{0}."
 
 serverPageAuthenticationGroupTitle = Authentication
 serverPageInvalidServerProperties = Cannot construct a server because all server properties are not valid

Modified: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/jobs/PublishJob.java
===================================================================
--- trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/jobs/PublishJob.java	2009-10-07 15:43:05 UTC (rev 1288)
+++ trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/jobs/PublishJob.java	2009-10-07 16:18:30 UTC (rev 1289)
@@ -226,11 +226,6 @@
                     // set persistent property on resource indicating it has been published
                     if (!status.isError()) {
                         resourceHelper.addPublishedProperty(eclipseFile, workspace);
-                        DnaContentProvider decorator = DnaContentProvider.getDecorator();
-
-                        if (decorator != null) {
-                            // decorator.refresh(eclipseFile);
-                        }
                     }
                 } else {
                     status = getServerManager().unpublish(this.workspace, path, file);
@@ -238,17 +233,19 @@
                     // clear persistent property on resource indicating it has been unpublished
                     if (!status.isError()) {
                         resourceHelper.removePublishedProperty(eclipseFile, workspace);
-                        DnaContentProvider decorator = DnaContentProvider.getDecorator();
-
-                        if (decorator != null) {
-                            // decorator.refresh(eclipseFile);
-                        }
                     }
                 }
 
                 ++numProcessed;
                 monitor.worked(1);
 
+                // let decorator know publishing state has changed on this file
+                DnaContentProvider decorator = DnaContentProvider.getDecorator();
+
+                if (decorator != null) {
+                    decorator.refresh(eclipseFile);
+                }
+
                 // write outcome message to console
                 if (isPublishing() && status.isOk()) {
                     URL url = getServerManager().getUrl(file, path, this.workspace);

Modified: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/DnaContentProvider.java
===================================================================
--- trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/DnaContentProvider.java	2009-10-07 15:43:05 UTC (rev 1288)
+++ trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/DnaContentProvider.java	2009-10-07 16:18:30 UTC (rev 1289)
@@ -24,11 +24,15 @@
 package org.jboss.dna.eclipse.jcr.rest.client.views;
 
 import static org.jboss.dna.eclipse.jcr.rest.client.IUiConstants.PUBLISHED_OVERLAY_IMAGE;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import net.jcip.annotations.GuardedBy;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.viewers.ColumnLabelProvider;
 import org.eclipse.jface.viewers.IDecoration;
-import org.eclipse.jface.viewers.ILabelProviderListener;
 import org.eclipse.jface.viewers.ILightweightLabelDecorator;
 import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
@@ -40,7 +44,9 @@
 import org.jboss.dna.eclipse.jcr.rest.client.DnaResourceHelper;
 import org.jboss.dna.eclipse.jcr.rest.client.RestClientI18n;
 import org.jboss.dna.eclipse.jcr.rest.client.Utils;
+import org.jboss.dna.web.jcr.rest.client.IServerRegistryListener;
 import org.jboss.dna.web.jcr.rest.client.ServerManager;
+import org.jboss.dna.web.jcr.rest.client.ServerRegistryEvent;
 import org.jboss.dna.web.jcr.rest.client.Status;
 import org.jboss.dna.web.jcr.rest.client.Status.Severity;
 import org.jboss.dna.web.jcr.rest.client.domain.IDnaObject;
@@ -49,9 +55,11 @@
 import org.jboss.dna.web.jcr.rest.client.domain.Workspace;
 
 /**
- * The <code>DnaContentProvider</code> is a content and label provider for DNA repositories.
+ * The <code>DnaContentProvider</code> is a content and label provider for DNA repositories. This class <strong>MUST</strong> be
+ * registered, and then unregistered, to receive server registry events.
  */
-public final class DnaContentProvider extends ColumnLabelProvider implements ILightweightLabelDecorator, ITreeContentProvider {
+public final class DnaContentProvider extends ColumnLabelProvider
+    implements ILightweightLabelDecorator, IServerRegistryListener, ITreeContentProvider {
 
     // ===========================================================================================================================
     // Constants
@@ -62,6 +70,11 @@
      */
     private static final String ID = "org.jboss.dna.eclipse.jcr.rest.client.dnaDecorator"; //$NON-NLS-1$
 
+    /**
+     * If a server connection cannot be established, wait this amount of time before trying again.
+     */
+    private static final long RETRY_DURATION = 10000;
+
     // ===========================================================================================================================
     // Class Methods
     // ===========================================================================================================================
@@ -79,11 +92,23 @@
         return null;
     }
 
-    // ===========================================dnaDecorator================================================================================
+    // ===========================================================================================================================
     // Fields
     // ===========================================================================================================================
 
     /**
+     * Servers that a connection can't be established. Value is the last time a establishing a connection was tried.
+     */
+    @GuardedBy( "offlineServersLock" )
+    private final Map<Server, Long> offlineServerMap = new HashMap<Server, Long>();
+
+    /**
+     * Lock used for when accessing the offline server map. The map will be accessed in different threads as the decorator runs in
+     * its own thread (not the UI thread).
+     */
+    private final ReadWriteLock offlineServersLock = new ReentrantReadWriteLock();
+
+    /**
      * The server manager where the server registry is managed.
      */
     private ServerManager serverManager;
@@ -93,13 +118,15 @@
     // ===========================================================================================================================
 
     /**
-     * {@inheritDoc}
-     * 
-     * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
+     * @param server the server that is offline
      */
-    @Override
-    public void addListener( ILabelProviderListener listener ) {
-        // nothing to do
+    private void addOfflineServer( Server server ) {
+        try {
+            this.offlineServersLock.writeLock().lock();
+            this.offlineServerMap.put(server, System.currentTimeMillis());
+        } finally {
+            this.offlineServersLock.writeLock().unlock();
+        }
     }
 
     /**
@@ -117,11 +144,18 @@
             return;
         }
 
-        // must be an IDnaObject
         if (getServerManager() != null) {
             if (element instanceof Server) {
-                Status status = getServerManager().ping((Server)element);
-                overlay = Utils.getOverlayImage(status);
+                Server server = (Server)element;
+
+                if (isOkToConnect(server)) {
+                    Status status = getServerManager().ping(server);
+                    overlay = Utils.getOverlayImage(status);
+
+                    if (status.isError()) {
+                        addOfflineServer(server);
+                    }
+                }
             } else if ((element instanceof IFile) && new DnaResourceHelper(getServerManager()).isPublished((IFile)element)) {
                 overlay = Activator.getDefault().getImageDescriptor(PUBLISHED_OVERLAY_IMAGE);
             }
@@ -152,22 +186,30 @@
         assert (parentElement instanceof IDnaObject);
 
         if (getServerManager() != null) {
-            try {
-                if ((parentElement instanceof Server) && getServerManager().ping((Server)parentElement).isOk()) {
-                    return getServerManager().getRepositories((Server)parentElement).toArray();
+            if (parentElement instanceof Server) {
+                Server server = (Server)parentElement;
+
+                if (isOkToConnect(server)) {
+                    try {
+                        return getServerManager().getRepositories(server).toArray();
+                    } catch (Exception e) {
+                        addOfflineServer(server);
+                        String msg = RestClientI18n.serverManagerGetRepositoriesExceptionMsg.text(server.getShortDescription());
+                        Activator.getDefault().log(new Status(Severity.ERROR, msg, e));
+                    }
                 }
-            } catch (Exception e) {
-                String msg = RestClientI18n.serverManagerGetRepositoriesExceptionMsg.text(((Server)parentElement).getShortDescription());
-                Activator.getDefault().log(new Status(Severity.ERROR, msg, e));
-            }
+            } else if (parentElement instanceof Repository) {
+                Repository repository = (Repository)parentElement;
 
-            try {
-                if (parentElement instanceof Repository) {
-                    return getServerManager().getWorkspaces((Repository)parentElement).toArray();
+                if (isOkToConnect(repository.getServer())) {
+                    try {
+                        return getServerManager().getWorkspaces(repository).toArray();
+                    } catch (Exception e) {
+                        addOfflineServer(repository.getServer());
+                        String msg = RestClientI18n.serverManagerGetWorkspacesExceptionMsg.text(repository.getShortDescription());
+                        Activator.getDefault().log(new Status(Severity.ERROR, msg, e));
+                    }
                 }
-            } catch (Exception e) {
-                String msg = RestClientI18n.serverManagerGetWorkspacesExceptionMsg.text();
-                Activator.getDefault().log(new Status(Severity.ERROR, msg, e));
             }
         }
 
@@ -291,20 +333,9 @@
     public void inputChanged( Viewer viewer,
                               Object oldInput,
                               Object newInput ) {
-        // this.viewer = (StructuredViewer)viewer;
+        // nothing to do
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String)
-     */
-    @Override
-    public boolean isLabelProperty( Object element,
-                                    String property ) {
-        return false;
-    }
-
     public void refresh( final Object element ) {
         final Display display = Display.getDefault();
 
@@ -327,13 +358,68 @@
     }
 
     /**
+     * Determines if a try to connect to a server should be done based on the last time a try was done and failed.
+     * 
+     * @param server the server being checked
+     * @return <code>true</code> if it is OK to try and connect
+     */
+    private boolean isOkToConnect( Server server ) {
+        boolean check = false; // check map for time
+
+        try {
+            this.offlineServersLock.readLock().lock();
+            check = this.offlineServerMap.containsKey(server);
+        } finally {
+            this.offlineServersLock.readLock().unlock();
+        }
+
+        if (check) {
+            try {
+                this.offlineServersLock.writeLock().lock();
+
+                if (this.offlineServerMap.containsKey(server)) {
+                    long checkTime = this.offlineServerMap.get(server);
+
+                    // OK to try and connect if last failed attempt was too long ago
+                    if ((System.currentTimeMillis() - checkTime) > RETRY_DURATION) {
+                        this.offlineServerMap.remove(server);
+                        return true;
+                    }
+
+                    // don't try and connect because we just tried and failed
+                    return false;
+                }
+            } finally {
+                this.offlineServersLock.writeLock().unlock();
+            }
+        }
+
+        // OK to try and connect
+        return true;
+    }
+
+    /**
      * {@inheritDoc}
      * 
-     * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
+     * @see org.jboss.dna.web.jcr.rest.client.IServerRegistryListener#serverRegistryChanged(org.jboss.dna.web.jcr.rest.client.ServerRegistryEvent)
      */
     @Override
-    public void removeListener( ILabelProviderListener listener ) {
-        // nothing to do
+    public Exception[] serverRegistryChanged( ServerRegistryEvent event ) {
+        Exception[] errors = null;
+
+        // only care about servers being removed or updated
+        if (event.isRemove() || event.isUpdate()) {
+            try {
+                this.offlineServersLock.writeLock().lock();
+                this.offlineServerMap.remove(event.getServer());
+            } catch (Exception e) {
+                errors = new Exception[] {e};
+            } finally {
+                this.offlineServersLock.writeLock().unlock();
+            }
+        }
+
+        return errors;
     }
 
 }

Modified: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/ServerView.java
===================================================================
--- trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/ServerView.java	2009-10-07 15:43:05 UTC (rev 1288)
+++ trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/ServerView.java	2009-10-07 16:18:30 UTC (rev 1289)
@@ -218,6 +218,7 @@
 
         // register to receive changes to the server registry
         getServerManager().addRegistryListener(this);
+        getServerManager().addRegistryListener(this.provider);
 
         // register with the help system
         IWorkbenchHelpSystem helpSystem = Activator.getDefault().getWorkbench().getHelpSystem();
@@ -232,6 +233,11 @@
     @Override
     public void dispose() {
         getServerManager().removeRegistryListener(this);
+
+        if (this.provider != null) {
+            getServerManager().removeRegistryListener(this.provider);
+        }
+
         super.dispose();
     }
 
@@ -283,14 +289,14 @@
                 }
             }
         });
-        
+
         // don't want cut, copy, or paste actions so hook them up with a disabled action
         class NoOpAction extends Action {
             NoOpAction() {
                 setEnabled(false);
             }
         }
-        
+
         IAction noop = new NoOpAction();
         bars.setGlobalActionHandler(ActionFactory.CUT.getId(), noop);
         bars.setGlobalActionHandler(ActionFactory.COPY.getId(), noop);


From dna-commits at lists.jboss.org  Thu Oct 15 15:53:51 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 15 Oct 2009 15:53:51 -0400
Subject: [dna-commits] DNA SVN: r1290 - in
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/query:
	model and 5 other directories.
Message-ID: <200910151953.n9FJrpiL020609@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-15 15:53:51 -0400 (Thu, 15 Oct 2009)
New Revision: 1290

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/package-info.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/package-info.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/package-info.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/package-info.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/package-info.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/package-info.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java
Log:
DNA-467 Add search/query support to the graph API

Added package documentation for the dna-graph packages.

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/package-info.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/package-info.java	2009-10-15 19:53:51 UTC (rev 1290)
@@ -0,0 +1,42 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+/**
+ * <p>The Abstract Query Model is a vocabulary that can be used to construct a language-neutral representation of a query.
+ * It consists of a family of Java classes each represent the important semantic elements
+ * needed to fully define a query.  A {@link QueryCommand} is the interface that represents a top-level query,
+ * with the {@link Query} and {@link SetQuery} classes being the two implementations.
+ * </p>
+ * <p>
+ * There are two ways to construct abstract query models.  The first is to programmatically construct a query
+ * model using the {@link org.jboss.dna.graph.query.QueryBuilder}, which provides a fluent API that makes it easy to create a query
+ * with Java code.  The second (and more common) approach is to use a {@link org.jboss.dna.graph.query.parse.QueryParser}
+ * that parses a query represented in a specific language (like SQL or XPath) and then creates the query's equivalent abstract query model.
+ * </p>
+ * <p>
+ * The abstract query model classes are immutable, making them very easily shared or reused if that is advantageous.
+ * </p>
+ */
+
+package org.jboss.dna.graph.query.model;
+


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/package-info.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/package-info.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/package-info.java	2009-10-15 19:53:51 UTC (rev 1290)
@@ -0,0 +1,40 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+/**
+ * This package contains the Optimizer interface, a rule-based optimizer implementation, and library of optimization rules.
+ * The Optimizer is responsible for evaluating, validating, and manipulating a canonical query plan to produce a more
+ * a single optimized query processing plan.  The query plan is often changed in situ, although this is not required
+ * of the {@link org.jboss.dna.graph.query.optimize.Optimizer} implementations. A library of existing 
+ * {@link org.jboss.dna.graph.query.optimize.OptimizerRule} classes is provided, though it's very easy to 
+ * add more optimizer rules.
+ * </p>
+ * <p>
+ * The {@link org.jboss.dna.graph.query.optimize.RuleBasedOptimizer} is an implementation that optimizes a query 
+ * using a stack of rules.  A new stack is created for each rule, though the rules are required to be immutable and thus
+ * often shared and reused.  And, the RuleBasedOptimizer is easily subclassed to define a custom stack of rules.
+ * </p>
+ */
+
+package org.jboss.dna.graph.query.optimize;
+


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/package-info.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/package-info.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/package-info.java	2009-10-15 19:53:51 UTC (rev 1290)
@@ -0,0 +1,129 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+/**
+ * <p>The Query API provides a mechanism for building and executing queries.  The framework
+ * provides a reusable and extensible query engine that is capable of planning, validating, optimizing, and executing queries
+ * against a generic back-end system.  Simply subclass the {@link org.jboss.dna.graph.query.process.QueryProcessor}
+ * that creates a {@link org.jboss.dna.graph.query.process.ProcessingComponent}
+ * to operates against the back-end system, and assemble a {@link QueryEngine} that can be used to provide a rich
+ * query capability.
+ * </p>
+ * <h3>Abstract query model</h3>
+ * <p>
+ * At the heart of the entire query system is a single representation of what constitutes a query.  The
+ * {@link org.jboss.dna.graph.query.model abstract query model} defines a language-independent vocabulary 
+ * for queries, and consists of a family of Java classes each represent the important semantic elements
+ * needed to fully define a query.
+ * </p>
+ * <p>
+ * There are two ways to construct abstract query models.  The first is to programmatically construct a query
+ * model using the {@link QueryBuilder}, which provides a fluent API that makes it easy to create a query
+ * with Java code.  The second (and more common) approach is to use a {@link org.jboss.dna.graph.query.parse.QueryParser}
+ * that parses a query represented in a specific language (like SQL or XPath) and then creates the query's equivalent abstract query model.
+ * </p>
+ * <p>
+ * The abstract query model classes are immutable, making them very easily shared or reused if that is advantageous.
+ * </p>
+ * <h3>SQL language</h3>
+ * <p>
+ * One of the {@link org.jboss.dna.graph.query.parse.QueryParser} implementation provided out of the box is
+ * the {@link org.jboss.dna.graph.query.parse.SqlQueryParser}, which understands a subset of SQL.
+ * </p>
+ * <h3>QueryEngine</h3>
+ * <p>
+ * The {@link QueryEngine} is the component that accepts and executes queries expressed as abstract query models.
+ * Each submitted query is planned, validated, optimized, and then processed to compute and return the final 
+ * {@link QueryResults query results}.
+ * </p>
+ * <p>
+ * Note that the QueryEngine is thread-safe.
+ * </p>
+ * <h4>Planning</h4>
+ * <p>
+ * In the <i>planning</i> stage, a canonical plan is generated for each query.  This plan is a tree of {@link org.jboss.dna.graph.query.plan.PlanNode}
+ * objects that each represent a different aspect of the query, and is a form that is easily manipulated by subsequent stages.
+ * Any implementation of {@link org.jboss.dna.graph.query.plan.Planner} can be used, though a {@link org.jboss.dna.graph.query.plan.CanonicalPlanner}
+ * implementation is provided and will be sufficient for most cases.  In fact, the subsequent execution steps often
+ * require the plan to be in its canonical form, so for most situations it may be best to simply reuse the CanonicalPlanner
+ * and in other simply extend it.
+ * </p>
+ * <p>
+ * Note that query plans are mutable and not thread-safe, meaning that such plans are not intended to be shared
+ * or reused.
+ * </p>
+ * <h4>Optimization</h4>
+ * <p>
+ * In the <i>optimization</i> stage, the canonical query plan is evaluated, validated, and manipulated to produce a more
+ * a single optimized query processing plan.  The query plan is often changed in situ, although this is not required
+ * of the {@link org.jboss.dna.graph.query.optimize.Optimizer} implementations. A library of existing 
+ * {@link org.jboss.dna.graph.query.optimize.OptimizerRule} classes is provided, though it's very easy to 
+ * add more optimizer rules.
+ * </p>
+ * <p>
+ * The {@link org.jboss.dna.graph.query.optimize.RuleBasedOptimizer} is an implementation that optimizes a query 
+ * using a stack of rules.  A new stack is created for each rule, though the rules are required to be immutable and thus
+ * often shared and reused.  And, the RuleBasedOptimizer is easily subclassed to define a custom stack of rules.
+ * </p>
+ * <h4>Validation</h4>
+ * <p>
+ * The canonical planner or the optimization rules have access to the table and column definitions that may be
+ * queried.  The query framework does not prescribe the semantics of a table or column, but instead provides
+ * a {@link org.jboss.dna.graph.query.validate.Schemata} interface that provides access to the immutable
+ * {@link org.jboss.dna.graph.query.validate.Schemata.Table} definitions (that then contain the
+ * {@link org.jboss.dna.graph.query.validate.Schemata.Column} and {@link org.jboss.dna.graph.query.validate.Schemata.Key} definitions).
+ * </p>
+ * <p>
+ * The canonical planner and a number of the provided optimizer rules use the Schemata to verify that the
+ * query is referencing an existing table and columns, whatever they are defined to be.  Although any Schemata
+ * implementaiton can be used, the query framework provides an {@link org.jboss.dna.graph.query.validate.ImmutableSchemata}
+ * class with a {@link org.jboss.dna.graph.query.validate.ImmutableSchemata.Builder builder} with a fluent API
+ * that can create the corresponding immutable table, column and key definitions.
+ * </p>
+ * <h4>Processing</h4>
+ * <p>
+ * In the <i>processing</i> stage, the optimized query plan is used to construct and assemble the 
+ * {@link org.jboss.dna.graph.query.process.ProcessingComponent} that correspond to the various parts of the
+ * quer plan.  The resulting components form the basic processing engine for that query.  At the bottom are
+ * the "access" components that perform the low-level access of the tuples from the graph container.
+ * Above these are the other components that implement various operations, such as limits, joins (using merge and 
+ * nested loop algorithms), unions, intersects, distinct, sorts, and even column projections.  At the top is
+ * a single component that produces tuples that represent the results of the query.
+ * </p>
+ * <p>
+ * Once the {@link org.jboss.dna.graph.query.process.QueryProcessor} creates the ProcessingComponent assembly,
+ * the top-level component is executed.  Execution involves requesting from the child processing component(s) the next batch of results,
+ * processing each of the tuples according to the specific ProcessingComponent algorithm, and finally returning
+ * the processed tuples.  
+ * </p>
+ * <h3>QueryResults</h3>
+ * <p>
+ * A query over a graph of content will result in a set of nodes that matched the criteria specified in the query.
+ * Each node contained in the results will be identified by its {@link org.jboss.dna.graph.Location} as well
+ * as any values for the selected properties. Typically, queries will result in a single node per row, although
+ * joins may result in multiple rows per row.
+ * </p>
+ */
+
+package org.jboss.dna.graph.query;
+


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/package-info.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/package-info.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/package-info.java	2009-10-15 19:53:51 UTC (rev 1290)
@@ -0,0 +1,34 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+/**
+ * This package defines the {@link QueryParser} interface, which defines a component that can parse a query represented
+ * in a specific language and produce the corresponding {@link org.jboss.dna.graph.query.model abstract query model} representation.
+ * <p>
+ * Several parsers are provided, including one that parses a subset of SQL and another that parses the full-text search
+ * expressions. However, other query parsers can easily be created and used.
+ * </p>
+ */
+
+package org.jboss.dna.graph.query.parse;
+


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/package-info.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/package-info.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/package-info.java	2009-10-15 19:53:51 UTC (rev 1290)
@@ -0,0 +1,43 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+/**
+ * This package defines the {@link Planner} interface, the {@link CanonicalPlanner} implementation, and the
+ * {@link PlanNode} class that is used to represent a canonical query plan.
+ * <p>
+ * The query plan is a tree of {@link org.jboss.dna.graph.query.plan.PlanNode} objects that each represent a 
+ * different aspect of the query, and is a form that is easily manipulated by subsequent stages.
+ * Any implementation of {@link org.jboss.dna.graph.query.plan.Planner} can be used, though a {@link org.jboss.dna.graph.query.plan.CanonicalPlanner}
+ * implementation is provided and will be sufficient for most cases.  In fact, the subsequent execution steps often
+ * require the plan to be in its canonical form, so for most situations it may be best to simply reuse the CanonicalPlanner
+ * and in other simply extend it.
+ * </p>
+ * <p>
+ * Note that query plans are mutable and not thread-safe, meaning that such plans are not intended to be shared
+ * or reused.
+ * </p>
+
+ */
+
+package org.jboss.dna.graph.query.plan;
+


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/package-info.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/package-info.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/package-info.java	2009-10-15 19:53:51 UTC (rev 1290)
@@ -0,0 +1,43 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+/**
+ * This package defines the {@link QueryProcessor} interface, which is responsible for constructing for each query
+ * a tree of {@link ProcessingComponent} objects that each are responsible for processing a specific aspect of
+ * the query and returning the tuples to the parent component.
+ * <p>
+ * At the bottom are the "access" components that perform the low-level access of the tuples from the graph container.
+ * Above these are the other components that implement various operations, such as limits, joins (using merge and 
+ * nested loop algorithms), unions, intersects, distinct, sorts, and even column projections.  At the top is
+ * a single component that produces tuples that represent the results of the query.
+ * </p>
+ * <p>
+ * Once the {@link org.jboss.dna.graph.query.process.QueryProcessor} creates the ProcessingComponent assembly,
+ * the top-level component is executed.  Execution involves requesting from the child processing component(s) the next batch of results,
+ * processing each of the tuples according to the specific ProcessingComponent algorithm, and finally returning
+ * the processed tuples.  
+ * </p>
+ */
+
+package org.jboss.dna.graph.query.process;
+


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/package-info.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java	2009-10-15 19:53:51 UTC (rev 1290)
@@ -0,0 +1,37 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+/**
+ * This package provides the interfaces that define the tables and columns that can be queried.  Though the query framework does not prescribe 
+ * the semantics of a table or column, it does provide
+ * a {@link org.jboss.dna.graph.query.validate.Schemata} interface that provides access to the immutable
+ * {@link org.jboss.dna.graph.query.validate.Schemata.Table} definitions (that then contain the
+ * {@link org.jboss.dna.graph.query.validate.Schemata.Column} and {@link org.jboss.dna.graph.query.validate.Schemata.Key} definitions).
+ * <p>
+ * Although any Schemata implementaiton can be used, the query framework provides an {@link org.jboss.dna.graph.query.validate.ImmutableSchemata}
+ * class with a {@link org.jboss.dna.graph.query.validate.ImmutableSchemata.Builder builder} with a fluent API
+ * that can create the corresponding immutable table, column and key definitions.
+ */
+
+package org.jboss.dna.graph.query.validate;
+


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Thu Oct 15 15:54:48 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 15 Oct 2009 15:54:48 -0400
Subject: [dna-commits] DNA SVN: r1291 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/query/parse and 3
	other directories.
Message-ID: <200910151954.n9FJsm1g020756@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-15 15:54:48 -0400 (Thu, 15 Oct 2009)
New Revision: 1291

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParsers.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/QueryParsersTest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/package-info.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
Log:
DNA-467 Add search/query support to the graph API

Broke out the language parsing from the QueryEngine and into a new QueryParsers.  The QueryEngine should do everything in terms of a QueryCommand, since it's possible for the query engines to be layered, and parsing languages is completely orthogonal to execution.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-10-15 19:53:51 UTC (rev 1290)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-10-15 19:54:48 UTC (rev 1291)
@@ -23,18 +23,11 @@
  */
 package org.jboss.dna.graph.query;
 
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import net.jcip.annotations.ThreadSafe;
-import org.jboss.dna.common.text.ParsingException;
 import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.query.QueryResults.Statistics;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Constraint;
@@ -44,8 +37,6 @@
 import org.jboss.dna.graph.query.model.Visitors;
 import org.jboss.dna.graph.query.optimize.Optimizer;
 import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
-import org.jboss.dna.graph.query.parse.InvalidQueryException;
-import org.jboss.dna.graph.query.parse.QueryParser;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
 import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
@@ -61,19 +52,30 @@
  * A query engine that is able to execute formal queries expressed in the Graph API's {@link QueryCommand Abstract Query Model}.
  */
 @ThreadSafe
-public class QueryEngine {
+public class QueryEngine implements Queryable {
 
     private final Planner planner;
     private final Optimizer optimizer;
     private final Processor processor;
     private final Schemata schemata;
-    private final ConcurrentMap<String, QueryParser> parsers = new ConcurrentHashMap<String, QueryParser>();
 
+    /**
+     * Create a new query engine given the {@link Planner planner}, {@link Optimizer optimizer}, {@link Processor processor}, and
+     * {@link Schemata schemata}.
+     * 
+     * @param planner the planner that should be used to generate canonical query plans for the queries; may be null if the
+     *        {@link CanonicalPlanner} should be used
+     * @param optimizer the optimizer that should be used to optimize the canonical query plan; may be null if the
+     *        {@link RuleBasedOptimizer} should be used
+     * @param processor the processor implementation that should be used to process the planned query and return the results
+     * @param schemata the schemata implementation, or null if an empty schema should be used (resulting in errors when named
+     *        tables are queried)
+     * @throws IllegalArgumentException if the processor reference is null
+     */
     public QueryEngine( Planner planner,
                         Optimizer optimizer,
                         Processor processor,
-                        Schemata schemata,
-                        QueryParser... parsers ) {
+                        Schemata schemata ) {
         CheckArg.isNotNull(processor, "processor");
         this.planner = planner != null ? planner : new CanonicalPlanner();
         this.optimizer = optimizer != null ? optimizer : new RuleBasedOptimizer();
@@ -85,93 +87,24 @@
                 return null;
             }
         };
-        for (QueryParser parser : parsers) {
-            if (parser != null) addLanguage(parser);
-        }
     }
 
     /**
-     * Add a language to this engine by supplying its parser.
+     * {@inheritDoc}
      * 
-     * @param languageParser the query parser for the language
-     * @throws IllegalArgumentException if the language parser is null
+     * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.ExecutionContext,
+     *      org.jboss.dna.graph.query.model.QueryCommand)
      */
-    public void addLanguage( QueryParser languageParser ) {
-        CheckArg.isNotNull(languageParser, "languageParser");
-        this.parsers.put(languageParser.getLanguage().toLowerCase(), languageParser);
-    }
-
-    /**
-     * Remove from this engine the language with the given name.
-     * 
-     * @param language the name of the language, which is to match the {@link QueryParser#getLanguage() language} of the parser
-     * @return the parser for the language, or null if the engine had no support for the named language
-     * @throws IllegalArgumentException if the language is null
-     */
-    public QueryParser removeLanguage( String language ) {
-        CheckArg.isNotNull(language, "language");
-        return this.parsers.remove(language.toLowerCase());
-    }
-
-    /**
-     * Get the set of languages that this engine is capable of parsing.
-     * 
-     * @return the unmodifiable copy of the set of languages; never null but possibly empty
-     */
-    public Set<String> getLanguages() {
-        Set<String> result = new HashSet<String>();
-        for (QueryParser parser : parsers.values()) {
-            result.add(parser.getLanguage());
-        }
-        return Collections.unmodifiableSet(result);
-    }
-
-    /**
-     * Execute the supplied query by planning, optimizing, and then processing it.
-     * 
-     * @param context the context in which the query should be executed
-     * @param language the language in which the query is expressed; must be one of the supported {@link #getLanguages()
-     *        languages}
-     * @param query the query that is to be executed
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the language, context or query references are null, or if the language is not know
-     * @throws ParsingException if there is an error parsing the supplied query
-     * @throws InvalidQueryException if the supplied query can be parsed but is invalid
-     */
     public QueryResults execute( ExecutionContext context,
-                                 String language,
-                                 String query ) {
-        CheckArg.isNotNull(language, "language");
-        CheckArg.isNotNull(context, "context");
-        CheckArg.isNotNull(query, "query");
-        QueryParser parser = parsers.get(language.toLowerCase());
-        if (parser == null) {
-            throw new IllegalArgumentException(GraphI18n.unknownQueryLanguage.text(language));
-        }
-        return execute(context, parser.parseQuery(query, context));
-    }
-
-    /**
-     * Execute the supplied query by planning, optimizing, and then processing it.
-     * 
-     * @param context the context in which the query should be executed
-     * @param query the query that is to be executed
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the context or query references are null
-     */
-    public QueryResults execute( ExecutionContext context,
                                  QueryCommand query ) {
         return execute(context, query, new PlanHints());
     }
 
     /**
-     * Execute the supplied query by planning, optimizing, and then processing it.
+     * {@inheritDoc}
      * 
-     * @param context the context in which the query should be executed
-     * @param query the query that is to be executed
-     * @param hints the hints for the execution; may be null if there are no hints
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the context or query references are null
+     * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.ExecutionContext,
+     *      org.jboss.dna.graph.query.model.QueryCommand, org.jboss.dna.graph.query.plan.PlanHints)
      */
     public QueryResults execute( ExecutionContext context,
                                  QueryCommand query,

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java	2009-10-15 19:54:48 UTC (rev 1291)
@@ -0,0 +1,58 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query;
+
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.plan.PlanHints;
+
+/**
+ * An interface defining the ability to submit a query and obtain results.
+ */
+public interface Queryable {
+
+    /**
+     * Execute the supplied query by planning, optimizing, and then processing it.
+     * 
+     * @param context the context in which the query should be executed
+     * @param query the query that is to be executed
+     * @return the query results; never null
+     * @throws IllegalArgumentException if the context or query references are null
+     */
+    QueryResults execute( ExecutionContext context,
+                          QueryCommand query );
+
+    /**
+     * Execute the supplied query by planning, optimizing, and then processing it.
+     * 
+     * @param context the context in which the query should be executed
+     * @param query the query that is to be executed
+     * @param hints the hints for the execution; may be null if there are no hints
+     * @return the query results; never null
+     * @throws IllegalArgumentException if the context or query references are null
+     */
+    QueryResults execute( ExecutionContext context,
+                          QueryCommand query,
+                          PlanHints hints );
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/package-info.java	2009-10-15 19:53:51 UTC (rev 1290)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/package-info.java	2009-10-15 19:54:48 UTC (rev 1291)
@@ -41,6 +41,7 @@
  * model using the {@link QueryBuilder}, which provides a fluent API that makes it easy to create a query
  * with Java code.  The second (and more common) approach is to use a {@link org.jboss.dna.graph.query.parse.QueryParser}
  * that parses a query represented in a specific language (like SQL or XPath) and then creates the query's equivalent abstract query model.
+ * There's even a {@link org.jboss.dna.graph.query.parse.QueryParsers} class that can manage the parsers for multiple languages.
  * </p>
  * <p>
  * The abstract query model classes are immutable, making them very easily shared or reused if that is advantageous.

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParsers.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParsers.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParsers.java	2009-10-15 19:54:48 UTC (rev 1291)
@@ -0,0 +1,215 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.parse;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.query.model.QueryCommand;
+
+/**
+ * A thread-safe collection of {@link QueryParser} implementations that can be used to parse queries by language.
+ */
+ at ThreadSafe
+public class QueryParsers {
+
+    private final ConcurrentMap<String, QueryParser> parsers = new ConcurrentHashMap<String, QueryParser>();
+
+    /**
+     * Create a new collection of the supplied {@link QueryParser} objects.
+     * 
+     * @param parsers the parsers
+     */
+    public QueryParsers( QueryParser... parsers ) {
+        if (parsers != null) {
+            for (QueryParser parser : parsers) {
+                if (parser != null) addLanguage(parser);
+            }
+        }
+    }
+
+    /**
+     * Create a new collection of the supplied {@link QueryParser} objects.
+     * 
+     * @param parsers the parsers
+     */
+    public QueryParsers( Iterable<QueryParser> parsers ) {
+        if (parsers != null) addLanguages(parsers);
+    }
+
+    /**
+     * Add a language to this engine by supplying its parser.
+     * 
+     * @param languageParser the query parser for the language
+     * @throws IllegalArgumentException if the language parser is null
+     */
+    public void addLanguage( QueryParser languageParser ) {
+        CheckArg.isNotNull(languageParser, "languageParser");
+        this.parsers.put(languageParser.getLanguage().toLowerCase(), languageParser);
+    }
+
+    /**
+     * Add one or more languages to this engine by supplying the corresponding parsers.
+     * 
+     * @param firstLanguage the query parser for the first language
+     * @param additionalLanguages the query parsers for the additional languages
+     * @throws IllegalArgumentException if the language parser is null
+     */
+    public void addLanguages( QueryParser firstLanguage,
+                              QueryParser... additionalLanguages ) {
+        addLanguage(firstLanguage);
+        for (QueryParser language : additionalLanguages) {
+            addLanguage(language);
+        }
+    }
+
+    /**
+     * Add one or more languages to this engine by supplying the corresponding parsers.
+     * 
+     * @param languages the query parsers for the languages that are to be added
+     * @throws IllegalArgumentException if the iterable reference is null
+     */
+    public void addLanguages( Iterable<QueryParser> languages ) {
+        CheckArg.isNotNull(languages, "languages");
+        for (QueryParser language : languages) {
+            addLanguage(language);
+        }
+    }
+
+    /**
+     * Remove from this engine the language with the given name.
+     * 
+     * @param language the name of the language, which is to match the {@link QueryParser#getLanguage() language} of the parser
+     * @return the parser for the language, or null if the engine had no support for the named language
+     * @throws IllegalArgumentException if the language is null
+     */
+    public QueryParser removeLanguage( String language ) {
+        CheckArg.isNotNull(language, "language");
+        return this.parsers.remove(language.toLowerCase());
+    }
+
+    /**
+     * Remove from this engine the language with the given name.
+     * 
+     * @param firstLanguage the name of the first language to remove, which must match the {@link QueryParser#getLanguage()
+     *        language} of the parser
+     * @param additionalLanguages the names of the additional languages to remove, which must match the
+     *        {@link QueryParser#getLanguage() language} of the parser
+     * @return the parser for the language, or null if the engine had no support for the named language
+     * @throws IllegalArgumentException if the language is null
+     */
+    public Collection<QueryParser> removeLanguages( String firstLanguage,
+                                                    String... additionalLanguages ) {
+        Collection<QueryParser> removed = new HashSet<QueryParser>();
+        QueryParser parser = removeLanguage(firstLanguage);
+        if (parser != null) removed.add(parser);
+        for (String language : additionalLanguages) {
+            parser = removeLanguage(language);
+            if (parser != null) removed.add(parser);
+        }
+        return removed;
+    }
+
+    /**
+     * Get the set of languages that this engine is capable of parsing.
+     * 
+     * @return the unmodifiable copy of the set of languages; never null but possibly empty
+     */
+    public Set<String> getLanguages() {
+        Set<String> result = new HashSet<String>();
+        for (QueryParser parser : parsers.values()) {
+            result.add(parser.getLanguage());
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    /**
+     * Execute the supplied query by planning, optimizing, and then processing it.
+     * 
+     * @param context the context in which the query should be executed
+     * @param language the language in which the query is expressed; must be one of the supported {@link #getLanguages()
+     *        languages}
+     * @param query the query that is to be executed
+     * @return the parsed query command; never null
+     * @throws IllegalArgumentException if the language, context or query references are null, or if the language is not known
+     * @throws ParsingException if there is an error parsing the supplied query
+     * @throws InvalidQueryException if the supplied query can be parsed but is invalid
+     */
+    public QueryCommand parse( ExecutionContext context,
+                               String language,
+                               String query ) {
+        CheckArg.isNotNull(language, "language");
+        CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(query, "query");
+        QueryParser parser = parsers.get(language.toLowerCase());
+        if (parser == null) {
+            throw new IllegalArgumentException(GraphI18n.unknownQueryLanguage.text(language));
+        }
+        return parser.parseQuery(query, context);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        // There won't be too many instances of QueryParsers, so have them all return the same hash code
+        return 1;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof QueryParsers) {
+            QueryParsers that = (QueryParsers)obj;
+            return this.getLanguages().equals(that.getLanguages());
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "Query parsers: " + parsers.keySet();
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParsers.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-10-15 19:53:51 UTC (rev 1290)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-10-15 19:54:48 UTC (rev 1291)
@@ -118,6 +118,31 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return LANGUAGE;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof QueryParser) {
+            QueryParser that = (QueryParser)obj;
+            return this.getLanguage().equals(that.getLanguage());
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(String, ExecutionContext)
      */
     public QueryCommand parseQuery( String query,

Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/QueryParsersTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/QueryParsersTest.java	                        (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/QueryParsersTest.java	2009-10-15 19:54:48 UTC (rev 1291)
@@ -0,0 +1,181 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.parse;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class QueryParsersTest {
+
+    private QueryParsers parsers;
+    private QueryParser parser1;
+    private QueryParser parser2;
+    private QueryParser parser3;
+
+    @Before
+    public void beforeEach() {
+        parsers = new QueryParsers();
+        parser1 = new MockParser("language 1");
+        parser2 = new MockParser("language 2");
+        parser3 = new MockParser("language 3");
+    }
+
+    @Test
+    public void shouldAllowNullArrayInConstructor() {
+        parsers = new QueryParsers((QueryParser[])null);
+        assertThat(parsers.getLanguages().isEmpty(), is(true));
+    }
+
+    @Test
+    public void shouldAllowNullIterableInConstructor() {
+        parsers = new QueryParsers((Iterable<QueryParser>)null);
+        assertThat(parsers.getLanguages().isEmpty(), is(true));
+    }
+
+    @Test
+    public void shouldAllowOneQueryParserReferenceInConstructor() {
+        parsers = new QueryParsers(parser1);
+        assertThat(parsers.getLanguages().size(), is(1));
+        assertThat(parsers.getLanguages().contains(parser1.getLanguage()), is(true));
+    }
+
+    @Test
+    public void shouldAllowTwoQueryParserReferencesInConstructor() {
+        parsers = new QueryParsers(parser1, parser2);
+        assertThat(parsers.getLanguages().size(), is(2));
+        assertThat(parsers.getLanguages().contains(parser1.getLanguage()), is(true));
+        assertThat(parsers.getLanguages().contains(parser2.getLanguage()), is(true));
+    }
+
+    @Test
+    public void shouldAllowThreeQueryParserReferencesInConstructor() {
+        parsers = new QueryParsers(parser1, parser2, parser3);
+        assertThat(parsers.getLanguages().size(), is(3));
+        assertThat(parsers.getLanguages().contains(parser1.getLanguage()), is(true));
+        assertThat(parsers.getLanguages().contains(parser2.getLanguage()), is(true));
+        assertThat(parsers.getLanguages().contains(parser3.getLanguage()), is(true));
+    }
+
+    @Test
+    public void shouldOverwriteQueryParsersWithSameLanguageInConstructor() {
+        parser2 = new MockParser(parser1.getLanguage());
+        parsers = new QueryParsers(parser1, parser2, parser3);
+        assertThat(parsers.getLanguages().size(), is(2));
+        assertThat(parsers.getLanguages().contains(parser2.getLanguage()), is(true));
+        assertThat(parsers.getLanguages().contains(parser3.getLanguage()), is(true));
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldNotAllowNullQueryParserReferenceInAddLanguagesMethod() {
+        parsers.addLanguages((QueryParser)null);
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldAllowNullIterableInAddLanguagesMethod() {
+        parsers.addLanguages((Iterable<QueryParser>)null);
+    }
+
+    @Test
+    public void shouldAddOneQueryParserReference() {
+        parsers.addLanguage(parser1);
+        assertThat(parsers.getLanguages().size(), is(1));
+        assertThat(parsers.getLanguages().contains(parser1.getLanguage()), is(true));
+    }
+
+    @Test
+    public void shouldAddTwoQueryParserReferences() {
+        parsers.addLanguages(parser1, parser2);
+        assertThat(parsers.getLanguages().size(), is(2));
+        assertThat(parsers.getLanguages().contains(parser1.getLanguage()), is(true));
+        assertThat(parsers.getLanguages().contains(parser2.getLanguage()), is(true));
+    }
+
+    @Test
+    public void shouldAddThreeQueryParserReferences() {
+        parsers.addLanguages(parser1, parser2, parser3);
+        assertThat(parsers.getLanguages().size(), is(3));
+        assertThat(parsers.getLanguages().contains(parser1.getLanguage()), is(true));
+        assertThat(parsers.getLanguages().contains(parser2.getLanguage()), is(true));
+        assertThat(parsers.getLanguages().contains(parser3.getLanguage()), is(true));
+    }
+
+    @Test
+    public void shouldOverwriteQueryParsersWithSameLanguageWhenAddingQueryParser() {
+        parser2 = new MockParser(parser1.getLanguage());
+        parsers.addLanguages(parser1, parser2, parser3);
+        assertThat(parsers.getLanguages().size(), is(2));
+        assertThat(parsers.getLanguages().contains(parser2.getLanguage()), is(true));
+        assertThat(parsers.getLanguages().contains(parser3.getLanguage()), is(true));
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldFailToParseUnknownLanguage() {
+        parsers.parse(new ExecutionContext(), "unknown language", "This is a bogus query");
+    }
+
+    protected static class MockParser implements QueryParser {
+        private final String language;
+
+        protected MockParser( String language ) {
+            this.language = language;
+            assert this.language != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.parse.QueryParser#getLanguage()
+         */
+        public String getLanguage() {
+            return language;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String, org.jboss.dna.graph.ExecutionContext)
+         */
+        public QueryCommand parseQuery( String query,
+                                        ExecutionContext context ) throws InvalidQueryException {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "Mock parser for " + language;
+        }
+    }
+}


Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/QueryParsersTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java	2009-10-15 19:53:51 UTC (rev 1290)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/xpath/XPathQueryParser.java	2009-10-15 19:54:48 UTC (rev 1291)
@@ -51,6 +51,31 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return LANGUAGE;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof QueryParser) {
+            QueryParser that = (QueryParser)obj;
+            return this.getLanguage().equals(that.getLanguage());
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(java.lang.String, org.jboss.dna.graph.ExecutionContext)
      */
     public QueryCommand parseQuery( String query,

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-10-15 19:53:51 UTC (rev 1290)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-10-15 19:54:48 UTC (rev 1291)
@@ -25,10 +25,7 @@
 
 import java.io.IOException;
 import java.util.LinkedList;
-import java.util.Set;
 import org.apache.lucene.queryParser.ParseException;
-import org.jboss.dna.common.text.ParsingException;
-import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryEngine;
 import org.jboss.dna.graph.query.QueryResults;
@@ -37,8 +34,6 @@
 import org.jboss.dna.graph.query.optimize.Optimizer;
 import org.jboss.dna.graph.query.optimize.OptimizerRule;
 import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
-import org.jboss.dna.graph.query.parse.InvalidQueryException;
-import org.jboss.dna.graph.query.parse.QueryParser;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
 import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
@@ -58,66 +53,10 @@
         engine = new QueryEngine(new CanonicalPlanner(), new LuceneOptimizer(), new LuceneProcessor(), schemata);
     }
 
-    public LuceneQueryEngine( Schemata schemata,
-                              QueryParser... languages ) {
-        engine = new QueryEngine(new CanonicalPlanner(), new LuceneOptimizer(), new LuceneProcessor(), schemata, languages);
-    }
-
     /**
-     * Add a language to this engine by supplying its parser.
-     * 
-     * @param languageParser the query parser for the language
-     * @throws IllegalArgumentException if the language parser is null
-     */
-    public void addLanguage( QueryParser languageParser ) {
-        this.engine.addLanguage(languageParser);
-    }
-
-    /**
-     * Remove from this engine the language with the given name.
-     * 
-     * @param language the name of the language, which is to match the {@link QueryParser#getLanguage() language} of the parser
-     * @return the parser for the language, or null if the engine had no support for the named language
-     * @throws IllegalArgumentException if the language is null
-     */
-    public QueryParser removeLanguage( String language ) {
-        return this.engine.removeLanguage(language);
-    }
-
-    /**
-     * Get the set of languages that this engine is capable of parsing.
-     * 
-     * @return the unmodifiable copy of the set of languages; never null but possibly empty
-     */
-    public Set<String> getLanguages() {
-        return this.engine.getLanguages();
-    }
-
-    /**
      * Execute the supplied query by planning, optimizing, and then processing it.
      * 
-     * @param indexes the context in which the query should be executed
-     * @param language the language in which the query is expressed; must be one of the supported {@link #getLanguages()
-     *        languages}
      * @param query the query that is to be executed
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the language, context or query references are null, or if the language is not know
-     * @throws ParsingException if there is an error parsing the supplied query
-     * @throws InvalidQueryException if the supplied query can be parsed but is invalid
-     */
-    public QueryResults execute( IndexContext indexes,
-                                 String language,
-                                 String query ) {
-        CheckArg.isNotNull(language, "language");
-        CheckArg.isNotNull(indexes, "indexes");
-        CheckArg.isNotNull(query, "query");
-        return engine.execute(indexes.context(), language, query);
-    }
-
-    /**
-     * Execute the supplied query by planning, optimizing, and then processing it.
-     * 
-     * @param query the query that is to be executed
      * @param indexes the indexes that should be used to execute the query; never null
      * @return the query results; never null
      * @throws IllegalArgumentException if the context or query references are null


From dna-commits at lists.jboss.org  Thu Oct 15 15:55:26 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 15 Oct 2009 15:55:26 -0400
Subject: [dna-commits] DNA SVN: r1292 - in trunk/dna-graph/src:
	main/java/org/jboss/dna/graph/query/model and 2 other directories.
Message-ID: <200910151955.n9FJtQ5M021123@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-15 15:55:26 -0400 (Thu, 15 Oct 2009)
New Revision: 1292

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
Log:
DNA-467 Add search/query support to the graph API

Added support for 'IN' clauses (called set criteria) in the Abstract Query Model.  Also added similar support to the SQL parser, along with support for 'NOT IN'.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java	2009-10-15 19:54:48 UTC (rev 1291)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java	2009-10-15 19:55:26 UTC (rev 1292)
@@ -25,6 +25,8 @@
 
 import java.math.BigDecimal;
 import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
@@ -78,8 +80,10 @@
 import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
 import org.jboss.dna.graph.query.model.Selector;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.SetCriteria;
 import org.jboss.dna.graph.query.model.SetQuery;
 import org.jboss.dna.graph.query.model.Source;
+import org.jboss.dna.graph.query.model.StaticOperand;
 import org.jboss.dna.graph.query.model.UpperCase;
 import org.jboss.dna.graph.query.model.Visitors;
 import org.jboss.dna.graph.query.model.SetQuery.Operation;
@@ -1693,6 +1697,17 @@
             this.constraintBuilder = constraintBuilder;
         }
 
+        public ConstraintBuilder isIn( Object firstLiteral,
+                                       Object... additionalLiterals ) {
+            CheckArg.isNotNull(firstLiteral, "firstLiteral");
+            Collection<StaticOperand> right = new ArrayList<StaticOperand>();
+            right.add(firstLiteral instanceof Literal ? (Literal)firstLiteral : new Literal(firstLiteral));
+            for (Object literal : additionalLiterals) {
+                right.add(literal instanceof Literal ? (Literal)literal : new Literal(literal));
+            }
+            return this.constraintBuilder.setConstraint(new SetCriteria(left, right));
+        }
+
         /**
          * Define the operator that will be used in the comparison, returning an interface that can be used to define the
          * right-hand-side of the comparison.

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java	2009-10-15 19:55:26 UTC (rev 1292)
@@ -0,0 +1,96 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.model;
+
+import java.util.Collection;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A constraint that evaluates to true when the defined operation evaluates to true.
+ */
+ at Immutable
+public class SetCriteria extends Constraint {
+
+    private final DynamicOperand left;
+    private final Collection<StaticOperand> setOperands;
+
+    public SetCriteria( DynamicOperand left,
+                        Collection<StaticOperand> setOperands ) {
+        CheckArg.isNotNull(left, "left");
+        CheckArg.isNotNull(setOperands, "setOperands");
+        this.left = left;
+        this.setOperands = setOperands;
+    }
+
+    /**
+     * @return operand1
+     */
+    public final DynamicOperand getLeftOperand() {
+        return left;
+    }
+
+    /**
+     * @return operand2
+     */
+    public final Collection<StaticOperand> getRightOperands() {
+        return setOperands;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return Visitors.readable(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof SetCriteria) {
+            SetCriteria that = (SetCriteria)obj;
+            if (!this.left.equals(that.left)) return false;
+            if (!this.setOperands.equals(that.setOperands)) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.model.Visitable#accept(org.jboss.dna.graph.query.model.Visitor)
+     */
+    public void accept( Visitor visitor ) {
+        visitor.visit(this);
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java	2009-10-15 19:54:48 UTC (rev 1291)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java	2009-10-15 19:55:26 UTC (rev 1292)
@@ -88,6 +88,8 @@
 
     void visit( SameNodeJoinCondition obj );
 
+    void visit( SetCriteria obj );
+
     void visit( SetQuery obj );
 
     void visit( UpperCase obj );

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-10-15 19:54:48 UTC (rev 1291)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-10-15 19:55:26 UTC (rev 1292)
@@ -24,6 +24,7 @@
 package org.jboss.dna.graph.query.model;
 
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Set;
 import org.jboss.dna.common.util.CheckArg;
@@ -155,12 +156,12 @@
 
             @Override
             public void visit( NodeDepth depth ) {
-                super.visit(depth);
+                symbols.add(depth.getSelectorName());
             }
 
             @Override
             public void visit( NodePath path ) {
-                super.visit(path);
+                symbols.add(path.getSelectorName());
             }
 
             @Override
@@ -462,6 +463,14 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SetCriteria)
+         */
+        public void visit( SetCriteria obj ) {
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SetQuery)
          */
         public void visit( SetQuery obj ) {
@@ -853,6 +862,20 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SetCriteria)
+         */
+        public void visit( SetCriteria setCriteria ) {
+            strategy.visit(setCriteria);
+            enqueue(setCriteria.getLeftOperand());
+            for (StaticOperand right : setCriteria.getRightOperands()) {
+                enqueue(right);
+            }
+            visitNext();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SetQuery)
          */
         public void visit( SetQuery setQuery ) {
@@ -1331,6 +1354,25 @@
         /**
          * {@inheritDoc}
          * 
+         * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SetCriteria)
+         */
+        public void visit( SetCriteria criteria ) {
+            criteria.getLeftOperand().accept(this);
+            append(" IN (");
+            Iterator<StaticOperand> iter = criteria.getRightOperands().iterator();
+            if (iter.hasNext()) {
+                iter.next().accept(this);
+                while (iter.hasNext()) {
+                    append(',');
+                    iter.next().accept(this);
+                }
+            }
+            append(')');
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
          * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SetQuery)
          */
         public void visit( SetQuery query ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-10-15 19:54:48 UTC (rev 1291)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-10-15 19:55:26 UTC (rev 1292)
@@ -25,6 +25,7 @@
 
 import static org.jboss.dna.common.text.TokenStream.ANY_VALUE;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -84,6 +85,7 @@
 import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
 import org.jboss.dna.graph.query.model.Selector;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.SetCriteria;
 import org.jboss.dna.graph.query.model.SetQuery;
 import org.jboss.dna.graph.query.model.Source;
 import org.jboss.dna.graph.query.model.StaticOperand;
@@ -414,9 +416,18 @@
                         String msg = GraphI18n.expectingConstraintCondition.text(name, pos2.getLine(), pos2.getColumn());
                         throw new ParsingException(pos, msg);
                     }
-                    Operator operator = parseComparisonOperator(tokens);
-                    StaticOperand right = parseStaticOperand(tokens, context);
-                    constraint = new Comparison(left, operator, right);
+                    if (tokens.matches("IN", "(")) {
+                        Collection<StaticOperand> staticOperands = parseInClause(tokens, context);
+                        constraint = new SetCriteria(left, staticOperands);
+                    } else if (tokens.matches("NOT", "IN", "(")) {
+                        tokens.consume("NOT");
+                        Collection<StaticOperand> staticOperands = parseInClause(tokens, context);
+                        constraint = new Not(new SetCriteria(left, staticOperands));
+                    } else {
+                        Operator operator = parseComparisonOperator(tokens);
+                        StaticOperand right = parseStaticOperand(tokens, context);
+                        constraint = new Comparison(left, operator, right);
+                    }
                 }
                 // else continue ...
             }
@@ -435,6 +446,21 @@
         return constraint;
     }
 
+    protected Collection<StaticOperand> parseInClause( TokenStream tokens,
+                                                       ExecutionContext context ) {
+        Collection<StaticOperand> result = new ArrayList<StaticOperand>();
+        tokens.consume("IN");
+        tokens.consume("(");
+        if (!tokens.canConsume(")")) {
+            // Not empty, so read the static operands ...
+            do {
+                result.add(parseStaticOperand(tokens, context));
+            } while (tokens.canConsume(','));
+            tokens.consume(")");
+        }
+        return result;
+    }
+
     protected Term parseFullTextSearchExpression( String expression,
                                                   Position position ) {
         try {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java	2009-10-15 19:54:48 UTC (rev 1291)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java	2009-10-15 19:55:26 UTC (rev 1292)
@@ -1325,4 +1325,17 @@
         assertThatSql(query, is("SELECT * FROM table AS nodes " + //
                                 "WHERE LOWER(UPPER(NAME(nodes))) = 'literal'"));
     }
+
+    @Test
+    public void shouldBuilderQueryWithSetCriteria() {
+        query = builder.selectStar()
+                       .from("table AS nodes")
+                       .where()
+                       .nodeName("nodes")
+                       .isIn("value1", "value2", "value3")
+                       .end()
+                       .query();
+        assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+                                "WHERE NAME(nodes) IN ('value1','value2','value3')"));
+    }
 }


From dna-commits at lists.jboss.org  Thu Oct 15 15:55:58 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 15 Oct 2009 15:55:58 -0400
Subject: [dna-commits] DNA SVN: r1293 - in trunk/dna-graph/src:
	test/java/org/jboss/dna/graph/query/parse and 1 other directory.
Message-ID: <200910151955.n9FJtw4w021246@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-15 15:55:58 -0400 (Thu, 15 Oct 2009)
New Revision: 1293

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
Log:
DNA-467 Add search/query support to the graph API

Updated test cases for SqlQueryParser.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-10-15 19:55:26 UTC (rev 1292)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java	2009-10-15 19:55:58 UTC (rev 1293)
@@ -446,9 +446,9 @@
         return constraint;
     }
 
-    protected Collection<StaticOperand> parseInClause( TokenStream tokens,
-                                                       ExecutionContext context ) {
-        Collection<StaticOperand> result = new ArrayList<StaticOperand>();
+    protected List<StaticOperand> parseInClause( TokenStream tokens,
+                                                 ExecutionContext context ) {
+        List<StaticOperand> result = new ArrayList<StaticOperand>();
         tokens.consume("IN");
         tokens.consume("(");
         if (!tokens.canConsume(")")) {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-10-15 19:55:26 UTC (rev 1292)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java	2009-10-15 19:55:58 UTC (rev 1293)
@@ -622,6 +622,58 @@
     }
 
     // ----------------------------------------------------------------------------------------------------------------
+    // parseInClause
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldParseInClauseFromStringWithSingleValidLiteral() {
+        List<StaticOperand> result = parser.parseInClause(tokens("IN ('value1')"), context);
+        assertThat(result.size(), is(1));
+        assertThat(result.get(0), is((StaticOperand)literal("value1")));
+    }
+
+    @Test
+    public void shouldParseInClauseFromStringWithTwoValidLiteral() {
+        List<StaticOperand> result = parser.parseInClause(tokens("IN ('value1','value2')"), context);
+        assertThat(result.size(), is(2));
+        assertThat(result.get(0), is((StaticOperand)literal("value1")));
+        assertThat(result.get(1), is((StaticOperand)literal("value2")));
+    }
+
+    @Test
+    public void shouldParseInClauseFromStringWithThreeValidLiteral() {
+        List<StaticOperand> result = parser.parseInClause(tokens("IN ('value1','value2','value3')"), context);
+        assertThat(result.size(), is(3));
+        assertThat(result.get(0), is((StaticOperand)literal("value1")));
+        assertThat(result.get(1), is((StaticOperand)literal("value2")));
+        assertThat(result.get(2), is((StaticOperand)literal("value3")));
+    }
+
+    @Test
+    public void shouldParseInClauseFromStringWithSingleValidLiteralCast() {
+        List<StaticOperand> result = parser.parseInClause(tokens("IN (CAST('value1' AS STRING))"), context);
+        assertThat(result.size(), is(1));
+        assertThat(result.iterator().next(), is((StaticOperand)literal("value1")));
+
+        result = parser.parseInClause(tokens("IN (CAST('3' AS LONG))"), context);
+        assertThat(result.size(), is(1));
+        assertThat(result.iterator().next(), is((StaticOperand)literal(new Long(3))));
+    }
+
+    @Test
+    public void shouldParseInClauseFromStringWithMultipleValidLiteralCasts() {
+        List<StaticOperand> result = parser.parseInClause(tokens("IN (CAST('value1' AS STRING),CAST('3' AS LONG),'4')"), context);
+        assertThat(result.size(), is(3));
+        assertThat(result.get(0), is((StaticOperand)literal("value1")));
+        assertThat(result.get(1), is((StaticOperand)literal(new Long(3))));
+        assertThat(result.get(2), is((StaticOperand)literal("4")));
+    }
+
+    protected Literal literal( Object literalValue ) {
+        return new Literal(literalValue);
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
     // parseFullTextSearchExpression
     // ----------------------------------------------------------------------------------------------------------------
 


From dna-commits at lists.jboss.org  Thu Oct 15 15:57:20 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 15 Oct 2009 15:57:20 -0400
Subject: [dna-commits] DNA SVN: r1294 - in trunk:
	dna-common/src/test/java/org/jboss/dna/common/collection and
	10 other directories.
Message-ID: <200910151957.n9FJvKtn021448@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-15 15:57:20 -0400 (Thu, 15 Oct 2009)
New Revision: 1294

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java
Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/collection/AbstractProblems.java
   trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ImmutableProblems.java
   trunk/dna-common/src/main/java/org/jboss/dna/common/collection/Problems.java
   trunk/dna-common/src/main/java/org/jboss/dna/common/collection/SimpleProblems.java
   trunk/dna-common/src/test/java/org/jboss/dna/common/collection/AbstractProblemsTest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AddAccessNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java
Log:
DNA-467 Added more query optimizer rules, and changed the way that the query plans are handled so that access nodes always are in the shape of a valid query.

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/AbstractProblems.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/collection/AbstractProblems.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/collection/AbstractProblems.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -47,17 +47,17 @@
         addProblem(new Problem(Problem.Status.ERROR, Problem.DEFAULT_CODE, message, params, null, null, throwable));
     }
 
-    public void addError( I18n message,
-                          String resource,
+    public void addError( String resource,
                           String location,
+                          I18n message,
                           Object... params ) {
         addProblem(new Problem(Problem.Status.ERROR, Problem.DEFAULT_CODE, message, params, resource, location, null));
     }
 
     public void addError( Throwable throwable,
-                          I18n message,
                           String resource,
                           String location,
+                          I18n message,
                           Object... params ) {
         addProblem(new Problem(Problem.Status.ERROR, Problem.DEFAULT_CODE, message, params, resource, location, throwable));
     }
@@ -76,18 +76,18 @@
     }
 
     public void addError( int code,
-                          I18n message,
                           String resource,
                           String location,
+                          I18n message,
                           Object... params ) {
         addProblem(new Problem(Problem.Status.ERROR, code, message, params, resource, location, null));
     }
 
     public void addError( Throwable throwable,
                           int code,
-                          I18n message,
                           String resource,
                           String location,
+                          I18n message,
                           Object... params ) {
         addProblem(new Problem(Problem.Status.ERROR, code, message, params, resource, location, throwable));
     }
@@ -103,17 +103,17 @@
         addProblem(new Problem(Problem.Status.WARNING, Problem.DEFAULT_CODE, message, params, null, null, throwable));
     }
 
-    public void addWarning( I18n message,
-                            String resource,
+    public void addWarning( String resource,
                             String location,
+                            I18n message,
                             Object... params ) {
         addProblem(new Problem(Problem.Status.WARNING, Problem.DEFAULT_CODE, message, params, resource, location, null));
     }
 
     public void addWarning( Throwable throwable,
-                            I18n message,
                             String resource,
                             String location,
+                            I18n message,
                             Object... params ) {
         addProblem(new Problem(Problem.Status.WARNING, Problem.DEFAULT_CODE, message, params, resource, location, throwable));
     }
@@ -132,18 +132,18 @@
     }
 
     public void addWarning( int code,
-                            I18n message,
                             String resource,
                             String location,
+                            I18n message,
                             Object... params ) {
         addProblem(new Problem(Problem.Status.WARNING, code, message, params, resource, location, null));
     }
 
     public void addWarning( Throwable throwable,
                             int code,
-                            I18n message,
                             String resource,
                             String location,
+                            I18n message,
                             Object... params ) {
         addProblem(new Problem(Problem.Status.WARNING, code, message, params, resource, location, throwable));
     }
@@ -159,17 +159,17 @@
         addProblem(new Problem(Problem.Status.INFO, Problem.DEFAULT_CODE, message, params, null, null, throwable));
     }
 
-    public void addInfo( I18n message,
-                         String resource,
+    public void addInfo( String resource,
                          String location,
+                         I18n message,
                          Object... params ) {
         addProblem(new Problem(Problem.Status.INFO, Problem.DEFAULT_CODE, message, params, resource, location, null));
     }
 
     public void addInfo( Throwable throwable,
-                         I18n message,
                          String resource,
                          String location,
+                         I18n message,
                          Object... params ) {
         addProblem(new Problem(Problem.Status.INFO, Problem.DEFAULT_CODE, message, params, resource, location, throwable));
     }
@@ -188,18 +188,18 @@
     }
 
     public void addInfo( int code,
-                         I18n message,
                          String resource,
                          String location,
+                         I18n message,
                          Object... params ) {
         addProblem(new Problem(Problem.Status.INFO, code, message, params, resource, location, null));
     }
 
     public void addInfo( Throwable throwable,
                          int code,
-                         I18n message,
                          String resource,
                          String location,
+                         I18n message,
                          Object... params ) {
         addProblem(new Problem(Problem.Status.INFO, code, message, params, resource, location, throwable));
     }

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ImmutableProblems.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ImmutableProblems.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ImmutableProblems.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -54,12 +54,12 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addError(org.jboss.dna.common.i18n.I18n, java.lang.String, java.lang.String,
+     * @see org.jboss.dna.common.collection.Problems#addError(java.lang.String, java.lang.String, org.jboss.dna.common.i18n.I18n,
      *      java.lang.Object[])
      */
-    public void addError( I18n message,
-                          String resource,
+    public void addError( String resource,
                           String location,
+                          I18n message,
                           Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -78,13 +78,12 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addError(int, org.jboss.dna.common.i18n.I18n, java.lang.String,
-     *      java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addError(int, String, String, I18n, Object...)
      */
     public void addError( int code,
-                          I18n message,
                           String resource,
                           String location,
+                          I18n message,
                           Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -104,13 +103,13 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addError(java.lang.Throwable, org.jboss.dna.common.i18n.I18n,
-     *      java.lang.String, java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addError(java.lang.Throwable, java.lang.String, java.lang.String,
+     *      org.jboss.dna.common.i18n.I18n,java.lang.Object[])
      */
     public void addError( Throwable throwable,
-                          I18n message,
                           String resource,
                           String location,
+                          I18n message,
                           Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -131,14 +130,14 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addError(java.lang.Throwable, int, org.jboss.dna.common.i18n.I18n,
-     *      java.lang.String, java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addError(java.lang.Throwable, int, java.lang.String, java.lang.String,
+     *      org.jboss.dna.common.i18n.I18n, java.lang.Object[])
      */
     public void addError( Throwable throwable,
                           int code,
-                          I18n message,
                           String resource,
                           String location,
+                          I18n message,
                           Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -156,12 +155,12 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addInfo(org.jboss.dna.common.i18n.I18n, java.lang.String, java.lang.String,
+     * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.String, java.lang.String, org.jboss.dna.common.i18n.I18n,
      *      java.lang.Object[])
      */
-    public void addInfo( I18n message,
-                         String resource,
+    public void addInfo( String resource,
                          String location,
+                         I18n message,
                          Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -180,13 +179,13 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addInfo(int, org.jboss.dna.common.i18n.I18n, java.lang.String,
-     *      java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addInfo(int, java.lang.String, java.lang.String,
+     *      org.jboss.dna.common.i18n.I18n, java.lang.Object[])
      */
     public void addInfo( int code,
-                         I18n message,
                          String resource,
                          String location,
+                         I18n message,
                          Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -206,13 +205,13 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.Throwable, org.jboss.dna.common.i18n.I18n,
-     *      java.lang.String, java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.Throwable, java.lang.String, java.lang.String,
+     *      org.jboss.dna.common.i18n.I18n, java.lang.Object[])
      */
     public void addInfo( Throwable throwable,
-                         I18n message,
                          String resource,
                          String location,
+                         I18n message,
                          Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -233,14 +232,14 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.Throwable, int, org.jboss.dna.common.i18n.I18n,
-     *      java.lang.String, java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addInfo(java.lang.Throwable, int, java.lang.String, java.lang.String,
+     *      org.jboss.dna.common.i18n.I18n, java.lang.Object[])
      */
     public void addInfo( Throwable throwable,
                          int code,
-                         I18n message,
                          String resource,
                          String location,
+                         I18n message,
                          Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -258,12 +257,12 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addWarning(org.jboss.dna.common.i18n.I18n, java.lang.String,
-     *      java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.String, java.lang.String,
+     *      org.jboss.dna.common.i18n.I18n, java.lang.Object[])
      */
-    public void addWarning( I18n message,
-                            String resource,
+    public void addWarning( String resource,
                             String location,
+                            I18n message,
                             Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -282,13 +281,13 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addWarning(int, org.jboss.dna.common.i18n.I18n, java.lang.String,
-     *      java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addWarning(int, java.lang.String, java.lang.String,
+     *      org.jboss.dna.common.i18n.I18n, java.lang.Object[])
      */
     public void addWarning( int code,
-                            I18n message,
                             String resource,
                             String location,
+                            I18n message,
                             Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -308,13 +307,13 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.Throwable, org.jboss.dna.common.i18n.I18n,
-     *      java.lang.String, java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.Throwable, java.lang.String, java.lang.String ,
+     *      org.jboss.dna.common.i18n.I18n,java.lang.Object[])
      */
     public void addWarning( Throwable throwable,
-                            I18n message,
                             String resource,
                             String location,
+                            I18n message,
                             Object... params ) {
         throw new UnsupportedOperationException();
     }
@@ -335,14 +334,14 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.Throwable, int, org.jboss.dna.common.i18n.I18n,
-     *      java.lang.String, java.lang.String, java.lang.Object[])
+     * @see org.jboss.dna.common.collection.Problems#addWarning(java.lang.Throwable, int, java.lang.String, java.lang.String,
+     *      org.jboss.dna.common.i18n.I18n,java.lang.Object[])
      */
     public void addWarning( Throwable throwable,
                             int code,
-                            I18n message,
                             String resource,
                             String location,
+                            I18n message,
                             Object... params ) {
         throw new UnsupportedOperationException();
     }

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/Problems.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/collection/Problems.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/collection/Problems.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -56,14 +56,14 @@
      * Add an error message with a description of the resource, its location, and the parameters that should be used when
      * localizing the message
      * 
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
-    void addError( I18n message,
-                   String resource,
+    void addError( String resource,
                    String location,
+                   I18n message,
                    Object... params );
 
     /**
@@ -71,15 +71,15 @@
      * when localizing the message
      * 
      * @param throwable the exception that represents the error; may be null
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
     void addError( Throwable throwable,
-                   I18n message,
                    String resource,
                    String location,
+                   I18n message,
                    Object... params );
 
     /**
@@ -111,15 +111,15 @@
      * localizing the message
      * 
      * @param code the error code
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
     void addError( int code,
-                   I18n message,
                    String resource,
                    String location,
+                   I18n message,
                    Object... params );
 
     /**
@@ -128,16 +128,16 @@
      * 
      * @param throwable the exception that represents the error; may be null
      * @param code the error code
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
     void addError( Throwable throwable,
                    int code,
-                   I18n message,
                    String resource,
                    String location,
+                   I18n message,
                    Object... params );
 
     /**
@@ -164,14 +164,14 @@
      * Add a warning message with a description of the resource, its location, and the parameters that should be used when
      * localizing the message
      * 
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
-    void addWarning( I18n message,
-                     String resource,
+    void addWarning( String resource,
                      String location,
+                     I18n message,
                      Object... params );
 
     /**
@@ -179,15 +179,15 @@
      * used when localizing the message
      * 
      * @param throwable the exception that represents the warning; may be null
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
     void addWarning( Throwable throwable,
-                     I18n message,
                      String resource,
                      String location,
+                     I18n message,
                      Object... params );
 
     /**
@@ -219,15 +219,15 @@
      * localizing the message
      * 
      * @param code the problem code
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
     void addWarning( int code,
-                     I18n message,
                      String resource,
                      String location,
+                     I18n message,
                      Object... params );
 
     /**
@@ -236,16 +236,16 @@
      * 
      * @param throwable the exception that represents the warning; may be null
      * @param code the problem code
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
     void addWarning( Throwable throwable,
                      int code,
-                     I18n message,
                      String resource,
                      String location,
+                     I18n message,
                      Object... params );
 
     /**
@@ -272,14 +272,14 @@
      * Add an informational message with a description of the resource, its location, and the parameters that should be used when
      * localizing the message
      * 
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
-    void addInfo( I18n message,
-                  String resource,
+    void addInfo( String resource,
                   String location,
+                  I18n message,
                   Object... params );
 
     /**
@@ -287,15 +287,15 @@
      * be used when localizing the message
      * 
      * @param throwable the exception that represents the problem; may be null
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
     void addInfo( Throwable throwable,
-                  I18n message,
                   String resource,
                   String location,
+                  I18n message,
                   Object... params );
 
     /**
@@ -327,15 +327,15 @@
      * localizing the message
      * 
      * @param code the problem code
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
     void addInfo( int code,
-                  I18n message,
                   String resource,
                   String location,
+                  I18n message,
                   Object... params );
 
     /**
@@ -344,16 +344,16 @@
      * 
      * @param throwable the exception that represents the problem; may be null
      * @param code the problem code
-     * @param message the internationalized message describing the problem
      * @param resource the description of the resource; may be null
      * @param location the location of the resource; may be null
+     * @param message the internationalized message describing the problem
      * @param params the values for the parameters in the message
      */
     void addInfo( Throwable throwable,
                   int code,
-                  I18n message,
                   String resource,
                   String location,
+                  I18n message,
                   Object... params );
 
     /**

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/SimpleProblems.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/collection/SimpleProblems.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/collection/SimpleProblems.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.common.collection;
 
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import net.jcip.annotations.NotThreadSafe;
@@ -57,4 +58,23 @@
     protected List<Problem> getProblems() {
         return this.problems != null ? problems : EMPTY_PROBLEMS;
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        Iterator<Problem> iter = getProblems().iterator();
+        if (iter.hasNext()) {
+            sb.append(iter.next());
+            while (iter.hasNext()) {
+                sb.append("\n");
+                sb.append(iter.next());
+            }
+        }
+        return sb.toString();
+    }
 }

Modified: trunk/dna-common/src/test/java/org/jboss/dna/common/collection/AbstractProblemsTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/collection/AbstractProblemsTest.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/collection/AbstractProblemsTest.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -104,7 +104,7 @@
     @Test
     public void shouldAddErrorByMessageAndResourceAndLocationAndParametersUsingDefaultCode() {
         error = new Problem(Status.ERROR, Problem.DEFAULT_CODE, message, new Object[] {"error msg"}, resource, location, null);
-        problems.addError(error.getMessage(), error.getResource(), error.getLocation(), error.getParameters());
+        problems.addError(error.getResource(), error.getLocation(), error.getMessage(), error.getParameters());
         assertThat(problems.iterator().next(), is(error));
         assertThat(problems.isEmpty(), is(false));
         assertThat(problems.size(), is(1));
@@ -113,7 +113,7 @@
     @Test
     public void shouldAddErrorByCodeAndMesssageAndResourceAndLocationAndParameters() {
         error = new Problem(Status.ERROR, 1, message, new Object[] {"error msg"}, resource, location, null);
-        problems.addError(error.getCode(), error.getMessage(), error.getResource(), error.getLocation(), error.getParameters());
+        problems.addError(error.getCode(), error.getResource(), error.getLocation(), error.getMessage(), error.getParameters());
         assertThat(problems.iterator().next(), is(error));
         assertThat(problems.isEmpty(), is(false));
         assertThat(problems.size(), is(1));
@@ -124,9 +124,9 @@
         error = new Problem(Status.ERROR, Problem.DEFAULT_CODE, message, new Object[] {"error msg"}, resource, location,
                             throwable);
         problems.addError(error.getThrowable(),
-                          error.getMessage(),
                           error.getResource(),
                           error.getLocation(),
+                          error.getMessage(),
                           error.getParameters());
         assertThat(problems.iterator().next(), is(error));
         assertThat(problems.isEmpty(), is(false));
@@ -138,9 +138,9 @@
         error = new Problem(Status.ERROR, 1, message, new Object[] {"error msg"}, resource, location, throwable);
         problems.addError(error.getThrowable(),
                           error.getCode(),
-                          error.getMessage(),
                           error.getResource(),
                           error.getLocation(),
+                          error.getMessage(),
                           error.getParameters());
         assertThat(problems.iterator().next(), is(error));
         assertThat(problems.isEmpty(), is(false));
@@ -186,7 +186,7 @@
     public void shouldAddWarningByMessageAndResourceAndLocationAndParametersUsingDefaultCode() {
         warning = new Problem(Status.WARNING, Problem.DEFAULT_CODE, message, new Object[] {"warning msg"}, resource, location,
                               null);
-        problems.addWarning(warning.getMessage(), warning.getResource(), warning.getLocation(), warning.getParameters());
+        problems.addWarning(warning.getResource(), warning.getLocation(), warning.getMessage(), warning.getParameters());
         assertThat(problems.iterator().next(), is(warning));
         assertThat(problems.isEmpty(), is(false));
         assertThat(problems.size(), is(1));
@@ -196,9 +196,9 @@
     public void shouldAddWarningByCodeAndMesssageAndResourceAndLocationAndParameters() {
         warning = new Problem(Status.WARNING, 1, message, new Object[] {"warning msg"}, resource, location, null);
         problems.addWarning(warning.getCode(),
-                            warning.getMessage(),
                             warning.getResource(),
                             warning.getLocation(),
+                            warning.getMessage(),
                             warning.getParameters());
         assertThat(problems.iterator().next(), is(warning));
         assertThat(problems.isEmpty(), is(false));
@@ -210,9 +210,9 @@
         warning = new Problem(Status.WARNING, Problem.DEFAULT_CODE, message, new Object[] {"warning msg"}, resource, location,
                               throwable);
         problems.addWarning(warning.getThrowable(),
-                            warning.getMessage(),
                             warning.getResource(),
                             warning.getLocation(),
+                            warning.getMessage(),
                             warning.getParameters());
         assertThat(problems.iterator().next(), is(warning));
         assertThat(problems.isEmpty(), is(false));
@@ -224,9 +224,9 @@
         warning = new Problem(Status.WARNING, 1, message, new Object[] {"warning msg"}, resource, location, throwable);
         problems.addWarning(warning.getThrowable(),
                             warning.getCode(),
-                            warning.getMessage(),
                             warning.getResource(),
                             warning.getLocation(),
+                            warning.getMessage(),
                             warning.getParameters());
         assertThat(problems.iterator().next(), is(warning));
         assertThat(problems.isEmpty(), is(false));
@@ -271,7 +271,7 @@
     @Test
     public void shouldAddInfoByMessageAndResourceAndLocationAndParametersUsingDefaultCode() {
         info = new Problem(Status.INFO, Problem.DEFAULT_CODE, message, new Object[] {"info msg"}, resource, location, null);
-        problems.addInfo(info.getMessage(), info.getResource(), info.getLocation(), info.getParameters());
+        problems.addInfo(info.getResource(), info.getLocation(), info.getMessage(), info.getParameters());
         assertThat(problems.iterator().next(), is(info));
         assertThat(problems.isEmpty(), is(false));
         assertThat(problems.size(), is(1));
@@ -280,7 +280,7 @@
     @Test
     public void shouldAddInfoByCodeAndMesssageAndResourceAndLocationAndParameters() {
         info = new Problem(Status.INFO, 1, message, new Object[] {"info msg"}, resource, location, null);
-        problems.addInfo(info.getCode(), info.getMessage(), info.getResource(), info.getLocation(), info.getParameters());
+        problems.addInfo(info.getCode(), info.getResource(), info.getLocation(), info.getMessage(), info.getParameters());
         assertThat(problems.iterator().next(), is(info));
         assertThat(problems.isEmpty(), is(false));
         assertThat(problems.size(), is(1));
@@ -289,7 +289,7 @@
     @Test
     public void shouldAddInfoByThrowableAndMessageAndResourceAndLocationAndParametersUsingDefaultCode() {
         info = new Problem(Status.INFO, Problem.DEFAULT_CODE, message, new Object[] {"info msg"}, resource, location, throwable);
-        problems.addInfo(info.getThrowable(), info.getMessage(), info.getResource(), info.getLocation(), info.getParameters());
+        problems.addInfo(info.getThrowable(), info.getResource(), info.getLocation(), info.getMessage(), info.getParameters());
         assertThat(problems.iterator().next(), is(info));
         assertThat(problems.isEmpty(), is(false));
         assertThat(problems.size(), is(1));
@@ -300,9 +300,9 @@
         info = new Problem(Status.INFO, 1, message, new Object[] {"info msg"}, resource, location, throwable);
         problems.addInfo(info.getThrowable(),
                          info.getCode(),
-                         info.getMessage(),
                          info.getResource(),
                          info.getLocation(),
+                         info.getMessage(),
                          info.getParameters());
         assertThat(problems.iterator().next(), is(info));
         assertThat(problems.isEmpty(), is(false));
@@ -318,9 +318,9 @@
         assertThat(problems.size(), is(0));
         problems.addWarning(warning.getThrowable(),
                             warning.getCode(),
-                            warning.getMessage(),
                             warning.getResource(),
                             warning.getLocation(),
+                            warning.getMessage(),
                             warning.getParameters());
         assertThat(problems.hasErrors(), is(false));
         assertThat(problems.hasWarnings(), is(true));
@@ -329,9 +329,9 @@
         assertThat(problems.size(), is(1));
         problems.addError(error.getThrowable(),
                           error.getCode(),
-                          error.getMessage(),
                           error.getResource(),
                           error.getLocation(),
+                          error.getMessage(),
                           error.getParameters());
         assertThat(problems.hasErrors(), is(true));
         assertThat(problems.hasWarnings(), is(true));
@@ -340,9 +340,9 @@
         assertThat(problems.size(), is(2));
         problems.addInfo(info.getThrowable(),
                          info.getCode(),
-                         info.getMessage(),
                          info.getResource(),
                          info.getLocation(),
+                         info.getMessage(),
                          info.getParameters());
         assertThat(problems.hasErrors(), is(true));
         assertThat(problems.hasWarnings(), is(true));
@@ -351,27 +351,27 @@
         assertThat(problems.size(), is(3));
         problems.addWarning(warning.getThrowable(),
                             warning.getCode(),
-                            warning.getMessage(),
                             warning.getResource(),
                             warning.getLocation(),
+                            warning.getMessage(),
                             warning.getParameters());
         problems.addError(error.getThrowable(),
                           error.getCode(),
-                          error.getMessage(),
                           error.getResource(),
                           error.getLocation(),
+                          error.getMessage(),
                           error.getParameters());
         problems.addWarning(warning.getThrowable(),
                             warning.getCode(),
-                            warning.getMessage(),
                             warning.getResource(),
                             warning.getLocation(),
+                            warning.getMessage(),
                             warning.getParameters());
         problems.addError(error.getThrowable(),
                           error.getCode(),
-                          error.getMessage(),
                           error.getResource(),
                           error.getLocation(),
+                          error.getMessage(),
                           error.getParameters());
         assertThat(problems.hasErrors(), is(true));
         assertThat(problems.hasWarnings(), is(true));

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.graph.query.model;
 
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -32,6 +33,7 @@
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.ValueFactory;
 
 /**
  * A set of common visitors that can be reused or extended, and methods that provide easy construction and calling of visitors.
@@ -51,7 +53,7 @@
      */
     public static <StrategyVisitor extends Visitor> StrategyVisitor visitAll( Visitable visitable,
                                                                               StrategyVisitor strategyVisitor ) {
-        visitable.accept(new WalkAllVisitor(strategyVisitor));
+        if (visitable != null) visitable.accept(new WalkAllVisitor(strategyVisitor));
         return strategyVisitor;
     }
 
@@ -66,7 +68,7 @@
      */
     public static <GeneralVisitor extends Visitor> GeneralVisitor visit( Visitable visitable,
                                                                          GeneralVisitor visitor ) {
-        visitable.accept(visitor);
+        if (visitable != null) visitable.accept(visitor);
         return visitor;
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AddAccessNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AddAccessNodes.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AddAccessNodes.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -32,8 +32,8 @@
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 
 /**
- * An {@link OptimizerRule optimizer rule} that inserts an ACCESS above each SOURCE node in a query plan. This rule is often the
- * first rule to run against a {@link CanonicalPlanner canonical plan} (see
+ * An {@link OptimizerRule optimizer rule} that inserts an ACCESS above each SOURCE leaf node in a query plan. This rule is often
+ * the first rule to run against a {@link CanonicalPlanner canonical plan} (see
  * {@link RuleBasedOptimizer#populateRuleStack(LinkedList, PlanHints)}.
  * <p>
  * Before:
@@ -72,7 +72,7 @@
                              LinkedList<OptimizerRule> ruleStack ) {
         // On each of the source nodes ...
         for (PlanNode source : plan.findAllAtOrBelow(Type.SOURCE)) {
-            // The source node should have no children ...
+            // The source node may have children if it is a view ...
             if (source.getChildCount() != 0) continue;
 
             // Create the ACCESS node, set the selectors, and insert above the source node ...

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -0,0 +1,192 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.PropertyExistence;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.Visitable;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.model.Visitors.AbstractVisitor;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanUtil;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that copies SELECT nodes that apply to one side of a equi-join condition so that they
+ * also apply to the other side fo the equi-join condition.
+ */
+ at Immutable
+public class CopyCriteria implements OptimizerRule {
+
+    public static final CopyCriteria INSTANCE = new CopyCriteria();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+     */
+    public PlanNode execute( QueryContext context,
+                             PlanNode plan,
+                             LinkedList<OptimizerRule> ruleStack ) {
+        Set<PlanNode> copiedSelectNodes = new HashSet<PlanNode>();
+
+        for (PlanNode join : plan.findAllAtOrBelow(Type.JOIN)) {
+            // Get the join condition ...
+            JoinCondition joinCondition = join.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+            if (joinCondition instanceof EquiJoinCondition) {
+                EquiJoinCondition equiJoinCondition = (EquiJoinCondition)joinCondition;
+                SelectorName selector1 = equiJoinCondition.getSelector1Name();
+                SelectorName selector2 = equiJoinCondition.getSelector2Name();
+                Name property1 = equiJoinCondition.getProperty1Name();
+                Name property2 = equiJoinCondition.getProperty2Name();
+
+                // Walk up the tree looking for SELECT nodes that apply to one of the sides ...
+                PlanNode node = join.getParent();
+                while (node != null) {
+                    if (!copiedSelectNodes.contains(node)) {
+                        PlanNode copy = copySelectNode(context, node, selector1, property1, selector2, property2);
+                        if (copy != null) {
+                            node.insertAsParent(copy);
+                            copiedSelectNodes.add(node);
+                            copiedSelectNodes.add(copy);
+                        } else {
+                            copy = copySelectNode(context, node, selector2, property2, selector1, property1);
+                            if (copy != null) {
+                                node.insertAsParent(copy);
+                                copiedSelectNodes.add(node);
+                                copiedSelectNodes.add(copy);
+                            }
+                        }
+                    }
+                    node = node.getParent();
+                }
+            }
+        }
+        return plan;
+    }
+
+    protected PlanNode copySelectNode( QueryContext context,
+                                       PlanNode selectNode,
+                                       SelectorName selectorName,
+                                       Name propertyName,
+                                       SelectorName copySelectorName,
+                                       Name copyPropertyName ) {
+        if (selectNode.isNot(Type.SELECT)) return null;
+        if (selectNode.getSelectors().size() != 1 || !selectNode.getSelectors().contains(selectorName)) return null;
+
+        Constraint constraint = selectNode.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+        Set<Column> columns = getColumnsReferencedBy(constraint, context.getExecutionContext());
+        if (columns.size() != 1) return null;
+        Column column = columns.iterator().next();
+        if (!column.getSelectorName().equals(selectorName)) return null;
+        if (!column.getPropertyName().equals(propertyName)) return null;
+
+        // We know that this constraint ONLY applies to the referenced selector and property,
+        // so we will duplicate this constraint ...
+
+        // Create the new node ...
+        PlanNode copy = new PlanNode(Type.SELECT, copySelectorName);
+
+        // Copy the constraint, but change the references to the copy selector and property ...
+        ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+        PlanUtil.ColumnMapping mappings = new PlanUtil.ColumnMapping(selectorName);
+        mappings.map(stringFactory.create(propertyName), new Column(copySelectorName, copyPropertyName,
+                                                                    stringFactory.create(copyPropertyName)));
+        Constraint newCriteria = PlanUtil.replaceReferences(context, constraint, mappings, copy);
+        copy.setProperty(Property.SELECT_CRITERIA, newCriteria);
+
+        return copy;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getClass().getSimpleName();
+    }
+
+    /**
+     * Get the set of Column objects that represent those columns referenced by the visitable object.
+     * 
+     * @param visitable the object to be visited
+     * @param context the context; may not be null
+     * @return the set of Column objects, with column names that always are the string-form of the
+     *         {@link Column#getPropertyName() property name}; never null
+     */
+    public static Set<Column> getColumnsReferencedBy( Visitable visitable,
+                                                      ExecutionContext context ) {
+        if (visitable == null) return Collections.emptySet();
+        final ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
+        final Set<Column> symbols = new HashSet<Column>();
+        // Walk the entire structure, so only supply a StrategyVisitor (that does no navigation) ...
+        Visitors.visitAll(visitable, new AbstractVisitor() {
+            protected void addColumnFor( SelectorName selectorName,
+                                         Name property ) {
+                symbols.add(new Column(selectorName, property, stringFactory.create(property)));
+            }
+
+            @Override
+            public void visit( Column column ) {
+                symbols.add(column);
+            }
+
+            @Override
+            public void visit( EquiJoinCondition joinCondition ) {
+                addColumnFor(joinCondition.getSelector1Name(), joinCondition.getProperty1Name());
+                addColumnFor(joinCondition.getSelector2Name(), joinCondition.getProperty2Name());
+            }
+
+            @Override
+            public void visit( PropertyExistence prop ) {
+                addColumnFor(prop.getSelectorName(), prop.getPropertyName());
+            }
+
+            @Override
+            public void visit( PropertyValue prop ) {
+                addColumnFor(prop.getSelectorName(), prop.getPropertyName());
+            }
+        });
+        return symbols;
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -0,0 +1,115 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanUtil;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * This rule attempts to ensure the proper location of {@link Type#PROJECT} nodes. For example, every {@link Type#ACCESS} node
+ * needs to have a PROJECT node below it and above any other nodes (e.g., {@link Type#SELECT} or {@link Type#SOURCE} nodes). This
+ * rule ensures that the PROJECT exists, but it also attempts to reduce any unnecessary columns in existing PROJECT nodes.
+ */
+public class PushProjects implements OptimizerRule {
+
+    public static final PushProjects INSTANCE = new PushProjects();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+     */
+    public PlanNode execute( QueryContext context,
+                             PlanNode plan,
+                             LinkedList<OptimizerRule> ruleStack ) {
+
+        // Find all of the ACCESS nodes to make sure there is a PROJECT ...
+        for (PlanNode access : plan.findAllAtOrBelow(Type.ACCESS)) {
+            // Find the first node that is below any LIMIT, SORT, or DUP_REMOVE ...
+            PlanNode parentOfProject = access;
+            while (parentOfProject.isOneOf(Type.LIMIT, Type.SORT, Type.DUP_REMOVE)) {
+                parentOfProject = parentOfProject.getParent();
+            }
+
+            // Is there already a PROJECT here ?
+            assert parentOfProject.getChildCount() == 1; // should only have one child ...
+            PlanNode child = parentOfProject.getFirstChild(); // should only have one child ...
+            if (child.is(Type.PROJECT)) continue;
+
+            // If the parent of the ACCESS node is a PROJECT, then we can simply move it to here ...
+            PlanNode accessParent = access.getParent();
+            if (accessParent != null && accessParent.is(Type.PROJECT)) {
+                PlanNode project = accessParent;
+                if (project == plan) {
+                    // The PROJECT node is the root, so the ACCESS node will be the root ...
+                    plan = access;
+                    access.removeFromParent();
+                } else {
+                    project.extractFromParent();
+                }
+                child.insertAsParent(project);
+                if (plan == access) return plan;
+                continue;
+            }
+
+            // There is no PROJECT, so find the columns that are required by the plan above this point ...
+            List<Column> requiredColumns = PlanUtil.findRequiredColumns(context, child);
+
+            // And insert the PROJECT ...
+            PlanNode projectNode = new PlanNode(Type.PROJECT);
+            projectNode.setProperty(Property.PROJECT_COLUMNS, requiredColumns);
+            projectNode.addSelectors(getSelectorsFor(requiredColumns));
+            child.insertAsParent(projectNode);
+        }
+        return plan;
+    }
+
+    protected Set<SelectorName> getSelectorsFor( List<Column> columns ) {
+        Set<SelectorName> selectors = new HashSet<SelectorName>();
+        for (Column column : columns) {
+            selectors.add(column.getSelectorName());
+        }
+        return selectors;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getClass().getSimpleName();
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -64,10 +64,10 @@
  * <pre>
  *          ...
  *           |
+ *        ACCESS
+ *           |
  *        PROJECT      with the list of columns being SELECTed
  *           |
- *        ACCESS
- *           |
  *        SELECT1
  *           |         One or more SELECT plan nodes that each have
  *        SELECT2      a single non-join constraint that are then all AND-ed
@@ -104,6 +104,9 @@
  *   ACCESS     ACCESS
  *    ('s1')    ('s2')
  *     |           |
+ *   PROJECT    PROJECT
+ *    ('s1')    ('s2')
+ *     |           |
  *   SOURCE     SOURCE
  *    ('s1')    ('s2')
  * </pre>
@@ -123,6 +126,9 @@
  *   ACCESS     ACCESS
  *   ('s1')     ('s2')
  *     |           |
+ *  PROJECT     PROJECT
+ *   ('s1')     ('s2')
+ *     |           |
  *  SELECT1     SELECT2
  *   ('s1')     ('s2')
  *     |           |
@@ -134,6 +140,9 @@
  * </pre>
  * 
  * </p>
+ * <p>
+ * Also, any SELECT that applies to one side of an equi-join will be applied to <i>both</i> sides of the JOIN.
+ * </p>
  */
 @Immutable
 public class PushSelectCriteria implements OptimizerRule {

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -0,0 +1,132 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanUtil;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.query.validate.Schemata.Table;
+import org.jboss.dna.graph.query.validate.Schemata.View;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that replaces any SOURCE nodes that happen to be {@link View views}. This rewriting
+ * changes all of the elements of the plan that reference the SOURCE and it's columns, including criteria, project nodes, etc.
+ */
+ at Immutable
+public class ReplaceViews implements OptimizerRule {
+
+    public static final ReplaceViews INSTANCE = new ReplaceViews();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+     */
+    public PlanNode execute( QueryContext context,
+                             PlanNode plan,
+                             LinkedList<OptimizerRule> ruleStack ) {
+        // Prepare a cache of the view plans ...
+        Map<SelectorName, PlanNode> viewPlanCache = new HashMap<SelectorName, PlanNode>();
+        CanonicalPlanner planner = new CanonicalPlanner();
+
+        // Prepare the maps that will record the old-to-new mappings from the old view SOURCE nodes to the table SOURCEs ...
+
+        // For each of the SOURCE nodes ...
+        Schemata schemata = context.getSchemata();
+        Set<PlanNode> processedSources = new HashSet<PlanNode>();
+        boolean foundViews = false;
+        do {
+            foundViews = false;
+            for (PlanNode sourceNode : plan.findAllAtOrBelow(Type.SOURCE)) {
+                if (processedSources.contains(sourceNode)) continue;
+                processedSources.add(sourceNode);
+
+                // Resolve the node to find the definition in the schemata ...
+                SelectorName tableName = sourceNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
+                Table table = schemata.getTable(tableName);
+                if (table instanceof View) {
+                    View view = (View)table;
+                    PlanNode viewPlan = viewPlanCache.get(tableName);
+                    if (viewPlan == null) {
+                        viewPlan = planner.createPlan(context, view.getDefinition());
+                        if (viewPlan != null) viewPlanCache.put(tableName, viewPlan);
+                    }
+                    if (viewPlan == null) continue; // there were likely errors when creating the plan
+                    viewPlan = viewPlan.clone();
+
+                    // Insert the view plan under the parent SOURCE node ...
+                    sourceNode.addLastChild(viewPlan);
+
+                    // Update the plan above this node to replace references to the view columns with references to the
+                    // tables/views used by the view ...
+                    PlanUtil.ColumnMapping viewMappings = PlanUtil.createMappingFor(view, viewPlan);
+                    PlanUtil.replaceViewReferences(context, viewPlan, viewMappings);
+
+                    if (viewPlan.is(Type.PROJECT)) {
+                        // The PROJECT from the plan may actually not be needed if there is another PROJECT above it ...
+                        PlanNode node = viewPlan.getParent();
+                        while (node != null) {
+                            if (node.is(Type.PROJECT) && viewPlan.getSelectors().containsAll(node.getSelectors())) {
+                                viewPlan.extractFromParent();
+                                break;
+                            }
+                            node = node.getParent();
+                        }
+                    }
+                    foundViews = true;
+                }
+            }
+        } while (foundViews);
+
+        if (foundViews) {
+            // We re-wrote at least one SOURCE, but the resulting plan tree for the view could actually reference
+            // other views. Therefore, re-run this rule ...
+            ruleStack.addFirst(this);
+        }
+        return plan;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getClass().getSimpleName();
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -24,46 +24,17 @@
 package org.jboss.dna.graph.query.optimize;
 
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.model.And;
-import org.jboss.dna.graph.query.model.ChildNode;
-import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
-import org.jboss.dna.graph.query.model.Column;
-import org.jboss.dna.graph.query.model.Comparison;
-import org.jboss.dna.graph.query.model.Constraint;
-import org.jboss.dna.graph.query.model.DescendantNode;
-import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
-import org.jboss.dna.graph.query.model.DynamicOperand;
 import org.jboss.dna.graph.query.model.EquiJoinCondition;
-import org.jboss.dna.graph.query.model.FullTextSearch;
-import org.jboss.dna.graph.query.model.FullTextSearchScore;
 import org.jboss.dna.graph.query.model.JoinCondition;
-import org.jboss.dna.graph.query.model.Length;
-import org.jboss.dna.graph.query.model.LowerCase;
-import org.jboss.dna.graph.query.model.NodeDepth;
-import org.jboss.dna.graph.query.model.NodeLocalName;
-import org.jboss.dna.graph.query.model.NodeName;
-import org.jboss.dna.graph.query.model.NodePath;
-import org.jboss.dna.graph.query.model.Not;
-import org.jboss.dna.graph.query.model.Or;
-import org.jboss.dna.graph.query.model.Ordering;
-import org.jboss.dna.graph.query.model.PropertyExistence;
-import org.jboss.dna.graph.query.model.PropertyValue;
-import org.jboss.dna.graph.query.model.SameNode;
-import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
 import org.jboss.dna.graph.query.model.SelectorName;
-import org.jboss.dna.graph.query.model.StaticOperand;
-import org.jboss.dna.graph.query.model.UpperCase;
 import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanUtil;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 import org.jboss.dna.graph.query.validate.Schemata;
@@ -170,7 +141,7 @@
             ruleStack.addFirst(this);
 
             // Now rewrite the various portions of the plan that make use of the now-removed selectors ...
-            replaceReferencesToRemovedSource(context, plan, rewrittenSelectors);
+            PlanUtil.replaceReferencesToRemovedSource(context, plan, rewrittenSelectors);
         } else {
             // There are no-untouched JOIN nodes, which means the sole JOIN node was rewritten as a single SOURCE node
             assert plan.findAllAtOrBelow(Type.JOIN).isEmpty();
@@ -206,255 +177,6 @@
         }
     }
 
-    protected void replaceReferencesToRemovedSource( QueryContext context,
-                                                     PlanNode planNode,
-                                                     Map<SelectorName, SelectorName> rewrittenSelectors ) {
-        switch (planNode.getType()) {
-            case PROJECT:
-                List<Column> columns = planNode.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
-                for (int i = 0; i != columns.size(); ++i) {
-                    Column column = columns.get(i);
-                    SelectorName replacement = rewrittenSelectors.get(column.getSelectorName());
-                    if (replacement != null) {
-                        columns.set(i, new Column(replacement, column.getPropertyName(), column.getColumnName()));
-                    }
-                }
-                break;
-            case SELECT:
-                Constraint constraint = planNode.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-                Constraint newConstraint = replaceReferencesToRemovedSource(context, constraint, rewrittenSelectors);
-                if (constraint != newConstraint) {
-                    planNode.setProperty(Property.SELECT_CRITERIA, newConstraint);
-                }
-                break;
-            case SORT:
-                List<Object> orderBys = planNode.getPropertyAsList(Property.SORT_ORDER_BY, Object.class);
-                if (orderBys != null && !orderBys.isEmpty()) {
-                    if (orderBys.get(0) instanceof SelectorName) {
-                        for (int i = 0; i != orderBys.size(); ++i) {
-                            SelectorName selectorName = (SelectorName)orderBys.get(i);
-                            SelectorName replacement = rewrittenSelectors.get(selectorName);
-                            if (replacement != null) {
-                                orderBys.set(i, replacement);
-                            }
-                        }
-                    } else {
-                        for (int i = 0; i != orderBys.size(); ++i) {
-                            Ordering ordering = (Ordering)orderBys.get(i);
-                            DynamicOperand operand = ordering.getOperand();
-                            orderBys.set(i, replaceReferencesToRemovedSource(context, operand, rewrittenSelectors));
-                        }
-                    }
-                }
-                break;
-            case JOIN:
-                // Update the join condition ...
-                JoinCondition joinCondition = planNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
-                JoinCondition newCondition = replaceReferencesToRemovedSource(context, joinCondition, rewrittenSelectors);
-                if (joinCondition != newCondition) {
-                    planNode.setProperty(Property.JOIN_CONDITION, newCondition);
-                }
-
-                // Update the join criteria (if there are some) ...
-                List<Constraint> constraints = planNode.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
-                if (constraints != null && !constraints.isEmpty()) {
-                    for (int i = 0; i != constraints.size(); ++i) {
-                        Constraint old = constraints.get(i);
-                        Constraint replacement = replaceReferencesToRemovedSource(context, old, rewrittenSelectors);
-                        if (replacement != old) {
-                            constraints.set(i, replacement);
-                        }
-                    }
-                }
-                break;
-            case GROUP:
-            case SET_OPERATION:
-            case DUP_REMOVE:
-            case LIMIT:
-            case NULL:
-            case SOURCE:
-            case ACCESS:
-                // None of these have to be changed ...
-                break;
-        }
-
-        // Update the selectors referenced by the node ...
-        Set<SelectorName> selectorsToAdd = null;
-        for (Iterator<SelectorName> iter = planNode.getSelectors().iterator(); iter.hasNext();) {
-            SelectorName replacement = rewrittenSelectors.get(iter.next());
-            if (replacement != null) {
-                iter.remove();
-                if (selectorsToAdd == null) selectorsToAdd = new HashSet<SelectorName>();
-                selectorsToAdd.add(replacement);
-            }
-        }
-        if (selectorsToAdd != null) planNode.getSelectors().addAll(selectorsToAdd);
-
-        // Now call recursively on the children ...
-        for (PlanNode child : planNode) {
-            replaceReferencesToRemovedSource(context, child, rewrittenSelectors);
-        }
-    }
-
-    protected DynamicOperand replaceReferencesToRemovedSource( QueryContext context,
-                                                               DynamicOperand operand,
-                                                               Map<SelectorName, SelectorName> rewrittenSelectors ) {
-        if (operand instanceof FullTextSearchScore) {
-            FullTextSearchScore score = (FullTextSearchScore)operand;
-            SelectorName replacement = rewrittenSelectors.get(score.getSelectorName());
-            if (replacement == null) return score;
-            return new FullTextSearchScore(replacement);
-        }
-        if (operand instanceof Length) {
-            Length operation = (Length)operand;
-            PropertyValue wrapped = operation.getPropertyValue();
-            SelectorName replacement = rewrittenSelectors.get(wrapped.getSelectorName());
-            if (replacement == null) return operand;
-            return new Length(new PropertyValue(replacement, wrapped.getPropertyName()));
-        }
-        if (operand instanceof LowerCase) {
-            LowerCase operation = (LowerCase)operand;
-            SelectorName replacement = rewrittenSelectors.get(operation.getSelectorName());
-            if (replacement == null) return operand;
-            return new LowerCase(replaceReferencesToRemovedSource(context, operand, rewrittenSelectors));
-        }
-        if (operand instanceof UpperCase) {
-            UpperCase operation = (UpperCase)operand;
-            SelectorName replacement = rewrittenSelectors.get(operation.getSelectorName());
-            if (replacement == null) return operand;
-            return new UpperCase(replaceReferencesToRemovedSource(context, operand, rewrittenSelectors));
-        }
-        if (operand instanceof NodeName) {
-            NodeName name = (NodeName)operand;
-            SelectorName replacement = rewrittenSelectors.get(name.getSelectorName());
-            if (replacement == null) return name;
-            return new NodeName(replacement);
-        }
-        if (operand instanceof NodeLocalName) {
-            NodeLocalName name = (NodeLocalName)operand;
-            SelectorName replacement = rewrittenSelectors.get(name.getSelectorName());
-            if (replacement == null) return name;
-            return new NodeLocalName(replacement);
-        }
-        if (operand instanceof PropertyValue) {
-            PropertyValue value = (PropertyValue)operand;
-            SelectorName replacement = rewrittenSelectors.get(value.getSelectorName());
-            if (replacement == null) return operand;
-            return new PropertyValue(replacement, value.getPropertyName());
-        }
-        if (operand instanceof NodeDepth) {
-            NodeDepth depth = (NodeDepth)operand;
-            SelectorName replacement = rewrittenSelectors.get(depth.getSelectorName());
-            if (replacement == null) return operand;
-            return new NodeDepth(replacement);
-        }
-        if (operand instanceof NodePath) {
-            NodePath path = (NodePath)operand;
-            SelectorName replacement = rewrittenSelectors.get(path.getSelectorName());
-            if (replacement == null) return operand;
-            return new NodePath(replacement);
-        }
-        return operand;
-    }
-
-    protected Constraint replaceReferencesToRemovedSource( QueryContext context,
-                                                           Constraint constraint,
-                                                           Map<SelectorName, SelectorName> rewrittenSelectors ) {
-        if (constraint instanceof And) {
-            And and = (And)constraint;
-            Constraint left = replaceReferencesToRemovedSource(context, and.getLeft(), rewrittenSelectors);
-            Constraint right = replaceReferencesToRemovedSource(context, and.getRight(), rewrittenSelectors);
-            if (left == and.getLeft() && right == and.getRight()) return and;
-            return new And(left, right);
-        }
-        if (constraint instanceof Or) {
-            Or or = (Or)constraint;
-            Constraint left = replaceReferencesToRemovedSource(context, or.getLeft(), rewrittenSelectors);
-            Constraint right = replaceReferencesToRemovedSource(context, or.getRight(), rewrittenSelectors);
-            if (left == or.getLeft() && right == or.getRight()) return or;
-            return new Or(left, right);
-        }
-        if (constraint instanceof Not) {
-            Not not = (Not)constraint;
-            Constraint wrapped = replaceReferencesToRemovedSource(context, not.getConstraint(), rewrittenSelectors);
-            if (wrapped == not.getConstraint()) return not;
-            return new Not(wrapped);
-        }
-        if (constraint instanceof SameNode) {
-            SameNode sameNode = (SameNode)constraint;
-            SelectorName replacement = rewrittenSelectors.get(sameNode.getSelectorName());
-            if (replacement == null) return sameNode;
-            return new SameNode(replacement, sameNode.getPath());
-        }
-        if (constraint instanceof ChildNode) {
-            ChildNode childNode = (ChildNode)constraint;
-            SelectorName replacement = rewrittenSelectors.get(childNode.getSelectorName());
-            if (replacement == null) return childNode;
-            return new ChildNode(replacement, childNode.getParentPath());
-        }
-        if (constraint instanceof DescendantNode) {
-            DescendantNode descendantNode = (DescendantNode)constraint;
-            SelectorName replacement = rewrittenSelectors.get(descendantNode.getSelectorName());
-            if (replacement == null) return descendantNode;
-            return new DescendantNode(replacement, descendantNode.getAncestorPath());
-        }
-        if (constraint instanceof PropertyExistence) {
-            PropertyExistence existence = (PropertyExistence)constraint;
-            SelectorName replacement = rewrittenSelectors.get(existence.getSelectorName());
-            if (replacement == null) return existence;
-            return new PropertyExistence(replacement, existence.getPropertyName());
-        }
-        if (constraint instanceof FullTextSearch) {
-            FullTextSearch search = (FullTextSearch)constraint;
-            SelectorName replacement = rewrittenSelectors.get(search.getSelectorName());
-            if (replacement == null) return search;
-            return new PropertyExistence(replacement, search.getPropertyName());
-        }
-        if (constraint instanceof Comparison) {
-            Comparison comparison = (Comparison)constraint;
-            DynamicOperand lhs = comparison.getOperand1();
-            StaticOperand rhs = comparison.getOperand2(); // Current only a literal; therefore, no reference to selector
-            DynamicOperand newLhs = replaceReferencesToRemovedSource(context, lhs, rewrittenSelectors);
-            if (lhs == newLhs) return comparison;
-            return new Comparison(newLhs, comparison.getOperator(), rhs);
-        }
-        return constraint;
-    }
-
-    protected JoinCondition replaceReferencesToRemovedSource( QueryContext context,
-                                                              JoinCondition joinCondition,
-                                                              Map<SelectorName, SelectorName> rewrittenSelectors ) {
-        if (joinCondition instanceof EquiJoinCondition) {
-            EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
-            SelectorName replacement1 = rewrittenSelectors.get(condition.getSelector1Name());
-            SelectorName replacement2 = rewrittenSelectors.get(condition.getSelector2Name());
-            if (replacement1 == condition.getSelector1Name() && replacement2 == condition.getSelector2Name()) return condition;
-            return new EquiJoinCondition(replacement1, condition.getProperty1Name(), replacement2, condition.getProperty2Name());
-        }
-        if (joinCondition instanceof SameNodeJoinCondition) {
-            SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
-            SelectorName replacement1 = rewrittenSelectors.get(condition.getSelector1Name());
-            SelectorName replacement2 = rewrittenSelectors.get(condition.getSelector2Name());
-            if (replacement1 == condition.getSelector1Name() && replacement2 == condition.getSelector2Name()) return condition;
-            return new SameNodeJoinCondition(replacement1, replacement2, condition.getSelector2Path());
-        }
-        if (joinCondition instanceof ChildNodeJoinCondition) {
-            ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
-            SelectorName childSelector = rewrittenSelectors.get(condition.getChildSelectorName());
-            SelectorName parentSelector = rewrittenSelectors.get(condition.getParentSelectorName());
-            if (childSelector == condition.getChildSelectorName() && parentSelector == condition.getParentSelectorName()) return condition;
-            return new ChildNodeJoinCondition(parentSelector, childSelector);
-        }
-        if (joinCondition instanceof DescendantNodeJoinCondition) {
-            DescendantNodeJoinCondition condition = (DescendantNodeJoinCondition)joinCondition;
-            SelectorName ancestor = rewrittenSelectors.get(condition.getAncestorSelectorName());
-            SelectorName descendant = rewrittenSelectors.get(condition.getDescendantSelectorName());
-            if (ancestor == condition.getAncestorSelectorName() && descendant == condition.getDescendantSelectorName()) return condition;
-            return new ChildNodeJoinCondition(ancestor, descendant);
-        }
-        return joinCondition;
-    }
-
     /**
      * {@inheritDoc}
      * 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -72,10 +72,15 @@
         if (hints.hasJoin) {
             ruleStack.addFirst(ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM);
         }
+        ruleStack.addFirst(PushProjects.INSTANCE);
         if (hints.hasCriteria) {
             ruleStack.addFirst(PushSelectCriteria.INSTANCE);
         }
         ruleStack.addFirst(AddAccessNodes.INSTANCE);
         ruleStack.addFirst(RightOuterToLeftOuterJoins.INSTANCE);
+        ruleStack.addFirst(CopyCriteria.INSTANCE);
+        if (hints.hasView) {
+            ruleStack.addFirst(ReplaceViews.INSTANCE);
+        }
     }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/InvalidQueryException.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -26,7 +26,7 @@
 import org.jboss.dna.common.util.CheckArg;
 
 /**
- * An exception signalling that an XPath query is invalid (but typically well-formed)
+ * An exception signalling that a query is invalid (but typically well-formed)
  */
 public class InvalidQueryException extends RuntimeException {
 
@@ -34,38 +34,38 @@
      */
     private static final long serialVersionUID = 1L;
 
-    private final String xpath;
+    private final String query;
 
     /**
      * Create an exception with the invalid query.
      * 
-     * @param xpath the XPath query that is invalid
+     * @param query the query that is invalid
      */
-    public InvalidQueryException( String xpath ) {
+    public InvalidQueryException( String query ) {
         super();
-        this.xpath = xpath;
+        this.query = query;
     }
 
     /**
      * Create an exception with the invalid query and a message.
      * 
-     * @param xpath the XPath query that is invalid
+     * @param query the query that is invalid
      * @param message
      */
-    public InvalidQueryException( String xpath,
+    public InvalidQueryException( String query,
                                   String message ) {
         super(message);
-        CheckArg.isNotNull(xpath, "xpath");
-        this.xpath = xpath;
+        CheckArg.isNotNull(query, "query");
+        this.query = query;
     }
 
     /**
      * Get the XPath query that is invalid.
      * 
-     * @return the query
+     * @return the query; never null
      */
-    public String getXPath() {
-        return xpath;
+    public String getQuery() {
+        return query;
     }
 
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -31,6 +31,7 @@
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.AllNodes;
 import org.jboss.dna.graph.query.model.And;
@@ -53,6 +54,7 @@
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.jboss.dna.graph.query.validate.Schemata.Table;
+import org.jboss.dna.graph.query.validate.Schemata.View;
 
 /**
  * The planner that produces a canonical query plan given a {@link QueryCommand query command}.
@@ -85,7 +87,7 @@
  * from the top node of the plan.
  * </p>
  * <p>
- * This canonical plan, however, is optimized and rearranged so that it performs faster.
+ * This canonical plan, however, is later optimized and rearranged so that it performs faster.
  * </p>
  */
 public class CanonicalPlanner implements Planner {
@@ -190,14 +192,15 @@
             }
             // Validate the source name and set the available columns ...
             Table table = context.getSchemata().getTable(selector.getName());
-            if (table == null) {
-                context.getProblems().addError(GraphI18n.tableDoesNotExist,
-                                               selector.getName().getString(context.getExecutionContext()));
-            } else {
+            if (table != null) {
+                if (table instanceof View) context.getHints().hasView = true;
                 if (usedSelectors.put(selector.getAliasOrName(), table) != null) {
                     // There was already a table with this alias or name ...
                 }
                 node.setProperty(Property.SOURCE_COLUMNS, table.getColumns());
+            } else {
+                context.getProblems().addError(GraphI18n.tableDoesNotExist,
+                                               selector.getName().getString(context.getExecutionContext()));
             }
             return node;
         }
@@ -217,7 +220,8 @@
             // Handle each child
             Source[] clauses = new Source[] {join.getLeft(), join.getRight()};
             for (int i = 0; i < 2; i++) {
-                node.addLastChild(createPlanNode(context, clauses[i], usedSelectors));
+                PlanNode sourceNode = createPlanNode(context, clauses[i], usedSelectors);
+                node.addLastChild(sourceNode);
 
                 // Add selectors to the joinNode
                 for (PlanNode child : node.getChildren()) {
@@ -269,6 +273,7 @@
                 }
             });
 
+            criteriaNode.addFirstChild(plan);
             plan = criteriaNode;
         }
         return plan;
@@ -379,6 +384,7 @@
             }
         } else {
             // Add the selector used by each column ...
+            ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
             for (Column column : columns) {
                 SelectorName tableName = column.getSelectorName();
                 // Add the selector that is being used ...
@@ -391,9 +397,11 @@
                                                    tableName.getString(context.getExecutionContext()));
                 } else {
                     // Make sure that the column is in the table ...
-                    if (table.getColumn(column.getColumnName()) == null) {
+                    Name columnName = column.getPropertyName();
+                    String name = stringFactory.create(columnName);
+                    if (table.getColumn(name) == null) {
                         context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable,
-                                                       column.getColumnName(),
+                                                       name,
                                                        tableName.getString(context.getExecutionContext()));
                     }
                 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -28,19 +28,19 @@
 @NotThreadSafe
 public final class PlanHints {
 
-    // This flag indicates that the plan has a criteria somewhere
+    /** This flag indicates that the plan has a criteria somewhere */
     public boolean hasCriteria = false;
 
-    // This flag indicates that the plan has a join somewhere
+    /** This flag indicates that the plan has a join somewhere */
     public boolean hasJoin = false;
 
-    // This flag indicates that the plan has a sort somewhere
+    /** This flag indicates that the plan has a sort somewhere */
     public boolean hasSort = false;
 
     // List of groups to make dependent
     // public List makeDepGroups = null;
 
-    // flag indicates that the plan has a union somewhere
+    /** flag indicates that the plan has a union somewhere */
     public boolean hasSetQuery = false;
 
     // flag indicating that the plan has a grouping node somewhere
@@ -55,6 +55,9 @@
 
     public boolean hasFullTextSearch = false;
 
+    /** Flag indicates that the plan has at least one view somewhere */
+    public boolean hasView = false;
+
     public PlanHints() {
     }
 
@@ -62,6 +65,7 @@
     public String toString() {
         StringBuilder sb = new StringBuilder("PlanHints {");
         sb.append(" hasCriteria=").append(hasCriteria);
+        sb.append(", hasView=").append(hasView);
         sb.append(", hasJoin=").append(hasJoin);
         sb.append(", hasSort=").append(hasSort);
         sb.append(", hasSetQuery=").append(hasSetQuery);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -36,6 +36,7 @@
 import java.util.Set;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.ObjectUtil;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Command;
@@ -54,7 +55,7 @@
  * A representation of a single node within a plan tree.
  */
 @NotThreadSafe
-public final class PlanNode implements Iterable<PlanNode>, Readable {
+public final class PlanNode implements Iterable<PlanNode>, Readable, Cloneable {
 
     /**
      * An enumeration dictating the type of plan tree nodes.
@@ -298,6 +299,70 @@
     }
 
     /**
+     * Return true if this node's type does not match the supplied type
+     * 
+     * @param type the type to compare
+     * @return true if this node's type is different than that supplied, or false if they are the same
+     */
+    public boolean isNot( Type type ) {
+        return this.type != type;
+    }
+
+    /**
+     * Return true if this node's type does not match any of the supplied types
+     * 
+     * @param first the type to compare
+     * @param rest the additional types to compare
+     * @return true if this node's type is different than all of those supplied, or false if matches one of the supplied types
+     */
+    public boolean isNotOneOf( Type first,
+                               Type... rest ) {
+        return isNotOneOf(EnumSet.of(first, rest));
+    }
+
+    /**
+     * Return true if this node's type does not match any of the supplied types
+     * 
+     * @param types the types to compare
+     * @return true if this node's type is different than all of those supplied, or false if matches one of the supplied types
+     */
+    public boolean isNotOneOf( Set<Type> types ) {
+        return !types.contains(type);
+    }
+
+    /**
+     * Return true if this node's type does match the supplied type
+     * 
+     * @param type the type to compare
+     * @return true if this node's type is the same as that supplied, or false if the types are different
+     */
+    public boolean is( Type type ) {
+        return this.type == type;
+    }
+
+    /**
+     * Return true if this node's type matches one of the supplied types
+     * 
+     * @param first the type to compare
+     * @param rest the additional types to compare
+     * @return true if this node's type is one of those supplied, or false otherwise
+     */
+    public boolean isOneOf( Type first,
+                            Type... rest ) {
+        return isOneOf(EnumSet.of(first, rest));
+    }
+
+    /**
+     * Return true if this node's type matches one of the supplied types
+     * 
+     * @param types the types to compare
+     * @return true if this node's type is one of those supplied, or false otherwise
+     */
+    public boolean isOneOf( Set<Type> types ) {
+        return types.contains(type);
+    }
+
+    /**
      * Get the parent of this node.
      * 
      * @return the parent node, or null if this node has no parent
@@ -357,6 +422,7 @@
      */
     public void insertAsParent( PlanNode newParent ) {
         if (newParent == null) return;
+        newParent.removeFromParent();
         if (this.parent != null) {
             this.parent.replaceChild(this, newParent);
         }
@@ -832,6 +898,34 @@
     }
 
     /**
+     * {@inheritDoc}
+     * <p>
+     * This class returns a new clone of the plan tree rooted at this node. However, the top node of the resulting plan tree (that
+     * is, the node returned from this method) has no parent.
+     * </p>
+     * 
+     * @see java.lang.Object#clone()
+     */
+    @Override
+    public PlanNode clone() {
+        return cloneWithoutNewParent();
+    }
+
+    protected PlanNode cloneWithoutNewParent() {
+        PlanNode result = new PlanNode(this.type, null, this.selectors);
+        if (this.nodeProperties != null && !this.nodeProperties.isEmpty()) {
+            result.nodeProperties = new HashMap<Property, Object>(this.nodeProperties);
+        }
+        // Clone the children ...
+        for (PlanNode child : children) {
+            PlanNode childClone = child.cloneWithoutNewParent();
+            // The child has no parent, so add the child to the new result ...
+            result.addLastChild(childClone);
+        }
+        return result;
+    }
+
+    /**
      * Determine whether the supplied plan is equivalent to this plan.
      * 
      * @param other the other plan to compare with this instance
@@ -840,9 +934,15 @@
     public boolean isSameAs( PlanNode other ) {
         if (other == null) return false;
         if (this.getType() != other.getType()) return false;
-        if (!this.nodeProperties.equals(other.nodeProperties)) return false;
+        if (!ObjectUtil.isEqualWithNulls(this.nodeProperties, other.nodeProperties)) return false;
         if (!this.getSelectors().equals(other.getSelectors())) return false;
-        return this.getChildren().equals(other.getChildren());
+        if (this.getChildCount() != other.getChildCount()) return false;
+        Iterator<PlanNode> thisChildren = this.getChildren().iterator();
+        Iterator<PlanNode> thatChildren = other.getChildren().iterator();
+        while (thisChildren.hasNext() && thatChildren.hasNext()) {
+            if (!thisChildren.next().isSameAs(thatChildren.next())) return false;
+        }
+        return true;
     }
 
     /**

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -0,0 +1,842 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.plan;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.ChildNode;
+import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.DescendantNode;
+import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.FullTextSearch;
+import org.jboss.dna.graph.query.model.FullTextSearchScore;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.LowerCase;
+import org.jboss.dna.graph.query.model.NodeDepth;
+import org.jboss.dna.graph.query.model.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.NodePath;
+import org.jboss.dna.graph.query.model.Not;
+import org.jboss.dna.graph.query.model.Or;
+import org.jboss.dna.graph.query.model.Ordering;
+import org.jboss.dna.graph.query.model.PropertyExistence;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.SameNode;
+import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.StaticOperand;
+import org.jboss.dna.graph.query.model.UpperCase;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.model.Visitors.AbstractVisitor;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.Schemata.View;
+
+/**
+ * Utilities for working with {@link PlanNode}s.
+ */
+public class PlanUtil {
+
+    /**
+     * Collected the minimum set of columns from the supplied table that are required by or used within the plan at the supplied
+     * node or above. This method first looks to see if the supplied node is a {@link Type#PROJECT} node, and if so immediately
+     * returns that node's list of {@link Property#PROJECT_COLUMNS projected columns}. Otherwise, this method finds all of the
+     * {@link Type#SOURCE} nodes below the supplied plan node, and accumulates the set of sources for which the columns are to be
+     * found. The method then walks up the plan, looking for references to columns of the supplied table, starting with the
+     * supplied node and walking up until a {@link PlanNode.Type#PROJECT PROJECT} node is found or the top of the plan is reached.
+     * <p>
+     * The resulting column list will always contain at the front the {@link Property#PROJECT_COLUMNS columns} from the nearest
+     * PROJECT ancestor, followed by any columns required by criteria. This is done so that the processing of the nearest PROJECT
+     * ancestor node can simply use the sublist of each tuple.
+     * </p>
+     * 
+     * @param context the query context; may not be null
+     * @param planNode the plan node at which the required columns need to be found; may not be null
+     * @return the list of {@link Column} objects that are used in the plan; never null but possibly empty if no columns are
+     *         actually needed
+     */
+    public static List<Column> findRequiredColumns( QueryContext context,
+                                                    PlanNode planNode ) {
+        List<Column> columns = null;
+        PlanNode node = planNode;
+        PlanNode projectNode = null;
+        // First find the columns from the nearest PROJECT ancestor ...
+        do {
+            switch (node.getType()) {
+                case PROJECT:
+                    columns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+                    projectNode = node;
+                    node = null;
+                    break;
+                default:
+                    node = node.getParent();
+                    break;
+            }
+        } while (node != null);
+
+        // If the supplied node is a PROJECT, just return the PROJECT node's column list ...
+        if (projectNode == planNode) return columns;
+
+        // Find the names of the selectors ...
+        Set<SelectorName> names = new HashSet<SelectorName>();
+        for (PlanNode source : planNode.findAllAtOrBelow(Type.SOURCE)) {
+            names.add(source.getProperty(Property.SOURCE_NAME, SelectorName.class));
+            names.add(source.getProperty(Property.SOURCE_ALIAS, SelectorName.class));
+        }
+
+        // Add the PROJECT columns first ...
+        RequiredColumnVisitor collectionVisitor = new RequiredColumnVisitor(context.getExecutionContext(), names);
+        if (columns != null) {
+            for (Column projectedColumn : columns) {
+                collectionVisitor.visit(projectedColumn);
+            }
+        }
+
+        // Now add the columns from the JOIN and SELECT ancestors ...
+        node = planNode;
+        do {
+            switch (node.getType()) {
+                case JOIN:
+                    Constraint criteria = node.getProperty(Property.JOIN_CONSTRAINTS, Constraint.class);
+                    JoinCondition joinCondition = node.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+                    Visitors.visitAll(criteria, collectionVisitor);
+                    Visitors.visitAll(joinCondition, collectionVisitor);
+                    break;
+                case SELECT:
+                    criteria = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                    Visitors.visitAll(criteria, collectionVisitor);
+                    break;
+                case PROJECT:
+                    // Already handled above, but we can stop looking for columns ...
+                    return collectionVisitor.getRequiredColumns();
+                default:
+                    break;
+            }
+            node = node.getParent();
+        } while (node != null);
+        return collectionVisitor.getRequiredColumns();
+    }
+
+    protected static class RequiredColumnVisitor extends AbstractVisitor {
+        private final ExecutionContext context;
+        private final Set<SelectorName> names;
+        private final List<Column> columns = new LinkedList<Column>();
+        private final Set<Name> requiredColumnNames = new HashSet<Name>();
+
+        protected RequiredColumnVisitor( ExecutionContext context,
+                                         Set<SelectorName> names ) {
+            this.names = names;
+            this.context = context;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.PropertyExistence)
+         */
+        @Override
+        public void visit( PropertyExistence existence ) {
+            requireColumn(existence.getSelectorName(), existence.getPropertyName());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.PropertyValue)
+         */
+        @Override
+        public void visit( PropertyValue value ) {
+            requireColumn(value.getSelectorName(), value.getPropertyName());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.EquiJoinCondition)
+         */
+        @Override
+        public void visit( EquiJoinCondition condition ) {
+            requireColumn(condition.getSelector1Name(), condition.getProperty1Name());
+            requireColumn(condition.getSelector2Name(), condition.getProperty2Name());
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.graph.query.model.Visitors.AbstractVisitor#visit(org.jboss.dna.graph.query.model.Column)
+         */
+        @Override
+        public void visit( Column column ) {
+            requireColumn(column.getSelectorName(), column.getPropertyName());
+        }
+
+        protected void requireColumn( SelectorName selector,
+                                      Name propertyName ) {
+            if (names.contains(selector)) {
+                // The column is part of the table we're interested in ...
+                if (requiredColumnNames.add(propertyName)) {
+                    String alias = context.getValueFactories().getStringFactory().create(propertyName);
+                    columns.add(new Column(selector, propertyName, alias));
+                }
+            }
+        }
+
+        /**
+         * Get the columns that are required.
+         * 
+         * @return the columns; never null
+         */
+        public List<Column> getRequiredColumns() {
+            return columns;
+        }
+    }
+
+    public static void replaceReferencesToRemovedSource( QueryContext context,
+                                                         PlanNode planNode,
+                                                         Map<SelectorName, SelectorName> rewrittenSelectors ) {
+        switch (planNode.getType()) {
+            case PROJECT:
+                List<Column> columns = planNode.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+                for (int i = 0; i != columns.size(); ++i) {
+                    Column column = columns.get(i);
+                    SelectorName replacement = rewrittenSelectors.get(column.getSelectorName());
+                    if (replacement != null) {
+                        columns.set(i, new Column(replacement, column.getPropertyName(), column.getColumnName()));
+                    }
+                }
+                break;
+            case SELECT:
+                Constraint constraint = planNode.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                Constraint newConstraint = replaceReferencesToRemovedSource(context, constraint, rewrittenSelectors);
+                if (constraint != newConstraint) {
+                    planNode.setProperty(Property.SELECT_CRITERIA, newConstraint);
+                }
+                break;
+            case SORT:
+                List<Object> orderBys = planNode.getPropertyAsList(Property.SORT_ORDER_BY, Object.class);
+                if (orderBys != null && !orderBys.isEmpty()) {
+                    if (orderBys.get(0) instanceof SelectorName) {
+                        for (int i = 0; i != orderBys.size(); ++i) {
+                            SelectorName selectorName = (SelectorName)orderBys.get(i);
+                            SelectorName replacement = rewrittenSelectors.get(selectorName);
+                            if (replacement != null) {
+                                orderBys.set(i, replacement);
+                            }
+                        }
+                    } else {
+                        for (int i = 0; i != orderBys.size(); ++i) {
+                            Ordering ordering = (Ordering)orderBys.get(i);
+                            DynamicOperand operand = ordering.getOperand();
+                            orderBys.set(i, replaceReferencesToRemovedSource(context, operand, rewrittenSelectors));
+                        }
+                    }
+                }
+                break;
+            case JOIN:
+                // Update the join condition ...
+                JoinCondition joinCondition = planNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+                JoinCondition newCondition = replaceReferencesToRemovedSource(context, joinCondition, rewrittenSelectors);
+                if (joinCondition != newCondition) {
+                    planNode.setProperty(Property.JOIN_CONDITION, newCondition);
+                }
+
+                // Update the join criteria (if there are some) ...
+                List<Constraint> constraints = planNode.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
+                if (constraints != null && !constraints.isEmpty()) {
+                    for (int i = 0; i != constraints.size(); ++i) {
+                        Constraint old = constraints.get(i);
+                        Constraint replacement = replaceReferencesToRemovedSource(context, old, rewrittenSelectors);
+                        if (replacement != old) {
+                            constraints.set(i, replacement);
+                        }
+                    }
+                }
+                break;
+            case GROUP:
+            case SET_OPERATION:
+            case DUP_REMOVE:
+            case LIMIT:
+            case NULL:
+            case SOURCE:
+            case ACCESS:
+                // None of these have to be changed ...
+                break;
+        }
+
+        // Update the selectors referenced by the node ...
+        Set<SelectorName> selectorsToAdd = null;
+        for (Iterator<SelectorName> iter = planNode.getSelectors().iterator(); iter.hasNext();) {
+            SelectorName replacement = rewrittenSelectors.get(iter.next());
+            if (replacement != null) {
+                iter.remove();
+                if (selectorsToAdd == null) selectorsToAdd = new HashSet<SelectorName>();
+                selectorsToAdd.add(replacement);
+            }
+        }
+        if (selectorsToAdd != null) planNode.getSelectors().addAll(selectorsToAdd);
+
+        // Now call recursively on the children ...
+        for (PlanNode child : planNode) {
+            replaceReferencesToRemovedSource(context, child, rewrittenSelectors);
+        }
+    }
+
+    public static DynamicOperand replaceReferencesToRemovedSource( QueryContext context,
+                                                                   DynamicOperand operand,
+                                                                   Map<SelectorName, SelectorName> rewrittenSelectors ) {
+        if (operand instanceof FullTextSearchScore) {
+            FullTextSearchScore score = (FullTextSearchScore)operand;
+            SelectorName replacement = rewrittenSelectors.get(score.getSelectorName());
+            if (replacement == null) return score;
+            return new FullTextSearchScore(replacement);
+        }
+        if (operand instanceof Length) {
+            Length operation = (Length)operand;
+            PropertyValue wrapped = operation.getPropertyValue();
+            SelectorName replacement = rewrittenSelectors.get(wrapped.getSelectorName());
+            if (replacement == null) return operand;
+            return new Length(new PropertyValue(replacement, wrapped.getPropertyName()));
+        }
+        if (operand instanceof LowerCase) {
+            LowerCase operation = (LowerCase)operand;
+            SelectorName replacement = rewrittenSelectors.get(operation.getSelectorName());
+            if (replacement == null) return operand;
+            return new LowerCase(replaceReferencesToRemovedSource(context, operation.getOperand(), rewrittenSelectors));
+        }
+        if (operand instanceof UpperCase) {
+            UpperCase operation = (UpperCase)operand;
+            SelectorName replacement = rewrittenSelectors.get(operation.getSelectorName());
+            if (replacement == null) return operand;
+            return new UpperCase(replaceReferencesToRemovedSource(context, operation.getOperand(), rewrittenSelectors));
+        }
+        if (operand instanceof NodeName) {
+            NodeName name = (NodeName)operand;
+            SelectorName replacement = rewrittenSelectors.get(name.getSelectorName());
+            if (replacement == null) return name;
+            return new NodeName(replacement);
+        }
+        if (operand instanceof NodeLocalName) {
+            NodeLocalName name = (NodeLocalName)operand;
+            SelectorName replacement = rewrittenSelectors.get(name.getSelectorName());
+            if (replacement == null) return name;
+            return new NodeLocalName(replacement);
+        }
+        if (operand instanceof PropertyValue) {
+            PropertyValue value = (PropertyValue)operand;
+            SelectorName replacement = rewrittenSelectors.get(value.getSelectorName());
+            if (replacement == null) return operand;
+            return new PropertyValue(replacement, value.getPropertyName());
+        }
+        if (operand instanceof NodeDepth) {
+            NodeDepth depth = (NodeDepth)operand;
+            SelectorName replacement = rewrittenSelectors.get(depth.getSelectorName());
+            if (replacement == null) return operand;
+            return new NodeDepth(replacement);
+        }
+        if (operand instanceof NodePath) {
+            NodePath path = (NodePath)operand;
+            SelectorName replacement = rewrittenSelectors.get(path.getSelectorName());
+            if (replacement == null) return operand;
+            return new NodePath(replacement);
+        }
+        return operand;
+    }
+
+    public static Constraint replaceReferencesToRemovedSource( QueryContext context,
+                                                               Constraint constraint,
+                                                               Map<SelectorName, SelectorName> rewrittenSelectors ) {
+        if (constraint instanceof And) {
+            And and = (And)constraint;
+            Constraint left = replaceReferencesToRemovedSource(context, and.getLeft(), rewrittenSelectors);
+            Constraint right = replaceReferencesToRemovedSource(context, and.getRight(), rewrittenSelectors);
+            if (left == and.getLeft() && right == and.getRight()) return and;
+            return new And(left, right);
+        }
+        if (constraint instanceof Or) {
+            Or or = (Or)constraint;
+            Constraint left = replaceReferencesToRemovedSource(context, or.getLeft(), rewrittenSelectors);
+            Constraint right = replaceReferencesToRemovedSource(context, or.getRight(), rewrittenSelectors);
+            if (left == or.getLeft() && right == or.getRight()) return or;
+            return new Or(left, right);
+        }
+        if (constraint instanceof Not) {
+            Not not = (Not)constraint;
+            Constraint wrapped = replaceReferencesToRemovedSource(context, not.getConstraint(), rewrittenSelectors);
+            if (wrapped == not.getConstraint()) return not;
+            return new Not(wrapped);
+        }
+        if (constraint instanceof SameNode) {
+            SameNode sameNode = (SameNode)constraint;
+            SelectorName replacement = rewrittenSelectors.get(sameNode.getSelectorName());
+            if (replacement == null) return sameNode;
+            return new SameNode(replacement, sameNode.getPath());
+        }
+        if (constraint instanceof ChildNode) {
+            ChildNode childNode = (ChildNode)constraint;
+            SelectorName replacement = rewrittenSelectors.get(childNode.getSelectorName());
+            if (replacement == null) return childNode;
+            return new ChildNode(replacement, childNode.getParentPath());
+        }
+        if (constraint instanceof DescendantNode) {
+            DescendantNode descendantNode = (DescendantNode)constraint;
+            SelectorName replacement = rewrittenSelectors.get(descendantNode.getSelectorName());
+            if (replacement == null) return descendantNode;
+            return new DescendantNode(replacement, descendantNode.getAncestorPath());
+        }
+        if (constraint instanceof PropertyExistence) {
+            PropertyExistence existence = (PropertyExistence)constraint;
+            SelectorName replacement = rewrittenSelectors.get(existence.getSelectorName());
+            if (replacement == null) return existence;
+            return new PropertyExistence(replacement, existence.getPropertyName());
+        }
+        if (constraint instanceof FullTextSearch) {
+            FullTextSearch search = (FullTextSearch)constraint;
+            SelectorName replacement = rewrittenSelectors.get(search.getSelectorName());
+            if (replacement == null) return search;
+            return new FullTextSearch(replacement, search.getPropertyName(), search.getFullTextSearchExpression());
+        }
+        if (constraint instanceof Comparison) {
+            Comparison comparison = (Comparison)constraint;
+            DynamicOperand lhs = comparison.getOperand1();
+            StaticOperand rhs = comparison.getOperand2(); // Current only a literal; therefore, no reference to selector
+            DynamicOperand newLhs = replaceReferencesToRemovedSource(context, lhs, rewrittenSelectors);
+            if (lhs == newLhs) return comparison;
+            return new Comparison(newLhs, comparison.getOperator(), rhs);
+        }
+        return constraint;
+    }
+
+    public static JoinCondition replaceReferencesToRemovedSource( QueryContext context,
+                                                                  JoinCondition joinCondition,
+                                                                  Map<SelectorName, SelectorName> rewrittenSelectors ) {
+        if (joinCondition instanceof EquiJoinCondition) {
+            EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
+            SelectorName replacement1 = rewrittenSelectors.get(condition.getSelector1Name());
+            SelectorName replacement2 = rewrittenSelectors.get(condition.getSelector2Name());
+            if (replacement1 == condition.getSelector1Name() && replacement2 == condition.getSelector2Name()) return condition;
+            return new EquiJoinCondition(replacement1, condition.getProperty1Name(), replacement2, condition.getProperty2Name());
+        }
+        if (joinCondition instanceof SameNodeJoinCondition) {
+            SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
+            SelectorName replacement1 = rewrittenSelectors.get(condition.getSelector1Name());
+            SelectorName replacement2 = rewrittenSelectors.get(condition.getSelector2Name());
+            if (replacement1 == condition.getSelector1Name() && replacement2 == condition.getSelector2Name()) return condition;
+            return new SameNodeJoinCondition(replacement1, replacement2, condition.getSelector2Path());
+        }
+        if (joinCondition instanceof ChildNodeJoinCondition) {
+            ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
+            SelectorName childSelector = rewrittenSelectors.get(condition.getChildSelectorName());
+            SelectorName parentSelector = rewrittenSelectors.get(condition.getParentSelectorName());
+            if (childSelector == condition.getChildSelectorName() && parentSelector == condition.getParentSelectorName()) return condition;
+            return new ChildNodeJoinCondition(parentSelector, childSelector);
+        }
+        if (joinCondition instanceof DescendantNodeJoinCondition) {
+            DescendantNodeJoinCondition condition = (DescendantNodeJoinCondition)joinCondition;
+            SelectorName ancestor = rewrittenSelectors.get(condition.getAncestorSelectorName());
+            SelectorName descendant = rewrittenSelectors.get(condition.getDescendantSelectorName());
+            if (ancestor == condition.getAncestorSelectorName() && descendant == condition.getDescendantSelectorName()) return condition;
+            return new ChildNodeJoinCondition(ancestor, descendant);
+        }
+        return joinCondition;
+    }
+
+    public static void replaceViewReferences( QueryContext context,
+                                              PlanNode topOfViewInPlan,
+                                              ColumnMapping mappings ) {
+        assert topOfViewInPlan != null;
+        SelectorName viewName = mappings.getOriginalName();
+
+        // Walk up the plan to change any references to the view columns into references to the source columns...
+        PlanNode node = topOfViewInPlan;
+        ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+        List<PlanNode> potentiallyRemovableSources = new LinkedList<PlanNode>();
+        do {
+            // Remove the view from the selectors ...
+            if (node.getSelectors().contains(viewName)) {
+                switch (node.getType()) {
+                    case PROJECT:
+                        // Adjust the columns ...
+                        node.getSelectors().remove(viewName);
+                        List<Column> columns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+                        if (columns != null) {
+                            for (int i = 0; i != columns.size(); ++i) {
+                                Column column = columns.get(i);
+                                if (column.getSelectorName().equals(viewName)) {
+                                    // This column references the view ...
+                                    String columnName = stringFactory.create(column.getPropertyName());
+                                    String columnAlias = column.getColumnName();
+                                    // Find the source column that this view column corresponds to ...
+                                    Column sourceColumn = mappings.getMappedColumn(columnName);
+                                    if (sourceColumn != null) {
+                                        SelectorName sourceName = sourceColumn.getSelectorName();
+                                        // Replace the view column with one that uses the same alias but that references the
+                                        // source
+                                        // column ...
+                                        columns.set(i, new Column(sourceName, sourceColumn.getPropertyName(), columnAlias));
+                                        node.addSelector(sourceName);
+                                    } else {
+                                        node.addSelector(column.getSelectorName());
+                                    }
+                                } else {
+                                    node.addSelector(column.getSelectorName());
+                                }
+                            }
+                        }
+                        break;
+                    case SELECT:
+                        Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                        Constraint newConstraint = replaceReferences(context, constraint, mappings, node);
+                        if (constraint != newConstraint) {
+                            node.getSelectors().clear();
+                            node.addSelectors(Visitors.getSelectorsReferencedBy(newConstraint));
+                            node.setProperty(Property.SELECT_CRITERIA, newConstraint);
+                        }
+                        break;
+                    case SET_OPERATION:
+                        break;
+                    case SOURCE:
+                        SelectorName sourceName = node.getProperty(Property.SOURCE_NAME, SelectorName.class);
+                        assert sourceName.equals(sourceName); // selector name already matches
+                        potentiallyRemovableSources.add(node);
+                        break;
+                    default:
+                        break;
+                }
+                if (!node.getSelectors().contains(viewName)) {
+                    // Used to reference view, so it needs to at least reference
+                }
+            }
+            // Move to the parent ...
+            node = node.getParent();
+        } while (node != null);
+
+        // Remove any source node that was the view but that is no longer used in any higher node ...
+        for (PlanNode sourceNode : potentiallyRemovableSources) {
+            boolean stillRequired = false;
+            node = sourceNode.getParent();
+            while (node != null) {
+                if (node.getSelectors().contains(viewName)) {
+                    stillRequired = true;
+                    break;
+                }
+                node = node.getParent();
+            }
+            if (!stillRequired) {
+                assert sourceNode.getParent() != null;
+                sourceNode.extractFromParent();
+            }
+        }
+    }
+
+    public static Constraint replaceReferences( QueryContext context,
+                                                Constraint constraint,
+                                                ColumnMapping mapping,
+                                                PlanNode node ) {
+        if (constraint instanceof And) {
+            And and = (And)constraint;
+            Constraint left = replaceReferences(context, and.getLeft(), mapping, node);
+            Constraint right = replaceReferences(context, and.getRight(), mapping, node);
+            if (left == and.getLeft() && right == and.getRight()) return and;
+            return new And(left, right);
+        }
+        if (constraint instanceof Or) {
+            Or or = (Or)constraint;
+            Constraint left = replaceReferences(context, or.getLeft(), mapping, node);
+            Constraint right = replaceReferences(context, or.getRight(), mapping, node);
+            if (left == or.getLeft() && right == or.getRight()) return or;
+            return new Or(left, right);
+        }
+        if (constraint instanceof Not) {
+            Not not = (Not)constraint;
+            Constraint wrapped = replaceReferences(context, not.getConstraint(), mapping, node);
+            return wrapped == not.getConstraint() ? not : new Not(wrapped);
+        }
+        if (constraint instanceof SameNode) {
+            SameNode sameNode = (SameNode)constraint;
+            if (!mapping.getOriginalName().equals(sameNode.getSelectorName())) return sameNode;
+            if (!mapping.isMappedToSingleSelector()) return sameNode;
+            SelectorName selector = mapping.getSingleMappedSelectorName();
+            node.addSelector(selector);
+            return new SameNode(selector, sameNode.getPath());
+        }
+        if (constraint instanceof ChildNode) {
+            ChildNode childNode = (ChildNode)constraint;
+            if (!mapping.getOriginalName().equals(childNode.getSelectorName())) return childNode;
+            if (!mapping.isMappedToSingleSelector()) return childNode;
+            SelectorName selector = mapping.getSingleMappedSelectorName();
+            node.addSelector(selector);
+            return new ChildNode(selector, childNode.getParentPath());
+        }
+        if (constraint instanceof DescendantNode) {
+            DescendantNode descendantNode = (DescendantNode)constraint;
+            if (!mapping.getOriginalName().equals(descendantNode.getSelectorName())) return descendantNode;
+            if (!mapping.isMappedToSingleSelector()) return descendantNode;
+            SelectorName selector = mapping.getSingleMappedSelectorName();
+            node.addSelector(selector);
+            return new DescendantNode(selector, descendantNode.getAncestorPath());
+        }
+        if (constraint instanceof PropertyExistence) {
+            PropertyExistence existence = (PropertyExistence)constraint;
+            if (!mapping.getOriginalName().equals(existence.getSelectorName())) return existence;
+            ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+            Column sourceColumn = mapping.getMappedColumn(stringFactory.create(existence.getPropertyName()));
+            if (sourceColumn == null) return existence;
+            node.addSelector(sourceColumn.getSelectorName());
+            return new PropertyExistence(sourceColumn.getSelectorName(), sourceColumn.getPropertyName());
+        }
+        if (constraint instanceof FullTextSearch) {
+            FullTextSearch search = (FullTextSearch)constraint;
+            if (!mapping.getOriginalName().equals(search.getSelectorName())) return search;
+            ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+            Column sourceColumn = mapping.getMappedColumn(stringFactory.create(search.getPropertyName()));
+            if (sourceColumn == null) return search;
+            node.addSelector(sourceColumn.getSelectorName());
+            return new FullTextSearch(sourceColumn.getSelectorName(), sourceColumn.getPropertyName(),
+                                      search.getFullTextSearchExpression());
+        }
+        if (constraint instanceof Comparison) {
+            Comparison comparison = (Comparison)constraint;
+            DynamicOperand lhs = comparison.getOperand1();
+            StaticOperand rhs = comparison.getOperand2(); // Current only a literal; therefore, no reference to selector
+            DynamicOperand newLhs = replaceViewReferences(context, lhs, mapping, node);
+            if (lhs == newLhs) return comparison;
+            return new Comparison(newLhs, comparison.getOperator(), rhs);
+        }
+        return constraint;
+    }
+
+    public static DynamicOperand replaceViewReferences( QueryContext context,
+                                                        DynamicOperand operand,
+                                                        ColumnMapping mapping,
+                                                        PlanNode node ) {
+        if (operand instanceof FullTextSearchScore) {
+            FullTextSearchScore score = (FullTextSearchScore)operand;
+            if (!mapping.getOriginalName().equals(score.getSelectorName())) return score;
+            if (!mapping.isMappedToSingleSelector()) return score;
+            return new FullTextSearchScore(mapping.getSingleMappedSelectorName());
+        }
+        if (operand instanceof Length) {
+            Length operation = (Length)operand;
+            return new Length((PropertyValue)replaceViewReferences(context, operation.getPropertyValue(), mapping, node));
+        }
+        if (operand instanceof LowerCase) {
+            LowerCase operation = (LowerCase)operand;
+            return new LowerCase(replaceViewReferences(context, operation.getOperand(), mapping, node));
+        }
+        if (operand instanceof UpperCase) {
+            UpperCase operation = (UpperCase)operand;
+            return new UpperCase(replaceViewReferences(context, operation.getOperand(), mapping, node));
+        }
+        if (operand instanceof NodeName) {
+            NodeName name = (NodeName)operand;
+            if (!mapping.getOriginalName().equals(name.getSelectorName())) return name;
+            if (!mapping.isMappedToSingleSelector()) return name;
+            node.addSelector(mapping.getSingleMappedSelectorName());
+            return new NodeName(mapping.getSingleMappedSelectorName());
+        }
+        if (operand instanceof NodeLocalName) {
+            NodeLocalName name = (NodeLocalName)operand;
+            if (!mapping.getOriginalName().equals(name.getSelectorName())) return name;
+            if (!mapping.isMappedToSingleSelector()) return name;
+            node.addSelector(mapping.getSingleMappedSelectorName());
+            return new NodeLocalName(mapping.getSingleMappedSelectorName());
+        }
+        if (operand instanceof PropertyValue) {
+            PropertyValue value = (PropertyValue)operand;
+            if (!mapping.getOriginalName().equals(value.getSelectorName())) return value;
+            ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+            Column sourceColumn = mapping.getMappedColumn(stringFactory.create(value.getPropertyName()));
+            if (sourceColumn == null) return value;
+            node.addSelector(sourceColumn.getSelectorName());
+            return new PropertyValue(sourceColumn.getSelectorName(), sourceColumn.getPropertyName());
+        }
+        if (operand instanceof NodeDepth) {
+            NodeDepth depth = (NodeDepth)operand;
+            if (!mapping.getOriginalName().equals(depth.getSelectorName())) return depth;
+            if (!mapping.isMappedToSingleSelector()) return depth;
+            node.addSelector(mapping.getSingleMappedSelectorName());
+            return new NodeDepth(mapping.getSingleMappedSelectorName());
+        }
+        if (operand instanceof NodePath) {
+            NodePath path = (NodePath)operand;
+            if (!mapping.getOriginalName().equals(path.getSelectorName())) return path;
+            if (!mapping.isMappedToSingleSelector()) return path;
+            node.addSelector(mapping.getSingleMappedSelectorName());
+            return new NodePath(mapping.getSingleMappedSelectorName());
+        }
+        return operand;
+    }
+
+    public static JoinCondition replaceReferences( QueryContext context,
+                                                   JoinCondition joinCondition,
+                                                   ColumnMapping mapping,
+                                                   PlanNode node ) {
+        if (joinCondition instanceof EquiJoinCondition) {
+            EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
+            SelectorName replacement1 = condition.getSelector1Name();
+            SelectorName replacement2 = condition.getSelector2Name();
+            Name property1 = condition.getProperty1Name();
+            Name property2 = condition.getProperty2Name();
+            if (replacement1.equals(mapping.getOriginalName())) {
+                ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+                Column sourceColumn = mapping.getMappedColumn(stringFactory.create(property1));
+                if (sourceColumn != null) {
+                    replacement1 = sourceColumn.getSelectorName();
+                    property1 = sourceColumn.getPropertyName();
+                }
+            }
+            if (replacement2.equals(mapping.getOriginalName())) {
+                ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+                Column sourceColumn = mapping.getMappedColumn(stringFactory.create(property2));
+                if (sourceColumn != null) {
+                    replacement2 = sourceColumn.getSelectorName();
+                    property2 = sourceColumn.getPropertyName();
+                }
+            }
+            if (replacement1 == condition.getSelector1Name() && replacement2 == condition.getSelector2Name()) return condition;
+            node.addSelector(replacement1, replacement2);
+            return new EquiJoinCondition(replacement1, property1, replacement2, property2);
+        }
+        // All the remaining conditions can only be rewritten if there is a single source ...
+        if (!mapping.isMappedToSingleSelector()) return joinCondition;
+
+        // There is only a single source ...
+        SelectorName viewName = mapping.getOriginalName();
+        SelectorName sourceName = mapping.getSingleMappedSelectorName();
+
+        if (joinCondition instanceof SameNodeJoinCondition) {
+            SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
+            SelectorName replacement1 = condition.getSelector1Name();
+            SelectorName replacement2 = condition.getSelector2Name();
+            if (replacement1.equals(viewName)) replacement1 = sourceName;
+            if (replacement2.equals(viewName)) replacement2 = sourceName;
+            if (replacement1 == condition.getSelector1Name() && replacement2 == condition.getSelector2Name()) return condition;
+            node.addSelector(replacement1, replacement2);
+            return new SameNodeJoinCondition(replacement1, replacement2, condition.getSelector2Path());
+        }
+        if (joinCondition instanceof ChildNodeJoinCondition) {
+            ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
+            SelectorName childSelector = condition.getChildSelectorName();
+            SelectorName parentSelector = condition.getParentSelectorName();
+            if (childSelector.equals(viewName)) childSelector = sourceName;
+            if (parentSelector.equals(viewName)) parentSelector = sourceName;
+            if (childSelector == condition.getChildSelectorName() && parentSelector == condition.getParentSelectorName()) return condition;
+            node.addSelector(childSelector, parentSelector);
+            return new ChildNodeJoinCondition(parentSelector, childSelector);
+        }
+        if (joinCondition instanceof DescendantNodeJoinCondition) {
+            DescendantNodeJoinCondition condition = (DescendantNodeJoinCondition)joinCondition;
+            SelectorName ancestor = condition.getAncestorSelectorName();
+            SelectorName descendant = condition.getDescendantSelectorName();
+            if (ancestor.equals(viewName)) ancestor = sourceName;
+            if (descendant.equals(viewName)) descendant = sourceName;
+            if (ancestor == condition.getAncestorSelectorName() && descendant == condition.getDescendantSelectorName()) return condition;
+            node.addSelector(ancestor, descendant);
+            return new ChildNodeJoinCondition(ancestor, descendant);
+        }
+        return joinCondition;
+    }
+
+    public static ColumnMapping createMappingFor( View view,
+                                                  PlanNode viewPlan ) {
+        ColumnMapping mapping = new ColumnMapping(view.getName());
+
+        // Find the PROJECT node in the view plan ...
+        PlanNode project = viewPlan.findAtOrBelow(Type.PROJECT);
+        assert project != null;
+
+        // Get the Columns from the PROJECT in the plan node ...
+        List<Column> projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+
+        // Get the Schemata columns defined by the view ...
+        List<org.jboss.dna.graph.query.validate.Schemata.Column> viewColumns = view.getColumns();
+        assert viewColumns.size() == projectedColumns.size();
+
+        for (int i = 0; i != viewColumns.size(); ++i) {
+            Column projectedColunn = projectedColumns.get(i);
+            String viewColumnName = viewColumns.get(i).getName();
+            mapping.map(viewColumnName, projectedColunn);
+        }
+        return mapping;
+    }
+
+    /**
+     * Defines how the view columns are mapped (or resolved) into the columns from the source tables.
+     */
+    public static class ColumnMapping {
+        private final SelectorName originalName;
+        private final Map<String, Column> mappedColumnsByOriginalColumnName = new HashMap<String, Column>();
+        private final Set<SelectorName> mappedSelectorNames = new HashSet<SelectorName>();
+
+        public ColumnMapping( SelectorName originalName ) {
+            this.originalName = originalName;
+        }
+
+        public void map( String originalColumnName,
+                         Column projectedColumn ) {
+            mappedColumnsByOriginalColumnName.put(originalColumnName, projectedColumn);
+            mappedSelectorNames.add(projectedColumn.getSelectorName());
+        }
+
+        public SelectorName getOriginalName() {
+            return originalName;
+        }
+
+        public Column getMappedColumn( String viewColumnName ) {
+            return mappedColumnsByOriginalColumnName.get(viewColumnName);
+        }
+
+        public boolean isMappedToSingleSelector() {
+            return mappedSelectorNames.size() == 1;
+        }
+
+        /**
+         * @return tableNames
+         */
+        public Set<SelectorName> getMappedSelectorNames() {
+            return mappedSelectorNames;
+        }
+
+        public SelectorName getSingleMappedSelectorName() {
+            return isMappedToSingleSelector() ? mappedSelectorNames.iterator().next() : null;
+        }
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -32,10 +32,23 @@
 import java.util.Set;
 import net.jcip.annotations.Immutable;
 import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.text.ParsingException;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.parse.InvalidQueryException;
+import org.jboss.dna.graph.query.parse.SqlQueryParser;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
 
 /**
  * An immutable {@link Schemata} implementation.
@@ -46,10 +59,13 @@
     /**
      * Obtain a new instance for building Schemata objects.
      * 
+     * @param context the execution context that can be used when building the schema
      * @return the new builder; never null
+     * @throws IllegalArgumentException if the context is null
      */
-    public static Builder createBuilder() {
-        return new Builder();
+    public static Builder createBuilder( ExecutionContext context ) {
+        CheckArg.isNotNull(context, "context");
+        return new Builder(context);
     }
 
     /**
@@ -58,8 +74,14 @@
     @NotThreadSafe
     public static class Builder {
 
-        private Map<SelectorName, ImmutableTable> tables = new HashMap<SelectorName, ImmutableTable>();
+        private final ExecutionContext context;
+        private final Map<SelectorName, ImmutableTable> tables = new HashMap<SelectorName, ImmutableTable>();
+        private final Map<SelectorName, QueryCommand> viewDefinitions = new HashMap<SelectorName, QueryCommand>();
 
+        protected Builder( ExecutionContext context ) {
+            this.context = context;
+        }
+
         /**
          * Add a table with the supplied name and column names. Each column will be given a type of {@link PropertyType#STRING}.
          * The table will also overwrite any existing table definition with the same name.
@@ -254,6 +276,43 @@
         }
 
         /**
+         * Add a view with the supplied name and SQL string definition. The column names and types will be inferred from the
+         * source table(s) and views(s) used in the definition.
+         * 
+         * @param name the name of the new view
+         * @param definition the SQL definition of the view
+         * @return this builder, for convenience in method chaining; never null
+         * @throws IllegalArgumentException if the view name is null or empty or the definition is null
+         * @throws ParsingException if the supplied definition is cannot be parsed as a SQL query
+         */
+        public Builder addView( String name,
+                                String definition ) {
+            CheckArg.isNotEmpty(name, "name");
+            CheckArg.isNotEmpty(definition, "definition");
+            SqlQueryParser parser = new SqlQueryParser();
+            QueryCommand command = parser.parseQuery(definition, context);
+            this.viewDefinitions.put(new SelectorName(name), command);
+            return this;
+        }
+
+        /**
+         * Add a view with the supplied name and definition. The column names and types will be inferred from the source table(s)
+         * used in the definition.
+         * 
+         * @param name the name of the new view
+         * @param definition the definition of the view
+         * @return this builder, for convenience in method chaining; never null
+         * @throws IllegalArgumentException if the view name is null or empty or the definition is null
+         */
+        public Builder addView( String name,
+                                QueryCommand definition ) {
+            CheckArg.isNotEmpty(name, "name");
+            CheckArg.isNotNull(definition, "definition");
+            this.viewDefinitions.put(new SelectorName(name), definition);
+            return this;
+        }
+
+        /**
          * Add a column with the supplied name and type to the named table. Any existing column with that name will be replaced
          * with the new column. If the table does not yet exist, it will be added.
          * 
@@ -320,9 +379,70 @@
          * tables (with their columns) as they exist at the moment this method is called.
          * 
          * @return the new Schemata; never null
+         * @throws InvalidQueryException if any of the view definitions is invalid and cannot be resolved
          */
         public Schemata build() {
-            return new ImmutableSchemata(new HashMap<SelectorName, Table>(tables));
+            ImmutableSchemata schemata = new ImmutableSchemata(new HashMap<SelectorName, Table>(tables));
+
+            // Make a copy of the view definitions, and create the views ...
+            Map<SelectorName, QueryCommand> definitions = new HashMap<SelectorName, QueryCommand>(viewDefinitions);
+            ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
+            boolean added = false;
+            do {
+                added = false;
+                Set<SelectorName> viewNames = new HashSet<SelectorName>(definitions.keySet());
+                for (SelectorName name : viewNames) {
+                    QueryCommand command = definitions.get(name);
+                    // Create the canonical plan for the definition ...
+                    QueryContext queryContext = new QueryContext(context, new PlanHints(), schemata);
+                    CanonicalPlanner planner = new CanonicalPlanner();
+                    PlanNode plan = planner.createPlan(queryContext, command);
+                    if (queryContext.getProblems().hasErrors()) continue;
+
+                    // Get the columns from the top-level PROJECT ...
+                    PlanNode project = plan.findAtOrBelow(Type.PROJECT);
+                    assert project != null;
+                    List<org.jboss.dna.graph.query.model.Column> columns = project.getPropertyAsList(Property.PROJECT_COLUMNS,
+                                                                                                     org.jboss.dna.graph.query.model.Column.class);
+                    assert !columns.isEmpty();
+
+                    // Go through all the columns and look up the types ...
+                    List<Column> viewColumns = new ArrayList<Column>(columns.size());
+                    for (org.jboss.dna.graph.query.model.Column column : columns) {
+                        // Find the table that the column came from ...
+                        Table source = schemata.getTable(column.getSelectorName());
+                        if (source == null) break;
+                        String viewColumnName = column.getColumnName();
+                        String sourceColumnName = stringFactory.create(column.getPropertyName()); // getColumnName() returns alias
+                        Column sourceColumn = source.getColumn(sourceColumnName);
+                        if (sourceColumn == null) {
+                            throw new InvalidQueryException(Visitors.readable(command),
+                                                            "The view references a non-existant column '"
+                                                            + column.getColumnName() + "' in '" + source.getName() + "'");
+                        }
+                        viewColumns.add(new ImmutableColumn(viewColumnName, sourceColumn.getPropertyType()));
+                    }
+                    if (viewColumns.size() != columns.size()) {
+                        // We weren't able to resolve all of the columns,
+                        // so maybe the columns were referencing yet-to-be-built views ...
+                        continue;
+                    }
+
+                    // If we could resolve the definition ...
+                    ImmutableView view = new ImmutableView(name, viewColumns, command);
+                    definitions.remove(name);
+                    schemata = schemata.with(view);
+                    added = true;
+                }
+            } while (added && !definitions.isEmpty());
+
+            if (!definitions.isEmpty()) {
+                QueryCommand command = definitions.values().iterator().next();
+                throw new InvalidQueryException(Visitors.readable(command), "The view definition cannot be resolved: "
+                                                                            + Visitors.readable(command));
+            }
+
+            return schemata;
         }
     }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -187,6 +187,14 @@
         return new ImmutableTable(getName(), newColumns);
     }
 
+    public ImmutableTable withColumns( Iterable<Column> columns ) {
+        List<Column> newColumns = new LinkedList<Column>(this.getColumns());
+        for (Column column : columns) {
+            newColumns.add(new ImmutableColumn(column.getName(), column.getPropertyType()));
+        }
+        return new ImmutableTable(getName(), newColumns);
+    }
+
     public ImmutableTable with( SelectorName name ) {
         return new ImmutableTable(name, columnsByName, columns, keys);
     }

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -0,0 +1,107 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.validate;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.validate.Schemata.Column;
+import org.jboss.dna.graph.query.validate.Schemata.Key;
+import org.jboss.dna.graph.query.validate.Schemata.View;
+
+/**
+ * 
+ */
+ at Immutable
+class ImmutableView extends ImmutableTable implements View {
+
+    private final QueryCommand definition;
+
+    protected ImmutableView( SelectorName name,
+                             Iterable<Column> columns,
+                             QueryCommand definition ) {
+        super(name, columns);
+        this.definition = definition;
+    }
+
+    protected ImmutableView( SelectorName name,
+                             Iterable<Column> columns,
+                             QueryCommand definition,
+                             Iterable<Column>... keyColumns ) {
+        super(name, columns, keyColumns);
+        this.definition = definition;
+    }
+
+    protected ImmutableView( SelectorName name,
+                             Map<String, Column> columnsByName,
+                             List<Column> columns,
+                             QueryCommand definition,
+                             Set<Key> keys ) {
+        super(name, columnsByName, columns, keys);
+        this.definition = definition;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.validate.Schemata.View#getDefinition()
+     */
+    public QueryCommand getDefinition() {
+        return definition;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(getName().getName());
+        sb.append('(');
+        boolean first = true;
+        for (Column column : getColumns()) {
+            if (first) first = false;
+            else sb.append(", ");
+            sb.append(column);
+        }
+        sb.append(") AS '");
+        sb.append(Visitors.readable(definition));
+        sb.append('\'');
+        if (!getKeys().isEmpty()) {
+            sb.append(" with keys ");
+            first = true;
+            for (Key key : getKeys()) {
+                if (first) first = false;
+                else sb.append(", ");
+                sb.append(key);
+            }
+        }
+        return sb.toString();
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableView.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -28,6 +28,7 @@
 import java.util.Map;
 import java.util.Set;
 import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
 
 /**
@@ -36,10 +37,14 @@
 public interface Schemata {
 
     /**
-     * Get the information for the table with the supplied name within this schema.
+     * Get the information for the table or view with the supplied name within this schema.
+     * <p>
+     * The resulting definition is immutable, though subsequent calls to this method with the same argument may result in a
+     * different definition.
+     * </p>
      * 
-     * @param name the table name; may not be null
-     * @return the table information, or null if there is no such table
+     * @param name the table or view name; may not be null
+     * @return the table or view information, or null if there is no such table
      */
     Table getTable( SelectorName name );
 
@@ -56,6 +61,9 @@
 
         /**
          * Get the information for a column with the supplied name within this table.
+         * <p>
+         * The resulting column definition is immutable.
+         * </p>
          * 
          * @param name the column name; may not be null
          * @return the column information, or null if there is no such column
@@ -65,21 +73,21 @@
         /**
          * Get the queryable columns in this table.
          * 
-         * @return the map of column objects by their name; never null
+         * @return the immutable map of immutable column objects by their name; never null
          */
         Map<String, Column> getColumnsByName();
 
         /**
          * Get the queryable columns in this table.
          * 
-         * @return the ordered column objects; never null
+         * @return the immutable, ordered list of immutable column objects; never null
          */
         List<Column> getColumns();
 
         /**
          * Get the collection of keys for this table.
          * 
-         * @return the immutable collection of keys; never null, but possibly empty
+         * @return the immutable collection of immutable keys; never null, but possibly empty
          */
         Collection<Key> getKeys();
 
@@ -101,6 +109,9 @@
 
         /**
          * Obtain this table's {@link #getKeys() key} that contains exactly those columns listed.
+         * <p>
+         * The resulting key definition is immutable.
+         * </p>
          * 
          * @param columns the columns for the key
          * @return the key that uses exactly the supplied columns, or null if there is no such key
@@ -109,6 +120,9 @@
 
         /**
          * Obtain this table's {@link #getKeys() key} that contains exactly those columns listed.
+         * <p>
+         * The resulting key definition is immutable.
+         * </p>
          * 
          * @param columns the columns for the key
          * @return the key that uses exactly the supplied columns, or null if there is no such key
@@ -117,6 +131,18 @@
     }
 
     /**
+     * Information about a view that is defined in terms of other views/tables.
+     */
+    public interface View extends Table {
+        /**
+         * Get the {@link QueryCommand query} that is the definition of the view.
+         * 
+         * @return the view definition; never null
+         */
+        QueryCommand getDefinition();
+    }
+
+    /**
      * Information about a queryable column.
      */
     public interface Column {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/package-info.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -25,8 +25,9 @@
  * This package provides the interfaces that define the tables and columns that can be queried.  Though the query framework does not prescribe 
  * the semantics of a table or column, it does provide
  * a {@link org.jboss.dna.graph.query.validate.Schemata} interface that provides access to the immutable
- * {@link org.jboss.dna.graph.query.validate.Schemata.Table} definitions (that then contain the
- * {@link org.jboss.dna.graph.query.validate.Schemata.Column} and {@link org.jboss.dna.graph.query.validate.Schemata.Key} definitions).
+ * {@link org.jboss.dna.graph.query.validate.Schemata.Table} and {@link org.jboss.dna.graph.query.validate.Schemata.View}
+ * definitions (that then contain the {@link org.jboss.dna.graph.query.validate.Schemata.Column} 
+ * and {@link org.jboss.dna.graph.query.validate.Schemata.Key} definitions).
  * <p>
  * Although any Schemata implementaiton can be used, the query framework provides an {@link org.jboss.dna.graph.query.validate.ImmutableSchemata}
  * class with a {@link org.jboss.dna.graph.query.validate.ImmutableSchemata.Builder builder} with a fluent API

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -27,6 +27,7 @@
 import static org.hamcrest.core.IsSame.sameInstance;
 import static org.junit.Assert.assertThat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -51,6 +52,14 @@
         }
     }
 
+    protected void assertSameChildren( PlanNode node,
+                                       PlanNode... children ) {
+        assertThat(node.getChildCount(), is(children.length));
+        for (int i = 0; i != node.getChildCount(); ++i) {
+            assertThat(node.getChild(i).isSameAs(children[i]), is(true));
+        }
+    }
+
     protected void assertSelectors( PlanNode node,
                                     String... selectors ) {
         Set<SelectorName> selectorSet = new HashSet<SelectorName>();
@@ -60,6 +69,19 @@
         assertThat("Selectors don't match", node.getSelectors(), is(selectorSet));
     }
 
+    protected void assertProperty( PlanNode node,
+                                   Property name,
+                                   Object value ) {
+        assertThat("Property value doesn't match", node.getProperty(name), is(value));
+    }
+
+    protected <T> void assertPropertyIsList( PlanNode node,
+                                             Property name,
+                                             Class<T> valueType,
+                                             T... values ) {
+        assertThat("Property value doesn't match", node.getPropertyAsList(name, valueType), is(Arrays.asList(values)));
+    }
+
     protected void assertSortOrderBy( PlanNode sortNode,
                                       String... selectors ) {
         List<SelectorName> expected = new ArrayList<SelectorName>(selectors.length);

Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	                        (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -0,0 +1,165 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.AbstractQueryTest;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.Literal;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.ImmutableSchemata;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class ReplaceViewsTest extends AbstractQueryTest {
+
+    private ReplaceViews rule;
+    private QueryContext context;
+    private Schemata schemata;
+    private ImmutableSchemata.Builder builder;
+
+    @Before
+    public void beforeEach() {
+        ExecutionContext execContext = new ExecutionContext();
+        rule = ReplaceViews.INSTANCE;
+        builder = ImmutableSchemata.createBuilder(execContext);
+        builder.addTable("t1", "c11", "c12", "c13");
+        builder.addTable("t2", "c21", "c22", "c23");
+        builder.addView("v1", "SELECT c11, c12 FROM t1 WHERE c13 < CAST('3' AS LONG)");
+        builder.addView("v2", "SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11 = t2.c21");
+        schemata = builder.build();
+        context = new QueryContext(execContext, new PlanHints(), schemata);
+    }
+
+    /**
+     * Before:
+     * 
+     * <pre>
+     *          ...
+     *           |
+     *        PROJECT      with the list of columns being SELECTed
+     *           |
+     *        SELECT1
+     *           |         One or more SELECT plan nodes that each have
+     *        SELECT2      a single non-join constraint that are then all AND-ed
+     *           |         together
+     *        SELECT3
+     *           |
+     *        SOURCE       A SOURCE that uses the view
+     * </pre>
+     * 
+     * And after:
+     * 
+     * <pre>
+     *          ...
+     *           |
+     *        PROJECT      with the list of columns being SELECTed
+     *           |
+     *        SELECT1
+     *           |         One or more SELECT plan nodes that each have
+     *        SELECT2      a single non-join constraint that are then all AND-ed
+     *           |         together
+     *        SELECT3
+     *           |
+     *        SOURCE
+     *           |
+     *        PROJECT      with the list of columns in the SELECT of the view
+     *           |
+     *        SELECT1      One or more SELECT plan nodes that each have a single non-join constraint
+     *           |         that are then all AND-ed together (as defined by the view plan
+     *           |
+     *        SOURCE       A SOURCE that the view uses
+     * </pre>
+     */
+    @Test
+    public void shouldReplaceViewWithPlanForViewWithSingleTable() {
+        // Each of the PROJECT, SELECT, and SELECT nodes must have the names of the selectors that they apply to ...
+        PlanNode project = new PlanNode(Type.PROJECT, selector("v1"));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("v1"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("v1"));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("v1"));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("v1"));
+        source.setProperty(Property.SOURCE_NAME, selector("v1"));
+
+        // Create the equivalent plan nodes for what should be created ...
+        PlanNode viewProject = new PlanNode(Type.PROJECT, selector("t1"));
+        PlanNode viewSelect = new PlanNode(Type.SELECT, viewProject, selector("t1"));
+        PlanNode viewSource = new PlanNode(Type.SOURCE, viewSelect, selector("t1"));
+        viewProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11"), column("t1", "c12")));
+        viewSelect.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c13")),
+                                                                        Operator.LESS_THAN, new Literal(3L)));
+        viewSource.setProperty(Property.SOURCE_NAME, selector("t1"));
+        viewSource.setProperty(Property.SOURCE_COLUMNS, schemata.getTable(selector("t1")).getColumns());
+
+        // Execute the rule ...
+        PlanNode result = rule.execute(context, project, new LinkedList<OptimizerRule>());
+        System.out.println(result);
+        assertThat(result, is(sameInstance(project)));
+        assertChildren(project, select1);
+        assertChildren(select1, select2);
+        assertChildren(select2, select3);
+        assertChildren(select3, source);
+        assertSameChildren(source, viewSelect);
+        assertSameChildren(source.getFirstChild(), viewSource);
+        assertSameChildren(source.getFirstChild().getFirstChild());
+    }
+
+    protected List<Column> columns( Column... columns ) {
+        return Arrays.asList(columns);
+    }
+
+    protected Column column( String table,
+                             String columnName ) {
+        return new Column(new SelectorName(table), name(columnName), columnName);
+    }
+
+    protected Column column( String table,
+                             String columnName,
+                             String alias ) {
+        return new Column(new SelectorName(table), name(columnName), alias);
+    }
+
+    protected Name name( String name ) {
+        return context.getExecutionContext().getValueFactories().getNameFactory().create(name);
+    }
+}


Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -25,18 +25,33 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
+import org.jboss.dna.common.collection.Problems;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Comparison;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.model.Literal;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.parse.SqlQueryParser;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.JoinAlgorithm;
 import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.ImmutableSchemata;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -44,7 +59,7 @@
 /**
  * 
  */
-public class RuleBasedOptimizerTest {
+public class RuleBasedOptimizerTest extends AbstractQueryTest {
 
     private RuleBasedOptimizer optimizer;
     private List<OptimizerRule> rules;
@@ -54,7 +69,15 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        ExecutionContext execContext = new ExecutionContext();
+        ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder(execContext);
+        builder.addTable("t1", "c11", "c12", "c13");
+        builder.addTable("t2", "c21", "c22", "c23");
+        builder.addView("v1", "SELECT c11, c12 AS c2 FROM t1 WHERE c13 < CAST('3' AS LONG)");
+        builder.addView("v2", "SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11 = t2.c21");
+        Schemata schemata = builder.build();
+        context = new QueryContext(execContext, new PlanHints(), schemata);
+
         node = new PlanNode(Type.ACCESS);
 
         ruleExecutionOrder = new ArrayList<Integer>();
@@ -85,6 +108,10 @@
         };
     }
 
+    // ----------------------------------------------------------------------------------------------------------------
+    // Test the rule stack logic
+    // ----------------------------------------------------------------------------------------------------------------
+
     @Test
     public void shouldExecuteEachRuleInSequence() {
         optimizer.optimize(context, node);
@@ -111,4 +138,184 @@
         assertThat(ruleExecutionOrder.get(2), is(2));
         assertThat(ruleExecutionOrder.size(), is(3));
     }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Test the actual rules
+    // ----------------------------------------------------------------------------------------------------------------
+
+    @Test
+    public void shouldOptimizePlanForSimpleQueryWithSelectColumns() {
+        node = optimize("SELECT c11,c12 FROM t1");
+        // Create the expected plan ...
+        PlanNode expected = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, expected, selector("t1"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11"), column("t1", "c12")));
+        PlanNode source = new PlanNode(Type.SOURCE, project, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("t1")).getColumns());
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(expected), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForSimpleQueryWithSelectStar() {
+        node = optimize("SELECT * FROM t1");
+        // Create the expected plan ...
+        PlanNode expected = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, expected, selector("t1"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11"), column("t1", "c12"), column("t1", "c13")));
+        PlanNode source = new PlanNode(Type.SOURCE, project, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("t1")).getColumns());
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(expected), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForSimpleQueryWithPropertyValueCriteria() {
+        node = optimize("SELECT c11, c12 FROM t1 WHERE c13 < CAST('3' AS LONG)");
+        // Create the expected plan ...
+        PlanNode expected = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, expected, selector("t1"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11"), column("t1", "c12")));
+        PlanNode select = new PlanNode(Type.SELECT, project, selector("t1"));
+        select.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c13")),
+                                                                    Operator.LESS_THAN, new Literal(3L)));
+        PlanNode source = new PlanNode(Type.SOURCE, select, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("t1")).getColumns());
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(expected), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForEquiJoinQuery() {
+        node = optimize("SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11 = t2.c21");
+
+        // Create the expected plan ...
+        PlanNode project = new PlanNode(Type.PROJECT, selector("t2"), selector("t1"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11"), column("t1", "c12"), column("t2", "c23")));
+        PlanNode join = new PlanNode(Type.JOIN, project, selector("t2"), selector("t1"));
+        join.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
+        join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("t1"), name("c11"), selector("t2"), name("c21")));
+
+        PlanNode leftAccess = new PlanNode(Type.ACCESS, join, selector("t1"));
+        PlanNode leftProject = new PlanNode(Type.PROJECT, leftAccess, selector("t1"));
+        leftProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11"), column("t1", "c12")));
+        PlanNode leftSource = new PlanNode(Type.SOURCE, leftProject, selector("t1"));
+        leftSource.setProperty(Property.SOURCE_NAME, selector("t1"));
+        leftSource.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("t1")).getColumns());
+
+        PlanNode rightAccess = new PlanNode(Type.ACCESS, join, selector("t2"));
+        PlanNode rightProject = new PlanNode(Type.PROJECT, rightAccess, selector("t2"));
+        rightProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t2", "c23"), column("t2", "c21")));
+        PlanNode rightSource = new PlanNode(Type.SOURCE, rightProject, selector("t2"));
+        rightSource.setProperty(Property.SOURCE_NAME, selector("t2"));
+        rightSource.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("t2")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(project), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForQueryUsingView() {
+        node = optimize("SELECT v1.c11 AS c1 FROM v1 WHERE v1.c11 = 'x' AND v1.c2 = 'y'");
+
+        // Create the expected plan ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("t1"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("t1"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11", "c1")));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("t1"));
+        select1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c11")),
+                                                                     Operator.EQUAL_TO, new Literal('x')));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("t1"));
+        select2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c12")),
+                                                                     Operator.EQUAL_TO, new Literal('y')));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("t1"));
+        select3.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c13")),
+                                                                     Operator.LESS_THAN, new Literal(3L)));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("t1"));
+        source.setProperty(Property.SOURCE_NAME, selector("t1"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("t1")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(access), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForQueryUsingViewContainingJoin() {
+        node = optimize("SELECT v2.c11 AS c1 FROM v2 WHERE v2.c11 = 'x' AND v2.c12 = 'y'");
+
+        // Create the expected plan ...
+        PlanNode project = new PlanNode(Type.PROJECT, selector("t1"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11", "c1")));
+        PlanNode join = new PlanNode(Type.JOIN, project, selector("t2"), selector("t1"));
+        join.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
+        join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("t1"), name("c11"), selector("t2"), name("c21")));
+
+        PlanNode leftAccess = new PlanNode(Type.ACCESS, join, selector("t1"));
+        PlanNode leftProject = new PlanNode(Type.PROJECT, leftAccess, selector("t1"));
+        leftProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t1", "c11")));
+        PlanNode leftSelect1 = new PlanNode(Type.SELECT, leftProject, selector("t1"));
+        leftSelect1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c11")),
+                                                                         Operator.EQUAL_TO, new Literal('x')));
+        PlanNode leftSelect2 = new PlanNode(Type.SELECT, leftSelect1, selector("t1"));
+        leftSelect2.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t1"), name("c12")),
+                                                                         Operator.EQUAL_TO, new Literal('y')));
+        PlanNode leftSource = new PlanNode(Type.SOURCE, leftSelect2, selector("t1"));
+        leftSource.setProperty(Property.SOURCE_NAME, selector("t1"));
+        leftSource.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("t1")).getColumns());
+
+        PlanNode rightAccess = new PlanNode(Type.ACCESS, join, selector("t2"));
+        PlanNode rightProject = new PlanNode(Type.PROJECT, rightAccess, selector("t2"));
+        rightProject.setProperty(Property.PROJECT_COLUMNS, columns(column("t2", "c21")));
+        PlanNode rightSelect1 = new PlanNode(Type.SELECT, rightProject, selector("t2"));
+        rightSelect1.setProperty(Property.SELECT_CRITERIA, new Comparison(new PropertyValue(selector("t2"), name("c21")),
+                                                                          Operator.EQUAL_TO, new Literal('x')));
+        PlanNode rightSource = new PlanNode(Type.SOURCE, rightSelect1, selector("t2"));
+        rightSource.setProperty(Property.SOURCE_NAME, selector("t2"));
+        rightSource.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("t2")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(project), is(true));
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Utility methods ...
+    // ----------------------------------------------------------------------------------------------------------------
+
+    protected List<Column> columns( Column... columns ) {
+        return Arrays.asList(columns);
+    }
+
+    protected Column column( String table,
+                             String columnName ) {
+        return new Column(new SelectorName(table), name(columnName), columnName);
+    }
+
+    protected Column column( String table,
+                             String columnName,
+                             String alias ) {
+        return new Column(new SelectorName(table), name(columnName), alias);
+    }
+
+    protected Name name( String name ) {
+        return context.getExecutionContext().getValueFactories().getNameFactory().create(name);
+    }
+
+    protected PlanNode optimize( String sql ) {
+        QueryCommand query = new SqlQueryParser().parseQuery(sql, context.getExecutionContext());
+        Problems problems = context.getProblems();
+        assertThat("Problems parsing query: " + sql + "\n" + problems, problems.hasErrors(), is(false));
+        PlanNode plan = new CanonicalPlanner().createPlan(context, query);
+        assertThat("Problems planning query: " + sql + "\n" + problems, problems.hasErrors(), is(false));
+        PlanNode optimized = new RuleBasedOptimizer().optimize(context, plan);
+        assertThat("Problems optimizing query: " + sql + "\n" + problems, problems.hasErrors(), is(false));
+        System.out.println();
+        System.out.println(sql);
+        System.out.println(optimized);
+        return optimized;
+    }
 }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -35,9 +35,6 @@
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
-import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 import org.jboss.dna.graph.query.validate.ImmutableSchemata;
@@ -68,7 +65,7 @@
         hints = new PlanHints();
         builder = new QueryBuilder(context);
         problems = new SimpleProblems();
-        schemataBuilder = ImmutableSchemata.createBuilder();
+        schemataBuilder = ImmutableSchemata.createBuilder(context);
     }
 
     protected SelectorName selector( String name ) {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java	2009-10-15 19:55:58 UTC (rev 1293)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -69,7 +69,7 @@
 
     protected Schemata schemataFor( Columns columns,
                                     PropertyType... types ) {
-        ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder();
+        ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder(executionContext);
         for (String selectorName : columns.getSelectorNames()) {
             final SelectorName selector = selector(selectorName);
             int i = 0;

Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java	                        (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java	2009-10-15 19:57:20 UTC (rev 1294)
@@ -0,0 +1,179 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.validate;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.validate.Schemata.Table;
+import org.jboss.dna.graph.query.validate.Schemata.View;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class ImmutableSchemataBuilderTest {
+
+    private ExecutionContext context;
+    private ImmutableSchemata.Builder builder;
+    private Schemata schemata;
+
+    @Before
+    public void beforeEach() {
+        context = new ExecutionContext();
+        builder = ImmutableSchemata.createBuilder(context);
+    }
+
+    @After
+    public void afterEach() {
+        context = null;
+        builder = null;
+        schemata = null;
+    }
+
+    @Test
+    public void shouldBuildSchemaForSingleTableWithDefaultTypeForColumns() {
+        builder.addTable("t1", "c1", "c2", "c3");
+        schemata = builder.build();
+        Table table = schemata.getTable(selector("t1"));
+        assertThat(table, is(notNullValue()));
+        assertThat(table.getColumn("c1").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c3").getPropertyType(), is(PropertyType.STRING));
+    }
+
+    @Test
+    public void shouldBuildSchemaForMultipleTablesWithDefaultTypeForColumns() {
+        builder.addTable("t1", "c11", "c12", "c13");
+        builder.addTable("t2", "c21", "c22", "c23");
+        schemata = builder.build();
+        Table table = schemata.getTable(selector("t1"));
+        assertThat(table, is(notNullValue()));
+        assertThat(table.getColumn("c11").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c13").getPropertyType(), is(PropertyType.STRING));
+        table = schemata.getTable(selector("t2"));
+        assertThat(table, is(notNullValue()));
+        assertThat(table.getColumn("c21").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c22").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+    }
+
+    @Test
+    public void shouldBuildSchemaContainingViewOfSingleTable() {
+        builder.addTable("t1", "c1", "c2", "c3");
+        builder.addView("t2", "SELECT * FROM t1 WHERE c1=3");
+        schemata = builder.build();
+        Table table = schemata.getTable(selector("t1"));
+        assertThat(table, is(notNullValue()));
+        assertThat(table.getColumn("c1").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c3").getPropertyType(), is(PropertyType.STRING));
+        table = schemata.getTable(selector("t2"));
+        assertThat(table, is(instanceOf(View.class)));
+        assertThat(table.getColumn("c1").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c3").getPropertyType(), is(PropertyType.STRING));
+    }
+
+    @Test
+    public void shouldBuildSchemaContainingViewUsingAliasesOfColumns() {
+        builder.addTable("t1", "c1", "c2", "c3");
+        builder.addView("t2", "SELECT c1 as v1, c2 FROM t1 WHERE c1=3");
+        schemata = builder.build();
+        Table table = schemata.getTable(selector("t1"));
+        assertThat(table, is(notNullValue()));
+        assertThat(table.getColumn("c1").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c3").getPropertyType(), is(PropertyType.STRING));
+        table = schemata.getTable(selector("t2"));
+        assertThat(table, is(instanceOf(View.class)));
+        assertThat(table.getColumn("v1").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c2").getPropertyType(), is(PropertyType.STRING));
+    }
+
+    @Test
+    public void shouldBuildSchemaContainingViewOfMultiTableJoin() {
+        builder.addTable("t1", "c11", "c12", "c13");
+        builder.addTable("t2", "c21", "c22", "c23");
+        builder.addView("v1", "SELECT t1.c11 as x1, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11=t2.c21 WHERE t1.c11=3");
+        schemata = builder.build();
+        Table table = schemata.getTable(selector("t1"));
+        assertThat(table, is(notNullValue()));
+        assertThat(table.getColumn("c11").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c13").getPropertyType(), is(PropertyType.STRING));
+        table = schemata.getTable(selector("t2"));
+        assertThat(table, is(notNullValue()));
+        assertThat(table.getColumn("c21").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c22").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+        schemata = builder.build();
+        table = schemata.getTable(selector("v1"));
+        assertThat(table, is(instanceOf(View.class)));
+        assertThat(table.getColumn("x1").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+    }
+
+    @Test
+    public void shouldBuildSchemaContainingViewUsingView() {
+        builder.addTable("t1", "c11", "c12", "c13");
+        builder.addTable("t2", "c21", "c22", "c23");
+        builder.addView("v2", "SELECT x1, c12 FROM v1 WHERE x1=3");
+        builder.addView("v1", "SELECT t1.c11 as x1, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11=t2.c21 WHERE t1.c11=3");
+        schemata = builder.build();
+        Table table = schemata.getTable(selector("t1"));
+        assertThat(table, is(notNullValue()));
+        assertThat(table.getColumn("c11").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c13").getPropertyType(), is(PropertyType.STRING));
+        table = schemata.getTable(selector("t2"));
+        assertThat(table, is(notNullValue()));
+        assertThat(table.getColumn("c21").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c22").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+        schemata = builder.build();
+        table = schemata.getTable(selector("v1"));
+        assertThat(table, is(instanceOf(View.class)));
+        assertThat(table.getColumn("x1").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c23").getPropertyType(), is(PropertyType.STRING));
+        table = schemata.getTable(selector("v2"));
+        assertThat(table, is(instanceOf(View.class)));
+        assertThat(table.getColumn("x1").getPropertyType(), is(PropertyType.STRING));
+        assertThat(table.getColumn("c12").getPropertyType(), is(PropertyType.STRING));
+    }
+
+    protected SelectorName selector( String name ) {
+        return new SelectorName(name);
+    }
+
+}


Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/validate/ImmutableSchemataBuilderTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF


From dna-commits at lists.jboss.org  Mon Oct 19 15:54:55 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 19 Oct 2009 15:54:55 -0400
Subject: [dna-commits] DNA SVN: r1295 - in trunk:
	dna-cnd/src/test/resources/cnd and 4 other directories.
Message-ID: <200910191954.n9JJstaU018998@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-10-19 15:54:55 -0400 (Mon, 19 Oct 2009)
New Revision: 1295

Added:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
Modified:
   trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java
   trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-170.cnd
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/dna_builtins.cnd
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/jsr_170_builtins.cnd
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
   trunk/web/dna-web-jcr-rest-war/src/test/java/org/jboss/dna/web/jcr/rest/JcrResourcesTest.java
Log:
DNA-457 Add JSR-170 Locking Optional Feature

Committed patch that provides a locking implementation based on tracking the UUIDs of locked nodes.  This implies that any lockable node must also be referenceable.  The patch allows all of the TCK locking tests to pass as well as a few L2 tests which assumed a locking implementation for the repository.  However, the locking approach does not yet provide any way of integrating the lock/unlock capabilities with the underlying repository.  I will leave the defect open until that need can be addressed as well.

Thanks much for the reviews and suggestions!



Modified: trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java
===================================================================
--- trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -306,7 +306,7 @@
         // mixin
         // - jcr:lockOwner (string) protected ignore
         // - jcr:lockIsDeep (boolean) protected ignore
-        assertNodeType("mix:lockable", NO_SUPERTYPES, NO_PRIMARY_NAME, NodeOptions.Mixin);
+        assertNodeType("mix:lockable", new String[] {"mix:referenceable"}, NO_PRIMARY_NAME, NodeOptions.Mixin);
         assertProperty("mix:lockable", "jcr:lockOwner", "String", NO_DEFAULTS, OnParentVersion.Ignore, PropertyOptions.Protected);
         assertProperty("mix:lockable",
                        "jcr:lockIsDeep",

Modified: trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-170.cnd
===================================================================
--- trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-170.cnd	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-cnd/src/test/resources/cnd/jcr-builtins-170.cnd	2009-10-19 19:54:55 UTC (rev 1295)
@@ -37,7 +37,7 @@
   mixin
   - jcr:uuid (string) mandatory autocreated protected initialize
 
-[mix:lockable]
+[mix:lockable] > mix:referenceable
   mixin
   - jcr:lockOwner (string) protected ignore
   - jcr:lockIsDeep (boolean) protected ignore

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -824,8 +824,8 @@
         JcrNodeType mixinCandidateType = cache.nodeTypes().getNodeType(mixinName);
 
         // Check this separately since it throws a different type of exception
-        if (this.isLocked()) {
-            throw new LockException();
+        if (this.isLocked() && !holdsLock()) {
+            throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
         }
 
         if (!canAddMixin(mixinName)) {
@@ -853,11 +853,10 @@
      */
     public final void removeMixin( String mixinName ) throws RepositoryException {
 
-        if (this.isLocked()) {
-            throw new LockException();
+        if (this.isLocked() && !holdsLock()) {
+            throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
         }
 
-        // TODO: Check access control when that support is added
         // TODO: Throw VersionException if this node is versionable and checked in or unversionable and the nearest versionable
         // ancestor is checked in
 
@@ -1007,6 +1006,12 @@
                                    UUID desiredUuid )
         throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException,
         RepositoryException {
+
+
+        if (isLocked() && !holdsLock()) {
+            throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
+        }
+        
         // Determine the path ...
         NodeEditor editor = null;
         Path path = null;
@@ -1331,8 +1336,10 @@
      * @return <code>false</code>
      * @see javax.jcr.Node#holdsLock()
      */
-    public final boolean holdsLock() {
-        return false;
+    public final boolean holdsLock() throws RepositoryException {
+        WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lockFor(this.location);
+
+        return lock != null && cache.session().lockTokens().contains(lock.getLockToken());
     }
 
     /**
@@ -1341,41 +1348,99 @@
      * @return <code>false</code>
      * @see javax.jcr.Node#isLocked()
      */
-    public final boolean isLocked() {
-        return false;
+    public final boolean isLocked() throws LockException, RepositoryException {
+        return lock() != null;
     }
-
+    
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedRepositoryOperationException always
      * @see javax.jcr.Node#lock(boolean, boolean)
      */
     public final Lock lock( boolean isDeep,
-                            boolean isSessionScoped ) throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
+                            boolean isSessionScoped ) throws LockException, RepositoryException {
+        if (isLocked()) {
+            throw new LockException(JcrI18n.alreadyLocked.text(this.location));
+        }
+
+        if (isDeep) {
+            LinkedList<Node<JcrNodePayload, JcrPropertyPayload>> nodesToVisit = new LinkedList<Node<JcrNodePayload, JcrPropertyPayload>>();
+            nodesToVisit.add(nodeInfo());
+
+            while (!nodesToVisit.isEmpty()) {
+                Node<JcrNodePayload, JcrPropertyPayload> node = nodesToVisit.pop();
+                if (session().workspace().lockManager().lockFor(node.getLocation()) != null) throw new LockException(
+                                                                                                                     JcrI18n.parentAlreadyLocked.text(this.location,
+                                                                                                                                                      node.getLocation()));
+
+                for (Node<JcrNodePayload, JcrPropertyPayload> child : node.getChildren()) {
+                    nodesToVisit.add(child);
+                }
+            }
+        }
+
+        WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lock(cache,
+                                                                                     this.location,
+                                                                                     session().getUserID(),
+                                                                                     isDeep,
+                                                                                     isSessionScoped);
+
+        cache.session().addLockToken(lock.getLockToken());
+        return lock.lockFor(cache);
     }
 
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedRepositoryOperationException always
      * @see javax.jcr.Node#unlock()
      */
-    public final void unlock() throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
+    public final void unlock() throws LockException, RepositoryException {
+        WorkspaceLockManager.DnaLock lock = session().workspace().lockManager().lockFor(this.location);
+
+        if (lock == null) {
+            throw new LockException(JcrI18n.notLocked.text(this.location));
+        }
+
+        if (!cache.session().lockTokens().contains(lock.getLockToken())) {
+            throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
+        }
+
+        session().workspace().lockManager().unlock(lock);
+        session().removeLockToken(lock.getLockToken());
     }
 
+    private final WorkspaceLockManager.DnaLock lock() throws RepositoryException {
+        // This can only happen in mocked testing.
+        if (session() == null || session().workspace() == null) return null;
+
+        WorkspaceLockManager lockManager = session().workspace().lockManager();
+        WorkspaceLockManager.DnaLock lock = lockManager.lockFor(this.location);
+        if (lock != null) return lock;
+
+        AbstractJcrNode parent = this;
+        while (!parent.isRoot()) {
+            parent = parent.getParent();
+
+            WorkspaceLockManager.DnaLock parentLock = lockManager.lockFor(parent.location);
+            if (parentLock != null && parentLock.isLive()) {
+                return parentLock.isDeep() ? parentLock : null;
+            }
+        }
+        return null;
+    }
+
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedRepositoryOperationException always
      * @see javax.jcr.Node#getLock()
      */
-    public final Lock getLock() throws UnsupportedRepositoryOperationException {
-        throw new UnsupportedRepositoryOperationException();
+    public final Lock getLock() throws LockException, RepositoryException {
+        WorkspaceLockManager.DnaLock lock = lock();
+
+        if (lock == null) throw new LockException(JcrI18n.notLocked.text(this.location));
+        return lock.lockFor(cache);
     }
-
+    
     /**
      * {@inheritDoc}
      * 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -31,6 +31,7 @@
 import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
+import javax.jcr.lock.Lock;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.PropertyDefinition;
@@ -69,6 +70,27 @@
     abstract boolean isMultiple();
 
     /**
+     * Checks that this property's parent node is not already locked by another session. If the parent node is not locked or the
+     * parent node is locked but the lock is owned by this {@code Session}, this method completes silently. If the parent node is
+     * locked (either directly or as part of a deep lock from an ancestor), this method throws a {@code LockException}.
+     * 
+     * @throws LockException if the parent node of this property is locked (that is, if {@code getParent().isLocked() == true &&
+     *         getParent().getLock().getLockToken() == null}.
+     * @throws RepositoryException if any other error occurs
+     * @see Node#isLocked()
+     * @see Lock#getLockToken()
+     */
+    protected final void checkForLock() throws LockException, RepositoryException {
+
+        if (this.getParent().isLocked()) {
+            Lock parentLock = this.getParent().getLock();
+            if (parentLock != null && parentLock.getLockToken() == null) {
+                throw new LockException(JcrI18n.lockTokenNotHeld.text(this.getParent().location));
+            }
+        }
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @throws IllegalArgumentException if <code>visitor</code> is <code>null</code>.
@@ -145,7 +167,7 @@
      * 
      * @see javax.jcr.Item#getParent()
      */
-    public final Node getParent() {
+    public final AbstractJcrNode getParent() {
         return node;
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -34,9 +34,15 @@
 public class DnaLexicon extends org.jboss.dna.repository.DnaLexicon {
 
     public static final Name BASE = new BasicName(Namespace.URI, "base");
+    public static final Name IS_HELD_BY_SESSION = new BasicName(Namespace.URI, "isHeldBySession");
+    public static final Name IS_SESSION_SCOPED = new BasicName(Namespace.URI, "isSessionScoped");    
+    public static final Name LOCK = new BasicName(Namespace.URI, "lock");    
+    public static final Name LOCKED_NODE = new BasicName(Namespace.URI, "lockedNode");    
+    public static final Name LOCKS = new BasicName(Namespace.URI, "locks");    
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");
     public static final Name NODE_TYPES = new BasicName(Namespace.URI, "nodeTypes");
     public static final Name REPOSITORIES = new BasicName(Namespace.URI, "repositories");
     public static final Name SYSTEM = new BasicName(Namespace.URI, "system");
     public static final Name URI = new BasicName(Namespace.URI, "uri");
+    public static final Name WORKSPACE = new BasicName(Namespace.URI, "workspace");    
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -166,6 +166,15 @@
     public static I18n cannotAddMixin;
     public static I18n invalidMixinTypeForNode;
     public static I18n notOrderable;
+    
+    // Lock messages
+    public static I18n cannotRemoveLockToken;
+    public static I18n alreadyLocked;
+    public static I18n parentAlreadyLocked;
+    public static I18n notLocked;
+    public static I18n lockTokenNotHeld;
+    public static I18n lockTokenAlreadyHeld;
+    public static I18n uuidRequiredForLock;
 
     static {
         try {

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -187,7 +187,8 @@
      */
     public final void setValue( Value[] values )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
-
+        checkForLock();
+        
         if (values == null) {
             this.remove();
             return;
@@ -208,6 +209,8 @@
      */
     public final void setValue( String[] values )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+        checkForLock();
+        
         if (values == null) {
             this.remove();
             return;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -26,6 +26,8 @@
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.session.GraphSession.NodeId;
@@ -96,7 +98,15 @@
      * 
      * @see javax.jcr.Item#remove()
      */
-    public void remove() throws RepositoryException {
+    public void remove() throws RepositoryException, LockException {
+        Node parentNode = getParent();
+        if (parentNode.isLocked()) {
+            Lock parentLock = parentNode.getLock();
+            if (parentLock != null && parentLock.getLockToken() == null) {
+                throw new LockException();
+            }
+        }
+
         editor().destroy();
     }
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -35,6 +35,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import javax.jcr.Credentials;
 import javax.jcr.NoSuchWorkspaceException;
 import javax.jcr.Repository;
@@ -45,6 +47,7 @@
 import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
+import net.jcip.annotations.GuardedBy;
 import net.jcip.annotations.Immutable;
 import net.jcip.annotations.ThreadSafe;
 import org.jboss.dna.common.i18n.I18n;
@@ -204,6 +207,9 @@
     private final ExecutionContext executionContext;
     private final RepositoryConnectionFactory connectionFactory;
     private final RepositoryNodeTypeManager repositoryTypeManager;
+    @GuardedBy( "lockManagersLock" )
+    private final ConcurrentMap<String, WorkspaceLockManager> lockManagers;
+    private final Path locksPath;
     private final Map<Option, String> options;
     private final String systemSourceName;
     private final String systemWorkspaceName;
@@ -257,7 +263,7 @@
         // Initialize required JCR descriptors.
         modifiableDescriptors.put(Repository.LEVEL_1_SUPPORTED, "true");
         modifiableDescriptors.put(Repository.LEVEL_2_SUPPORTED, "true");
-        modifiableDescriptors.put(Repository.OPTION_LOCKING_SUPPORTED, "false");
+        modifiableDescriptors.put(Repository.OPTION_LOCKING_SUPPORTED, "true");
         modifiableDescriptors.put(Repository.OPTION_OBSERVATION_SUPPORTED, "false");
         modifiableDescriptors.put(Repository.OPTION_QUERY_SQL_SUPPORTED, "false");
         modifiableDescriptors.put(Repository.OPTION_TRANSACTIONS_SUPPORTED, "false");
@@ -402,6 +408,9 @@
         this.federatedSource = new FederatedRepositorySource();
         this.federatedSource.setName("JCR " + repositorySourceName);
         this.federatedSource.initialize(new FederatedRepositoryContext(this.connectionFactory));
+
+        this.lockManagers = new ConcurrentHashMap<String, WorkspaceLockManager>();
+        this.locksPath = pathFactory.create(pathFactory.createRootPath(), JcrLexicon.SYSTEM, DnaLexicon.LOCKS);    
     }
 
     protected void initializeSystemContent( Graph systemGraph ) {
@@ -662,6 +671,24 @@
     }
 
     /**
+     * Returns the lock manager for the named workspace (if one already exists) or creates a new lock manager and returns it. This
+     * method is thread-safe.
+     * 
+     * @param workspaceName the name of the workspace for which the lock manager should be returned
+     * @return the lock manager for the workspace; never null
+     */
+    WorkspaceLockManager getLockManager( String workspaceName ) {
+        WorkspaceLockManager lockManager = lockManagers.get(workspaceName);
+        if (lockManager != null) return lockManager;
+
+        lockManager = new WorkspaceLockManager(executionContext, this, workspaceName, locksPath);
+        WorkspaceLockManager newLockManager = lockManagers.putIfAbsent(workspaceName, lockManager);
+
+        if (newLockManager != null) return newLockManager;
+        return lockManager;
+    }
+
+    /**
      * Returns the name of this repository
      * 
      * @return the name of this repository

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -28,6 +28,8 @@
 import java.io.OutputStream;
 import java.security.AccessControlException;
 import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
@@ -48,6 +50,7 @@
 import javax.jcr.ValueFactory;
 import javax.jcr.ValueFormatException;
 import javax.jcr.Workspace;
+import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import net.jcip.annotations.Immutable;
 import net.jcip.annotations.NotThreadSafe;
@@ -68,6 +71,7 @@
 import org.jboss.dna.jcr.JcrContentHandler.SaveMode;
 import org.jboss.dna.jcr.JcrNamespaceRegistry.Behavior;
 import org.jboss.dna.jcr.SessionCache.JcrPropertyPayload;
+import org.jboss.dna.jcr.WorkspaceLockManager.DnaLock;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
@@ -128,6 +132,8 @@
 
     private final SessionCache cache;
 
+    private final Set<String> lockTokens;
+
     /**
      * A cached instance of the root path.
      */
@@ -159,6 +165,7 @@
 
         this.cache = new SessionCache(this);
         this.isLive = true;
+        this.lockTokens = new HashSet<String>();
 
         assert this.sessionAttributes != null;
         assert this.workspace != null;
@@ -194,6 +201,10 @@
         return this.repository;
     }
 
+    final Collection<String> lockTokens() {
+        return lockTokens;
+    }
+    
     Graph.Batch createBatch() {
         return graph.batch();
     }
@@ -286,11 +297,22 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#addLockToken(java.lang.String)
      */
-    public void addLockToken( String lt ) {
-        throw new UnsupportedOperationException();
+    public void addLockToken( String lt ) throws LockException {
+        CheckArg.isNotNull(lt, "lock token");
+
+        // Trivial case of giving a token back to ourself
+        if (lockTokens.contains(lt)) {
+            return;
+        }
+        
+        if (workspace().lockManager().isHeldBySession(lt)) {
+            throw new LockException(JcrI18n.lockTokenAlreadyHeld.text(lt));
+        }
+        
+        workspace().lockManager().setHeldBySession(lt, true);
+        lockTokens.add(lt);
     }
 
     /**
@@ -493,11 +515,10 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#getLockTokens()
      */
     public String[] getLockTokens() {
-        throw new UnsupportedOperationException();
+        return lockTokens.toArray(new String[lockTokens.size()]);
     }
 
     /**
@@ -751,6 +772,7 @@
             return;
         }
 
+        this.workspace().lockManager().cleanLocks(lockTokens);
         this.executionContext.getSecurityContext().logout();
         isLive = false;
     }
@@ -782,6 +804,20 @@
             throw new ItemExistsException(JcrI18n.childNodeAlreadyExists.text(newNodeNameAsString, newParentNode.getPath()));
         }
 
+        if (sourceNode.isLocked()) {
+            javax.jcr.lock.Lock sourceLock = sourceNode.getLock();
+            if (sourceLock != null && sourceLock.getLockToken() == null) {
+                throw new LockException(JcrI18n.lockTokenNotHeld.text(srcAbsPath));
+            }
+        }
+
+        if (newParentNode.isLocked()) {
+            javax.jcr.lock.Lock newParentLock = newParentNode.getLock();
+            if (newParentLock != null && newParentLock.getLockToken() == null) {
+                throw new LockException(JcrI18n.lockTokenNotHeld.text(destAbsPath));
+            }
+        }
+
         newParentNode.editor().moveToBeChild(sourceNode, newNodeName.getName());
     }
 
@@ -797,11 +833,29 @@
     /**
      * {@inheritDoc}
      * 
-     * @throws UnsupportedOperationException always
      * @see javax.jcr.Session#removeLockToken(java.lang.String)
      */
     public void removeLockToken( String lt ) {
-        throw new UnsupportedOperationException();
+        CheckArg.isNotNull(lt, "lock token");
+        // A LockException is thrown if the lock associated with the specified lock token is session-scoped.
+        /*
+         * The JCR API library that we're using diverges from the spec in that it doesn't declare
+         * this method to throw a LockException.  We'll throw a runtime exception for now.
+         */
+
+        DnaLock lock = workspace().lockManager().lockFor(lt);
+        if (lock == null) {
+            // The lock is no longer valid
+            lockTokens.remove(lt);
+            return;
+        }
+        
+        if (lock.isSessionScoped()) {
+            throw new IllegalStateException(JcrI18n.cannotRemoveLockToken.text(lt));
+        }
+
+        workspace().lockManager().setHeldBySession(lt, false);
+        lockTokens.remove(lt);
     }
 
     /**

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -197,6 +197,8 @@
     public void setValue( Value value )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         JcrValue jcrValue = null;
+        checkForLock();
+        
         if (value instanceof JcrValue) {
             jcrValue = (JcrValue)value;
 
@@ -249,6 +251,9 @@
     protected void setValue( JcrValue jcrValue )
         throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         assert jcrValue != null;
+        
+        checkForLock();
+        
         editor().setProperty(name(), jcrValue);
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -41,6 +41,7 @@
 import javax.jcr.Session;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Workspace;
+import javax.jcr.lock.Lock;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NodeTypeManager;
@@ -62,6 +63,7 @@
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.property.ValueFormatException;
 import org.jboss.dna.graph.request.InvalidWorkspaceException;
 import org.jboss.dna.graph.request.ReadBranchRequest;
@@ -71,6 +73,7 @@
 import org.jboss.dna.jcr.JcrContentHandler.SaveMode;
 import org.jboss.dna.jcr.SessionCache.JcrNodePayload;
 import org.jboss.dna.jcr.SessionCache.JcrPropertyPayload;
+import org.jboss.dna.jcr.WorkspaceLockManager.DnaLock;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
@@ -128,6 +131,8 @@
      */
     private final JcrQueryManager queryManager;
 
+    private final WorkspaceLockManager lockManager;
+    
     /**
      * The {@link Session} instance that this corresponds with this workspace.
      */
@@ -144,6 +149,7 @@
         assert repository != null;
         this.name = workspaceName;
         this.repository = repository;
+        this.lockManager = repository.getLockManager(workspaceName);
 
         // // Set up the execution context for this workspace, which should use the namespace registry that persists
         // // the namespaces in the graph ...
@@ -204,6 +210,10 @@
         return this.context;
     }
 
+    final WorkspaceLockManager lockManager() {
+        return this.lockManager;
+    }
+    
     /**
      * {@inheritDoc}
      */
@@ -315,6 +325,32 @@
             // This also performs the check permission for reading the parent ...
             Name newNodeName = destPath.getLastSegment().getName();
             SessionCache cache = this.session.cache();
+            
+
+            /*
+             * Find the UUID for the source node.  Have to go directly against the graph.
+             */
+            org.jboss.dna.graph.Node sourceNode = repository.createWorkspaceGraph(srcWorkspace).getNodeAt(srcPath);
+            Property uuidProp = sourceNode.getProperty(DnaLexicon.UUID);
+
+            if (uuidProp != null) {
+                UUID sourceUuid = this.context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue());
+
+                DnaLock sourceLock = lockManager().lockFor(Location.create(sourceUuid));
+                if (sourceLock != null && sourceLock.getLockToken() == null) {
+                    throw new LockException(JcrI18n.lockTokenNotHeld.text(srcAbsPath));
+                }
+            }
+
+            AbstractJcrNode parentNode = cache.findJcrNode(Location.create(destPath.getParent()));
+
+            if (parentNode.isLocked()) {
+                Lock newParentLock = parentNode.getLock();
+                if (newParentLock != null && newParentLock.getLockToken() == null) {
+                    throw new LockException(destAbsPath);
+                }
+            }
+            
             Node<JcrNodePayload, JcrPropertyPayload> parent = cache.findNode(null, destPath.getParent());
             cache.findBestNodeDefinition(parent, newNodeName, parent.getPayload().getPrimaryTypeName());
 
@@ -445,6 +481,32 @@
             // This also performs the check permission for reading the parent ...
             Name newNodeName = destPath.getLastSegment().getName();
             SessionCache cache = this.session.cache();
+            
+
+            /*
+             * Find the UUID for the source node.  Have to go directly against the graph.
+             */
+            org.jboss.dna.graph.Node sourceNode = repository.createWorkspaceGraph(srcWorkspace).getNodeAt(srcPath);
+            Property uuidProp = sourceNode.getProperty(DnaLexicon.UUID);
+
+            if (uuidProp != null) {
+                UUID sourceUuid = this.context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue());
+
+                DnaLock sourceLock = lockManager().lockFor(Location.create(sourceUuid));
+                if (sourceLock != null && sourceLock.getLockToken() == null) {
+                    throw new LockException(srcAbsPath);
+                }
+            }
+
+            AbstractJcrNode parentNode = cache.findJcrNode(Location.create(destPath.getParent()));
+
+            if (parentNode.isLocked()) {
+                Lock newParentLock = parentNode.getLock();
+                if (newParentLock != null && newParentLock.getLockToken() == null) {
+                    throw new LockException(destAbsPath);
+                }
+            }
+            
             Node<JcrNodePayload, JcrPropertyPayload> parent = cache.findNode(null, destPath.getParent());
             cache.findBestNodeDefinition(parent, newNodeName, parent.getPayload().getPrimaryTypeName());
 
@@ -555,10 +617,32 @@
             Node<JcrNodePayload, JcrPropertyPayload> newParent = cache.findNode(null, destPath.getParent());
             cache.findBestNodeDefinition(newParent, newNodeName, newParent.getPayload().getPrimaryTypeName());
 
+            AbstractJcrNode sourceNode = cache.findJcrNode(Location.create(srcPath));
+
+            if (sourceNode.isLocked()) {
+                Lock sourceLock = sourceNode.getLock();
+                if (sourceLock != null && sourceLock.getLockToken() == null) {
+                    throw new LockException(srcAbsPath);
+                }
+            }
+
+            AbstractJcrNode parentNode = cache.findJcrNode(Location.create(destPath.getParent()));
+
+            if (parentNode.isLocked()) {
+                Lock newParentLock = parentNode.getLock();
+                if (newParentLock != null && newParentLock.getLockToken() == null) {
+                    throw new LockException(destAbsPath);
+                }
+            }            
+
             // Now perform the clone, using the direct (non-session) method ...
             cache.graphSession().immediateMove(srcPath, destPath);
         } catch (AccessControlException ace) {
             throw new AccessDeniedException(ace);
+        } catch (ItemNotFoundException infe) {
+            throw new PathNotFoundException(infe);
+        } catch (org.jboss.dna.graph.property.PathNotFoundException pnfe) {
+            throw new PathNotFoundException(pnfe);
         }
 
         // /*

Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	                        (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -0,0 +1,314 @@
+package org.jboss.dna.jcr;
+
+import java.util.Collection;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.jcr.SessionCache.NodeEditor;
+
+/**
+ * Manages the locks for a particular workspace in a repository. Locks are stored in a {@code Map<UUID, DnaLock>} while they exist
+ * and are discarded after they are discarded (through the {@link Node#unlock()} method).
+ */
+ at ThreadSafe
+class WorkspaceLockManager {
+
+    private final ExecutionContext context;
+    private final Path locksPath;
+    private final JcrRepository repository;
+    private final String workspaceName;
+    private final ConcurrentMap<UUID, DnaLock> workspaceLocksByNodeUuid;
+
+    WorkspaceLockManager( ExecutionContext context,
+                          JcrRepository repository,
+                          String workspaceName,
+                          Path locksPath ) {
+        this.context = context;
+        this.repository = repository;
+        this.workspaceName = workspaceName;
+        this.locksPath = locksPath;
+
+        this.workspaceLocksByNodeUuid = new ConcurrentHashMap<UUID, DnaLock>();
+
+        Property locksPrimaryType = context.getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.LOCKS);
+        repository.createSystemGraph().create(locksPath, locksPrimaryType).ifAbsent().and();
+    }
+
+    /**
+     * Creates a lock on the node with the given {@link Location}. This method creates a new lock, registers it in the list of
+     * locks, immediately modifies the {@code jcr:lockOwner} and {@code jcr:lockIsDeep} properties on the node in the underlying
+     * repository, and adds the lock to the system view.
+     * <p>
+     * The location given in {@code nodeLocation} must have a UUID.
+     * </p>
+     * 
+     * @param cache the session cache from which the node was loaded
+     * @param nodeLocation the location for the node; may not be null and must have a UUID
+     * @param lockOwner the owner of the new lock
+     * @param isDeep whether the node's descendants in the content graph should also be locked
+     * @param isSessionScoped whether the lock should outlive the session in which it was created
+     * @return an object representing the newly created lock
+     * @throws RepositoryException if an error occurs updating the graph state
+     */
+    DnaLock lock( SessionCache cache,
+                  Location nodeLocation,
+                  String lockOwner,
+                  boolean isDeep,
+                  boolean isSessionScoped ) throws RepositoryException {
+        assert nodeLocation != null;
+        
+        UUID lockUuid = UUID.randomUUID();
+        UUID nodeUuid = uuidFor(nodeLocation);
+
+        if (nodeUuid == null) {
+            throw new RepositoryException(JcrI18n.uuidRequiredForLock.text(nodeLocation));
+        }
+        
+        DnaLock lock = new DnaLock(lockOwner, lockUuid, nodeUuid, isDeep, isSessionScoped);
+
+        Graph.Batch batch = repository.createSystemGraph().batch();
+
+        PropertyFactory propFactory = context.getPropertyFactory();
+        PathFactory pathFactory = context.getValueFactories().getPathFactory();
+        Property lockOwnerProp = propFactory.create(JcrLexicon.LOCK_OWNER, lockOwner);
+        Property lockIsDeepProp = propFactory.create(JcrLexicon.LOCK_IS_DEEP, isDeep);
+
+        batch.create(pathFactory.create(locksPath, pathFactory.createSegment(lockUuid.toString())),
+                     propFactory.create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.LOCK),
+                     propFactory.create(JcrLexicon.UUID, nodeUuid.toString()),
+                     propFactory.create(DnaLexicon.WORKSPACE, workspaceName),
+                     propFactory.create(DnaLexicon.LOCKED_NODE, nodeUuid.toString()),
+                     propFactory.create(DnaLexicon.IS_SESSION_SCOPED, isSessionScoped),
+                  // This gets set after the lock succeeds and the lock token gets added to the session
+                     propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, false),  
+                     lockOwnerProp,
+                     lockIsDeepProp).ifAbsent().and();
+        batch.execute();
+
+        AbstractJcrNode lockedNode = cache.findJcrNode(Location.create(nodeUuid));
+        NodeEditor editor = cache.getEditorFor(lockedNode.nodeInfo());
+
+        // Set the properties in the cache...
+        editor.setProperty(JcrLexicon.LOCK_OWNER,
+                           (JcrValue)cache.session().getValueFactory().createValue(lockOwner, PropertyType.STRING),
+                           false);
+        editor.setProperty(JcrLexicon.LOCK_IS_DEEP, (JcrValue)cache.session().getValueFactory().createValue(isDeep), false);
+
+        // Write them directly to the underlying graph
+        repository.createWorkspaceGraph(workspaceName).set(lockOwnerProp, lockIsDeepProp).on(nodeUuid);
+
+        workspaceLocksByNodeUuid.put(nodeUuid, lock);
+
+        return lock;
+    }
+
+    /**
+     * Removes the provided lock, effectively unlocking the node to which the lock is associated.
+     * 
+     * @param lock the lock to be removed
+     */
+    void unlock( DnaLock lock ) {
+        try {
+            PathFactory pathFactory = context.getValueFactories().getPathFactory();
+
+            Graph.Batch batch = repository.createSystemGraph().batch();
+
+            batch.delete(pathFactory.create(locksPath, pathFactory.createSegment(lock.lockUuid.toString())));
+            batch.remove(JcrLexicon.LOCK_OWNER, JcrLexicon.LOCK_IS_DEEP).on(lock.nodeUuid);
+            batch.execute();
+            workspaceLocksByNodeUuid.remove(lock.nodeUuid);
+        } catch (PathNotFoundException pnfe) {
+            /*
+             * This can legitimately happen if there is a session-scoped lock on a node which is then deleted by the lock owner and the lock
+             * owner logs out.  At that point, the lock owner still holds the token for the lock, so it will get cleaned up with a call to cleanLocks.
+             */
+            if (!lock.nodeUuid.equals(pnfe.getLocation().getUuid())) {
+                // If the lock node under dna:locks is not found, this is an internal error
+                throw new IllegalStateException(pnfe);
+            }
+            workspaceLocksByNodeUuid.remove(lock.nodeUuid);
+
+        }
+    }
+
+    boolean isHeldBySession( String lockToken ) {
+        ValueFactory<Boolean> booleanFactory = context.getValueFactories().getBooleanFactory();
+        PathFactory pathFactory = context.getValueFactories().getPathFactory();
+
+        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph().getNodeAt(pathFactory.create(locksPath,
+                                                                                        pathFactory.createSegment(lockToken)));
+
+        return booleanFactory.create(lockNode.getProperty(DnaLexicon.IS_HELD_BY_SESSION).getFirstValue());
+        
+    }
+    
+    void setHeldBySession( String lockToken,
+                           boolean value ) {
+        PropertyFactory propFactory = context.getPropertyFactory();
+        PathFactory pathFactory = context.getValueFactories().getPathFactory();
+
+        repository.createSystemGraph().set(propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, value)).on(pathFactory.create(locksPath,
+                                                                                                                           pathFactory.createSegment(lockToken)));
+    }
+    
+    /**
+     * Returns the lock that corresponds to the given lock token
+     * 
+     * @param lockToken the lock token
+     * @return the corresponding lock, possibly null
+     * @see Session#addLockToken(String)
+     * @see Session#removeLockToken(String)
+     */
+    DnaLock lockFor( String lockToken ) {
+        for (DnaLock lock : workspaceLocksByNodeUuid.values()) {
+            if (lockToken.equals(lock.getLockToken())) {
+                return lock;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the lock that corresponds to the given UUID
+     * 
+     * @param nodeUuid the node UUID
+     * @return the corresponding lock, possibly null
+     */
+    DnaLock lockFor( Location nodeLocation ) throws RepositoryException {
+        UUID nodeUuid = uuidFor(nodeLocation);
+        if (nodeUuid == null) return null;
+        return workspaceLocksByNodeUuid.get(nodeUuid);
+    }
+    
+
+    UUID uuidFor( Location location ) throws RepositoryException {
+        if (location.getUuid() != null) return location.getUuid();
+
+        org.jboss.dna.graph.property.Property uuidProp = location.getIdProperty(JcrLexicon.UUID);
+        if (uuidProp == null) return null;
+
+        return context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue());
+    }
+
+    /**
+     * Unlocks all locks corresponding to the tokens in the {@code lockTokens} collection that are session scoped.
+     * 
+     * @param lockTokens the collection of lock tokens
+     */
+    void cleanLocks( Collection<String> lockTokens ) {
+        for (String lockToken : lockTokens) {
+            DnaLock lock = lockFor(lockToken);
+            if (lock != null && lock.isSessionScoped()) {
+                unlock(lock);
+            }
+        }
+    }
+
+    /**
+     * Internal representation of a locked node. This class should only be created through calls to
+     * {@link WorkspaceLockManager#lock(SessionCache, Location, String, boolean, boolean)}.
+     */
+    @ThreadSafe
+    public class DnaLock {
+        final UUID nodeUuid;
+        private final UUID lockUuid;
+        private final String lockOwner;
+        private final boolean deep;
+        private final boolean sessionScoped;
+
+        DnaLock( String lockOwner,
+                 UUID lockUuid,
+                 UUID nodeUuid,
+                 boolean deep,
+                 boolean sessionScoped ) {
+            super();
+            this.lockOwner = lockOwner;
+            this.lockUuid = lockUuid;
+            this.nodeUuid = nodeUuid;
+            this.deep = deep;
+            this.sessionScoped = sessionScoped;
+        }
+
+        public boolean isLive() {
+            return workspaceLocksByNodeUuid.containsKey(nodeUuid);
+        }
+
+        public boolean isDeep() {
+            return deep;
+        }
+
+        public String getLockOwner() {
+            return lockOwner;
+        }
+
+        public boolean isSessionScoped() {
+            return sessionScoped;
+        }
+
+        public String getLockToken() {
+            return lockUuid.toString();
+        }
+
+        public Lock lockFor( SessionCache cache ) throws RepositoryException {
+            final AbstractJcrNode node = cache.findJcrNode(Location.create(nodeUuid));
+            final JcrSession session = cache.session();
+            return new Lock() {
+                @Override
+                public String getLockOwner() {
+                    return lockOwner;
+                }
+
+                @Override
+                public String getLockToken() {
+                    String uuidString = lockUuid.toString();
+                    return session.lockTokens().contains(uuidString) ? uuidString : null;
+                }
+
+                @Override
+                public Node getNode() {
+                    return node;
+                }
+
+                @Override
+                public boolean isDeep() {
+                    return deep;
+                }
+
+                @Override
+                public boolean isLive() throws RepositoryException {
+                    return workspaceLocksByNodeUuid.containsKey(nodeUuid);
+                }
+
+                @Override
+                public boolean isSessionScoped() {
+                    return sessionScoped;
+                }
+
+                @Override
+                public void refresh() throws LockException, RepositoryException {
+                    if (getLockToken() == null) {
+                        throw new LockException(JcrI18n.notLocked.text(node.location));
+                    }
+                }
+            };
+        }
+
+    }
+}


Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-10-19 19:54:55 UTC (rev 1295)
@@ -150,3 +150,12 @@
 cannotAddMixin = This node does not allow adding the mixin type "{0}"
 invalidMixinTypeForNode = "{0}" is not currently a mixin type for node "{1}"
 notOrderable = The primary type "{0}" for this node (at "{1}") does not have orderable children
+
+# Lock messages
+cannotRemoveLockToken = The lock token '{0}' is a session-scoped lock
+alreadyLocked = The node at location '{0}' is already locked
+parentAlreadyLocked = The node at location '{0}' cannot be locked because the parent node at location '{1}' is already locked
+notLocked = The node at location '{0}' is not locked
+lockTokenNotHeld = The node at location '{0}' is locked and this session does not hold its lock token
+lockTokenAlreadyHeld = The lock token '{0}' is already held by another session.  It must be removed from that session before it can be added to another session.
+uuidRequiredForLock = Only referenceable nodes can be locked.  The node at location '(0}' is not referenceable.

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/dna_builtins.cnd
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/dna_builtins.cnd	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/dna_builtins.cnd	2009-10-19 19:54:55 UTC (rev 1295)
@@ -43,8 +43,21 @@
 [dna:nodeTypes] > nt:base
 + * (nt:nodeType) = nt:nodeType protected version
 
+[dna:lock] > nt:base
+- dna:lockedNode (string) protected ignore
+- jcr:lockOwner (string) protected ignore
+- jcr:uuid (string) protected ignore
+- dna:sessionScope (boolean) protected ignore
+- jcr:isDeep (boolean) protected ignore
+- dna:isHeldBySession (boolean) protected ignore
+- dna:workspace (string) protected ignore
+
+[dna:locks] > nt:base
++ * (dna:lock) = dna:lock protected ignore
+
 [dna:system] > nt:base
 + dna:namespaces (dna:namespaces) = dna:namespaces autocreated mandatory protected version
++ dna:locks (dna:locks) = dna:locks autocreated mandatory protected ignore
 + jcr:nodeTypes (dna:nodeTypes) = dna:nodeTypes autocreated mandatory protected version
 
 [dna:root] > nt:base, mix:referenceable orderable

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/jsr_170_builtins.cnd
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/jsr_170_builtins.cnd	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/jsr_170_builtins.cnd	2009-10-19 19:54:55 UTC (rev 1295)
@@ -98,6 +98,6 @@
 - jcr:statement (string)
 - jcr:language (string) 
 
-[mix:lockable] mixin 
+[mix:lockable] > mix:referenceable mixin 
 - jcr:lockOwner (string) protected ignore 
 - jcr:lockIsDeep (boolean) protected ignore

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -39,6 +39,7 @@
 import javax.jcr.Repository;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Workspace;
+import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.version.Version;
 import org.jboss.dna.graph.Graph;
@@ -402,17 +403,12 @@
         hybrid.getBaseVersion();
     }
 
-    @Test( expected = UnsupportedRepositoryOperationException.class )
-    public void shouldNotAllowGetLock() throws Exception {
+    @Test( expected = LockException.class )
+    public void shouldNotAllowGetLockIfNoLock() throws Exception {
         hybrid.getLock();
     }
 
     @Test
-    public void shouldNotAllowHoldsLock() throws Exception {
-        assertThat(hybrid.holdsLock(), is(false));
-    }
-
-    @Test
     public void shouldNotAllowIsCheckedOut() throws Exception {
         assertThat(hybrid.isCheckedOut(), is(false));
     }
@@ -422,10 +418,11 @@
         assertThat(hybrid.isLocked(), is(false));
     }
 
-    @Test( expected = UnsupportedRepositoryOperationException.class )
-    public void shouldNotAllowLock() throws Exception {
-        hybrid.lock(false, false);
-    }
+    // Now tested in TCK
+    // @Test( expected = UnsupportedRepositoryOperationException.class )
+    // public void shouldAllowLock() throws Exception {
+    // hybrid.lock(false, false);
+    // }
 
     @Test( expected = UnsupportedOperationException.class )
     public void shouldNotAllowMerge() throws Exception {
@@ -457,10 +454,11 @@
         hybrid.restoreByLabel(null, false);
     }
 
-    @Test( expected = UnsupportedRepositoryOperationException.class )
-    public void shouldNotAllowUnlock() throws Exception {
-        hybrid.unlock();
-    }
+    // Now tested in TCK
+    // @Test( expected = UnsupportedRepositoryOperationException.class )
+    // public void shouldNotAllowUnlock() throws Exception {
+    // hybrid.unlock();
+    // }
 
     /*
      * Primary-type and -item methods

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractSessionTest.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -41,6 +41,7 @@
 import org.jboss.dna.graph.connector.RepositorySourceException;
 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
 import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.jcr.nodetype.NodeTypeTemplate;
 import org.mockito.MockitoAnnotations;
@@ -66,6 +67,7 @@
     protected Map<String, Object> sessionAttributes;
     protected Map<JcrRepository.Option, String> options;
     protected NamespaceRegistry registry;
+    protected WorkspaceLockManager workspaceLockManager;
     @Mock
     protected JcrRepository repository;
 
@@ -140,7 +142,21 @@
                 return graph;
             }
         });
+        stub(repository.createSystemGraph()).toAnswer(new Answer<Graph>() {
+            public Graph answer( InvocationOnMock invocation ) throws Throwable {
+                return graph;
+            }
+        });
 
+        Path locksPath = pathFactory.createAbsolutePath(JcrLexicon.SYSTEM, DnaLexicon.LOCKS);
+        workspaceLockManager = new WorkspaceLockManager(context, repository, workspaceName, locksPath);
+
+        stub(repository.getLockManager(anyString())).toAnswer(new Answer<WorkspaceLockManager>() {
+            public WorkspaceLockManager answer( InvocationOnMock invocation ) throws Throwable {
+                return workspaceLockManager;
+            }
+        });
+
         initializeOptions();
         stub(repository.getOptions()).toReturn(options);
 
@@ -148,7 +164,7 @@
         // Set up the session attributes ...
         sessionAttributes = new HashMap<String, Object>();
         sessionAttributes.put("attribute1", "value1");
-
+        
         // Now create the workspace ...
         SecurityContext mockSecurityContext = new MockSecurityContext(null,
                                                                       Collections.singleton(JcrSession.DNA_WRITE_PERMISSION));

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -432,7 +432,7 @@
     private void testDescriptorValues( Repository repository ) {
         assertThat(repository.getDescriptor(Repository.LEVEL_1_SUPPORTED), is("true"));
         assertThat(repository.getDescriptor(Repository.LEVEL_2_SUPPORTED), is("true"));
-        assertThat(repository.getDescriptor(Repository.OPTION_LOCKING_SUPPORTED), is("false"));
+        assertThat(repository.getDescriptor(Repository.OPTION_LOCKING_SUPPORTED), is("true"));
         assertThat(repository.getDescriptor(Repository.OPTION_OBSERVATION_SUPPORTED), is("false"));
         assertThat(repository.getDescriptor(Repository.OPTION_QUERY_SQL_SUPPORTED), is("false"));
         assertThat(repository.getDescriptor(Repository.OPTION_TRANSACTIONS_SUPPORTED), is("false"));

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -58,6 +58,7 @@
 import javax.security.auth.login.LoginContext;
 import org.jboss.dna.graph.JaasSecurityContext;
 import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -98,7 +99,7 @@
         }
     }
 
-    @Test( expected = UnsupportedOperationException.class )
+    @Test( expected = IllegalArgumentException.class )
     public void shouldNotAllowAddLockToken() throws Exception {
         session.addLockToken(null);
     }
@@ -158,6 +159,9 @@
         sessionAttributes = new HashMap<String, Object>();
 
         // Now create the workspace ...
+        PathFactory pathFactory = context.getValueFactories().getPathFactory();
+        Path locksPath = pathFactory.create(pathFactory.createRootPath(), JcrLexicon.SYSTEM, DnaLexicon.LOCKS);
+        WorkspaceLockManager wlm = new WorkspaceLockManager(context, repository, workspaceName, locksPath);
         workspace = new JcrWorkspace(repository, workspaceName, context, sessionAttributes);
 
         // Create the session and log in ...
@@ -394,7 +398,7 @@
     @Test( expected = UnsupportedRepositoryOperationException.class )
     public void shouldNotProvideUuidIfNotReferenceable() throws Exception {
         // The b node was not set up to be referenceable in this test, but does have a mixin type
-        Node node = session.getRootNode().getNode("a");
+        Node node = session.getRootNode().getNode("a").getNode("b").getNode("c");
 
         node.getUUID();
     }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -64,7 +64,9 @@
 import org.apache.jackrabbit.test.api.SetValueValueFormatExceptionTest;
 import org.apache.jackrabbit.test.api.SetValueVersionExceptionTest;
 import org.apache.jackrabbit.test.api.ValueFactoryTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneReferenceableTest;
 import org.apache.jackrabbit.test.api.WorkspaceCloneSameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneTest;
 import org.apache.jackrabbit.test.api.WorkspaceCloneVersionableTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesReferenceableTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesSameNameSibsTest;
@@ -72,9 +74,11 @@
 import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesVersionableTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyReferenceableTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopySameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyTest;
 import org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest;
 import org.apache.jackrabbit.test.api.WorkspaceMoveReferenceableTest;
 import org.apache.jackrabbit.test.api.WorkspaceMoveSameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveTest;
 import org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest;
 
 /**
@@ -230,9 +234,9 @@
             addTestSuite(NodeCanAddMixinTest.class);
             addTestSuite(NodeRemoveMixinTest.class);
 
-            // DNA-518 addTestSuite(WorkspaceCloneReferenceableTest.class);
+            addTestSuite(WorkspaceCloneReferenceableTest.class);
             addTestSuite(WorkspaceCloneSameNameSibsTest.class);
-            // addTestSuite(WorkspaceCloneTest.class);
+            addTestSuite(WorkspaceCloneTest.class);
             addTestSuite(WorkspaceCloneVersionableTest.class);
             addTestSuite(WorkspaceCopyBetweenWorkspacesReferenceableTest.class);
             addTestSuite(WorkspaceCopyBetweenWorkspacesSameNameSibsTest.class);
@@ -240,11 +244,11 @@
             addTestSuite(WorkspaceCopyBetweenWorkspacesVersionableTest.class);
             addTestSuite(WorkspaceCopyReferenceableTest.class);
             addTestSuite(WorkspaceCopySameNameSibsTest.class);
-            // addTestSuite(WorkspaceCopyTest.class);
+            addTestSuite(WorkspaceCopyTest.class);
             addTestSuite(WorkspaceCopyVersionableTest.class);
             addTestSuite(WorkspaceMoveReferenceableTest.class);
             addTestSuite(WorkspaceMoveSameNameSibsTest.class);
-            // addTestSuite(WorkspaceMoveTest.class);
+            addTestSuite(WorkspaceMoveTest.class);
             addTestSuite(WorkspaceMoveVersionableTest.class);
 
             addTestSuite(RepositoryLoginTest.class);
@@ -269,7 +273,7 @@
 
             // addTest(org.apache.jackrabbit.test.api.observation.TestAll.suite());
             // addTest(org.apache.jackrabbit.test.api.version.TestAll.suite());
-            // addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite());
+            addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite());
             addTest(org.apache.jackrabbit.test.api.util.TestAll.suite());
         }
     }

Modified: trunk/web/dna-web-jcr-rest-war/src/test/java/org/jboss/dna/web/jcr/rest/JcrResourcesTest.java
===================================================================
--- trunk/web/dna-web-jcr-rest-war/src/test/java/org/jboss/dna/web/jcr/rest/JcrResourcesTest.java	2009-10-15 19:57:20 UTC (rev 1294)
+++ trunk/web/dna-web-jcr-rest-war/src/test/java/org/jboss/dna/web/jcr/rest/JcrResourcesTest.java	2009-10-19 19:54:55 UTC (rev 1295)
@@ -236,8 +236,9 @@
         assertThat(properties.getString("jcr:primaryType"), is("dna:system"));
 
         JSONArray namespaces = system.getJSONArray("children");
-        assertThat(namespaces.length(), is(1));
+        assertThat(namespaces.length(), is(2));
         assertThat(namespaces.getString(0), is("dna:namespaces"));
+        assertThat(namespaces.getString(1), is("dna:locks"));
 
         assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
         connection.disconnect();
@@ -260,10 +261,12 @@
         assertThat(properties.getString("jcr:primaryType"), is("dna:system"));
 
         JSONObject children = body.getJSONObject("children");
-        assertThat(children.length(), is(1));
+        assertThat(children.length(), is(2));
 
         JSONObject namespaces = children.getJSONObject("dna:namespaces");
         assertThat(namespaces.length(), is(2));
+        JSONObject locks = children.getJSONObject("dna:locks");
+        assertThat(locks.length(), is(1));
 
         properties = namespaces.getJSONObject("properties");
         assertThat(properties.length(), is(1));


From dna-commits at lists.jboss.org  Mon Oct 19 16:26:00 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 19 Oct 2009 16:26:00 -0400
Subject: [dna-commits] DNA SVN: r1296 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <200910192026.n9JKQ0cH026174@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-10-19 16:26:00 -0400 (Mon, 19 Oct 2009)
New Revision: 1296

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
Log:
DNA-457 Add JSR-170 Locking Optional Feature

Quick change to address some Java5/6 compilation issues.

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-10-19 19:54:55 UTC (rev 1295)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-10-19 20:26:00 UTC (rev 1296)
@@ -1368,7 +1368,7 @@
             nodesToVisit.add(nodeInfo());
 
             while (!nodesToVisit.isEmpty()) {
-                Node<JcrNodePayload, JcrPropertyPayload> node = nodesToVisit.pop();
+                Node<JcrNodePayload, JcrPropertyPayload> node = nodesToVisit.get(nodesToVisit.size() - 1);
                 if (session().workspace().lockManager().lockFor(node.getLocation()) != null) throw new LockException(
                                                                                                                      JcrI18n.parentAlreadyLocked.text(this.location,
                                                                                                                                                       node.getLocation()));

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-19 19:54:55 UTC (rev 1295)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-19 20:26:00 UTC (rev 1296)
@@ -270,38 +270,31 @@
             final AbstractJcrNode node = cache.findJcrNode(Location.create(nodeUuid));
             final JcrSession session = cache.session();
             return new Lock() {
-                @Override
                 public String getLockOwner() {
                     return lockOwner;
                 }
 
-                @Override
                 public String getLockToken() {
                     String uuidString = lockUuid.toString();
                     return session.lockTokens().contains(uuidString) ? uuidString : null;
                 }
 
-                @Override
                 public Node getNode() {
                     return node;
                 }
 
-                @Override
                 public boolean isDeep() {
                     return deep;
                 }
 
-                @Override
                 public boolean isLive() throws RepositoryException {
                     return workspaceLocksByNodeUuid.containsKey(nodeUuid);
                 }
 
-                @Override
                 public boolean isSessionScoped() {
                     return sessionScoped;
                 }
 
-                @Override
                 public void refresh() throws LockException, RepositoryException {
                     if (getLockToken() == null) {
                         throw new LockException(JcrI18n.notLocked.text(node.location));


From dna-commits at lists.jboss.org  Tue Oct 20 12:14:27 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 20 Oct 2009 12:14:27 -0400
Subject: [dna-commits] DNA SVN: r1297 - in
	trunk/docs/reference/src/main/docbook/en-US: content/core and
	1 other directory.
Message-ID: <200910201614.n9KGER0U025622@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-10-20 12:14:27 -0400 (Tue, 20 Oct 2009)
New Revision: 1297

Modified:
   trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
   trunk/docs/reference/src/main/docbook/en-US/custom.dtd
Log:
DNA-523 Spelling and grammar errors in on-line JBoss DNA Reference Guide

Committed patch that corrects all of the noted grammar errors.

Modified: trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml	2009-10-19 20:26:00 UTC (rev 1296)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml	2009-10-20 16:14:27 UTC (rev 1297)
@@ -105,7 +105,7 @@
 			via the <code>getValueFactories()</code> method.  But before we see that, let's first discuss how names are represented as strings.
 		</para>
 		<para>
-			We'll see how names are used later one, but one more point to make:  &Name; is both serializable and comparable,
+			We'll see how names are used later on, but one more point to make:  &Name; is both serializable and comparable,
 			and all implementations should support <code>equals(...)</code> and <code>hashCode()</code> so that &Name; can
 			be used as a key in a hash-based map.  &Name; also extends the &Readable; interface, which we'll learn
 			more about later in this chapter.
@@ -257,7 +257,7 @@
 		<para>
 		  JBoss DNA properties can hold a wide range of value objects, including normal Java strings, names, paths, 
 		  URIs, booleans, longs, doubles, decimals, binary content,	dates, UUIDs, references to other nodes, 
-		  or any other serializable object.  All but three these are the standard Java classes: dates are
+		  or any other serializable object.  All but three of these are the standard Java classes: dates are
 		  represented by an immutable &DateTime; class; binary content is represented by an immutable &Binary;
 		  interface patterned after the proposed interface of the same name in <ulink url="&JSR283;">JSR-283</ulink>;
 		  and &Reference; is an immutable interface patterned after the corresponding interface is 
@@ -352,7 +352,7 @@
 	<sect1 id="graph-value-factories">
 		<title>Values and value factories</title>
 		<para>
-			JBoss DNA properties can hold a variety of types of value objects: strings, names, paths, 
+			JBoss DNA properties can hold a variety of value object types: strings, names, paths, 
 		  URIs, booleans, longs, doubles, decimals, binary content,	dates, UUIDs, references to other nodes, 
 		  or any other serializable object.  To assist in the creation of these values and conversion
 		  into other types, JBoss DNA defines a &ValueFactory; interface.  This interface is parameterized
@@ -459,7 +459,7 @@
 		</programlisting>
 		<para>
 			What we've glossed over so far, however, is how to obtain the correct &ValueFactory; for the desired type.
-			If you remember back to the previous chapter, &ExecutionContext; has a <code>getValueFactories()</code> method
+			If you remember back in the previous chapter, &ExecutionContext; has a <code>getValueFactories()</code> method
 			that return a &ValueFactories; interface:
 		</para>
 		<programlisting>
@@ -597,7 +597,7 @@
 		<para>
 			JBoss DNA provides efficient implementations of all of these interfaces: the &ValueFactory; interfaces and subinterfaces;
 			the &Path;, &PathSegment;, &Name;, &Binary;, &DateTime;, and &Reference; interfaces; and the &ValueFactories; interface
-			return by the &ExecutionContext;.  In fact, some of these interfaces have multiple implementations that are optimized for 
+			returned by the &ExecutionContext;.  In fact, some of these interfaces have multiple implementations that are optimized for 
 			specific but frequently-occurring conditions.
 		</para>
 	</sect1>
@@ -782,8 +782,8 @@
 		<para>
 			JBoss DNA's <emphasis>Graph API</emphasis> was designed as a lightweight public API for working with graph information.
 			The &Graph; class is the primary class in API, and each instance represents a single, independent
-			view of a single graph.  &Graph; instances don't maintain state, so every request (or batch of requests) operate against
-			the underlying graph and the return <emphasis>immutable snapshots</emphasis> of the requested state at the time
+			view of a single graph.  &Graph; instances don't maintain state, so every request (or batch of requests) operates against
+			the underlying graph and then returns <emphasis>immutable snapshots</emphasis> of the requested state at the time
 			the request was made.
 		</para>
 		<para>
@@ -807,8 +807,8 @@
 		<para>
 			JBoss DNA graphs have the notion of <emphasis>workspaces</emphasis> that provide different views of the content.  Some graphs may have
 			one workspace, while others may have multiple workspaces.  Some graphs will allow a client to create new workspaces or destroy
-			existing workspaces, while other graphs will not allow adding or removing workspaces.  Some graphs may have workspaces may show the same (or very
-			similar) content, while other graphs may have workspaces that each contain completely independent content.
+			existing workspaces, while other graphs will not allow adding or removing workspaces.  Some graphs may have workspaces that may show the same (or very
+			similar) content, while other graphs may have workspaces that contain completely independent content.
 		</para>
 		<para>
 			The &Graph; object is always bound to a workspace, which initially is the <emphasis>default workspace</emphasis>.  To find out
@@ -1001,7 +1001,7 @@
 					<row>
 						<entry>ReadNodeRequest</entry>
 						<entry>
-							A request to read from the named workspace in the source a node's properties and children.
+							A request to read a node's properties and children from the named workspace in the source.
 							The node may be specified by path and/or by identification properties.
 							The connector returns all properties and the locations for all children,
 							or sets a &PathNotFoundException; error on the request if the node did not exist in the workspace.
@@ -1012,7 +1012,7 @@
 					<row>
 						<entry>VerifyNodeExistsRequest</entry>
 						<entry>
-							A request to verify the existance of a node at the specified location in the named workspace of the source.
+							A request to verify the existence of a node at the specified location in the named workspace of the source.
 							The connector returns all the actual location for the node if it exists, or
 							sets a &PathNotFoundException; error on the request if the node does not exist in the workspace.
 							The connector sets a &InvalidWorkspaceException; error on the request if the named workspace does not exist.
@@ -1021,7 +1021,7 @@
 					<row>
 						<entry>ReadAllPropertiesRequest</entry>
 						<entry>
-							A request to read from the named workspace in the source all of the properties of a node.
+							A request to read all of the properties of a node from the named workspace in the source.
 							The node may be specified by path and/or by identification properties.
 							The connector returns all properties that were found on the node,
 							or sets a &PathNotFoundException; error on the request if the node did not exist in the workspace.
@@ -1032,7 +1032,7 @@
 					<row>
 						<entry>ReadPropertyRequest</entry>
 						<entry>
-							A request to read from the named workspace in the source a single property of a node.
+							A request to read  a single property of a node from the named workspace in the source.
 							The node may be specified by path and/or by identification properties,
 							and the property is specified by name.
 							The connector returns the property if found on the node,
@@ -1044,7 +1044,7 @@
 					<row>
 						<entry>ReadAllChildrenRequest</entry>
 						<entry>
-							A request to read from the named workspace in the source all of the children of a node.
+							A request to read all of the children of a node from the named workspace in the source.
 							The node may be specified by path and/or by identification properties.
 							The connector returns an ordered list of locations for each child found on the node,
 							an empty list if the node had no children,
@@ -1056,7 +1056,7 @@
 					<row>
 						<entry>ReadBlockOfChildrenRequest</entry>
 						<entry>
-							A request to read from the named workspace in the source a block of children of a node, starting with the n<superscript>th</superscript> children.
+							A request to read a block of children of a node, starting with the n<superscript>th</superscript> child from the named workspace in the source.
 							This is designed to allow paging through the children, which is much more efficient for large numbers of children.
 							The node may be specified by path and/or by identification properties, and the block
 							is defined by a starting index and a count (i.e., the block size).
@@ -1070,8 +1070,8 @@
 					<row>
 						<entry>ReadNextBlockOfChildrenRequest</entry>
 						<entry>
-							A request to read from the named workspace in the source a block of children of a node, starting with the children that immediately follow
-							a previously-returned child.
+							A request to read a block of children of a node, starting with the children that immediately follow
+							a previously-returned child from the named workspace in the source.
 							This is designed to allow paging through the children, which is much more efficient for large numbers of children.
 							The node may be specified by path and/or by identification properties, and the block
 							is defined by the location of the node immediately preceding the block and a count (i.e., the block size).
@@ -1336,7 +1336,7 @@
 			<title>Observable</title>
 			<para>
 				Any component that can have changes and be observed can implement the &Observable; interface.  This interface
-				allows &Observers; to register (or be registered) to receive notifications of the changes.  However, a concrete and thread-safe 
+				allows &Observer;s to register (or be registered) to receive notifications of the changes.  However, a concrete and thread-safe 
 				implementation of this interface, called &ChangeObservers;, is available and should be used where possible, since it 
 				automatically manages the registered &ChangeObserver; instances and properly implements the register and unregister mechanisms.
 			</para>

Modified: trunk/docs/reference/src/main/docbook/en-US/custom.dtd
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/custom.dtd	2009-10-19 20:26:00 UTC (rev 1296)
+++ trunk/docs/reference/src/main/docbook/en-US/custom.dtd	2009-10-20 16:14:27 UTC (rev 1297)
@@ -53,7 +53,7 @@
 <!ENTITY HttpServletRequest			      "<ulink url='&JavaEE;javax/servlet/http/HttpServletRequest.html'><classname>HttpServletRequest</classname></ulink>">
 <!ENTITY Serializable							    "<ulink url='&Java;java/io/Serializable.html'><interface>Serializable</interface></ulink>">
 <!ENTITY Iterator									    "<ulink url='&Java;java/util/Iterator.html'><interface>Iterator</interface></ulink>">
-<!ENTITY Iteratable								    "<ulink url='&Java;java/util/Iteratable.html'><interface>Iteratable</interface></ulink>">
+<!ENTITY Iterable								    "<ulink url='&Java;java/util/Iterable.html'><interface>Iterable</interface></ulink>">
 <!ENTITY Set											    "<ulink url='&Java;java/util/Set.html'><interface>Set</interface></ulink>">
 <!ENTITY Map											    "<ulink url='&Java;java/util/Map.html'><interface>Map</interface></ulink>">
 <!ENTITY List											    "<ulink url='&Java;java/util/List.html'><interface>List</interface></ulink>">
@@ -105,6 +105,7 @@
 <!ENTITY Property						       		"<ulink url='&API;graph/property/Property.html'><interface>Property</interface></ulink>">
 <!ENTITY DateTime						       		"<ulink url='&API;graph/property/DateTime.html'><interface>DateTime</interface></ulink>">
 <!ENTITY Binary							       		"<ulink url='&API;graph/property/Binary.html'><interface>Binary</interface></ulink>">
+<!ENTITY Reference							       		"<ulink url='&API;graph/property/Reference'><interface>Reference</interface></ulink>">
 <!ENTITY ValueFactory		       				"<ulink url='&API;graph/property/ValueFactory.html'><interface>ValueFactory</interface></ulink>">
 <!ENTITY ValueFactories       				"<ulink url='&API;graph/property/ValueFactories.html'><interface>ValueFactories</interface></ulink>">
 <!ENTITY ValueFormatException  				"<ulink url='&API;graph/property/ValueFormatException.html'><classname>ValueFormatException</classname></ulink>">


From dna-commits at lists.jboss.org  Wed Oct 21 13:01:36 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 21 Oct 2009 13:01:36 -0400
Subject: [dna-commits] DNA SVN: r1298 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/query/optimize
	and 4 other directories.
Message-ID: <200910211701.n9LH1aoK005446@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-21 13:01:35 -0400 (Wed, 21 Oct 2009)
New Revision: 1298

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/BindVariableName.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearchScore.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Not.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Or.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Ordering.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/UpperCase.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java
Log:
DNA-467 Added another optimization rule to merge set criteria, and added hashCode() methods to all of the AQM classes.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -60,6 +60,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A constraint that evaluates to true when <i>both</i> of the other constraints evaluate to true.
@@ -34,6 +35,7 @@
 
     private final Constraint left;
     private final Constraint right;
+    private final int hc;
 
     public And( Constraint left,
                 Constraint right ) {
@@ -41,6 +43,7 @@
         CheckArg.isNotNull(right, "right");
         this.left = left;
         this.right = right;
+        this.hc = HashCode.compute(this.left, this.right);
     }
 
     /**
@@ -70,6 +73,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -77,6 +90,7 @@
         if (obj == this) return true;
         if (obj instanceof And) {
             And that = (And)obj;
+            if (this.hc != that.hc) return false;
             return left.equals(that.left) && right.equals(that.right);
         }
         return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/BindVariableName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/BindVariableName.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/BindVariableName.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -59,6 +59,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return variableName.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Path;
 
 /**
@@ -34,6 +35,7 @@
 public class ChildNode extends Constraint {
     private final SelectorName selectorName;
     private final Path parentPath;
+    private final int hc;
 
     /**
      * Create a constraint requiring that the node identified by the selector is a child of the node reachable by the supplied
@@ -48,6 +50,7 @@
         CheckArg.isNotNull(parentPath, "parentPath");
         this.selectorName = selectorName;
         this.parentPath = parentPath;
+        this.hc = HashCode.compute(this.selectorName, this.parentPath);
     }
 
     /**
@@ -77,6 +80,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -84,6 +97,7 @@
         if (obj == this) return true;
         if (obj instanceof ChildNode) {
             ChildNode that = (ChildNode)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selectorName.equals(that.selectorName)) return false;
             if (!this.parentPath.equals(that.parentPath)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A join condition that evaluates to true only when the named child node is indeed a child of the named parent node.
@@ -33,6 +34,7 @@
 public class ChildNodeJoinCondition extends JoinCondition {
     private final SelectorName childSelectorName;
     private final SelectorName parentSelectorName;
+    private final int hc;
 
     /**
      * Create a join condition that determines whether the node identified by the child selector is a child of the node identified
@@ -47,6 +49,7 @@
         CheckArg.isNotNull(parentSelectorName, "parentSelectorName");
         this.childSelectorName = childSelectorName;
         this.parentSelectorName = parentSelectorName;
+        this.hc = HashCode.compute(this.childSelectorName, this.parentSelectorName);
     }
 
     /**
@@ -76,6 +79,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -83,6 +96,7 @@
         if (obj == this) return true;
         if (obj instanceof ChildNodeJoinCondition) {
             ChildNodeJoinCondition that = (ChildNodeJoinCondition)obj;
+            if (this.hc != that.hc) return false;
             if (!this.childSelectorName.equals(that.childSelectorName)) return false;
             if (!this.parentSelectorName.equals(that.parentSelectorName)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -102,6 +102,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A constraint that evaluates to true when the defined operation evaluates to true.
@@ -35,6 +36,7 @@
     private final DynamicOperand operand1;
     private final StaticOperand operand2;
     private final Operator operator;
+    private final int hc;
 
     public Comparison( DynamicOperand operand1,
                        Operator operator,
@@ -45,6 +47,7 @@
         this.operand1 = operand1;
         this.operand2 = operand2;
         this.operator = operator;
+        this.hc = HashCode.compute(this.operand1, this.operand2, this.operator);
     }
 
     /**
@@ -81,6 +84,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -88,6 +101,7 @@
         if (obj == this) return true;
         if (obj instanceof Comparison) {
             Comparison that = (Comparison)obj;
+            if (this.hc != that.hc) return false;
             if (!this.operator.equals(that.operator)) return false;
             if (!this.operand1.equals(that.operand1)) return false;
             if (!this.operand2.equals(that.operand2)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -77,6 +77,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A join condition that evaluates to true only when the named node is a descendant of another named node.
@@ -33,6 +34,7 @@
 public class DescendantNodeJoinCondition extends JoinCondition {
     private final SelectorName descendantSelectorName;
     private final SelectorName ancestorSelectorName;
+    private final int hc;
 
     /**
      * Create a join condition that determines whether the node identified by the descendant selector is indeed a descendant of
@@ -47,6 +49,7 @@
         CheckArg.isNotNull(ancestorSelectorName, "ancestorSelectorName");
         this.descendantSelectorName = descendantSelectorName;
         this.ancestorSelectorName = ancestorSelectorName;
+        this.hc = HashCode.compute(this.descendantSelectorName, this.ancestorSelectorName);
     }
 
     /**
@@ -76,6 +79,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -83,6 +96,7 @@
         if (obj == this) return true;
         if (obj instanceof DescendantNodeJoinCondition) {
             DescendantNodeJoinCondition that = (DescendantNodeJoinCondition)obj;
+            if (this.hc != that.hc) return false;
             if (!this.descendantSelectorName.equals(that.descendantSelectorName)) return false;
             if (!this.ancestorSelectorName.equals(that.ancestorSelectorName)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 
 /**
@@ -42,6 +43,7 @@
     private final Name property1Name;
     private final SelectorName selector2Name;
     private final Name property2Name;
+    private final int hc;
 
     public EquiJoinCondition( SelectorName selector1Name,
                               Name property1Name,
@@ -55,6 +57,7 @@
         this.property1Name = property1Name;
         this.selector2Name = selector2Name;
         this.property2Name = property2Name;
+        this.hc = HashCode.compute(this.selector1Name, this.property1Name, this.selector2Name, this.property2Name);
     }
 
     public EquiJoinCondition( Column column1,
@@ -103,6 +106,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -110,6 +123,7 @@
         if (obj == this) return true;
         if (obj instanceof EquiJoinCondition) {
             EquiJoinCondition that = (EquiJoinCondition)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selector1Name.equals(that.selector1Name)) return false;
             if (!this.selector2Name.equals(that.selector2Name)) return false;
             if (!this.property1Name.equals(that.property1Name)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -28,6 +28,7 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.text.ParsingException;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.parse.FullTextSearchParser;
@@ -42,6 +43,7 @@
     private final Name propertyName;
     private final String fullTextSearchExpression;
     private Term term;
+    private final int hc;
 
     /**
      * Create a constraint defining a full-text search against the property values on node within the search scope.
@@ -60,6 +62,7 @@
         this.selectorName = selectorName;
         this.propertyName = propertyName;
         this.fullTextSearchExpression = fullTextSearchExpression;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName, this.fullTextSearchExpression);
     }
 
     /**
@@ -78,6 +81,7 @@
         this.propertyName = propertyName;
         this.fullTextSearchExpression = fullTextSearchExpression;
         this.term = null;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName, this.fullTextSearchExpression);
     }
 
     /**
@@ -93,6 +97,7 @@
         this.selectorName = selectorName;
         this.propertyName = null;
         this.fullTextSearchExpression = fullTextSearchExpression;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName, this.fullTextSearchExpression);
     }
 
     /**
@@ -143,6 +148,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -150,6 +165,7 @@
         if (obj == this) return true;
         if (obj instanceof FullTextSearch) {
             FullTextSearch that = (FullTextSearch)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selectorName.equals(that.selectorName)) return false;
             if (!ObjectUtil.isEqualWithNulls(this.propertyName, that.propertyName)) return false;
             if (!this.fullTextSearchExpression.equals(that.fullTextSearchExpression)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearchScore.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearchScore.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearchScore.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * 
@@ -36,6 +37,7 @@
     private final Source right;
     private final JoinType type;
     private final JoinCondition joinCondition;
+    private final int hc;
 
     /**
      * Create a join of the left and right sources, using the supplied join condition. The outputs of the left and right sources
@@ -58,6 +60,7 @@
         this.right = right;
         this.type = type;
         this.joinCondition = joinCondition;
+        this.hc = HashCode.compute(this.left, this.right, this.type, this.joinCondition);
     }
 
     /**
@@ -101,6 +104,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -108,6 +121,7 @@
         if (obj == this) return true;
         if (obj instanceof Join) {
             Join that = (Join)obj;
+            if (this.hc != that.hc) return false;
             if (!this.type.equals(that.type)) return false;
             if (!this.left.equals(that.left)) return false;
             if (!this.right.equals(that.right)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -73,6 +73,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getPropertyValue().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -107,6 +107,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return rowLimit;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -59,6 +59,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getValue().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -74,6 +74,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return operand.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -66,6 +66,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -66,6 +66,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Not.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Not.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Not.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getConstraint().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Or.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Or.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Or.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A constraint that evaluates to true when either of the other constraints evaluates to true.
@@ -34,6 +35,7 @@
 
     private final Constraint left;
     private final Constraint right;
+    private final int hc;
 
     /**
      * Create a constraint that evaluates to true if either of the two supplied constraints evaluates to true.
@@ -48,6 +50,7 @@
         CheckArg.isNotNull(right, "right");
         this.left = left;
         this.right = right;
+        this.hc = HashCode.compute(this.left, this.right);
     }
 
     /**
@@ -81,6 +84,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -88,6 +101,7 @@
         if (obj == this) return true;
         if (obj instanceof Or) {
             Or that = (Or)obj;
+            if (this.hc != that.hc) return false;
             return this.left.equals(that.left) && this.right.equals(that.right);
         }
         return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Ordering.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Ordering.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Ordering.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -81,6 +81,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return operand.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 
 /**
@@ -34,6 +35,7 @@
 public class PropertyExistence extends Constraint {
     private final SelectorName selectorName;
     private final Name propertyName;
+    private final int hc;
 
     /**
      * Create a constraint requiring that a property exist on a node.
@@ -47,6 +49,7 @@
         CheckArg.isNotNull(propertyName, "propertyName");
         this.selectorName = selectorName;
         this.propertyName = propertyName;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName);
     }
 
     /**
@@ -80,6 +83,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -87,6 +100,7 @@
         if (obj == this) return true;
         if (obj instanceof PropertyExistence) {
             PropertyExistence that = (PropertyExistence)obj;
+            if (this.hc != that.hc) return false;
             return this.selectorName.equals(that.selectorName) && this.propertyName.equals(that.propertyName);
         }
         return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 
 /**
@@ -34,6 +35,7 @@
 public class PropertyValue extends DynamicOperand {
     private final SelectorName selectorName;
     private final Name propertyName;
+    private final int hc;
 
     /**
      * Create a dynamic operand that evaluates to the property values of the node identified by the selector.
@@ -48,6 +50,7 @@
         CheckArg.isNotNull(propertyName, "propertyName");
         this.selectorName = selectorName;
         this.propertyName = propertyName;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName);
     }
 
     /**
@@ -82,6 +85,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -89,6 +102,7 @@
         if (obj == this) return true;
         if (obj instanceof PropertyValue) {
             PropertyValue that = (PropertyValue)obj;
+            if (this.hc != that.hc) return false;
             return this.selectorName.equals(that.selectorName) && this.propertyName.equals(that.propertyName);
         }
         return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -29,6 +29,7 @@
 import java.util.List;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
 
 /**
@@ -43,6 +44,7 @@
     private final Constraint constraint;
     private final List<Column> columns;
     private final boolean distinct;
+    private final int hc;
 
     /**
      * Create a new query that uses the supplied source.
@@ -57,6 +59,7 @@
         this.constraint = null;
         this.columns = Collections.<Column>emptyList();
         this.distinct = IS_DISTINCT_DEFAULT;
+        this.hc = HashCode.compute(this.source, this.constraint, this.columns, this.distinct);
     }
 
     /**
@@ -84,6 +87,7 @@
         this.constraint = constraint;
         this.columns = columns != null ? columns : Collections.<Column>emptyList();
         this.distinct = isDistinct;
+        this.hc = HashCode.compute(this.source, this.constraint, this.columns, this.distinct);
     }
 
     /**
@@ -189,6 +193,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -196,6 +210,7 @@
         if (obj == this) return true;
         if (obj instanceof Query) {
             Query that = (Query)obj;
+            if (this.hc != that.hc) return false;
             if (this.distinct != that.distinct) return false;
             if (!this.source.equals(that.source)) return false;
             if (!ObjectUtil.isEqualWithNulls(this.getLimits(), that.getLimits())) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Path;
 
 /**
@@ -34,6 +35,7 @@
 public class SameNode extends Constraint {
     private final SelectorName selectorName;
     private final Path path;
+    private final int hc;
 
     /**
      * Create a constraint requiring that the node identified by the selector is reachable by the supplied absolute path.
@@ -48,6 +50,7 @@
         CheckArg.isNotNull(path, "path");
         this.selectorName = selectorName;
         this.path = path;
+        this.hc = HashCode.compute(this.selectorName, this.path);
     }
 
     /**
@@ -81,6 +84,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -88,6 +101,7 @@
         if (obj == this) return true;
         if (obj instanceof SameNode) {
             SameNode that = (SameNode)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selectorName.equals(that.selectorName)) return false;
             if (!this.path.equals(that.path)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
 import org.jboss.dna.graph.property.Path;
 
@@ -37,6 +38,7 @@
     private final SelectorName selector1Name;
     private final SelectorName selector2Name;
     private final Path selector2Path;
+    private final int hc;
 
     /**
      * Create a join condition that determines whether the node identified by the first selector is the same as the node at the
@@ -56,6 +58,7 @@
         this.selector1Name = selector1Name;
         this.selector2Name = selector2Name;
         this.selector2Path = selector2Path;
+        this.hc = HashCode.compute(this.selector1Name, this.selector2Name, this.selector2Path);
     }
 
     /**
@@ -73,6 +76,7 @@
         this.selector1Name = selector1Name;
         this.selector2Name = selector2Name;
         this.selector2Path = null;
+        this.hc = HashCode.compute(this.selector1Name, this.selector2Name, this.selector2Path);
     }
 
     /**
@@ -115,6 +119,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -122,6 +136,7 @@
         if (obj == this) return true;
         if (obj instanceof SameNodeJoinCondition) {
             SameNodeJoinCondition that = (SameNodeJoinCondition)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selector1Name.equals(that.selector1Name)) return false;
             if (!this.selector2Name.equals(that.selector2Name)) return false;
             if (!ObjectUtil.isEqualWithNulls(this.selector2Path, that.selector2Path)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -96,5 +96,4 @@
     public boolean hasAlias() {
         return alias != null;
     }
-
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -23,7 +23,9 @@
  */
 package org.jboss.dna.graph.query.model;
 
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
 
@@ -44,6 +46,14 @@
         this.setOperands = setOperands;
     }
 
+    public SetCriteria( DynamicOperand left,
+                        StaticOperand... setOperands ) {
+        CheckArg.isNotNull(left, "left");
+        CheckArg.isNotNull(setOperands, "setOperands");
+        this.left = left;
+        this.setOperands = Collections.unmodifiableList(Arrays.asList(setOperands));
+    }
+
     /**
      * @return operand1
      */
@@ -71,6 +81,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return left.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -31,6 +31,7 @@
 import java.util.Map;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
 import org.jboss.dna.graph.ExecutionContext;
 
@@ -101,6 +102,7 @@
     private final QueryCommand right;
     private final Operation operation;
     private final boolean all;
+    private final int hc;
 
     public SetQuery( QueryCommand left,
                      Operation operation,
@@ -114,6 +116,7 @@
         this.right = right;
         this.operation = operation;
         this.all = all;
+        this.hc = HashCode.compute(this.left, this.right, this.operation);
     }
 
     public SetQuery( QueryCommand left,
@@ -130,6 +133,7 @@
         this.right = right;
         this.operation = operation;
         this.all = all;
+        this.hc = HashCode.compute(this.left, this.right, this.operation);
     }
 
     /**
@@ -181,6 +185,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -188,6 +202,7 @@
         if (obj == this) return true;
         if (obj instanceof SetQuery) {
             SetQuery that = (SetQuery)obj;
+            if (this.hc != that.hc) return false;
             if (this.operation != that.operation) return false;
             if (!this.left.equals(that.left)) return false;
             if (!this.right.equals(that.right)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/UpperCase.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/UpperCase.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/UpperCase.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -76,6 +76,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getOperand().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -0,0 +1,113 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.SetCriteria;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
+ * the same selector.
+ * 
+ * @param <SimilarityType> the type used to compare and identify similar nodes
+ * @param <ConstraintType> the concrete type of Constraint objects that are being merged
+ */
+ at Immutable
+public abstract class AbstractMergeSelectNodes<SimilarityType, ConstraintType extends Constraint> implements OptimizerRule {
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+     */
+    @SuppressWarnings( "unchecked" )
+    public PlanNode execute( QueryContext context,
+                             PlanNode plan,
+                             LinkedList<OptimizerRule> ruleStack ) {
+        Map<SimilarityType, LinkedList<PlanNode>> mergeables = new HashMap<SimilarityType, LinkedList<PlanNode>>();
+        for (PlanNode source : plan.findAllAtOrBelow(Type.SOURCE)) {
+            // Walk up from the SOURCE and look for all SELECT nodes ...
+            PlanNode node = source.getParent();
+            while (node != null && node.is(Type.SELECT)) {
+                Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                SimilarityType operand = getSimilarityKey(constraint);
+                if (operand != null) {
+                    LinkedList<PlanNode> nodes = mergeables.get(operand);
+                    if (nodes == null) {
+                        nodes = new LinkedList<PlanNode>();
+                        mergeables.put(operand, nodes);
+                    }
+                    nodes.addFirst(node); // So that the list is in the downward order they appear in the plan
+                }
+                node = node.getParent();
+            }
+
+            // Merge all mergeable SELECT nodes ...
+            for (LinkedList<PlanNode> mergeable : mergeables.values()) {
+                if (mergeable.size() > 1) {
+                    Iterator<PlanNode> iter = mergeable.iterator();
+                    PlanNode firstSelect = iter.next();
+                    Constraint constraint = firstSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                    while (iter.hasNext()) {
+                        PlanNode nextSelect = iter.next();
+                        Constraint nextConstraint = nextSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                        constraint = merge((ConstraintType)constraint, (ConstraintType)nextConstraint);
+                        nextSelect.extractFromParent();
+                    }
+                    firstSelect.setProperty(Property.SELECT_CRITERIA, constraint);
+                }
+            }
+            mergeables.clear();
+        }
+        return plan;
+    }
+
+    /**
+     * Obtain the similarity key that will be used to determine whether two SELECT nodes should be merged.
+     * 
+     * @param constraint the constraint from the SELECT node
+     * @return the similarity key, or null if the constraint should not be merged
+     */
+    protected abstract SimilarityType getSimilarityKey( Constraint constraint );
+
+    /**
+     * Merge the two constraints.
+     * 
+     * @param firstConstraint the first constraint
+     * @param secondConstraint the second constraint
+     * @return the merged constraint
+     */
+    protected abstract Constraint merge( ConstraintType firstConstraint,
+                                         ConstraintType secondConstraint );
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -0,0 +1,89 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+import org.jboss.dna.graph.query.model.SetCriteria;
+import org.jboss.dna.graph.query.model.StaticOperand;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
+ * the same selector.
+ */
+ at Immutable
+public class MergeSetCriteria extends AbstractMergeSelectNodes<DynamicOperand, SetCriteria> {
+
+    public static final MergeSetCriteria INSTANCE = new MergeSetCriteria();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#getSimilarityKey(org.jboss.dna.graph.query.model.Constraint)
+     */
+    @Override
+    protected DynamicOperand getSimilarityKey( Constraint constraint ) {
+        if (constraint instanceof SetCriteria) {
+            // Look for the list of plan nodes for this operand ...
+            return ((SetCriteria)constraint).getLeftOperand();
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#merge(org.jboss.dna.graph.query.model.Constraint,
+     *      org.jboss.dna.graph.query.model.Constraint)
+     */
+    @Override
+    protected Constraint merge( SetCriteria firstConstraint,
+                                SetCriteria secondConstraint ) {
+        assert firstConstraint.getLeftOperand().equals(secondConstraint.getLeftOperand());
+        Set<StaticOperand> allOperands = new HashSet<StaticOperand>();
+        List<StaticOperand> orderedOperands = new ArrayList<StaticOperand>(firstConstraint.getRightOperands());
+        allOperands.addAll(firstConstraint.getRightOperands());
+        for (StaticOperand second : secondConstraint.getRightOperands()) {
+            if (allOperands.add(second)) {
+                orderedOperands.add(second);
+            }
+        }
+        return new SetCriteria(firstConstraint.getLeftOperand(), orderedOperands);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getClass().getSimpleName();
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -65,7 +65,16 @@
             // Is there already a PROJECT here ?
             assert parentOfProject.getChildCount() == 1; // should only have one child ...
             PlanNode child = parentOfProject.getFirstChild(); // should only have one child ...
-            if (child.is(Type.PROJECT)) continue;
+            if (child.is(Type.PROJECT)) {
+                // Check to see if there is a PROJECT above the access node ...
+                PlanNode accessParent = access.getParent();
+                if (accessParent == null || accessParent.isNot(Type.PROJECT)) continue;
+                // Otherwise, the parent is a PROJECT, but there is another PROJECT above the ACCESS node.
+                // Remove the lower PROJECT so the next code block moves the top PROJECT down below the ACCESS node ...
+                assert accessParent.is(Type.PROJECT);
+                child.extractFromParent();
+                child = parentOfProject.getFirstChild();
+            }
 
             // If the parent of the ACCESS node is a PROJECT, then we can simply move it to here ...
             PlanNode accessParent = access.getParent();

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -297,6 +297,7 @@
 
         // first look for originating nodes that exactly match the required selectors ...
         for (PlanNode originatingNode : originatingNodes) {
+            if (!criteriaNode.isAbove(originatingNode)) continue;
             if (originatingNode.getSelectors().equals(requiredSelectors)) return originatingNode;
         }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -99,6 +99,7 @@
                         // The PROJECT from the plan may actually not be needed if there is another PROJECT above it ...
                         PlanNode node = viewPlan.getParent();
                         while (node != null) {
+                            if (node.isOneOf(Type.JOIN)) break;
                             if (node.is(Type.PROJECT) && viewPlan.getSelectors().containsAll(node.getSelectors())) {
                                 viewPlan.extractFromParent();
                                 break;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,13 +25,16 @@
 
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.EquiJoinCondition;
 import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
 import org.jboss.dna.graph.query.model.SelectorName;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanUtil;
@@ -82,55 +85,68 @@
     public PlanNode execute( QueryContext context,
                              PlanNode plan,
                              LinkedList<OptimizerRule> ruleStack ) {
+        if (!context.getHints().hasJoin) return plan;
+
         // For each of the JOIN nodes ...
         Map<SelectorName, SelectorName> rewrittenSelectors = null;
+        int rewrittenJoins = 0;
+        int numJoins = 0;
         for (PlanNode joinNode : plan.findAllAtOrBelow(Type.JOIN)) {
+            ++numJoins;
             JoinCondition condition = joinNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
             if (condition instanceof EquiJoinCondition) {
-                PlanNode leftNode = joinNode.getFirstChild();
-                PlanNode rightNode = joinNode.getLastChild();
+                PlanNode leftNode = joinNode.getFirstChild().findAtOrBelow(Type.SOURCE);
+                PlanNode rightNode = joinNode.getLastChild().findAtOrBelow(Type.SOURCE);
                 assert leftNode != null;
                 assert rightNode != null;
-                if (leftNode.getType() == Type.SOURCE && rightNode.getType() == Type.SOURCE) {
-                    EquiJoinCondition equiJoin = (EquiJoinCondition)condition;
-                    // Find the names (or aliases) of the tables ...
-                    Schemata schemata = context.getSchemata();
-                    assert schemata != null;
-                    SelectorName leftTableName = leftNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
-                    SelectorName rightTableName = rightNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
-                    assert leftTableName != null;
-                    assert rightTableName != null;
-                    // Presumably the join condition is using at least one alias, but we only care about the actual name ...
-                    if (!leftTableName.equals(rightTableName)) {
-                        // The join is not joining the same table, so this doesn't meet the condition ...
-                        continue;
-                    }
-                    // Find the schemata columns referenced by the join condition ...
-                    Table table = schemata.getTable(leftTableName);
-                    if (table == null) {
-                        context.getProblems().addError(GraphI18n.tableDoesNotExist, leftTableName);
-                        continue;
-                    }
-                    ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
-                    String leftColumnName = stringFactory.create(equiJoin.getProperty1Name());
-                    String rightColumnName = stringFactory.create(equiJoin.getProperty2Name());
-                    Schemata.Column leftColumn = table.getColumn(leftColumnName);
-                    Schemata.Column rightColumn = table.getColumn(rightColumnName);
-                    if (leftColumn == null) {
-                        context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, leftColumnName, leftTableName);
-                        continue;
-                    }
-                    if (rightColumn == null) {
-                        context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, rightColumnName, leftTableName);
-                        continue;
-                    }
-                    // Are the join columns (on both sides) keys?
-                    if (table.hasKey(leftColumn) && (rightColumn == leftColumn || table.hasKey(rightColumn))) {
-                        // It meets all the criteria, so rewrite this join node ...
-                        if (rewrittenSelectors == null) rewrittenSelectors = new HashMap<SelectorName, SelectorName>();
-                        rewriteJoinNode(context, joinNode, equiJoin, rewrittenSelectors);
-                    }
+                EquiJoinCondition equiJoin = (EquiJoinCondition)condition;
+                // Find the names (or aliases) of the tables ...
+                Schemata schemata = context.getSchemata();
+                assert schemata != null;
+                SelectorName leftTableName = leftNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
+                SelectorName rightTableName = rightNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
+                assert leftTableName != null;
+                assert rightTableName != null;
+                // Presumably the join condition is using at least one alias, but we only care about the actual name ...
+                if (!leftTableName.equals(rightTableName)) {
+                    // The join is not joining the same table, so this doesn't meet the condition ...
+                    continue;
                 }
+                // Find the schemata columns referenced by the join condition ...
+                Table table = schemata.getTable(leftTableName);
+                if (table == null) {
+                    context.getProblems().addError(GraphI18n.tableDoesNotExist, leftTableName);
+                    continue;
+                }
+                ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+                String leftColumnName = stringFactory.create(equiJoin.getProperty1Name());
+                String rightColumnName = stringFactory.create(equiJoin.getProperty2Name());
+                Schemata.Column leftColumn = table.getColumn(leftColumnName);
+                Schemata.Column rightColumn = table.getColumn(rightColumnName);
+                if (leftColumn == null) {
+                    context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, leftColumnName, leftTableName);
+                    continue;
+                }
+                if (rightColumn == null) {
+                    context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, rightColumnName, leftTableName);
+                    continue;
+                }
+                // Are the join columns (on both sides) keys?
+                if (table.hasKey(leftColumn) && (rightColumn == leftColumn || table.hasKey(rightColumn))) {
+                    // It meets all the criteria, so rewrite this join node ...
+                    if (rewrittenSelectors == null) rewrittenSelectors = new HashMap<SelectorName, SelectorName>();
+                    rewriteJoinNode(context, joinNode, rewrittenSelectors);
+                    ++rewrittenJoins;
+                }
+            } else if (condition instanceof SameNodeJoinCondition) {
+                SameNodeJoinCondition sameNodeCondition = (SameNodeJoinCondition)condition;
+                if (sameNodeCondition.getSelector1Name().equals(sameNodeCondition.getSelector2Name())
+                    && sameNodeCondition.getSelector2Path() == null) {
+                    // It meets all the criteria, so rewrite this join node ...
+                    if (rewrittenSelectors == null) rewrittenSelectors = new HashMap<SelectorName, SelectorName>();
+                    rewriteJoinNode(context, joinNode, rewrittenSelectors);
+                    ++rewrittenJoins;
+                }
             }
         }
 
@@ -140,28 +156,72 @@
             // this criteria, so we need to re-run this rule...
             ruleStack.addFirst(this);
 
+            // After this rule is done as is no longer needed, we need to try to push SELECTs and PROJECTs again ...
+            if (!(ruleStack.peekFirst() instanceof PushSelectCriteria)) {
+                // We haven't already added these, so add them now ...
+                ruleStack.addFirst(PushProjects.INSTANCE);
+                if (context.getHints().hasCriteria) {
+                    ruleStack.addFirst(PushSelectCriteria.INSTANCE);
+                }
+            }
+
             // Now rewrite the various portions of the plan that make use of the now-removed selectors ...
             PlanUtil.replaceReferencesToRemovedSource(context, plan, rewrittenSelectors);
-        } else {
-            // There are no-untouched JOIN nodes, which means the sole JOIN node was rewritten as a single SOURCE node
-            assert plan.findAllAtOrBelow(Type.JOIN).isEmpty();
-            context.getHints().hasJoin = false;
+
+            assert rewrittenJoins > 0;
+            if (rewrittenJoins == numJoins) {
+                assert plan.findAllAtOrBelow(Type.JOIN).isEmpty();
+                context.getHints().hasJoin = false;
+            }
         }
         return plan;
     }
 
     protected void rewriteJoinNode( QueryContext context,
                                     PlanNode joinNode,
-                                    EquiJoinCondition joinCondition,
                                     Map<SelectorName, SelectorName> rewrittenSelectors ) {
         // Remove the right source node from the join node ...
-        PlanNode rightSource = joinNode.getLastChild();
-        rightSource.removeFromParent();
+        PlanNode rightChild = joinNode.getLastChild();
+        rightChild.removeFromParent();
+        PlanNode rightSource = rightChild.findAtOrBelow(Type.SOURCE);
 
         // Replace the join node with the left source node ...
-        PlanNode leftSource = joinNode.getFirstChild();
+        PlanNode leftChild = joinNode.getFirstChild();
         joinNode.extractFromParent();
+        PlanNode leftSource = leftChild.findAtOrBelow(Type.SOURCE);
 
+        // Combine the right PROJECT node with that on the left ...
+        PlanNode rightProject = rightChild.findAtOrBelow(Type.PROJECT);
+        if (rightProject != null) {
+            PlanNode leftProject = leftChild.findAtOrBelow(Type.PROJECT);
+            if (leftProject != null) {
+                List<Column> leftColumns = leftProject.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+                for (Column rightColumn : rightProject.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class)) {
+                    if (!leftColumns.contains(rightColumn)) leftColumns.add(rightColumn);
+                }
+            } else {
+                // Just create a project on the left side ...
+                leftProject = new PlanNode(Type.PROJECT);
+                leftProject.setProperty(Property.PROJECT_COLUMNS, rightProject.getProperty(Property.PROJECT_COLUMNS));
+                leftChild.getFirstChild().insertAsParent(leftProject);
+            }
+        }
+
+        // Accumulate any SELECT nodes from the right side and add to the left ...
+        PlanNode topRightSelect = rightChild.findAtOrBelow(Type.SELECT);
+        if (topRightSelect != null) {
+            PlanNode bottomRightSelect = topRightSelect;
+            while (true) {
+                if (bottomRightSelect.getFirstChild().isNot(Type.SELECT)) break;
+                bottomRightSelect = bottomRightSelect.getFirstChild();
+            }
+            topRightSelect.setParent(null);
+            bottomRightSelect.removeAllChildren();
+            // Place just above the left source ...
+            leftSource.getParent().addLastChild(topRightSelect);
+            leftSource.setParent(bottomRightSelect);
+        }
+
         // Now record that references to the right selector name should be removed ...
         SelectorName rightTableName = rightSource.getProperty(Property.SOURCE_NAME, SelectorName.class);
         SelectorName rightTableAlias = rightSource.getProperty(Property.SOURCE_ALIAS, SelectorName.class);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -69,8 +69,10 @@
      */
     protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
                                       PlanHints hints ) {
+        ruleStack.addFirst(MergeSetCriteria.INSTANCE);
         if (hints.hasJoin) {
             ruleStack.addFirst(ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM);
+            ruleStack.addFirst(RewriteIdentityJoins.INSTANCE);
         }
         ruleStack.addFirst(PushProjects.INSTANCE);
         if (hints.hasCriteria) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -70,6 +70,8 @@
  *         |
  *      PROJECT      with the list of columns being SELECTed
  *         |
+ *       GROUP       if 'GROUP BY' is used
+ *         |
  *      SELECT1
  *         |         One or more SELECT plan nodes that each have
  *      SELECT2      a single non-join constraint that are then all AND-ed
@@ -127,6 +129,9 @@
         // Attach criteria (on top) ...
         plan = attachCriteria(context, plan, query.getConstraint());
 
+        // Attach groupbys (on top) ...
+        // plan = attachGrouping(context,plan,query.getGroupBy());
+
         // Attach the project ...
         plan = attachProject(context, plan, query.getColumns(), usedSources);
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -173,6 +173,12 @@
         PROJECT_COLUMNS,
 
         /**
+         * For GROUP nodes, the ordered collection of columns used to group the result tuples. Value is a Collection of
+         * {@link Column} objects.
+         */
+        GROUP_COLUMNS,
+
+        /**
          * For SET_OPERATION nodes, the list of orderings for the results. Value is either a Collection of {@link Ordering}
          * objects or a collection of {@link SelectorName} objects (if the sorting is being done as an input to a merge-join).
          */
@@ -363,6 +369,31 @@
     }
 
     /**
+     * Determine if the supplied node is an ancestor of this node.
+     * 
+     * @param possibleAncestor the node that is to be determined if it is an ancestor
+     * @return true if the supplied node is indeed an ancestor, or false if it is not an ancestor
+     */
+    public boolean isBelow( PlanNode possibleAncestor ) {
+        PlanNode node = this;
+        while (node != null) {
+            if (node == possibleAncestor) return true;
+            node = node.getParent();
+        }
+        return false;
+    }
+
+    /**
+     * Determine if the supplied node is a descendant of this node.
+     * 
+     * @param possibleDescendant the node that is to be determined if it is a descendant
+     * @return true if the supplied node is indeed an ancestor, or false if it is not an ancestor
+     */
+    public boolean isAbove( PlanNode possibleDescendant ) {
+        return possibleDescendant != null && possibleDescendant.isBelow(this);
+    }
+
+    /**
      * Get the parent of this node.
      * 
      * @return the parent node, or null if this node has no parent

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.graph.query.plan;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -489,11 +490,10 @@
         List<PlanNode> potentiallyRemovableSources = new LinkedList<PlanNode>();
         do {
             // Remove the view from the selectors ...
-            if (node.getSelectors().contains(viewName)) {
+            if (node.getSelectors().remove(viewName)) {
                 switch (node.getType()) {
                     case PROJECT:
                         // Adjust the columns ...
-                        node.getSelectors().remove(viewName);
                         List<Column> columns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
                         if (columns != null) {
                             for (int i = 0; i != columns.size(); ++i) {
@@ -529,19 +529,41 @@
                             node.setProperty(Property.SELECT_CRITERIA, newConstraint);
                         }
                         break;
-                    case SET_OPERATION:
-                        break;
                     case SOURCE:
                         SelectorName sourceName = node.getProperty(Property.SOURCE_NAME, SelectorName.class);
                         assert sourceName.equals(sourceName); // selector name already matches
                         potentiallyRemovableSources.add(node);
                         break;
-                    default:
+                    case JOIN:
+                        JoinCondition joinCondition = node.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+                        JoinCondition newJoinCondition = replaceViewReferences(context, joinCondition, mappings, node);
+                        node.getSelectors().clear();
+                        node.setProperty(Property.JOIN_CONDITION, newJoinCondition);
+                        node.addSelectors(Visitors.getSelectorsReferencedBy(newJoinCondition));
+                        List<Constraint> joinConstraints = node.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
+                        if (joinConstraints != null && !joinConstraints.isEmpty()) {
+                            List<Constraint> newConstraints = new ArrayList<Constraint>(joinConstraints.size());
+                            for (Constraint joinConstraint : joinConstraints) {
+                                newConstraint = replaceReferences(context, joinConstraint, mappings, node);
+                                newConstraints.add(newConstraint);
+                                node.addSelectors(Visitors.getSelectorsReferencedBy(newConstraint));
+                            }
+                            node.setProperty(Property.JOIN_CONSTRAINTS, newConstraints);
+                        }
                         break;
+                    case ACCESS:
+                        // Nothing to do here, as the selector names are fixed elsewhere
+                        break;
+                    case SORT:
+                        break;
+                    case GROUP:
+                        // Don't yet use GROUP BY
+                    case SET_OPERATION:
+                    case DUP_REMOVE:
+                    case LIMIT:
+                    case NULL:
+                        break;
                 }
-                if (!node.getSelectors().contains(viewName)) {
-                    // Used to reference view, so it needs to at least reference
-                }
             }
             // Move to the parent ...
             node = node.getParent();
@@ -704,10 +726,10 @@
         return operand;
     }
 
-    public static JoinCondition replaceReferences( QueryContext context,
-                                                   JoinCondition joinCondition,
-                                                   ColumnMapping mapping,
-                                                   PlanNode node ) {
+    public static JoinCondition replaceViewReferences( QueryContext context,
+                                                       JoinCondition joinCondition,
+                                                       ColumnMapping mapping,
+                                                       PlanNode node ) {
         if (joinCondition instanceof EquiJoinCondition) {
             EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
             SelectorName replacement1 = condition.getSelector1Name();
@@ -749,6 +771,7 @@
             if (replacement2.equals(viewName)) replacement2 = sourceName;
             if (replacement1 == condition.getSelector1Name() && replacement2 == condition.getSelector2Name()) return condition;
             node.addSelector(replacement1, replacement2);
+            if (condition.getSelector2Path() == null) return new SameNodeJoinCondition(replacement1, replacement2);
             return new SameNodeJoinCondition(replacement1, replacement2, condition.getSelector2Path());
         }
         if (joinCondition instanceof ChildNodeJoinCondition) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -356,7 +356,7 @@
                                String... columnNames ) {
             CheckArg.isNotEmpty(tableName, "tableName");
             CheckArg.isNotEmpty(columnNames, "columnNames");
-            ImmutableTable existing = tables.get(tableName);
+            ImmutableTable existing = tables.get(new SelectorName(tableName));
             if (existing == null) {
                 throw new IllegalArgumentException(GraphI18n.tableDoesNotExist.text(tableName));
             }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -24,7 +24,6 @@
 package org.jboss.dna.graph.query.optimize;
 
 import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsSame.sameInstance;
 import static org.junit.Assert.assertThat;
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -133,15 +132,12 @@
 
         // Execute the rule ...
         PlanNode result = rule.execute(context, project, new LinkedList<OptimizerRule>());
+        System.out.println(project);
         System.out.println(result);
-        assertThat(result, is(sameInstance(project)));
+        assertThat(result.isSameAs(project), is(true));
         assertChildren(project, select1);
         assertChildren(select1, select2);
         assertChildren(select2, select3);
-        assertChildren(select3, source);
-        assertSameChildren(source, viewSelect);
-        assertSameChildren(source.getFirstChild(), viewSource);
-        assertSameChildren(source.getFirstChild().getFirstChild());
     }
 
     protected List<Column> columns( Column... columns ) {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -38,12 +38,14 @@
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Comparison;
 import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.FullTextSearch;
 import org.jboss.dna.graph.query.model.JoinType;
 import org.jboss.dna.graph.query.model.Literal;
 import org.jboss.dna.graph.query.model.Operator;
 import org.jboss.dna.graph.query.model.PropertyValue;
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.SetCriteria;
 import org.jboss.dna.graph.query.parse.SqlQueryParser;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
 import org.jboss.dna.graph.query.plan.JoinAlgorithm;
@@ -66,6 +68,7 @@
     private List<Integer> ruleExecutionOrder;
     private QueryContext context;
     private PlanNode node;
+    private boolean print = false;
 
     @Before
     public void beforeEach() {
@@ -73,8 +76,15 @@
         ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder(execContext);
         builder.addTable("t1", "c11", "c12", "c13");
         builder.addTable("t2", "c21", "c22", "c23");
+        builder.addTable("all", "a1", "a2", "a3", "a4", "primaryType", "mixins");
+        builder.addKey("all", "a1");
+        builder.addKey("all", "a3");
         builder.addView("v1", "SELECT c11, c12 AS c2 FROM t1 WHERE c13 < CAST('3' AS LONG)");
         builder.addView("v2", "SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11 = t2.c21");
+        builder.addView("type1",
+                        "SELECT all.a1, all.a2 FROM all WHERE all.primaryType IN ('t1','t0') AND all.mixins IN ('t3','t4')");
+        builder.addView("type2",
+                        "SELECT all.a3, all.a4 FROM all WHERE all.primaryType IN ('t2','t0') AND all.mixins IN ('t4','t5')");
         Schemata schemata = builder.build();
         context = new QueryContext(execContext, new PlanHints(), schemata);
 
@@ -282,6 +292,136 @@
         assertThat(node.isSameAs(project), is(true));
     }
 
+    @Test
+    public void shouldOptimizePlanForQueryUsingTypeView() {
+        node = optimize("SELECT type1.a1 AS a, type1.a2 AS b FROM type1 WHERE CONTAINS(type1.a2,'something')");
+
+        // Create the expected plan ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("all"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("all"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1", "a"), column("all", "a2", "b")));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
+        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+                                                                      new Literal("t1"), new Literal("t0")));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
+        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                      new Literal("t3"), new Literal("t4")));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("all"));
+        source.setProperty(Property.SOURCE_NAME, selector("all"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(access), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForQueryJoiningMultipleTypeViewsUsingIdentityEquiJoin() {
+        node = optimize("SELECT type1.a1 AS a, type1.a2 AS b, type2.a3 as c, type2.a4 as d "
+                        + "FROM type1 JOIN type2 ON type1.a1 = type2.a3 WHERE CONTAINS(type1.a2,'something')");
+
+        // Create the expected plan ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("all"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("all"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1", "a"),
+                                                              column("all", "a2", "b"),
+                                                              column("all", "a3", "c"),
+                                                              column("all", "a4", "d")));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
+        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+                                                                      new Literal("t1"), new Literal("t0"), new Literal("t2")));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
+        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                      new Literal("t3"), new Literal("t4"), new Literal("t5")));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("all"));
+        source.setProperty(Property.SOURCE_NAME, selector("all"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(access), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForQueryJoiningMultipleTypeViewsUsingNonIdentityEquiJoin() {
+        node = optimize("SELECT type1.a1 AS a, type1.a2 AS b, type2.a3 as c, type2.a4 as d "
+                        + "FROM type1 JOIN type2 ON type1.a2 = type2.a3 WHERE CONTAINS(type1.a1,'something')");
+
+        // Create the expected plan ...
+        PlanNode project = new PlanNode(Type.PROJECT, selector("all"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1", "a"),
+                                                              column("all", "a2", "b"),
+                                                              column("all", "a3", "c"),
+                                                              column("all", "a4", "d")));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a1"), "something"));
+        PlanNode join = new PlanNode(Type.JOIN, select1, selector("all"));
+        join.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
+        join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("all"), name("a2"), selector("all"), name("a3")));
+
+        PlanNode leftAccess = new PlanNode(Type.ACCESS, join, selector("all"));
+        PlanNode leftProject = new PlanNode(Type.PROJECT, leftAccess, selector("all"));
+        leftProject.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1"), column("all", "a2")));
+        PlanNode leftSelect1 = new PlanNode(Type.SELECT, leftProject, selector("all"));
+        leftSelect1.setProperty(Property.SELECT_CRITERIA,
+                                new SetCriteria(new PropertyValue(selector("all"), name("primaryType")), new Literal("t1"),
+                                                new Literal("t0")));
+        PlanNode leftSelect2 = new PlanNode(Type.SELECT, leftSelect1, selector("all"));
+        leftSelect2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                          new Literal("t3"), new Literal("t4")));
+        PlanNode leftSource = new PlanNode(Type.SOURCE, leftSelect2, selector("all"));
+        leftSource.setProperty(Property.SOURCE_NAME, selector("all"));
+        leftSource.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        PlanNode rightAccess = new PlanNode(Type.ACCESS, join, selector("all"));
+        PlanNode rightProject = new PlanNode(Type.PROJECT, rightAccess, selector("all"));
+        rightProject.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a3"), column("all", "a4")));
+        PlanNode rightSelect1 = new PlanNode(Type.SELECT, rightProject, selector("all"));
+        rightSelect1.setProperty(Property.SELECT_CRITERIA,
+                                 new SetCriteria(new PropertyValue(selector("all"), name("primaryType")), new Literal("t2"),
+                                                 new Literal("t0")));
+        PlanNode rightSelect2 = new PlanNode(Type.SELECT, rightSelect1, selector("all"));
+        rightSelect2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                           new Literal("t4"), new Literal("t5")));
+        PlanNode rightSource = new PlanNode(Type.SOURCE, rightSelect2, selector("all"));
+        rightSource.setProperty(Property.SOURCE_NAME, selector("all"));
+        rightSource.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(project), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForQueryJoiningMultipleTypeViewsUsingSameNodeJoin() {
+        node = optimize("SELECT type1.a1 AS a, type1.a2 AS b, type2.a3 as c, type2.a4 as d "
+                        + "FROM type1 JOIN type2 ON ISSAMENODE(type1,type2) WHERE CONTAINS(type1.a2,'something')");
+
+        // Create the expected plan ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("all"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("all"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1", "a"),
+                                                              column("all", "a2", "b"),
+                                                              column("all", "a3", "c"),
+                                                              column("all", "a4", "d")));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
+        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+                                                                      new Literal("t1"), new Literal("t0"), new Literal("t2")));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
+        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                      new Literal("t3"), new Literal("t4"), new Literal("t5")));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("all"));
+        source.setProperty(Property.SOURCE_NAME, selector("all"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(access), is(true));
+    }
+
     // ----------------------------------------------------------------------------------------------------------------
     // Utility methods ...
     // ----------------------------------------------------------------------------------------------------------------
@@ -313,9 +453,11 @@
         assertThat("Problems planning query: " + sql + "\n" + problems, problems.hasErrors(), is(false));
         PlanNode optimized = new RuleBasedOptimizer().optimize(context, plan);
         assertThat("Problems optimizing query: " + sql + "\n" + problems, problems.hasErrors(), is(false));
-        System.out.println();
-        System.out.println(sql);
-        System.out.println(optimized);
+        if (print) {
+            System.out.println();
+            System.out.println(sql);
+            System.out.println(optimized);
+        }
         return optimized;
     }
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -73,7 +73,7 @@
     @Test
     public void shouldTranslateFromXPathContainingExplicitPath() {
         assertThat(xpath("/jcr:root/a"),
-                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'a' AND DEPTH(nodeSet1) = 1"));
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'a' AND DEPTH(nodeSet1) = CAST(1 AS LONG)"));
         assertThat(xpath("/jcr:root/a/b"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b'"));
         assertThat(xpath("/jcr:root/a/b/c"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b/c'"));
         assertThat(xpath("/jcr:root/a/b/c/d"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b/c/d'"));
@@ -218,13 +218,13 @@
     @Test
     public void shouldTranslateFromXPathOfNodeWithNameUnderRoot() {
         assertThat(xpath("/jcr:root/element(nodeName,*)"),
-                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = 1"));
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = CAST(1 AS LONG)"));
 
         assertThat(xpath("/jcr:root/nodeName"),
-                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = 1"));
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = CAST(1 AS LONG)"));
 
         assertThat(xpath("nodeName"),
-                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = 1"));
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = CAST(1 AS LONG)"));
     }
 
     @Test


From dna-commits at lists.jboss.org  Wed Oct 21 13:02:10 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 21 Oct 2009 13:02:10 -0400
Subject: [dna-commits] DNA SVN: r1299 - in trunk/dna-graph/src:
	test/java/org/jboss/dna/graph/query/optimize and 1 other directory.
Message-ID: <200910211702.n9LH2AsF005540@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-21 13:02:10 -0400 (Wed, 21 Oct 2009)
New Revision: 1299

Removed:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
Log:
DNA-467 Removed optimization rule that combined set criteria (e.g., 'IN (1,2,3)'), since it was AND-ing together OR-ed criteria and was thus invalid.

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	2009-10-21 17:01:35 UTC (rev 1298)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	2009-10-21 17:02:10 UTC (rev 1299)
@@ -1,113 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.graph.query.optimize;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
-import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.model.Constraint;
-import org.jboss.dna.graph.query.model.SetCriteria;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.plan.PlanNode.Property;
-import org.jboss.dna.graph.query.plan.PlanNode.Type;
-
-/**
- * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
- * the same selector.
- * 
- * @param <SimilarityType> the type used to compare and identify similar nodes
- * @param <ConstraintType> the concrete type of Constraint objects that are being merged
- */
- at Immutable
-public abstract class AbstractMergeSelectNodes<SimilarityType, ConstraintType extends Constraint> implements OptimizerRule {
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
-     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
-     */
-    @SuppressWarnings( "unchecked" )
-    public PlanNode execute( QueryContext context,
-                             PlanNode plan,
-                             LinkedList<OptimizerRule> ruleStack ) {
-        Map<SimilarityType, LinkedList<PlanNode>> mergeables = new HashMap<SimilarityType, LinkedList<PlanNode>>();
-        for (PlanNode source : plan.findAllAtOrBelow(Type.SOURCE)) {
-            // Walk up from the SOURCE and look for all SELECT nodes ...
-            PlanNode node = source.getParent();
-            while (node != null && node.is(Type.SELECT)) {
-                Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-                SimilarityType operand = getSimilarityKey(constraint);
-                if (operand != null) {
-                    LinkedList<PlanNode> nodes = mergeables.get(operand);
-                    if (nodes == null) {
-                        nodes = new LinkedList<PlanNode>();
-                        mergeables.put(operand, nodes);
-                    }
-                    nodes.addFirst(node); // So that the list is in the downward order they appear in the plan
-                }
-                node = node.getParent();
-            }
-
-            // Merge all mergeable SELECT nodes ...
-            for (LinkedList<PlanNode> mergeable : mergeables.values()) {
-                if (mergeable.size() > 1) {
-                    Iterator<PlanNode> iter = mergeable.iterator();
-                    PlanNode firstSelect = iter.next();
-                    Constraint constraint = firstSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-                    while (iter.hasNext()) {
-                        PlanNode nextSelect = iter.next();
-                        Constraint nextConstraint = nextSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-                        constraint = merge((ConstraintType)constraint, (ConstraintType)nextConstraint);
-                        nextSelect.extractFromParent();
-                    }
-                    firstSelect.setProperty(Property.SELECT_CRITERIA, constraint);
-                }
-            }
-            mergeables.clear();
-        }
-        return plan;
-    }
-
-    /**
-     * Obtain the similarity key that will be used to determine whether two SELECT nodes should be merged.
-     * 
-     * @param constraint the constraint from the SELECT node
-     * @return the similarity key, or null if the constraint should not be merged
-     */
-    protected abstract SimilarityType getSimilarityKey( Constraint constraint );
-
-    /**
-     * Merge the two constraints.
-     * 
-     * @param firstConstraint the first constraint
-     * @param secondConstraint the second constraint
-     * @return the merged constraint
-     */
-    protected abstract Constraint merge( ConstraintType firstConstraint,
-                                         ConstraintType secondConstraint );
-}

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	2009-10-21 17:01:35 UTC (rev 1298)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	2009-10-21 17:02:10 UTC (rev 1299)
@@ -1,89 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.graph.query.optimize;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.query.model.Constraint;
-import org.jboss.dna.graph.query.model.DynamicOperand;
-import org.jboss.dna.graph.query.model.SetCriteria;
-import org.jboss.dna.graph.query.model.StaticOperand;
-
-/**
- * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
- * the same selector.
- */
- at Immutable
-public class MergeSetCriteria extends AbstractMergeSelectNodes<DynamicOperand, SetCriteria> {
-
-    public static final MergeSetCriteria INSTANCE = new MergeSetCriteria();
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#getSimilarityKey(org.jboss.dna.graph.query.model.Constraint)
-     */
-    @Override
-    protected DynamicOperand getSimilarityKey( Constraint constraint ) {
-        if (constraint instanceof SetCriteria) {
-            // Look for the list of plan nodes for this operand ...
-            return ((SetCriteria)constraint).getLeftOperand();
-        }
-        return null;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#merge(org.jboss.dna.graph.query.model.Constraint,
-     *      org.jboss.dna.graph.query.model.Constraint)
-     */
-    @Override
-    protected Constraint merge( SetCriteria firstConstraint,
-                                SetCriteria secondConstraint ) {
-        assert firstConstraint.getLeftOperand().equals(secondConstraint.getLeftOperand());
-        Set<StaticOperand> allOperands = new HashSet<StaticOperand>();
-        List<StaticOperand> orderedOperands = new ArrayList<StaticOperand>(firstConstraint.getRightOperands());
-        allOperands.addAll(firstConstraint.getRightOperands());
-        for (StaticOperand second : secondConstraint.getRightOperands()) {
-            if (allOperands.add(second)) {
-                orderedOperands.add(second);
-            }
-        }
-        return new SetCriteria(firstConstraint.getLeftOperand(), orderedOperands);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        return getClass().getSimpleName();
-    }
-}

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-21 17:01:35 UTC (rev 1298)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-21 17:02:10 UTC (rev 1299)
@@ -69,7 +69,6 @@
      */
     protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
                                       PlanHints hints ) {
-        ruleStack.addFirst(MergeSetCriteria.INSTANCE);
         if (hints.hasJoin) {
             ruleStack.addFirst(ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM);
             ruleStack.addFirst(RewriteIdentityJoins.INSTANCE);

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-10-21 17:01:35 UTC (rev 1298)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-10-21 17:02:10 UTC (rev 1299)
@@ -332,11 +332,17 @@
         select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
         PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
         select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
-                                                                      new Literal("t1"), new Literal("t0"), new Literal("t2")));
+                                                                      new Literal("t1"), new Literal("t0")));
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
         select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
-                                                                      new Literal("t3"), new Literal("t4"), new Literal("t5")));
-        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("all"));
+                                                                      new Literal("t3"), new Literal("t4")));
+        PlanNode select4 = new PlanNode(Type.SELECT, select3, selector("all"));
+        select4.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+                                                                      new Literal("t2"), new Literal("t0")));
+        PlanNode select5 = new PlanNode(Type.SELECT, select4, selector("all"));
+        select5.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                      new Literal("t4"), new Literal("t5")));
+        PlanNode source = new PlanNode(Type.SOURCE, select5, selector("all"));
         source.setProperty(Property.SOURCE_NAME, selector("all"));
         source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
 
@@ -410,11 +416,17 @@
         select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
         PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
         select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
-                                                                      new Literal("t1"), new Literal("t0"), new Literal("t2")));
+                                                                      new Literal("t1"), new Literal("t0")));
         PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
         select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
-                                                                      new Literal("t3"), new Literal("t4"), new Literal("t5")));
-        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("all"));
+                                                                      new Literal("t3"), new Literal("t4")));
+        PlanNode select4 = new PlanNode(Type.SELECT, select3, selector("all"));
+        select4.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+                                                                      new Literal("t2"), new Literal("t0")));
+        PlanNode select5 = new PlanNode(Type.SELECT, select4, selector("all"));
+        select5.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                      new Literal("t4"), new Literal("t5")));
+        PlanNode source = new PlanNode(Type.SOURCE, select5, selector("all"));
         source.setProperty(Property.SOURCE_NAME, selector("all"));
         source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
 


From dna-commits at lists.jboss.org  Wed Oct 21 13:02:40 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 21 Oct 2009 13:02:40 -0400
Subject: [dna-commits] DNA SVN: r1300 - in
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/query:
	optimize and 2 other directories.
Message-ID: <200910211702.n9LH2ej7005596@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-21 13:02:40 -0400 (Wed, 21 Oct 2009)
New Revision: 1300

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RaiseSelectCriteria.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
Log:
DNA-467 Added query optimizer rule that raises criteria added because of a view but below a join, in case that same criteria could be applied to the other side of the join.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-10-21 17:02:10 UTC (rev 1299)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java	2009-10-21 17:02:40 UTC (rev 1300)
@@ -23,7 +23,6 @@
  */
 package org.jboss.dna.graph.query.model;
 
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -33,7 +32,6 @@
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.NamespaceRegistry;
 import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.ValueFactory;
 
 /**
  * A set of common visitors that can be reused or extended, and methods that provide easy construction and calling of visitors.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java	2009-10-21 17:02:10 UTC (rev 1299)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java	2009-10-21 17:02:40 UTC (rev 1300)
@@ -38,6 +38,7 @@
 import org.jboss.dna.graph.query.model.JoinCondition;
 import org.jboss.dna.graph.query.model.PropertyExistence;
 import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
 import org.jboss.dna.graph.query.model.SelectorName;
 import org.jboss.dna.graph.query.model.Visitable;
 import org.jboss.dna.graph.query.model.Visitors;
@@ -98,10 +99,49 @@
                     node = node.getParent();
                 }
             }
+
+            if (joinCondition instanceof EquiJoinCondition || joinCondition instanceof SameNodeJoinCondition) {
+                // Then for each side of the join ...
+                PlanNode left = join.getFirstChild();
+                PlanNode right = join.getLastChild();
+                copySelectNodes(context, left, right);
+                copySelectNodes(context, right, left);
+            }
         }
         return plan;
     }
 
+    protected void copySelectNodes( QueryContext context,
+                                    PlanNode fromJoined,
+                                    PlanNode toJoined ) {
+        // Find all of the selectors used on the 'to' side ...
+        Set<SelectorName> toSelectors = new HashSet<SelectorName>();
+        for (PlanNode toNode : toJoined.findAllAtOrBelow()) {
+            toSelectors.addAll(toNode.getSelectors());
+        }
+
+        PlanNode nodeBelowSelects = null;
+
+        // Walk down the 'fromJoined' side looking for all SELECT nodes ...
+        for (PlanNode select : fromJoined.findAllAtOrBelow(Type.SELECT)) {
+            // If all of the SELECT's selectors are also found on the right ...
+            if (toSelectors.containsAll(select.getSelectors())) {
+                // Copy the criteria ...
+                PlanNode copy = new PlanNode(Type.SELECT, select.getSelectors());
+                copy.setProperty(Property.SELECT_CRITERIA, select.getProperty(Property.SELECT_CRITERIA));
+
+                if (nodeBelowSelects == null) {
+                    nodeBelowSelects = toJoined.findAtOrBelow(Type.SOURCE, Type.JOIN, Type.SET_OPERATION, Type.NULL);
+                    if (nodeBelowSelects == null) {
+                        nodeBelowSelects = toJoined;
+                    }
+                }
+                nodeBelowSelects.insertAsParent(copy);
+                nodeBelowSelects = copy;
+            }
+        }
+    }
+
     protected PlanNode copySelectNode( QueryContext context,
                                        PlanNode selectNode,
                                        SelectorName selectorName,

Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RaiseSelectCriteria.java (from rev 1299, trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/CopyCriteria.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RaiseSelectCriteria.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RaiseSelectCriteria.java	2009-10-21 17:02:40 UTC (rev 1300)
@@ -0,0 +1,254 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.PropertyExistence;
+import org.jboss.dna.graph.query.model.PropertyValue;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.Visitable;
+import org.jboss.dna.graph.query.model.Visitors;
+import org.jboss.dna.graph.query.model.Visitors.AbstractVisitor;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanUtil;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that moves up higher in the plan any SELECT node that appears below a JOIN node and
+ * that applies to selectors that are on the other side of the join.
+ * <p>
+ * This step is often counterintuitive, since one of the best optimizations a query optimizer can do is to
+ * {@link PushSelectCriteria push down SELECT nodes} as far as they'll go. But consider the case of a SOURCE node that appears
+ * below a JOIN, where the SOURCE node is a view. The optimizer {@link ReplaceViews replaces the SOURCE node with the view
+ * definition}, and if the view definition includes a SELECT node, that SELECT node appears below the JOIN. Plus, that SELECT node
+ * is already pushed down as far as it can go (assuming the view isn't defined to use another view). However, the JOIN may use the
+ * same selector on the opposite side, and it may be possible that the same SELECT node may apply to the other side of the JOIN.
+ * In this case, we can push <i>up</i> the SELECT node higher than the JOIN, and then the push-down would cause the SELECT to be
+ * copied to both sides of the JOIN.
+ * </p>
+ * <p>
+ * Here is an example plan that involves a JOIN of two SOURCE nodes:
+ * 
+ * <pre>
+ *          ...
+ *           |
+ *         JOIN
+ *        /     \
+ *       /       SOURCE({@link Property#SOURCE_NAME SOURCE_NAME}=&quot;t1&quot;)   
+ *      /
+ *   SOURCE({@link Property#SOURCE_NAME SOURCE_NAME}=&quot;v1&quot;)
+ * </pre>
+ * 
+ * If the right-side SOURCE references the "t1" table, and the left-side SOURCE references a view "v1" defined as "
+ * <code>SELECT * FROM t1 WHERE t1.id &lt; 3</code>", then the {@link ReplaceViews} rule would change this plan to be:
+ * 
+ * <pre>
+ *           ...
+ *           |
+ *         JOIN
+ *        /     \
+ *       /       SOURCE({@link Property#SOURCE_NAME SOURCE_NAME}=&quot;t1&quot;)   
+ *      /
+ *    PROJECT
+ *      |
+ *    SELECT     applies the &quot;t1.id &lt; 3&quot; criteria
+ *      |
+ *   SOURCE({@link Property#SOURCE_NAME SOURCE_NAME}=&quot;t1&quot;)
+ * </pre>
+ * 
+ * Again, the SELECT cannot be pushed down any further. But the whole query can be made more efficient - because the SELECT on the
+ * left-side of the JOIN will include only those tuples from 't1' that satisfy the SELECT, the JOIN will only include those tuples
+ * that also satisfy this criteria, even though more tuples are returned from the right-side SOURCE.
+ * </p>
+ * <p>
+ * In this case, the left-hand SELECT can actually be copied to the right-hand side of the JOIN, resulting in:
+ * 
+ * <pre>
+ *           ...
+ *           |
+ *         JOIN
+ *        /     \
+ *       /       SELECT   applies the &quot;t1.id &lt; 3&quot; criteria
+ *      /          |
+ *    PROJECT    SOURCE({@link Property#SOURCE_NAME SOURCE_NAME}=&quot;t1&quot;)   
+ *      |
+ *    SELECT   applies the &quot;t1.id &lt; 3&quot; criteria
+ *      |
+ *   SOURCE({@link Property#SOURCE_NAME SOURCE_NAME}=&quot;t1&quot;)
+ * </pre>
+ * 
+ * </p>
+ */
+ at Immutable
+public class RaiseSelectCriteria implements OptimizerRule {
+
+    public static final RaiseSelectCriteria INSTANCE = new RaiseSelectCriteria();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+     */
+    public PlanNode execute( QueryContext context,
+                             PlanNode plan,
+                             LinkedList<OptimizerRule> ruleStack ) {
+        Set<PlanNode> copiedSelectNodes = new HashSet<PlanNode>();
+
+        for (PlanNode join : plan.findAllAtOrBelow(Type.JOIN)) {
+            // Get the join condition ...
+            JoinCondition joinCondition = join.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+            if (joinCondition instanceof EquiJoinCondition) {
+                EquiJoinCondition equiJoinCondition = (EquiJoinCondition)joinCondition;
+                SelectorName selector1 = equiJoinCondition.getSelector1Name();
+                SelectorName selector2 = equiJoinCondition.getSelector2Name();
+                Name property1 = equiJoinCondition.getProperty1Name();
+                Name property2 = equiJoinCondition.getProperty2Name();
+
+                // Walk up the tree looking for SELECT nodes that apply to one of the sides ...
+                PlanNode node = join.getParent();
+                while (node != null) {
+                    if (!copiedSelectNodes.contains(node)) {
+                        PlanNode copy = copySelectNode(context, node, selector1, property1, selector2, property2);
+                        if (copy != null) {
+                            node.insertAsParent(copy);
+                            copiedSelectNodes.add(node);
+                            copiedSelectNodes.add(copy);
+                        } else {
+                            copy = copySelectNode(context, node, selector2, property2, selector1, property1);
+                            if (copy != null) {
+                                node.insertAsParent(copy);
+                                copiedSelectNodes.add(node);
+                                copiedSelectNodes.add(copy);
+                            }
+                        }
+                    }
+                    node = node.getParent();
+                }
+            }
+        }
+        return plan;
+    }
+
+    protected PlanNode copySelectNode( QueryContext context,
+                                       PlanNode selectNode,
+                                       SelectorName selectorName,
+                                       Name propertyName,
+                                       SelectorName copySelectorName,
+                                       Name copyPropertyName ) {
+        if (selectNode.isNot(Type.SELECT)) return null;
+        if (selectNode.getSelectors().size() != 1 || !selectNode.getSelectors().contains(selectorName)) return null;
+
+        Constraint constraint = selectNode.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+        Set<Column> columns = getColumnsReferencedBy(constraint, context.getExecutionContext());
+        if (columns.size() != 1) return null;
+        Column column = columns.iterator().next();
+        if (!column.getSelectorName().equals(selectorName)) return null;
+        if (!column.getPropertyName().equals(propertyName)) return null;
+
+        // We know that this constraint ONLY applies to the referenced selector and property,
+        // so we will duplicate this constraint ...
+
+        // Create the new node ...
+        PlanNode copy = new PlanNode(Type.SELECT, copySelectorName);
+
+        // Copy the constraint, but change the references to the copy selector and property ...
+        ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+        PlanUtil.ColumnMapping mappings = new PlanUtil.ColumnMapping(selectorName);
+        mappings.map(stringFactory.create(propertyName), new Column(copySelectorName, copyPropertyName,
+                                                                    stringFactory.create(copyPropertyName)));
+        Constraint newCriteria = PlanUtil.replaceReferences(context, constraint, mappings, copy);
+        copy.setProperty(Property.SELECT_CRITERIA, newCriteria);
+
+        return copy;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getClass().getSimpleName();
+    }
+
+    /**
+     * Get the set of Column objects that represent those columns referenced by the visitable object.
+     * 
+     * @param visitable the object to be visited
+     * @param context the context; may not be null
+     * @return the set of Column objects, with column names that always are the string-form of the
+     *         {@link Column#getPropertyName() property name}; never null
+     */
+    public static Set<Column> getColumnsReferencedBy( Visitable visitable,
+                                                      ExecutionContext context ) {
+        if (visitable == null) return Collections.emptySet();
+        final ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
+        final Set<Column> symbols = new HashSet<Column>();
+        // Walk the entire structure, so only supply a StrategyVisitor (that does no navigation) ...
+        Visitors.visitAll(visitable, new AbstractVisitor() {
+            protected void addColumnFor( SelectorName selectorName,
+                                         Name property ) {
+                symbols.add(new Column(selectorName, property, stringFactory.create(property)));
+            }
+
+            @Override
+            public void visit( Column column ) {
+                symbols.add(column);
+            }
+
+            @Override
+            public void visit( EquiJoinCondition joinCondition ) {
+                addColumnFor(joinCondition.getSelector1Name(), joinCondition.getProperty1Name());
+                addColumnFor(joinCondition.getSelector2Name(), joinCondition.getProperty2Name());
+            }
+
+            @Override
+            public void visit( PropertyExistence prop ) {
+                addColumnFor(prop.getSelectorName(), prop.getPropertyName());
+            }
+
+            @Override
+            public void visit( PropertyValue prop ) {
+                addColumnFor(prop.getSelectorName(), prop.getPropertyName());
+            }
+        });
+        return symbols;
+    }
+
+}

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java	2009-10-21 17:02:10 UTC (rev 1299)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java	2009-10-21 17:02:40 UTC (rev 1300)
@@ -43,6 +43,32 @@
 /**
  * An {@link OptimizerRule optimizer rule} that replaces any SOURCE nodes that happen to be {@link View views}. This rewriting
  * changes all of the elements of the plan that reference the SOURCE and it's columns, including criteria, project nodes, etc.
+ * <p>
+ * For example, here is the portion of a plan that uses a single SOURCE that is defined to use a view.
+ * 
+ * <pre>
+ *          ...
+ *           |
+ *        SOURCE1
+ * </pre>
+ * 
+ * This same SOURCE node is then replaced with the view's definition:
+ * 
+ * <pre>
+ *          ...
+ *           |
+ *        PROJECT      with the list of columns being SELECTed
+ *           |
+ *        SELECT1
+ *           |         One or more SELECT plan nodes that each have
+ *        SELECT2      a single non-join constraint that are then all AND-ed
+ *           |         together
+ *        SELECTn
+ *           |
+ *        SOURCE
+ * </pre>
+ * 
+ * </p>
  */
 @Immutable
 public class ReplaceViews implements OptimizerRule {
@@ -113,6 +139,12 @@
         } while (foundViews);
 
         if (foundViews) {
+            // We'll need to try to push up criteria from the join, but we only should do this after this rule
+            // is completely done ...
+            if (!(ruleStack.getFirst() instanceof RaiseSelectCriteria)) {
+                ruleStack.addFirst(RaiseSelectCriteria.INSTANCE);
+            }
+
             // We re-wrote at least one SOURCE, but the resulting plan tree for the view could actually reference
             // other views. Therefore, re-run this rule ...
             ruleStack.addFirst(this);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-10-21 17:02:10 UTC (rev 1299)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-10-21 17:02:40 UTC (rev 1300)
@@ -1090,6 +1090,40 @@
     }
 
     /**
+     * Find all of the nodes that are at or below this node.
+     * 
+     * @return the collection of nodes that are at or below this node; never null and never empty
+     */
+    public List<PlanNode> findAllAtOrBelow() {
+        return findAllAtOrBelow(Traversal.PRE_ORDER);
+    }
+
+    /**
+     * Find all of the nodes that are at or below this node.
+     * 
+     * @param order the order to traverse; may not be null
+     * @return the collection of nodes that are at or below this node; never null and never empty
+     */
+    public List<PlanNode> findAllAtOrBelow( Traversal order ) {
+        assert order != null;
+        List<PlanNode> results = new LinkedList<PlanNode>();
+        LinkedList<PlanNode> queue = new LinkedList<PlanNode>();
+        queue.add(this);
+        while (!queue.isEmpty()) {
+            PlanNode aNode = queue.poll();
+            switch (order) {
+                case LEVEL_ORDER:
+                    queue.addAll(aNode.getChildren());
+                    break;
+                case PRE_ORDER:
+                    queue.addAll(0, aNode.getChildren());
+                    break;
+            }
+        }
+        return results;
+    }
+
+    /**
      * Find all of the nodes of the specified type that are at or below this node.
      * 
      * @param typeToFind the type of node to find; may not be null

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java	2009-10-21 17:02:10 UTC (rev 1299)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java	2009-10-21 17:02:40 UTC (rev 1300)
@@ -113,6 +113,15 @@
         return null;
     }
 
+    /**
+     * Create the {@link ProcessingComponent} that processes a single {@link Type#ACCESS} branch of a query plan.
+     * 
+     * @param context the context in which query is being evaluated; never null
+     * @param accessNode the node in the query plan that represents the {@link Type#ACCESS} plan; never null
+     * @param resultColumns the columns that are to be returned; never null
+     * @param analyzer the criteria analyzer; never null
+     * @return the processing component; may not be null
+     */
     protected abstract ProcessingComponent createAccessComponent( QueryContext context,
                                                                   PlanNode accessNode,
                                                                   Columns resultColumns,
@@ -141,10 +150,11 @@
         ProcessingComponent component = null;
         switch (node.getType()) {
             case ACCESS:
-                // Create the component under the ACCESS ...
+                // Create the component to handle the ACCESS node ...
                 assert node.getChildCount() == 1;
-                // Don't do anything special with an access node at the moment ...
-                component = createComponent(context, node.getFirstChild(), columns, analyzer);
+                component = createAccessComponent(context, node, columns, analyzer);
+                // // Don't do anything special with an access node at the moment ...
+                // component = createComponent(context, node.getFirstChild(), columns, analyzer);
                 break;
             case DUP_REMOVE:
                 // Create the component under the DUP_REMOVE ...


From dna-commits at lists.jboss.org  Wed Oct 21 13:21:57 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 21 Oct 2009 13:21:57 -0400
Subject: [dna-commits] DNA SVN: r1301 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <200910211721.n9LHLv9v009599@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-21 13:21:57 -0400 (Wed, 21 Oct 2009)
New Revision: 1301

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
Log:
Corrected compiler warnings and JavaDoc errors

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-21 17:02:40 UTC (rev 1300)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-21 17:21:57 UTC (rev 1301)
@@ -72,14 +72,14 @@
                   boolean isDeep,
                   boolean isSessionScoped ) throws RepositoryException {
         assert nodeLocation != null;
-        
+
         UUID lockUuid = UUID.randomUUID();
         UUID nodeUuid = uuidFor(nodeLocation);
 
         if (nodeUuid == null) {
             throw new RepositoryException(JcrI18n.uuidRequiredForLock.text(nodeLocation));
         }
-        
+
         DnaLock lock = new DnaLock(lockOwner, lockUuid, nodeUuid, isDeep, isSessionScoped);
 
         Graph.Batch batch = repository.createSystemGraph().batch();
@@ -95,8 +95,8 @@
                      propFactory.create(DnaLexicon.WORKSPACE, workspaceName),
                      propFactory.create(DnaLexicon.LOCKED_NODE, nodeUuid.toString()),
                      propFactory.create(DnaLexicon.IS_SESSION_SCOPED, isSessionScoped),
-                  // This gets set after the lock succeeds and the lock token gets added to the session
-                     propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, false),  
+                     // This gets set after the lock succeeds and the lock token gets added to the session
+                     propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, false),
                      lockOwnerProp,
                      lockIsDeepProp).ifAbsent().and();
         batch.execute();
@@ -129,7 +129,7 @@
 
             Graph.Batch batch = repository.createSystemGraph().batch();
 
-            batch.delete(pathFactory.create(locksPath, pathFactory.createSegment(lock.lockUuid.toString())));
+            batch.delete(pathFactory.create(locksPath, pathFactory.createSegment(lock.getUuid().toString())));
             batch.remove(JcrLexicon.LOCK_OWNER, JcrLexicon.LOCK_IS_DEEP).on(lock.nodeUuid);
             batch.execute();
             workspaceLocksByNodeUuid.remove(lock.nodeUuid);
@@ -151,22 +151,24 @@
         ValueFactory<Boolean> booleanFactory = context.getValueFactories().getBooleanFactory();
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
-        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph().getNodeAt(pathFactory.create(locksPath,
-                                                                                        pathFactory.createSegment(lockToken)));
+        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph()
+                                                      .getNodeAt(pathFactory.create(locksPath,
+                                                                                    pathFactory.createSegment(lockToken)));
 
         return booleanFactory.create(lockNode.getProperty(DnaLexicon.IS_HELD_BY_SESSION).getFirstValue());
-        
+
     }
-    
+
     void setHeldBySession( String lockToken,
                            boolean value ) {
         PropertyFactory propFactory = context.getPropertyFactory();
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
-        repository.createSystemGraph().set(propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, value)).on(pathFactory.create(locksPath,
-                                                                                                                           pathFactory.createSegment(lockToken)));
+        repository.createSystemGraph()
+                  .set(propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, value))
+                  .on(pathFactory.create(locksPath, pathFactory.createSegment(lockToken)));
     }
-    
+
     /**
      * Returns the lock that corresponds to the given lock token
      * 
@@ -188,17 +190,17 @@
     /**
      * Returns the lock that corresponds to the given UUID
      * 
-     * @param nodeUuid the node UUID
-     * @return the corresponding lock, possibly null
+     * @param nodeLocation the node UUID
+     * @return the corresponding lock, possibly null if there is no such lock
+     * @throws RepositoryException if there is a problem obtaining the information for a lock
      */
     DnaLock lockFor( Location nodeLocation ) throws RepositoryException {
         UUID nodeUuid = uuidFor(nodeLocation);
         if (nodeUuid == null) return null;
         return workspaceLocksByNodeUuid.get(nodeUuid);
     }
-    
 
-    UUID uuidFor( Location location ) throws RepositoryException {
+    UUID uuidFor( Location location ) {
         if (location.getUuid() != null) return location.getUuid();
 
         org.jboss.dna.graph.property.Property uuidProp = location.getIdProperty(JcrLexicon.UUID);
@@ -246,10 +248,15 @@
             this.sessionScoped = sessionScoped;
         }
 
+        @SuppressWarnings( "synthetic-access" )
         public boolean isLive() {
             return workspaceLocksByNodeUuid.containsKey(nodeUuid);
         }
 
+        public UUID getUuid() {
+            return lockUuid;
+        }
+
         public boolean isDeep() {
             return deep;
         }
@@ -266,6 +273,7 @@
             return lockUuid.toString();
         }
 
+        @SuppressWarnings( "synthetic-access" )
         public Lock lockFor( SessionCache cache ) throws RepositoryException {
             final AbstractJcrNode node = cache.findJcrNode(Location.create(nodeUuid));
             final JcrSession session = cache.session();
@@ -287,7 +295,7 @@
                     return deep;
                 }
 
-                public boolean isLive() throws RepositoryException {
+                public boolean isLive() {
                     return workspaceLocksByNodeUuid.containsKey(nodeUuid);
                 }
 


From dna-commits at lists.jboss.org  Wed Oct 21 13:29:08 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 21 Oct 2009 13:29:08 -0400
Subject: [dna-commits] DNA SVN: r1302 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize.
Message-ID: <200910211729.n9LHT8Ea010829@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-21 13:29:08 -0400 (Wed, 21 Oct 2009)
New Revision: 1302

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
Log:
Fixed merge conflicts

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	2009-10-21 17:29:08 UTC (rev 1302)
@@ -0,0 +1,113 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.SetCriteria;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
+ * the same selector.
+ * 
+ * @param <SimilarityType> the type used to compare and identify similar nodes
+ * @param <ConstraintType> the concrete type of Constraint objects that are being merged
+ */
+ at Immutable
+public abstract class AbstractMergeSelectNodes<SimilarityType, ConstraintType extends Constraint> implements OptimizerRule {
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+     */
+    @SuppressWarnings( "unchecked" )
+    public PlanNode execute( QueryContext context,
+                             PlanNode plan,
+                             LinkedList<OptimizerRule> ruleStack ) {
+        Map<SimilarityType, LinkedList<PlanNode>> mergeables = new HashMap<SimilarityType, LinkedList<PlanNode>>();
+        for (PlanNode source : plan.findAllAtOrBelow(Type.SOURCE)) {
+            // Walk up from the SOURCE and look for all SELECT nodes ...
+            PlanNode node = source.getParent();
+            while (node != null && node.is(Type.SELECT)) {
+                Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                SimilarityType operand = getSimilarityKey(constraint);
+                if (operand != null) {
+                    LinkedList<PlanNode> nodes = mergeables.get(operand);
+                    if (nodes == null) {
+                        nodes = new LinkedList<PlanNode>();
+                        mergeables.put(operand, nodes);
+                    }
+                    nodes.addFirst(node); // So that the list is in the downward order they appear in the plan
+                }
+                node = node.getParent();
+            }
+
+            // Merge all mergeable SELECT nodes ...
+            for (LinkedList<PlanNode> mergeable : mergeables.values()) {
+                if (mergeable.size() > 1) {
+                    Iterator<PlanNode> iter = mergeable.iterator();
+                    PlanNode firstSelect = iter.next();
+                    Constraint constraint = firstSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                    while (iter.hasNext()) {
+                        PlanNode nextSelect = iter.next();
+                        Constraint nextConstraint = nextSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                        constraint = merge((ConstraintType)constraint, (ConstraintType)nextConstraint);
+                        nextSelect.extractFromParent();
+                    }
+                    firstSelect.setProperty(Property.SELECT_CRITERIA, constraint);
+                }
+            }
+            mergeables.clear();
+        }
+        return plan;
+    }
+
+    /**
+     * Obtain the similarity key that will be used to determine whether two SELECT nodes should be merged.
+     * 
+     * @param constraint the constraint from the SELECT node
+     * @return the similarity key, or null if the constraint should not be merged
+     */
+    protected abstract SimilarityType getSimilarityKey( Constraint constraint );
+
+    /**
+     * Merge the two constraints.
+     * 
+     * @param firstConstraint the first constraint
+     * @param secondConstraint the second constraint
+     * @return the merged constraint
+     */
+    protected abstract Constraint merge( ConstraintType firstConstraint,
+                                         ConstraintType secondConstraint );
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	2009-10-21 17:29:08 UTC (rev 1302)
@@ -0,0 +1,89 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+import org.jboss.dna.graph.query.model.SetCriteria;
+import org.jboss.dna.graph.query.model.StaticOperand;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
+ * the same selector.
+ */
+ at Immutable
+public class MergeSetCriteria extends AbstractMergeSelectNodes<DynamicOperand, SetCriteria> {
+
+    public static final MergeSetCriteria INSTANCE = new MergeSetCriteria();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#getSimilarityKey(org.jboss.dna.graph.query.model.Constraint)
+     */
+    @Override
+    protected DynamicOperand getSimilarityKey( Constraint constraint ) {
+        if (constraint instanceof SetCriteria) {
+            // Look for the list of plan nodes for this operand ...
+            return ((SetCriteria)constraint).getLeftOperand();
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#merge(org.jboss.dna.graph.query.model.Constraint,
+     *      org.jboss.dna.graph.query.model.Constraint)
+     */
+    @Override
+    protected Constraint merge( SetCriteria firstConstraint,
+                                SetCriteria secondConstraint ) {
+        assert firstConstraint.getLeftOperand().equals(secondConstraint.getLeftOperand());
+        Set<StaticOperand> allOperands = new HashSet<StaticOperand>();
+        List<StaticOperand> orderedOperands = new ArrayList<StaticOperand>(firstConstraint.getRightOperands());
+        allOperands.addAll(firstConstraint.getRightOperands());
+        for (StaticOperand second : secondConstraint.getRightOperands()) {
+            if (allOperands.add(second)) {
+                orderedOperands.add(second);
+            }
+        }
+        return new SetCriteria(firstConstraint.getLeftOperand(), orderedOperands);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getClass().getSimpleName();
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-21 17:21:57 UTC (rev 1301)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-21 17:29:08 UTC (rev 1302)
@@ -69,6 +69,7 @@
      */
     protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
                                       PlanHints hints ) {
+        ruleStack.addFirst(MergeSetCriteria.INSTANCE);
         if (hints.hasJoin) {
             ruleStack.addFirst(ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM);
             ruleStack.addFirst(RewriteIdentityJoins.INSTANCE);


From dna-commits at lists.jboss.org  Wed Oct 21 13:29:30 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 21 Oct 2009 13:29:30 -0400
Subject: [dna-commits] DNA SVN: r1303 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize.
Message-ID: <200910211729.n9LHTUF5010871@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-21 13:29:30 -0400 (Wed, 21 Oct 2009)
New Revision: 1303

Removed:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
Log:
DNA-467 Removed optimization rule that combined set criteria (e.g., 'IN (1,2,3)'), since it was AND-ing together OR-ed criteria and was thus invalid.

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	2009-10-21 17:29:08 UTC (rev 1302)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	2009-10-21 17:29:30 UTC (rev 1303)
@@ -1,113 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.graph.query.optimize;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
-import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.model.Constraint;
-import org.jboss.dna.graph.query.model.SetCriteria;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.plan.PlanNode.Property;
-import org.jboss.dna.graph.query.plan.PlanNode.Type;
-
-/**
- * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
- * the same selector.
- * 
- * @param <SimilarityType> the type used to compare and identify similar nodes
- * @param <ConstraintType> the concrete type of Constraint objects that are being merged
- */
- at Immutable
-public abstract class AbstractMergeSelectNodes<SimilarityType, ConstraintType extends Constraint> implements OptimizerRule {
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
-     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
-     */
-    @SuppressWarnings( "unchecked" )
-    public PlanNode execute( QueryContext context,
-                             PlanNode plan,
-                             LinkedList<OptimizerRule> ruleStack ) {
-        Map<SimilarityType, LinkedList<PlanNode>> mergeables = new HashMap<SimilarityType, LinkedList<PlanNode>>();
-        for (PlanNode source : plan.findAllAtOrBelow(Type.SOURCE)) {
-            // Walk up from the SOURCE and look for all SELECT nodes ...
-            PlanNode node = source.getParent();
-            while (node != null && node.is(Type.SELECT)) {
-                Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-                SimilarityType operand = getSimilarityKey(constraint);
-                if (operand != null) {
-                    LinkedList<PlanNode> nodes = mergeables.get(operand);
-                    if (nodes == null) {
-                        nodes = new LinkedList<PlanNode>();
-                        mergeables.put(operand, nodes);
-                    }
-                    nodes.addFirst(node); // So that the list is in the downward order they appear in the plan
-                }
-                node = node.getParent();
-            }
-
-            // Merge all mergeable SELECT nodes ...
-            for (LinkedList<PlanNode> mergeable : mergeables.values()) {
-                if (mergeable.size() > 1) {
-                    Iterator<PlanNode> iter = mergeable.iterator();
-                    PlanNode firstSelect = iter.next();
-                    Constraint constraint = firstSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-                    while (iter.hasNext()) {
-                        PlanNode nextSelect = iter.next();
-                        Constraint nextConstraint = nextSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
-                        constraint = merge((ConstraintType)constraint, (ConstraintType)nextConstraint);
-                        nextSelect.extractFromParent();
-                    }
-                    firstSelect.setProperty(Property.SELECT_CRITERIA, constraint);
-                }
-            }
-            mergeables.clear();
-        }
-        return plan;
-    }
-
-    /**
-     * Obtain the similarity key that will be used to determine whether two SELECT nodes should be merged.
-     * 
-     * @param constraint the constraint from the SELECT node
-     * @return the similarity key, or null if the constraint should not be merged
-     */
-    protected abstract SimilarityType getSimilarityKey( Constraint constraint );
-
-    /**
-     * Merge the two constraints.
-     * 
-     * @param firstConstraint the first constraint
-     * @param secondConstraint the second constraint
-     * @return the merged constraint
-     */
-    protected abstract Constraint merge( ConstraintType firstConstraint,
-                                         ConstraintType secondConstraint );
-}

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	2009-10-21 17:29:08 UTC (rev 1302)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	2009-10-21 17:29:30 UTC (rev 1303)
@@ -1,89 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.graph.query.optimize;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import net.jcip.annotations.Immutable;
-import org.jboss.dna.graph.query.model.Constraint;
-import org.jboss.dna.graph.query.model.DynamicOperand;
-import org.jboss.dna.graph.query.model.SetCriteria;
-import org.jboss.dna.graph.query.model.StaticOperand;
-
-/**
- * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
- * the same selector.
- */
- at Immutable
-public class MergeSetCriteria extends AbstractMergeSelectNodes<DynamicOperand, SetCriteria> {
-
-    public static final MergeSetCriteria INSTANCE = new MergeSetCriteria();
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#getSimilarityKey(org.jboss.dna.graph.query.model.Constraint)
-     */
-    @Override
-    protected DynamicOperand getSimilarityKey( Constraint constraint ) {
-        if (constraint instanceof SetCriteria) {
-            // Look for the list of plan nodes for this operand ...
-            return ((SetCriteria)constraint).getLeftOperand();
-        }
-        return null;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#merge(org.jboss.dna.graph.query.model.Constraint,
-     *      org.jboss.dna.graph.query.model.Constraint)
-     */
-    @Override
-    protected Constraint merge( SetCriteria firstConstraint,
-                                SetCriteria secondConstraint ) {
-        assert firstConstraint.getLeftOperand().equals(secondConstraint.getLeftOperand());
-        Set<StaticOperand> allOperands = new HashSet<StaticOperand>();
-        List<StaticOperand> orderedOperands = new ArrayList<StaticOperand>(firstConstraint.getRightOperands());
-        allOperands.addAll(firstConstraint.getRightOperands());
-        for (StaticOperand second : secondConstraint.getRightOperands()) {
-            if (allOperands.add(second)) {
-                orderedOperands.add(second);
-            }
-        }
-        return new SetCriteria(firstConstraint.getLeftOperand(), orderedOperands);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        return getClass().getSimpleName();
-    }
-}

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-21 17:29:08 UTC (rev 1302)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-21 17:29:30 UTC (rev 1303)
@@ -69,7 +69,6 @@
      */
     protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
                                       PlanHints hints ) {
-        ruleStack.addFirst(MergeSetCriteria.INSTANCE);
         if (hints.hasJoin) {
             ruleStack.addFirst(ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM);
             ruleStack.addFirst(RewriteIdentityJoins.INSTANCE);


From dna-commits at lists.jboss.org  Wed Oct 21 18:12:12 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 21 Oct 2009 18:12:12 -0400
Subject: [dna-commits] DNA SVN: r1304 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/connector and 12
	other directories.
Message-ID: <200910212212.n9LMCCBS002137@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-10-21 18:12:10 -0400 (Wed, 21 Oct 2009)
New Revision: 1304

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/LockFailedException.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
   trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
   trunk/extensions/dna-connector-infinispan/src/main/java/org/jboss/dna/connector/infinispan/InfinispanRepository.java
   trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRepository.java
Log:
DNA-457 Add JSR-170 Locking Optional Feature

Committed patch (DNA-457_repo_locks.patch) that adds comments to address the previous feedback and provides a graph-layer API for locking nodes in repositories that provide their own locking mechanism (e.g., SVN). The patch also modifies the JCR locking code to propagate JCR locks to the graph layer.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -406,6 +406,165 @@
     }
 
     /**
+     * Request to lock the specified node. This request is submitted to the repository immediately.
+     * 
+     * @param at the node that is to be locked
+     * @return an object that allows the scope of the lock to be defined
+     */
+    public LockScope<LockTimeout<Conjunction<Graph>>> lock( Node at ) {
+        return lock(at.getLocation());
+    }
+
+    /**
+     * Request to lock the node at the given path. This request is submitted to the repository immediately.
+     * 
+     * @param atPath the path of the node that is to be locked
+     * @return an object that allows the scope of the lock to be defined
+     */
+    public LockScope<LockTimeout<Conjunction<Graph>>> lock( String atPath ) {
+        return lock(Location.create(createPath(atPath)));
+    }
+
+    /**
+     * Request to lock the node at the given path. This request is submitted to the repository immediately.
+     * 
+     * @param at the path of the node that is to be locked
+     * @return an object that allows the scope of the lock to be defined
+     */
+    public LockScope<LockTimeout<Conjunction<Graph>>> lock( Path at ) {
+        return lock(Location.create(at));
+    }
+
+    /**
+     * Request to lock the node with the given UUID. This request is submitted to the repository immediately.
+     * 
+     * @param at the UUID of the node that is to be locked
+     * @return an object that allows the scope of the lock to be defined
+     */
+    public LockScope<LockTimeout<Conjunction<Graph>>> lock( UUID at ) {
+        return lock(Location.create(at));
+    }
+
+    /**
+     * Request to lock the node with the given unique identification property. This request is submitted to the repository
+     * immediately.
+     * 
+     * @param idProperty the unique identifying property of the node that is to be locked
+     * @return an object that allows the scope of the lock to be defined
+     */
+    public LockScope<LockTimeout<Conjunction<Graph>>> lock( Property idProperty ) {
+        return lock(Location.create(idProperty));
+    }
+
+    /**
+     * Request to lock the node with the given identification properties. The identification properties should uniquely identify a
+     * single node. This request is submitted to the repository immediately.
+     * 
+     * @param firstIdProperty the first identification property of the node that is to be copied
+     * @param additionalIdProperties the remaining identification properties of the node that is to be copied
+     * @return an object that allows the scope of the lock to be defined
+     */
+    public LockScope<LockTimeout<Conjunction<Graph>>> lock( Property firstIdProperty,
+                                               Property... additionalIdProperties ) {
+        return lock(Location.create(firstIdProperty, additionalIdProperties));
+    }
+
+    /**
+     * Request to lock the node at the given location. This request is submitted to the repository immediately.
+     * 
+     * @param at the location of the node that is to be locked
+     * @return an object that allows the scope of the lock to be defined
+     */
+    public LockScope<LockTimeout<Conjunction<Graph>>> lock( Location at ) {
+        return new LockAction<Conjunction<Graph>>(this.nextGraph, at) {
+            @Override
+            protected Conjunction<Graph> submit( Location target,
+                                                 org.jboss.dna.graph.request.LockBranchRequest.LockScope lockScope,
+                                                 long lockTimeoutInMillis ) {
+                String workspaceName = getCurrentWorkspaceName();
+                requests.lockBranch(workspaceName, target, lockScope, lockTimeoutInMillis);
+                return and();
+            }
+        };
+    }
+
+    /**
+     * Request to unlock the specified node. This request is submitted to the repository immediately.
+     * 
+     * @param at the node that is to be unlocked
+     * @return an object that may be used to start another request
+     */
+    public Conjunction<Graph> unlock( Node at ) {
+        return unlock(at.getLocation());
+    }
+
+    /**
+     * Request to unlock the node at the given path. This request is submitted to the repository immediately.
+     * 
+     * @param atPath the path of the node that is to be unlocked
+     * @return an object that may be used to start another request
+     */
+    public Conjunction<Graph> unlock( String atPath ) {
+        return unlock(Location.create(createPath(atPath)));
+    }
+
+    /**
+     * Request to unlock the node at the given path. This request is submitted to the repository immediately.
+     * 
+     * @param at the path of the node that is to be unlocked
+     * @return an object that may be used to start another request
+     */
+    public Conjunction<Graph> unlock( Path at ) {
+        return unlock(Location.create(at));
+    }
+
+    /**
+     * Request to unlock the node with the given UUID. This request is submitted to the repository immediately.
+     * 
+     * @param at the UUID of the node that is to be unlocked
+     * @return an object that may be used to start another request
+     */
+    public Conjunction<Graph> unlock( UUID at ) {
+        return unlock(Location.create(at));
+    }
+
+    /**
+     * Request to unlock the node with the given unique identification property. This request is submitted to the repository
+     * immediately.
+     * 
+     * @param idProperty the unique identifying property of the node that is to be unlocked
+     * @return an object that may be used to start another request
+     */
+    public Conjunction<Graph> unlock( Property idProperty ) {
+        return unlock(Location.create(idProperty));
+    }
+
+    /**
+     * Request to unlock the node with the given identification properties. The identification properties should uniquely identify
+     * a single node. This request is submitted to the repository immediately.
+     * 
+     * @param firstIdProperty the first identification property of the node that is to be copied
+     * @param additionalIdProperties the remaining identification properties of the node that is to be copied
+     * @return an object that may be used to start another request
+     */
+    public Conjunction<Graph> unlock( Property firstIdProperty,
+                                      Property... additionalIdProperties ) {
+        return unlock(Location.create(firstIdProperty, additionalIdProperties));
+    }
+
+    /**
+     * Request to unlock the node at the given location. This request is submitted to the repository immediately.
+     * 
+     * @param at the location of the node that is to be unlocked
+     * @return an object that may be used to start another request
+     */
+    public Conjunction<Graph> unlock( Location at ) {
+        String workspaceName = getCurrentWorkspaceName();
+        requests.unlockBranch(workspaceName, at);
+        return this.nextGraph;
+    }
+
+    /**
      * Begin the request to move the specified node into a parent node at a different location, which is specified via the
      * <code>into(...)</code> method on the returned {@link Move} object.
      * <p>
@@ -1847,15 +2006,13 @@
                             }
 
                             public List<Location> under( Location at ) {
-                                return requests.readBlockOfChildren(at, getCurrentWorkspaceName(), startingIndex, blockSize)
-                                               .getChildren();
+                                return requests.readBlockOfChildren(at, getCurrentWorkspaceName(), startingIndex, blockSize).getChildren();
                             }
                         };
                     }
 
                     public List<Location> startingAfter( final Location previousSibling ) {
-                        return requests.readNextBlockOfChildren(previousSibling, getCurrentWorkspaceName(), blockSize)
-                                       .getChildren();
+                        return requests.readNextBlockOfChildren(previousSibling, getCurrentWorkspaceName(), blockSize).getChildren();
                     }
 
                     public List<Location> startingAfter( String pathOfPreviousSibling ) {
@@ -2450,6 +2607,164 @@
         }
 
         /**
+         * Request to lock the specified node. This request is submitted to the repository immediately.
+         * 
+         * @param at the node that is to be locked
+         * @return an object that allows the scope of the lock to be defined
+         */
+        public LockScope<LockTimeout<BatchConjunction>> lock( Node at ) {
+            return lock(at.getLocation());
+        }
+
+        /**
+         * Request to lock the node at the given path. This request is submitted to the repository immediately.
+         * 
+         * @param atPath the path of the node that is to be locked
+         * @return an object that allows the scope of the lock to be defined
+         */
+        public LockScope<LockTimeout<BatchConjunction>> lock( String atPath ) {
+            return lock(Location.create(createPath(atPath)));
+        }
+
+        /**
+         * Request to lock the node at the given path. This request is submitted to the repository immediately.
+         * 
+         * @param at the path of the node that is to be locked
+         * @return an object that allows the scope of the lock to be defined
+         */
+        public LockScope<LockTimeout<BatchConjunction>> lock( Path at ) {
+            return lock(Location.create(at));
+        }
+
+        /**
+         * Request to lock the node with the given UUID. This request is submitted to the repository immediately.
+         * 
+         * @param at the UUID of the node that is to be locked
+         * @return an object that allows the scope of the lock to be defined
+         */
+        public LockScope<LockTimeout<BatchConjunction>> lock( UUID at ) {
+            return lock(Location.create(at));
+        }
+
+        /**
+         * Request to lock the node with the given unique identification property. This request is submitted to the repository
+         * immediately.
+         * 
+         * @param idProperty the unique identifying property of the node that is to be locked
+         * @return an object that allows the scope of the lock to be defined
+         */
+        public LockScope<LockTimeout<BatchConjunction>> lock( Property idProperty ) {
+            return lock(Location.create(idProperty));
+        }
+
+        /**
+         * Request to lock the node with the given identification properties. The identification properties should uniquely
+         * identify a single node. This request is submitted to the repository immediately.
+         * 
+         * @param firstIdProperty the first identification property of the node that is to be copied
+         * @param additionalIdProperties the remaining identification properties of the node that is to be copied
+         * @return an object that allows the scope of the lock to be defined
+         */
+        public LockScope<LockTimeout<BatchConjunction>> lock( Property firstIdProperty,
+                                                 Property... additionalIdProperties ) {
+            return lock(Location.create(firstIdProperty, additionalIdProperties));
+        }
+
+        /**
+         * Request to lock the node at the given location. This request is submitted to the repository immediately.
+         * 
+         * @param at the location of the node that is to be locked
+         * @return an object that allows the scope of the lock to be defined
+         */
+        public LockScope<LockTimeout<BatchConjunction>> lock( Location at ) {
+            return new LockAction<BatchConjunction>(this.nextRequests, at) {
+                @Override
+                protected BatchConjunction submit( Location target,
+                                                   org.jboss.dna.graph.request.LockBranchRequest.LockScope lockScope,
+                                                   long lockTimeoutInMillis ) {
+                    String workspaceName = getCurrentWorkspaceName();
+                    requests.lockBranch(workspaceName, target, lockScope, lockTimeoutInMillis);
+                    return and();
+                }
+            };
+        }
+
+        /**
+         * Request to unlock the specified node. This request is submitted to the repository immediately.
+         * 
+         * @param at the node that is to be unlocked
+         * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
+         */
+        public BatchConjunction unlock( Node at ) {
+            return unlock(at.getLocation());
+        }
+
+        /**
+         * Request to unlock the node at the given path. This request is submitted to the repository immediately.
+         * 
+         * @param atPath the path of the node that is to be unlocked
+         * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
+         */
+        public BatchConjunction unlock( String atPath ) {
+            return unlock(Location.create(createPath(atPath)));
+        }
+
+        /**
+         * Request to unlock the node at the given path. This request is submitted to the repository immediately.
+         * 
+         * @param at the path of the node that is to be unlocked
+         * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
+         */
+        public BatchConjunction unlock( Path at ) {
+            return unlock(Location.create(at));
+        }
+
+        /**
+         * Request to unlock the node with the given UUID. This request is submitted to the repository immediately.
+         * 
+         * @param at the UUID of the node that is to be unlocked
+         * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
+         */
+        public BatchConjunction unlock( UUID at ) {
+            return unlock(Location.create(at));
+        }
+
+        /**
+         * Request to unlock the node with the given unique identification property. This request is submitted to the repository
+         * immediately.
+         * 
+         * @param idProperty the unique identifying property of the node that is to be unlocked
+         * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
+         */
+        public BatchConjunction unlock( Property idProperty ) {
+            return unlock(Location.create(idProperty));
+        }
+
+        /**
+         * Request to unlock the node with the given identification properties. The identification properties should uniquely
+         * identify a single node. This request is submitted to the repository immediately.
+         * 
+         * @param firstIdProperty the first identification property of the node that is to be copied
+         * @param additionalIdProperties the remaining identification properties of the node that is to be copied
+         * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
+         */
+        public BatchConjunction unlock( Property firstIdProperty,
+                                        Property... additionalIdProperties ) {
+            return unlock(Location.create(firstIdProperty, additionalIdProperties));
+        }
+
+        /**
+         * Request to unlock the node at the given location. This request is submitted to the repository immediately.
+         * 
+         * @param at the location of the node that is to be unlocked
+         * @return the interface that can either execute the batched requests or continue to add additional requests to the batch
+         */
+        public BatchConjunction unlock( Location at ) {
+            requests.unlockBranch(workspaceName, at);
+            return this.nextRequests;
+        }
+
+        /**
          * Begin the request to clone the specified node into a parent node at a different location, which is specified via the
          * <code>into(...)</code> method on the returned {@link Clone} object.
          * <p>
@@ -4390,6 +4705,78 @@
     }
 
     /**
+     * Interface for specifying whether a lock should be deep in scope
+     * 
+     * @param <Next> The interface that is to be returned when this create request is completed
+     */
+    public interface LockScope<Next> {
+        Next andItsDescendants();
+
+        Next only();
+    }
+
+    /**
+     * Interface for specifying whether the maximum length of the lock
+     * 
+     * @param <Next> The interface that is to be returned when this create request is completed
+     */
+    public interface LockTimeout<Next> {
+        Next withDefaultTimeout();
+
+        Next withTimeoutOf( long milliseconds );
+    }
+
+    @NotThreadSafe
+    protected abstract class LockAction<T> extends AbstractAction<T> implements LockScope<LockTimeout<T>> {
+        private final Location target;
+
+        /*package*/LockAction( T afterConjunction,
+                                Location target ) {
+            super(afterConjunction);
+            this.target = target;
+        }
+
+        protected abstract T submit( Location lock,
+                                     org.jboss.dna.graph.request.LockBranchRequest.LockScope lockScope,
+                                     long lockTimeoutInMillis );
+
+        public LockTimeout<T> andItsDescendants() {
+            return new LockTimeout<T>() {
+
+                @Override
+                public T withDefaultTimeout() {
+                    return submit(target, org.jboss.dna.graph.request.LockBranchRequest.LockScope.SELF_AND_DESCENDANTS, 0);
+                }
+
+                @Override
+                public T withTimeoutOf( long milliseconds ) {
+                    return submit(target,
+                                  org.jboss.dna.graph.request.LockBranchRequest.LockScope.SELF_AND_DESCENDANTS,
+                                  milliseconds);
+                }
+
+            };
+        }
+
+        public LockTimeout<T> only() {
+            return new LockTimeout<T>() {
+
+                @Override
+                public T withDefaultTimeout() {
+                    return submit(target, org.jboss.dna.graph.request.LockBranchRequest.LockScope.SELF_ONLY, 0);
+                }
+
+                @Override
+                public T withTimeoutOf( long milliseconds ) {
+                    return submit(target, org.jboss.dna.graph.request.LockBranchRequest.LockScope.SELF_ONLY, milliseconds);
+                }
+
+            };
+        }
+
+    }
+
+    /**
      * The interface for defining additional properties on a new node.
      * 
      * @param <Next> The interface that is to be returned when this create request is completed
@@ -6041,10 +6428,7 @@
         }
 
         public SubgraphNode getNode( Name relativePath ) {
-            Path path = getGraph().getContext()
-                                  .getValueFactories()
-                                  .getPathFactory()
-                                  .create(getLocation().getPath(), relativePath);
+            Path path = getGraph().getContext().getValueFactories().getPathFactory().create(getLocation().getPath(), relativePath);
             path = path.getNormalizedPath();
             return getNode(path);
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -89,6 +89,7 @@
     public static I18n errorImportingContent;
     public static I18n unableToFindRepositorySourceWithName;
     public static I18n nodeAlreadyExistsWithUuid;
+    public static I18n couldNotAcquireLock;
 
     /* In-Memory Connector */
     public static I18n inMemoryNodeDoesNotExist;

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/LockFailedException.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/LockFailedException.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/LockFailedException.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -0,0 +1,46 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+
+/**
+ * Exception that indicates that a lock request failed at the repository level. This may be due to a conflict with an existing
+ * lock, an internal timeout that occurred while attempting to lock the target, a lack of permissions, or any other reason.
+ */
+ at Immutable
+public class LockFailedException extends RepositorySourceException {
+
+    private static final long serialVersionUID = 1L;
+
+    public LockFailedException( String repositorySourceName,
+                                Location target,
+                                String workspaceName,
+                                Throwable cause ) {
+        super(repositorySourceName, GraphI18n.couldNotAcquireLock.text(target, workspaceName), cause);
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/LockFailedException.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -59,24 +59,30 @@
      */
     public static final boolean DEFAULT_SUPPORT_REFERENCES = true;
 
-    private boolean sameNameSiblings;
-    private boolean updates;
-    private boolean events;
-    private boolean creatingWorkspaces;
-    private boolean references;
+    /**
+     * The default support for creating locks is {@value} .
+     */
+    public static final boolean DEFAULT_SUPPORT_LOCKS = false;
 
+    private final boolean sameNameSiblings;
+    private final boolean updates;
+    private final boolean events;
+    private final boolean creatingWorkspaces;
+    private final boolean references;
+    private final boolean locks;
+
     /**
      * Create a capabilities object using the defaults, .
      */
     public RepositorySourceCapabilities() {
         this(DEFAULT_SUPPORT_SAME_NAME_SIBLINGS, DEFAULT_SUPPORT_UPDATES, DEFAULT_SUPPORT_EVENTS,
-             DEFAULT_SUPPORT_CREATING_WORKSPACES, DEFAULT_SUPPORT_REFERENCES);
+             DEFAULT_SUPPORT_CREATING_WORKSPACES, DEFAULT_SUPPORT_REFERENCES, DEFAULT_SUPPORT_LOCKS);
     }
 
     public RepositorySourceCapabilities( boolean supportsSameNameSiblings,
                                          boolean supportsUpdates ) {
         this(supportsSameNameSiblings, supportsUpdates, DEFAULT_SUPPORT_EVENTS, DEFAULT_SUPPORT_CREATING_WORKSPACES,
-             DEFAULT_SUPPORT_REFERENCES);
+             DEFAULT_SUPPORT_REFERENCES, DEFAULT_SUPPORT_LOCKS);
     }
 
     public RepositorySourceCapabilities( boolean supportsSameNameSiblings,
@@ -84,11 +90,24 @@
                                          boolean supportsEvents,
                                          boolean supportsCreatingWorkspaces,
                                          boolean supportsReferences ) {
+        this(supportsSameNameSiblings, supportsUpdates, supportsEvents, supportsCreatingWorkspaces, supportsReferences,
+             DEFAULT_SUPPORT_LOCKS);
+
+    }
+
+    public RepositorySourceCapabilities( boolean supportsSameNameSiblings,
+                                         boolean supportsUpdates,
+                                         boolean supportsEvents,
+                                         boolean supportsCreatingWorkspaces,
+                                         boolean supportsReferences,
+                                         boolean supportsLocks ) {
+
         this.sameNameSiblings = supportsSameNameSiblings;
         this.updates = supportsUpdates;
         this.events = supportsEvents;
         this.creatingWorkspaces = supportsCreatingWorkspaces;
         this.references = supportsReferences;
+        this.locks = supportsLocks;
     }
 
     /**
@@ -121,6 +140,19 @@
     }
 
     /**
+     * Return whether the source supports locking nodes.
+     * <p>
+     * Sources that support locking nodes must be able to explicitly lock and unlock nodes in a manner that is persistent and
+     * stable across repository connections. Sources that cannot provide this capability should return false from this method.
+     * </p>
+     * 
+     * @return true if locks are supported, or false otherwise
+     */
+    public boolean supportsLocks() {
+        return locks;
+    }
+
+    /**
      * Return whether the source supports publishing change events.
      * 
      * @return true if events are supported, or false if the source is not capable of generating events

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -29,11 +29,13 @@
 import net.jcip.annotations.GuardedBy;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.LockFailedException;
 import org.jboss.dna.graph.connector.map.AbstractMapWorkspace;
 import org.jboss.dna.graph.connector.map.MapNode;
 import org.jboss.dna.graph.connector.map.MapRepository;
 import org.jboss.dna.graph.connector.map.MapWorkspace;
 import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
 
 /**
  * A specialized {@link MapRepository} that represents an in-memory repository.
@@ -124,6 +126,18 @@
             return super.copyNode(context, original, newWorkspace, newParent, desiredName, recursive, oldToNewUuids);
         }
 
+        @Override
+        public void lockNode( MapNode node,
+                              LockScope lockScope,
+                              long lockTimeoutInMillis ) throws LockFailedException {
+            // Locking is not supported by this connector
+        }
+
+        @Override
+        public void unlockNode( MapNode node ) {
+            // Locking is not supported by this connector
+        }
+
         /**
          * Method added to support testing
          * 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRepository.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -266,5 +266,4 @@
     public boolean destroyWorkspace( String name ) {
         return workspaces.remove(name) != null;
     }
-
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -54,10 +54,12 @@
 import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
 import org.jboss.dna.graph.request.GetWorkspacesRequest;
 import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.jboss.dna.graph.request.LockBranchRequest;
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
 import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
 import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -103,6 +105,32 @@
     }
 
     @Override
+    public void process( LockBranchRequest request ) {
+        MapWorkspace workspace = getWorkspace(request, request.inWorkspace());
+        MapNode node = getTargetNode(workspace, request, request.at());
+        if (node == null) return;
+
+        workspace.lockNode(node, request.lockScope(), request.lockTimeoutInMillis());
+
+        Location actualLocation = getActualLocation(request.at(), node);
+        request.setActualLocation(actualLocation);
+        recordChange(request);
+    }
+
+    @Override
+    public void process( UnlockBranchRequest request ) {
+        MapWorkspace workspace = getWorkspace(request, request.inWorkspace());
+        MapNode node = getTargetNode(workspace, request, request.at());
+        if (node == null) return;
+
+        workspace.unlockNode(node);
+
+        Location actualLocation = getActualLocation(request.at(), node);
+        request.setActualLocation(actualLocation);
+        recordChange(request);
+    }
+
+    @Override
     public void process( ReadAllPropertiesRequest request ) {
         MapWorkspace workspace = getWorkspace(request, request.inWorkspace());
         MapNode node = getTargetNode(workspace, request, request.at());

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapWorkspace.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -27,10 +27,12 @@
 import java.util.UUID;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.LockFailedException;
 import org.jboss.dna.graph.connector.UuidAlreadyExistsException;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
 
 /**
  * The {@code MapWorkspace} defines the required methods for workspaces in a {@link MapRepository map repository}. By default, a
@@ -180,6 +182,27 @@
                        Set<Location> removedExistingNodes ) throws UuidAlreadyExistsException;
 
     /**
+     * Attempts to lock the given node with the given timeout. If the lock attempt fails, a {@link LockFailedException} will be
+     * thrown.
+     * 
+     * @param node the node to be locked; may not be null
+     * @param lockScope the scope of the lock (i.e., whether descendants of {@code node} should be included in the lock
+     * @param lockTimeoutInMillis the maximum lifetime of the lock in milliseconds; zero (0) indicates that the connector default
+     *        should be used
+     * @throws LockFailedException if the implementing connector supports locking but the lock could not be acquired.
+     */
+    void lockNode( MapNode node,
+                   LockScope lockScope,
+                   long lockTimeoutInMillis ) throws LockFailedException;
+
+    /**
+     * Attempts to unlock the given node.
+     * 
+     * @param node the node to be unlocked; may not be null
+     */
+    void unlockNode( MapNode node );
+
+    /**
      * Find the lowest existing node along the path.
      * 
      * @param path the path to the node; may not be null

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -0,0 +1,234 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.request;
+
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * Instruction to lock an existing node or branch. Connectors that do not support locking (as defined in
+ * {@link RepositorySourceCapabilities#supportsLocks()}) must ignore this request.
+ */
+public class LockBranchRequest extends ChangeRequest {
+
+    private static final long serialVersionUID = 1L;
+
+    public enum LockScope {
+        SELF_ONLY,
+        SELF_AND_DESCENDANTS;
+    }
+
+    private final Location at;
+    private final String workspaceName;
+    private final LockScope isDeep;
+    private final long lockTimeoutInMillis;
+    private Location actualLocation;
+
+    /**
+     * Create a request to lock the node or branch at the supplied location.
+     * 
+     * @param at the location of the node to be read
+     * @param workspaceName the name of the workspace containing the node
+     * @param isDeep whether the lock should be deep (i.e., should include all of the node's descendants)
+     * @param lockTimeoutInMillis the number of milliseconds that the lock should last before the lock times out; zero (0)
+     *        indicates that the connector default should be used
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public LockBranchRequest( Location at,
+                              String workspaceName,
+                              LockScope isDeep,
+                              long lockTimeoutInMillis ) {
+        CheckArg.isNotNull(at, "at");
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        this.workspaceName = workspaceName;
+        this.at = at;
+        this.isDeep = isDeep;
+        this.lockTimeoutInMillis = lockTimeoutInMillis;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.Request#isReadOnly()
+     */
+    @Override
+    public boolean isReadOnly() {
+        return false;
+    }
+
+    /**
+     * Get the location defining the node that is to be read.
+     * 
+     * @return the location of the node; never null
+     */
+    public Location at() {
+        return at;
+    }
+
+    /**
+     * Get the name of the workspace in which the node exists.
+     * 
+     * @return the name of the workspace; never null
+     */
+    public String inWorkspace() {
+        return workspaceName;
+    }
+
+    /**
+     * Get whether the lock should include all of the descendants of the node as well as the node itself
+     * 
+     * @return {@link LockScope#SELF_AND_DESCENDANTS} if the lock should include all of the descendants of the node,
+     *         {@link LockScope#SELF_ONLY} otherwise
+     */
+    public LockScope lockScope() {
+        return isDeep;
+    }
+
+    /**
+     * Gets the maximum length of the lock in milliseconds
+     * 
+     * @return the number of milliseconds that the lock should last before the lock times out; zero (0) indicates that the
+     *         connector default should be used
+     */
+    public long lockTimeoutInMillis() {
+        return lockTimeoutInMillis;
+    }
+
+    /**
+     * Sets the actual and complete location of the node being locked. This method must be called when processing the request, and
+     * the actual location must have a {@link Location#getPath() path}.
+     * 
+     * @param actualLocation the actual location of the node before being locked
+     * @throws IllegalArgumentException if the either location is null or is missing its path, if the old location does not
+     *         represent the {@link Location#isSame(Location) same location} as the {@link #at() current location}
+     * @throws IllegalStateException if the request is frozen
+     */
+    public void setActualLocation( Location actualLocation ) {
+        checkNotFrozen();
+        if (!at.isSame(actualLocation)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actualLocation, at));
+        }
+        assert actualLocation != null;
+        if (!actualLocation.hasPath()) {
+            throw new IllegalArgumentException(GraphI18n.actualOldLocationMustHavePath.text(actualLocation));
+        }
+        this.actualLocation = actualLocation;
+    }
+
+    /**
+     * Get the actual location of the node that was locked.
+     * 
+     * @return the actual location of the node being locked, or null if the actual location was not set
+     */
+    public Location getActualLocation() {
+        return actualLocation;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    @Override
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && at.hasPath() && at.getPath().getParent().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    @Override
+    public Location changedLocation() {
+        return at;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedWorkspace()
+     */
+    @Override
+    public String changedWorkspace() {
+        return workspaceName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.Request#cancel()
+     */
+    @Override
+    public void cancel() {
+        super.cancel();
+        this.actualLocation = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return HashCode.compute(at, workspaceName, lockScope(), lockTimeoutInMillis);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (this.getClass().isInstance(obj)) {
+            LockBranchRequest that = (LockBranchRequest)obj;
+            if (this.lockTimeoutInMillis() != that.lockTimeoutInMillis()) return false;
+            if (!this.at().equals(that.at())) return false;
+            if (this.lockScope() != that.lockScope()) return false;
+            if (!this.inWorkspace().equals(that.inWorkspace())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "lock " + (LockScope.SELF_AND_DESCENDANTS == lockScope() ? "branch rooted" : "node") + " at " + at()
+               + " in the \"" + workspaceName + "\" workspace";
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RequestBuilder.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -35,6 +35,7 @@
 import org.jboss.dna.graph.property.Property;
 import org.jboss.dna.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
 import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
+import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
 
 /**
  * A component that can be used to build requests while allowing different strategies for how requests are handled. Subclasses can
@@ -616,4 +617,38 @@
         return request;
     }
 
+    /**
+     * Create a request to lock a branch or node
+     * 
+     * @param workspaceName the name of the workspace containing the node; may not be null
+     * @param target the location of the top node in the existing branch that is to be locked
+     * @param lockScope the {@link LockBranchRequest#lockScope()} scope of the lock
+     * @param lockTimeoutInMillis the number of milliseconds that the lock should last before the lock times out; zero (0)
+     *        indicates that the connector default should be used
+     * @return the request; never null
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public LockBranchRequest lockBranch( String workspaceName,
+                                         Location target,
+                                         LockScope lockScope,
+                                         long lockTimeoutInMillis ) {
+        return process(new LockBranchRequest(target, workspaceName, lockScope, lockTimeoutInMillis));
+    }
+
+    /**
+     * Create a request to unlock a branch or node
+     * <p>
+     * The lock on the node should be removed. If the lock was deep (i.e., locked the entire branch under the node, then all of
+     * the descendants of the node affected by the lock should also be unlocked after this request is processed.
+     * </p>
+     * 
+     * @param workspaceName the name of the workspace containing the node; may not be null
+     * @param target the location of the top node in the existing branch that is to be unlocked
+     * @return the request; never null
+     * @throws IllegalArgumentException if any of the parameters are null
+     */
+    public UnlockBranchRequest unlockBranch( String workspaceName,
+                                             Location target ) {
+        return process(new UnlockBranchRequest(target, workspaceName));
+    }
 }

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -0,0 +1,197 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors. 
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.request;
+
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * Instruction to unlock an existing node or branch. Connectors that do not support locking (as defined in
+ * {@link RepositorySourceCapabilities#supportsLocks()}) must ignore this request.
+ */
+public class UnlockBranchRequest extends ChangeRequest {
+
+    private static final long serialVersionUID = 1L;
+
+    private final Location at;
+    private final String workspaceName;
+    private Location actualLocation;
+
+    /**
+     * Create a request to unlock the node or branch at the supplied location.
+     * 
+     * @param at the location of the node to be unlocked
+     * @param workspaceName the name of the workspace containing the node
+     * @throws IllegalArgumentException if the location or workspace name is null
+     */
+    public UnlockBranchRequest( Location at,
+                                String workspaceName ) {
+        CheckArg.isNotNull(at, "at");
+        CheckArg.isNotNull(workspaceName, "workspaceName");
+        this.workspaceName = workspaceName;
+        this.at = at;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.Request#isReadOnly()
+     */
+    @Override
+    public boolean isReadOnly() {
+        return false;
+    }
+
+    /**
+     * Get the location defining the node that is to be read.
+     * 
+     * @return the location of the node; never null
+     */
+    public Location at() {
+        return at;
+    }
+
+    /**
+     * Get the name of the workspace in which the node exists.
+     * 
+     * @return the name of the workspace; never null
+     */
+    public String inWorkspace() {
+        return workspaceName;
+    }
+
+    /**
+     * Sets the actual and complete location of the node being unlocked. This method must be called when processing the request,
+     * and the actual location must have a {@link Location#getPath() path}.
+     * 
+     * @param actualLocation the actual location of the node before being unlocked
+     * @throws IllegalArgumentException if the either location is null or is missing its path, if the old location does not
+     *         represent the {@link Location#isSame(Location) same location} as the {@link #at() current location}
+     * @throws IllegalStateException if the request is frozen
+     */
+    public void setActualLocation( Location actualLocation ) {
+        checkNotFrozen();
+        if (!at.isSame(actualLocation)) { // not same if actual is null
+            throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actualLocation, at));
+        }
+        assert actualLocation != null;
+        if (!actualLocation.hasPath()) {
+            throw new IllegalArgumentException(GraphI18n.actualOldLocationMustHavePath.text(actualLocation));
+        }
+        this.actualLocation = actualLocation;
+    }
+
+    /**
+     * Get the actual location of the node that was unlocked.
+     * 
+     * @return the actual location of the node being unlocked, or null if the actual location was not set
+     */
+    public Location getActualLocation() {
+        return actualLocation;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+     */
+    @Override
+    public boolean changes( String workspace,
+                            Path path ) {
+        return this.workspaceName.equals(workspace) && at.hasPath() && at.getPath().getParent().isAtOrBelow(path);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+     */
+    @Override
+    public Location changedLocation() {
+        return at;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.ChangeRequest#changedWorkspace()
+     */
+    @Override
+    public String changedWorkspace() {
+        return workspaceName;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.Request#cancel()
+     */
+    @Override
+    public void cancel() {
+        super.cancel();
+        this.actualLocation = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return HashCode.compute(at, workspaceName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (this.getClass().isInstance(obj)) {
+            UnlockBranchRequest that = (UnlockBranchRequest)obj;
+            if (!this.at().equals(that.at())) return false;
+            if (!this.inWorkspace().equals(that.inWorkspace())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "unlock branch at " + at() + " in the \"" + workspaceName + "\" workspace";
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/LoggingRequestProcessor.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -36,6 +36,7 @@
 import org.jboss.dna.graph.request.DeleteBranchRequest;
 import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
 import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.LockBranchRequest;
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
@@ -48,6 +49,7 @@
 import org.jboss.dna.graph.request.RenameNodeRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
@@ -356,6 +358,30 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.LockBranchRequest)
+     */
+    @Override
+    public void process( LockBranchRequest request ) {
+        logger.log(level, GraphI18n.executingRequest, request);
+        delegate.process(request);
+        logger.log(level, GraphI18n.executedRequest, request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UnlockBranchRequest)
+     */
+    @Override
+    public void process( UnlockBranchRequest request ) {
+        logger.log(level, GraphI18n.executingRequest, request);
+        delegate.process(request);
+        logger.log(level, GraphI18n.executedRequest, request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.Request)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -37,6 +37,8 @@
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.LockFailedException;
+import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
 import org.jboss.dna.graph.observe.Changes;
 import org.jboss.dna.graph.observe.Observer;
 import org.jboss.dna.graph.property.DateTime;
@@ -57,6 +59,7 @@
 import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
 import org.jboss.dna.graph.request.GetWorkspacesRequest;
 import org.jboss.dna.graph.request.InvalidRequestException;
+import org.jboss.dna.graph.request.LockBranchRequest;
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
@@ -69,6 +72,7 @@
 import org.jboss.dna.graph.request.RenameNodeRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
 import org.jboss.dna.graph.request.UnsupportedRequestException;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.UpdateValuesRequest;
@@ -251,6 +255,10 @@
                 process((UpdatePropertiesRequest)request);
             } else if (request instanceof VerifyNodeExistsRequest) {
                 process((VerifyNodeExistsRequest)request);
+            } else if (request instanceof LockBranchRequest) {
+                process((LockBranchRequest)request);
+            } else if (request instanceof UnlockBranchRequest) {
+                process((UnlockBranchRequest)request);
             } else if (request instanceof VerifyWorkspaceRequest) {
                 process((VerifyWorkspaceRequest)request);
             } else if (request instanceof GetWorkspacesRequest) {
@@ -779,7 +787,7 @@
         Property property = readProperty.getProperty();
         List<Object> actualRemovedValues = new ArrayList<Object>(request.removedValues().size());
         List<Object> newValues = property == null ? new LinkedList<Object>() : new LinkedList<Object>(
-                                                                                          Arrays.asList(property.getValuesAsArray()));
+                                                                                                      Arrays.asList(property.getValuesAsArray()));
         // Calculate what the new values should be
         for (Object removedValue : request.removedValues()) {
             for (Iterator<Object> iter = newValues.iterator(); iter.hasNext();) {
@@ -793,7 +801,7 @@
 
         newValues.addAll(request.addedValues());
         Property newProperty = getExecutionContext().getPropertyFactory().create(propertyName, newValues);
-        
+
         // Update the current values
         SetPropertyRequest setProperty = new SetPropertyRequest(on, workspaceName, newProperty);
         process(setProperty);
@@ -845,6 +853,35 @@
     }
 
     /**
+     * Process a request to lock a node or branch within a workspace
+     * <p>
+     * The default implementation of this method does nothing, as most connectors will not support locking (as defined in
+     * {@link RepositorySourceCapabilities#supportsLocks()}). Any implementation of this method should do nothing if the request
+     * is null.
+     * </p>
+     * <p>
+     * Implementations that do support locking should throw a {@link LockFailedException} if the request could not be fulfilled.
+     * </p>
+     * 
+     * @param request the request
+     */
+    public void process( LockBranchRequest request ) {
+    }
+
+    /**
+     * Process a request to unlock a node or branch within a workspace
+     * <p>
+     * The default implementation of this method does nothing, as most connectors will not support locking (as defined in
+     * {@link RepositorySourceCapabilities#supportsLocks()}). Any implementation of this method should do nothing if the request
+     * is null.
+     * </p>
+     * 
+     * @param request the request
+     */
+    public void process( UnlockBranchRequest request ) {
+    }
+
+    /**
      * A class that represents a location at a known depth
      * 
      * @author Randall Hauch

Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties	2009-10-21 22:12:10 UTC (rev 1304)
@@ -77,6 +77,7 @@
 errorImportingContent = Error importing {0} content from {1}
 unableToFindRepositorySourceWithName = Unable to find a repository source named "{0}"
 nodeAlreadyExistsWithUuid = A node with UUID "{0}" already exists at path "{1}" in workspace "{2}"
+couldNotAcquireLock = Could not acquire lock on the node at "{0}" in workspace "{1}"
 
 # In-memory connector
 inMemoryNodeDoesNotExist = Could not find an existing node at {0}

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -63,6 +63,7 @@
 import org.jboss.dna.graph.request.DeleteBranchRequest;
 import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
 import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.LockBranchRequest;
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
@@ -72,11 +73,13 @@
 import org.jboss.dna.graph.request.ReadPropertyRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
 import org.jboss.dna.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
 import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
+import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
 import org.jboss.dna.graph.request.processor.RequestProcessor;
 import org.junit.Before;
 import org.junit.Test;
@@ -206,6 +209,24 @@
         assertThat(delete.at(), is(at));
     }
 
+    protected void assertNextRequestIsLock( Location at,
+                                            LockScope lockScope,
+                                            long lockTimeout ) {
+        Request request = executedRequests.poll();
+        assertThat(request, is(instanceOf(LockBranchRequest.class)));
+        LockBranchRequest lock = (LockBranchRequest)request;
+        assertThat(lock.at(), is(at));
+        assertThat(lock.lockScope(), is(lockScope));
+        assertThat(lock.lockTimeoutInMillis(), is(lockTimeout));
+    }
+
+    protected void assertNextRequestIsUnlock( Location at ) {
+        Request request = executedRequests.poll();
+        assertThat(request, is(instanceOf(UnlockBranchRequest.class)));
+        UnlockBranchRequest unlock = (UnlockBranchRequest)request;
+        assertThat(unlock.at(), is(at));
+    }
+
     protected void assertNextRequestIsCreate( Location parent,
                                               String child,
                                               Property... properties ) {
@@ -1072,6 +1093,24 @@
         graph.useWorkspace("something");
     }
 
+    @Test
+    public void shouldLockNodeButNotDescendants() {
+        graph.lock(validPath).only().withDefaultTimeout();
+        assertNextRequestIsLock(Location.create(validPath), LockScope.SELF_ONLY, 0);
+    }
+
+    @Test
+    public void shouldLockNodeAndItsDescendants() {
+        graph.lock(validPath).andItsDescendants().withTimeoutOf(12345);
+        assertNextRequestIsLock(Location.create(validPath), LockScope.SELF_AND_DESCENDANTS, 12345);
+    }
+
+    @Test
+    public void shouldUnlockNode() {
+        graph.unlock(validPath);
+        assertNextRequestIsUnlock(Location.create(validPath));
+    }
+
     // ----------------------------------------------------------------------------------------------------------------
     // Implementation of RepositoryConnection and RequestProcessor for tests
     // ----------------------------------------------------------------------------------------------------------------
@@ -1136,6 +1175,18 @@
         }
 
         @Override
+        public void process( LockBranchRequest request ) {
+            // Just update the actual location
+            request.setActualLocation(actualLocationOf(request.at()));
+        }
+
+        @Override
+        public void process( UnlockBranchRequest request ) {
+            // Just update the actual location
+            request.setActualLocation(actualLocationOf(request.at()));
+        }
+
+        @Override
         public void process( MoveBranchRequest request ) {
             // Just update the actual location ...
             Name newName = request.desiredName();

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -25,6 +25,7 @@
 
 import java.util.Queue;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
 import org.jboss.dna.graph.request.CloneBranchRequest;
 import org.jboss.dna.graph.request.CloneWorkspaceRequest;
 import org.jboss.dna.graph.request.CopyBranchRequest;
@@ -33,6 +34,7 @@
 import org.jboss.dna.graph.request.DeleteBranchRequest;
 import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
 import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.LockBranchRequest;
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
@@ -45,6 +47,7 @@
 import org.jboss.dna.graph.request.RenameNodeRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
@@ -55,7 +58,9 @@
  */
 public class MockRepositoryRequestProcessor extends RequestProcessor {
 
-    private Queue<Request> processed;
+    private static final String DEFAULT_WORKSPACE_NAME = "default";
+    private final Queue<Request> processed;
+    private final Location defaultWorkspaceRoot;
 
     /**
      * @param sourceName
@@ -67,7 +72,9 @@
                                            Queue<Request> processed ) {
         super(sourceName, context, null);
         assert processed != null;
+
         this.processed = processed;
+        this.defaultWorkspaceRoot = Location.create(context.getValueFactories().getPathFactory().createRootPath());
     }
 
     protected void record( Request request ) {
@@ -82,6 +89,10 @@
     @Override
     public void process( VerifyWorkspaceRequest request ) {
         record(request);
+
+        // Need to add this loopback in so we can use this with JCR layer test cases
+        request.setActualWorkspaceName(request.getActualWorkspaceName() == null ? DEFAULT_WORKSPACE_NAME : request.getActualWorkspaceName());
+        request.setActualRootLocation(request.getActualLocationOfRoot() == null ? defaultWorkspaceRoot : request.getActualLocationOfRoot());
     }
 
     /**
@@ -177,6 +188,26 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.LockBranchRequest)
+     */
+    @Override
+    public void process( LockBranchRequest request ) {
+        record(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UnlockBranchRequest)
+     */
+    @Override
+    public void process( UnlockBranchRequest request ) {
+        record(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
      */
     @Override

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -83,6 +83,10 @@
         super.afterEach();
     }
 
+    private boolean supportsLocks() {
+        return source.getCapabilities().supportsLocks();
+    }
+
     /**
      * These tests require that the source supports updates, since all of the tests do some form of updates.
      */
@@ -120,20 +124,9 @@
 
     @Test
     public void shouldAddChildrenAndSettingProperties() {
-        graph.batch()
-             .set("propA")
-             .to("valueA")
-             .on("/")
-             .and()
-             .create("/a")
-             .with("propB", "valueB")
-             .and("propC", "valueC")
-             .and()
-             .create("/b")
-             .with("propD", "valueD")
-             .and("propE", "valueE")
-             .and()
-             .execute();
+        graph.batch().set("propA").to("valueA").on("/").and().create("/a").with("propB", "valueB").and("propC", "valueC").and().create("/b").with("propD",
+                                                                                                                                                  "valueD").and("propE",
+                                                                                                                                                                "valueE").and().execute();
         // Now look up the root node ...
         Node root = graph.getNodeAt("/");
         assertThat(root, is(notNullValue()));
@@ -886,20 +879,12 @@
         graph.create("/newUuids").and();
         // Copy once to get the UUID into the default workspace
         // graph.copy("/node1/node1/node1").failingIfUuidsMatch().fromWorkspace(workspaceName).to("/newUuids/node1");
-        graph.clone("/node1/node1/node1")
-             .fromWorkspace(workspaceName)
-             .as(name("node1"))
-             .into("/newUuids")
-             .failingIfAnyUuidsMatch();
+        graph.clone("/node1/node1/node1").fromWorkspace(workspaceName).as(name("node1")).into("/newUuids").failingIfAnyUuidsMatch();
 
         try {
             // Copy again to get the exception since the UUID is already in the default workspace
             // graph.copy("/node1/node1").failingIfUuidsMatch().fromWorkspace(workspaceName).to("/newUuids/shouldNotWork");
-            graph.clone("/node1/node1/node1")
-                 .fromWorkspace(workspaceName)
-                 .as(name("shouldNotWork"))
-                 .into("/newUuids")
-                 .failingIfAnyUuidsMatch();
+            graph.clone("/node1/node1/node1").fromWorkspace(workspaceName).as(name("shouldNotWork")).into("/newUuids").failingIfAnyUuidsMatch();
             fail("Should not be able to copy a node into a workspace if another node with the "
                  + "same UUID already exists in the workspace and UUID behavior is failingIfUuidsMatch");
         } catch (UuidAlreadyExistsException ex) {
@@ -931,11 +916,7 @@
         graph.create("/newUuids").and();
         // Copy once to get the UUID into the default workspace
         // graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/node1");
-        graph.clone("/node1")
-             .fromWorkspace(workspaceName)
-             .as(name("node1"))
-             .into("/newUuids")
-             .replacingExistingNodesWithSameUuids();
+        graph.clone("/node1").fromWorkspace(workspaceName).as(name("node1")).into("/newUuids").replacingExistingNodesWithSameUuids();
 
         // Make sure that the node wasn't moved by the clone
         graph.useWorkspace(workspaceName);
@@ -948,11 +929,7 @@
         // Copy again to test the behavior now that the UUIDs are already in the default workspace
         // This should remove /newUuids/node1/shouldBeRemoved
         // graph.copy("/node1").replacingExistingNodesWithSameUuids().fromWorkspace(workspaceName).to("/newUuids/otherNode");
-        graph.clone("/node1")
-             .fromWorkspace(workspaceName)
-             .as(name("otherNode"))
-             .into("/newUuids")
-             .replacingExistingNodesWithSameUuids();
+        graph.clone("/node1").fromWorkspace(workspaceName).as(name("otherNode")).into("/newUuids").replacingExistingNodesWithSameUuids();
 
         /*
          * Focus on testing node structure, since shouldCopyNodeWithChildren tests that properties get copied
@@ -1009,11 +986,7 @@
 
         // Copy again to test the behavior now that the UUIDs are already in the default workspace
         // This should remove /segmentTestUuids/node1[1]
-        graph.clone("/node1")
-             .fromWorkspace(workspaceName)
-             .as(segment("node1[1]"))
-             .into("/segmentTestUuids")
-             .replacingExistingNodesWithSameUuids();
+        graph.clone("/node1").fromWorkspace(workspaceName).as(segment("node1[1]")).into("/segmentTestUuids").replacingExistingNodesWithSameUuids();
 
         /*
          * Focus on testing node structure, since shouldCopyNodeWithChildren tests that properties get copied
@@ -1955,4 +1928,25 @@
         assertThat(subgraph.getNode("node1"), hasProperty("property1", "The quick brown fox jumped over the moon. What? "));
     }
 
+    @Test
+    public void shouldLockNode() {
+        if (!supportsLocks()) return;
+
+        fail("Need to add test body here");
+    }
+
+    @Test
+    public void shouldNotAllowMultipleConcurrentLocksOnSameNode() {
+        if (!supportsLocks()) return;
+
+        fail("Need to add test body here");
+    }
+
+    @Test
+    public void shouldUnlockNode() {
+        if (!supportsLocks()) return;
+
+        fail("Need to add test body here");
+    }
+
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -1368,7 +1368,7 @@
             nodesToVisit.add(nodeInfo());
 
             while (!nodesToVisit.isEmpty()) {
-                Node<JcrNodePayload, JcrPropertyPayload> node = nodesToVisit.get(nodesToVisit.size() - 1);
+                Node<JcrNodePayload, JcrPropertyPayload> node = nodesToVisit.remove(nodesToVisit.size() - 1);
                 if (session().workspace().lockManager().lockFor(node.getLocation()) != null) throw new LockException(
                                                                                                                      JcrI18n.parentAlreadyLocked.text(this.location,
                                                                                                                                                       node.getLocation()));

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -4,6 +4,7 @@
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import javax.jcr.Item;
 import javax.jcr.Node;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
@@ -14,6 +15,8 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.LockFailedException;
+import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.PathNotFoundException;
@@ -80,7 +83,7 @@
             throw new RepositoryException(JcrI18n.uuidRequiredForLock.text(nodeLocation));
         }
 
-        DnaLock lock = new DnaLock(lockOwner, lockUuid, nodeUuid, isDeep, isSessionScoped);
+        DnaLock lock = createLock(lockOwner, lockUuid, nodeUuid, isDeep, isSessionScoped);
 
         Graph.Batch batch = repository.createSystemGraph().batch();
 
@@ -110,15 +113,73 @@
                            false);
         editor.setProperty(JcrLexicon.LOCK_IS_DEEP, (JcrValue)cache.session().getValueFactory().createValue(isDeep), false);
 
-        // Write them directly to the underlying graph
-        repository.createWorkspaceGraph(workspaceName).set(lockOwnerProp, lockIsDeepProp).on(nodeUuid);
-
+        lockNodeInRepository(nodeUuid, lockOwnerProp, lockIsDeepProp, lock, isDeep);
         workspaceLocksByNodeUuid.put(nodeUuid, lock);
 
         return lock;
     }
 
+    /* Factory method added to facilitate mocked testing */
+    DnaLock createLock( String lockOwner,
+                        UUID lockUuid,
+                        UUID nodeUuid,
+                        boolean isDeep,
+                        boolean isSessionScoped ) {
+        return new DnaLock(lockOwner, lockUuid, nodeUuid, isDeep, isSessionScoped);
+    }
+
     /**
+     * Marks the node as locked in the underlying repository with an immediate write (that is, the write is not part of the JCR
+     * session scope and cannot be "rolled back" with a refresh at the {@link Session#refresh(boolean) session} or
+     * {@link Item#refresh(boolean) item} level.
+     * <p>
+     * This method will also attempt to {@link Graph#lock(Location) lock the node in the underlying repository}. If the underlying
+     * repository {@link RepositorySourceCapabilities#supportsLocks() supports locks} and {@link LockFailedException the lock
+     * attempt fails}, this method will cancel the lock attempt by calling {@link #unlock(DnaLock)} and will throw a {@code
+     * RepositoryException}.
+     * </p>
+     * <p>
+     * This method does not modify the system graph. In other words, it will not create the record for the lock in the {@code
+     * /jcr:system/dna:locks} subgraph.
+     * </p>
+     * 
+     * @param nodeUuid the UUID of the node to lock
+     * @param lockOwnerProp an existing property with name {@link JcrLexicon#LOCK_OWNER} and the value being the name of the lock
+     *        owner
+     * @param lockIsDeepProp an existing property with name {@link JcrLexicon#LOCK_IS_DEEP} and the value being {@code true} if
+     *        the lock should include all descendants of the locked node or {@code false} if the lock should only include the
+     *        specified node and not its descendants
+     * @param lock the internal lock representation
+     * @param isDeep {@code true} if the lock should include all descendants of the locked node or {@code false} if the lock
+     *        should only include the specified node and not its descendants. This value is redundant with the lockIsDeep
+     *        parameter, but is included separately as a minor performance optimization
+     * @throws RepositoryException if the repository in which the node represented by {@code nodeUuid} supports locking but
+     *         signals that the lock for the node cannot be acquired
+     */
+    void lockNodeInRepository( UUID nodeUuid,
+                               Property lockOwnerProp,
+                               Property lockIsDeepProp,
+                               DnaLock lock,
+                               boolean isDeep ) throws RepositoryException {
+        // Write them directly to the underlying graph
+        Graph.Batch workspaceBatch = repository.createWorkspaceGraph(workspaceName).batch();
+        workspaceBatch.set(lockOwnerProp, lockIsDeepProp).on(nodeUuid);
+        if (isDeep) {
+            workspaceBatch.lock(nodeUuid).andItsDescendants().withDefaultTimeout();
+        } else {
+            workspaceBatch.lock(nodeUuid).only().withDefaultTimeout();
+        }
+        try {
+            workspaceBatch.execute();
+        } catch (LockFailedException lfe) {
+            // Attempt to lock node at the repo level failed - cancel lock
+            unlock(lock);
+            throw new RepositoryException(lfe);
+        }
+
+    }
+
+    /**
      * Removes the provided lock, effectively unlocking the node to which the lock is associated.
      * 
      * @param lock the lock to be removed
@@ -132,6 +193,9 @@
             batch.delete(pathFactory.create(locksPath, pathFactory.createSegment(lock.getUuid().toString())));
             batch.remove(JcrLexicon.LOCK_OWNER, JcrLexicon.LOCK_IS_DEEP).on(lock.nodeUuid);
             batch.execute();
+
+            unlockNodeInRepository(lock);
+
             workspaceLocksByNodeUuid.remove(lock.nodeUuid);
         } catch (PathNotFoundException pnfe) {
             /*
@@ -147,20 +211,58 @@
         }
     }
 
+    /**
+     * Removes the workspace record of the lock in the underlying repository. This method clears the {@code jcr:lockOwner} and
+     * {@code jcr:lockIsDeep} properties on the node and sends an {@link Graph#unlock(Location) unlock request} to the underlying
+     * repository to clear any locks that it is holding on the node.
+     * <p>
+     * This method does not modify the system graph. In other words, it will not remove the record for the lock in the {@code
+     * /jcr:system/dna:locks} subgraph.
+     * </p>
+     * 
+     * @param lock
+     */
+    void unlockNodeInRepository( DnaLock lock ) {
+        Graph.Batch workspaceBatch = repository.createWorkspaceGraph(this.workspaceName).batch();
+
+        workspaceBatch.remove(JcrLexicon.LOCK_OWNER, JcrLexicon.LOCK_IS_DEEP).on(lock.nodeUuid);
+        workspaceBatch.unlock(lock.nodeUuid);
+
+        workspaceBatch.execute();
+    }
+
+    /**
+     * Checks whether the given lock token is currently held by any session by querying the lock record in the underlying
+     * repository.
+     * 
+     * @param lockToken the lock token to check; may not be null
+     * @return true if a session currently holds the lock token, false otherwise
+     */
     boolean isHeldBySession( String lockToken ) {
+        assert lockToken != null;
+
         ValueFactory<Boolean> booleanFactory = context.getValueFactories().getBooleanFactory();
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
-        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph()
-                                                      .getNodeAt(pathFactory.create(locksPath,
-                                                                                    pathFactory.createSegment(lockToken)));
+        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph().getNodeAt(pathFactory.create(locksPath,
+                                                                                                        pathFactory.createSegment(lockToken)));
 
         return booleanFactory.create(lockNode.getProperty(DnaLexicon.IS_HELD_BY_SESSION).getFirstValue());
 
     }
 
+    /**
+     * Updates the underlying repository directly (i.e., outside the scope of the {@link Session}) to mark the token for the given
+     * lock as being held (or not held) by some {@link Session}. Note that this method does not identify <i>which</i> (if any)
+     * session holds the token for the lock, just that <i>some</i> session holds the token for the lock.
+     * 
+     * @param lockToken the lock token for which the "held" status should be modified; may not be null
+     * @param value the new value
+     */
     void setHeldBySession( String lockToken,
                            boolean value ) {
+        assert lockToken != null;
+
         PropertyFactory propFactory = context.getPropertyFactory();
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
@@ -194,13 +296,23 @@
      * @return the corresponding lock, possibly null if there is no such lock
      * @throws RepositoryException if there is a problem obtaining the information for a lock
      */
-    DnaLock lockFor( Location nodeLocation ) throws RepositoryException {
+    DnaLock lockFor( Location nodeLocation ) {
         UUID nodeUuid = uuidFor(nodeLocation);
         if (nodeUuid == null) return null;
         return workspaceLocksByNodeUuid.get(nodeUuid);
     }
 
+    /**
+     * Returns the UUID that identifies the given location or {@code null} if the location does not have a UUID. The method
+     * returns the {@link Location#getUuid() default UUID} if it exists. If it does not, the method returns the value of the
+     * {@link JcrLexicon#UUID} property as a UUID. If the location does not contain that property, the method returns null.
+     * 
+     * @param location the location for which the UUID should be returned
+     * @return the UUID that identifies the given location or {@code null} if the location does not have a UUID.
+     */
     UUID uuidFor( Location location ) {
+        assert location != null;
+
         if (location.getUuid() != null) return location.getUuid();
 
         org.jboss.dna.graph.property.Property uuidProp = location.getIdProperty(JcrLexicon.UUID);
@@ -303,7 +415,7 @@
                     return sessionScoped;
                 }
 
-                public void refresh() throws LockException, RepositoryException {
+                public void refresh() throws LockException {
                     if (getLockToken() == null) {
                         throw new LockException(JcrI18n.notLocked.text(node.location));
                     }

Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java	                        (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -0,0 +1,156 @@
+package org.jboss.dna.jcr;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.stub;
+import java.util.LinkedList;
+import java.util.UUID;
+import javax.jcr.RepositoryException;
+import javax.security.auth.login.LoginException;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.MockRepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.graph.request.LockBranchRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
+import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
+import org.jboss.dna.jcr.WorkspaceLockManager.DnaLock;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class WorkspaceLockManagerTest {
+
+    private Graph graph;
+    private ExecutionContext context;
+    private UUID validUuid;
+    private Location validLocation;
+    private String sourceName;
+    private String workspaceName;
+    private MockRepositoryConnection connection;
+    private LinkedList<Request> executedRequests;
+
+    private RepositoryNodeTypeManager repoTypeManager;
+    private WorkspaceLockManager workspaceLockManager;
+
+    @Mock
+    private RepositoryConnectionFactory connectionFactory;
+    @Mock
+    protected JcrRepository repository;
+
+    @Before
+    public void beforeEach() throws LoginException {
+        MockitoAnnotations.initMocks(this);
+        executedRequests = new LinkedList<Request>();
+        sourceName = "Source";
+        workspaceName = "default";
+        context = new ExecutionContext();
+        connection = new MockRepositoryConnection(sourceName, executedRequests);
+        stub(connectionFactory.createConnection(sourceName)).toReturn(connection);
+        graph = Graph.create(sourceName, connectionFactory, context);
+
+        validUuid = UUID.randomUUID();
+        validLocation = Location.create(validUuid);
+
+        // Stub out the repository, since we only need a few methods ...
+        repoTypeManager = new RepositoryNodeTypeManager(context);
+
+        PathFactory pathFactory = context.getValueFactories().getPathFactory();
+        stub(repository.getRepositoryTypeManager()).toReturn(repoTypeManager);
+        stub(repository.getRepositorySourceName()).toReturn(sourceName);
+        stub(repository.getPersistentRegistry()).toReturn(context.getNamespaceRegistry());
+        stub(repository.createWorkspaceGraph(anyString())).toAnswer(new Answer<Graph>() {
+            public Graph answer( InvocationOnMock invocation ) throws Throwable {
+                return graph;
+            }
+        });
+        stub(repository.createSystemGraph()).toAnswer(new Answer<Graph>() {
+            public Graph answer( InvocationOnMock invocation ) throws Throwable {
+                return graph;
+            }
+        });
+
+        Path locksPath = pathFactory.createAbsolutePath(JcrLexicon.SYSTEM, DnaLexicon.LOCKS);
+        workspaceLockManager = new WorkspaceLockManager(context, repository, workspaceName, locksPath);
+
+        stub(repository.getLockManager(anyString())).toAnswer(new Answer<WorkspaceLockManager>() {
+            public WorkspaceLockManager answer( InvocationOnMock invocation ) throws Throwable {
+                return workspaceLockManager;
+            }
+        });
+
+        executedRequests.clear();
+    }
+
+    protected Path createPath( String path ) {
+        return context.getValueFactories().getPathFactory().create(path);
+    }
+
+    protected Path createPath( Path parent,
+                               String path ) {
+        return context.getValueFactories().getPathFactory().create(parent, path);
+    }
+
+    protected Name createName( String name ) {
+        return context.getValueFactories().getNameFactory().create(name);
+    }
+
+    protected Property createProperty( String name,
+                                       Object... values ) {
+        return context.getPropertyFactory().create(createName(name), values);
+    }
+
+    protected void assertNextRequestIsLock( Location at,
+                                            LockScope lockScope,
+                                            long lockTimeout ) {
+        Request request = executedRequests.poll();
+        assertThat(request, is(instanceOf(LockBranchRequest.class)));
+        LockBranchRequest lock = (LockBranchRequest)request;
+        assertThat(lock.at(), is(at));
+        assertThat(lock.lockScope(), is(lockScope));
+        assertThat(lock.lockTimeoutInMillis(), is(lockTimeout));
+    }
+
+    protected void assertNextRequestIsUnlock( Location at ) {
+        Request request = executedRequests.poll();
+        assertThat(request, is(instanceOf(UnlockBranchRequest.class)));
+        UnlockBranchRequest unlock = (UnlockBranchRequest)request;
+        assertThat(unlock.at(), is(at));
+    }
+
+    @Test
+    public void shouldCreateLockRequestWhenLockingNode() throws RepositoryException {
+        DnaLock lock = workspaceLockManager.createLock("testOwner", UUID.randomUUID(), validUuid, false, false);
+        PropertyFactory propFactory = context.getPropertyFactory();
+        String lockOwner = "testOwner";
+        boolean isDeep = false;
+
+        Property lockOwnerProp = propFactory.create(JcrLexicon.LOCK_OWNER, lockOwner);
+        Property lockIsDeepProp = propFactory.create(JcrLexicon.LOCK_IS_DEEP, isDeep);
+
+        workspaceLockManager.lockNodeInRepository(validUuid, lockOwnerProp, lockIsDeepProp, lock, isDeep);
+
+        assertNextRequestIsLock(validLocation, LockScope.SELF_ONLY, 0);
+    }
+
+    @Test
+    public void shouldCreateLockRequestWhenUnlockingNode() {
+        DnaLock lock = workspaceLockManager.createLock("testOwner", UUID.randomUUID(), validUuid, false, false);
+        workspaceLockManager.unlockNodeInRepository(lock);
+
+        assertNextRequestIsUnlock(validLocation);
+    }
+
+}


Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/WorkspaceLockManagerTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/extensions/dna-connector-infinispan/src/main/java/org/jboss/dna/connector/infinispan/InfinispanRepository.java
===================================================================
--- trunk/extensions/dna-connector-infinispan/src/main/java/org/jboss/dna/connector/infinispan/InfinispanRepository.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/extensions/dna-connector-infinispan/src/main/java/org/jboss/dna/connector/infinispan/InfinispanRepository.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -4,10 +4,12 @@
 import org.infinispan.Cache;
 import org.infinispan.manager.CacheManager;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.LockFailedException;
 import org.jboss.dna.graph.connector.map.AbstractMapWorkspace;
 import org.jboss.dna.graph.connector.map.MapNode;
 import org.jboss.dna.graph.connector.map.MapRepository;
 import org.jboss.dna.graph.connector.map.MapWorkspace;
+import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
 
 /**
  * The repository that uses an Infinispan instance.
@@ -80,6 +82,18 @@
             assert nodeUuid != null;
             return workspaceCache.get(nodeUuid);
         }
+
+        @Override
+        public void lockNode( MapNode node,
+                              LockScope lockScope,
+                              long lockTimeoutInMillis ) throws LockFailedException {
+            // Locking is not supported by this connector
+        }
+
+        @Override
+        public void unlockNode( MapNode node ) {
+            // Locking is not supported by this connector
+        }
     }
 
 }

Modified: trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRepository.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRepository.java	2009-10-21 17:29:30 UTC (rev 1303)
+++ trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRepository.java	2009-10-21 22:12:10 UTC (rev 1304)
@@ -5,10 +5,12 @@
 import org.jboss.cache.Fqn;
 import org.jboss.cache.Node;
 import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.LockFailedException;
 import org.jboss.dna.graph.connector.map.AbstractMapWorkspace;
 import org.jboss.dna.graph.connector.map.MapNode;
 import org.jboss.dna.graph.connector.map.MapRepository;
 import org.jboss.dna.graph.connector.map.MapWorkspace;
+import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
 
 /**
  * A repository implementation that uses JBoss Cache.
@@ -81,6 +83,19 @@
             assert nodeUuid != null;
             return workspaceNode.get(nodeUuid);
         }
+
+        @Override
+        public void lockNode( MapNode node,
+                              LockScope lockScope,
+                              long lockTimeoutInMillis ) throws LockFailedException {
+            // Locking is not supported by this connector
+        }
+
+        @Override
+        public void unlockNode( MapNode node ) {
+            // Locking is not supported by this connector
+        }
+
     }
 
 }


From dna-commits at lists.jboss.org  Thu Oct 22 06:27:13 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 22 Oct 2009 06:27:13 -0400
Subject: [dna-commits] DNA SVN: r1305 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory
	and 3 other directories.
Message-ID: <200910221027.n9MARDKr020393@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-10-22 06:27:12 -0400 (Thu, 22 Oct 2009)
New Revision: 1305

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
   trunk/extensions/dna-connector-infinispan/src/main/java/org/jboss/dna/connector/infinispan/InfinispanRepository.java
   trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRepository.java
Log:
Cleaned up some things that work in Java6 but are breaking the build in Java5

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-10-21 22:12:10 UTC (rev 1304)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-10-22 10:27:12 UTC (rev 1305)
@@ -4743,12 +4743,10 @@
         public LockTimeout<T> andItsDescendants() {
             return new LockTimeout<T>() {
 
-                @Override
                 public T withDefaultTimeout() {
                     return submit(target, org.jboss.dna.graph.request.LockBranchRequest.LockScope.SELF_AND_DESCENDANTS, 0);
                 }
 
-                @Override
                 public T withTimeoutOf( long milliseconds ) {
                     return submit(target,
                                   org.jboss.dna.graph.request.LockBranchRequest.LockScope.SELF_AND_DESCENDANTS,
@@ -4761,12 +4759,10 @@
         public LockTimeout<T> only() {
             return new LockTimeout<T>() {
 
-                @Override
                 public T withDefaultTimeout() {
                     return submit(target, org.jboss.dna.graph.request.LockBranchRequest.LockScope.SELF_ONLY, 0);
                 }
 
-                @Override
                 public T withTimeoutOf( long milliseconds ) {
                     return submit(target, org.jboss.dna.graph.request.LockBranchRequest.LockScope.SELF_ONLY, milliseconds);
                 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-10-21 22:12:10 UTC (rev 1304)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepository.java	2009-10-22 10:27:12 UTC (rev 1305)
@@ -126,14 +126,12 @@
             return super.copyNode(context, original, newWorkspace, newParent, desiredName, recursive, oldToNewUuids);
         }
 
-        @Override
         public void lockNode( MapNode node,
                               LockScope lockScope,
                               long lockTimeoutInMillis ) throws LockFailedException {
             // Locking is not supported by this connector
         }
 
-        @Override
         public void unlockNode( MapNode node ) {
             // Locking is not supported by this connector
         }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-21 22:12:10 UTC (rev 1304)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-22 10:27:12 UTC (rev 1305)
@@ -157,7 +157,7 @@
             ruleStack.addFirst(this);
 
             // After this rule is done as is no longer needed, we need to try to push SELECTs and PROJECTs again ...
-            if (!(ruleStack.peekFirst() instanceof PushSelectCriteria)) {
+            if (!(ruleStack.peek() instanceof PushSelectCriteria)) {
                 // We haven't already added these, so add them now ...
                 ruleStack.addFirst(PushProjects.INSTANCE);
                 if (context.getHints().hasCriteria) {

Modified: trunk/extensions/dna-connector-infinispan/src/main/java/org/jboss/dna/connector/infinispan/InfinispanRepository.java
===================================================================
--- trunk/extensions/dna-connector-infinispan/src/main/java/org/jboss/dna/connector/infinispan/InfinispanRepository.java	2009-10-21 22:12:10 UTC (rev 1304)
+++ trunk/extensions/dna-connector-infinispan/src/main/java/org/jboss/dna/connector/infinispan/InfinispanRepository.java	2009-10-22 10:27:12 UTC (rev 1305)
@@ -83,14 +83,12 @@
             return workspaceCache.get(nodeUuid);
         }
 
-        @Override
         public void lockNode( MapNode node,
                               LockScope lockScope,
                               long lockTimeoutInMillis ) throws LockFailedException {
             // Locking is not supported by this connector
         }
 
-        @Override
         public void unlockNode( MapNode node ) {
             // Locking is not supported by this connector
         }

Modified: trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRepository.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRepository.java	2009-10-21 22:12:10 UTC (rev 1304)
+++ trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheRepository.java	2009-10-22 10:27:12 UTC (rev 1305)
@@ -84,14 +84,12 @@
             return workspaceNode.get(nodeUuid);
         }
 
-        @Override
         public void lockNode( MapNode node,
                               LockScope lockScope,
                               long lockTimeoutInMillis ) throws LockFailedException {
             // Locking is not supported by this connector
         }
 
-        @Override
         public void unlockNode( MapNode node ) {
             // Locking is not supported by this connector
         }


From dna-commits at lists.jboss.org  Thu Oct 22 16:22:06 2009
From: dna-commits at lists.jboss.org (© VIAGRA ® Official)
Date: Thu, 22 Oct 2009 16:22:06 -0400
Subject: [dna-commits] Dear dna-commits@lists.jboss.org 79% 0FF on Pfizer !
Message-ID: <200910222022.n9MKM63d007068@lists01.dmz-a.mwc.hst.phx2.redhat.com>

An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/dna-commits/attachments/20091022/4c42644d/attachment.html 

From dna-commits at lists.jboss.org  Thu Oct 22 20:54:08 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 22 Oct 2009 20:54:08 -0400
Subject: [dna-commits] DNA SVN: r1306 - in trunk:
	extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic
	and 1 other directory.
Message-ID: <200910230054.n9N0s8ax029621@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-10-22 20:54:08 -0400 (Thu, 22 Oct 2009)
New Revision: 1306

Modified:
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
Log:
DNA-518 JPA connector fails to properly clone or update from another workspace

Committed patch that pushes the desiredSegment down into the copyNode call (if the desiredSegment is specified). Previously, copyNode was silently converting the desiredSegment field into a desiredName.


Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-10-22 10:27:12 UTC (rev 1305)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java	2009-10-23 00:54:08 UTC (rev 1306)
@@ -37,6 +37,7 @@
 import org.apache.jackrabbit.test.api.NodeItemIsNewTest;
 import org.apache.jackrabbit.test.api.NodeOrderableChildNodesTest;
 import org.apache.jackrabbit.test.api.NodeRemoveMixinTest;
+import org.apache.jackrabbit.test.api.NodeTest;
 import org.apache.jackrabbit.test.api.PropertyItemIsModifiedTest;
 import org.apache.jackrabbit.test.api.PropertyItemIsNewTest;
 import org.apache.jackrabbit.test.api.PropertyTest;
@@ -198,7 +199,7 @@
             // addTestSuite(ReferencesTest.class);
             addTestSuite(SessionTest.class);
             // addTestSuite(SessionUUIDTest.class);
-            // DNA-518 addTestSuite(NodeTest.class);
+            addTestSuite(NodeTest.class);
             // addTestSuite(NodeUUIDTest.class);
             addTestSuite(NodeOrderableChildNodesTest.class);
             addTestSuite(PropertyTest.class);

Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java	2009-10-22 10:27:12 UTC (rev 1305)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java	2009-10-23 00:54:08 UTC (rev 1306)
@@ -1152,13 +1152,14 @@
                                      ActualLocation actualNewParent,
                                      UuidConflictBehavior uuidConflictBehavior,
                                      Name desiredName,
+                                     Path.Segment desiredSegment,
                                      Map<String, String> oldUuidsToNewUuids,
                                      Map<String, ChildEntity> addedLocations,
                                      Map<String, Location> deletedLocations ) {
         assert fromWorkspace != null;
         assert intoWorkspace != null;
         assert original != null;
-        assert desiredName != null;
+        assert (desiredName != null ? desiredSegment == null : desiredSegment != null) : "Either desiredName or desiredSegment must not be null";
         assert oldUuidsToNewUuids != null;
 
         // Assume that UUID isn't changing. If the conflict behavior says that it does change, the switch statement will handle it
@@ -1184,10 +1185,17 @@
                 break;
             case REPLACE_EXISTING_NODE:
                 try {
-                    existingLocation = getActualLocation(intoWorkspace, Location.create(oldUuid));
-                    deletedLocations.putAll(computeDeletedLocations(intoWorkspace, existingLocation.location, true));
+                    if (desiredSegment != null) {
+                        existingLocation = getActualLocation(intoWorkspace,
+                                                             Location.create(pathFactory.create(actualNewParent.location.getPath(),
+                                                                                                desiredSegment)));
+                        newUuid = existingLocation.childEntity.getId().getChildUuidString();
+
+                    } else {
+                        existingLocation = getActualLocation(intoWorkspace, Location.create(oldUuid));
+                        deletedLocations.putAll(computeDeletedLocations(intoWorkspace, existingLocation.location, true));
+                    }
                 } catch (PathNotFoundException pnfe) {
-
                 }
                 break;
             default:
@@ -1196,6 +1204,7 @@
         oldUuidsToNewUuids.put(original.getId().getChildUuidString(), newUuid);
 
         if (existingLocation != null && existingLocation.childEntity.getParentUuidString().equals(actualNewParent.uuid)) {
+            if (desiredName == null) desiredName = desiredSegment.getName();
             NamespaceEntity namespace = NamespaceEntity.findByUri(entities, desiredName.getNamespaceUri());
 
             ChildEntity existingChild = existingLocation.childEntity;
@@ -1285,6 +1294,7 @@
                                                      actualNewParent,
                                                      UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID,
                                                      desiredName,
+                                                     null,
                                                      originalToNewUuid,
                                                      addedLocations,
                                                      deletedLocations).location;
@@ -1306,6 +1316,7 @@
                                   actualNewParent,
                                   UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID,
                                   desiredName,
+                                  null,
                                   originalToNewUuid,
                                   addedLocations,
                                   deletedLocations);
@@ -1472,7 +1483,6 @@
                     ChildEntity original = originalIter.next();
 
                     Name desiredName = request.desiredName();
-                    if (desiredName == null) desiredName = fromPath.getLastSegment().getName();
                     actualToLocation = this.copyNode(entities,
                                                      fromWorkspace,
                                                      intoWorkspace,
@@ -1480,6 +1490,7 @@
                                                      actualNewParent,
                                                      conflictBehavior,
                                                      desiredName,
+                                                     desiredName != null ? null : fromPath.getLastSegment(),
                                                      originalToNewUuid,
                                                      addedLocations,
                                                      deletedLocations).location;
@@ -1501,6 +1512,7 @@
                                   actualNewParent,
                                   conflictBehavior,
                                   desiredName,
+                                  null,
                                   originalToNewUuid,
                                   addedLocations,
                                   deletedLocations);
@@ -2087,8 +2099,8 @@
                 } else {
 
                     ActualLocation actualBeforeLocation = getActualLocation(workspace, beforeLocation);
-                    ActualLocation actualIntoLocation = getActualLocation(workspace, Location.create(beforeLocation.getPath()
-                                                                                                                   .getParent()));
+                    ActualLocation actualIntoLocation = getActualLocation(workspace,
+                                                                          Location.create(beforeLocation.getPath().getParent()));
 
                     actualNewLocation = moveNodeBefore(workspace, actualLocation, actualIntoLocation, actualBeforeLocation);
                 }


From dna-commits at lists.jboss.org  Mon Oct 26 12:01:30 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 26 Oct 2009 12:01:30 -0400
Subject: [dna-commits] DNA SVN: r1307 -
	trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
Message-ID: <200910261601.n9QG1U5L020295@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-26 12:01:30 -0400 (Mon, 26 Oct 2009)
New Revision: 1307

Modified:
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
Log:
Removed unused exception from JavaDoc

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-23 00:54:08 UTC (rev 1306)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-26 16:01:30 UTC (rev 1307)
@@ -244,8 +244,9 @@
         ValueFactory<Boolean> booleanFactory = context.getValueFactories().getBooleanFactory();
         PathFactory pathFactory = context.getValueFactories().getPathFactory();
 
-        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph().getNodeAt(pathFactory.create(locksPath,
-                                                                                                        pathFactory.createSegment(lockToken)));
+        org.jboss.dna.graph.Node lockNode = repository.createSystemGraph()
+                                                      .getNodeAt(pathFactory.create(locksPath,
+                                                                                    pathFactory.createSegment(lockToken)));
 
         return booleanFactory.create(lockNode.getProperty(DnaLexicon.IS_HELD_BY_SESSION).getFirstValue());
 
@@ -294,7 +295,6 @@
      * 
      * @param nodeLocation the node UUID
      * @return the corresponding lock, possibly null if there is no such lock
-     * @throws RepositoryException if there is a problem obtaining the information for a lock
      */
     DnaLock lockFor( Location nodeLocation ) {
         UUID nodeUuid = uuidFor(nodeLocation);


From dna-commits at lists.jboss.org  Mon Oct 26 12:05:53 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Mon, 26 Oct 2009 12:05:53 -0400
Subject: [dna-commits] DNA SVN: r1308 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph.
Message-ID: <200910261605.n9QG5rli020882@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-26 12:05:53 -0400 (Mon, 26 Oct 2009)
New Revision: 1308

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
Log:
Merge branch 'patches'

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-10-26 16:01:30 UTC (rev 1307)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java	2009-10-26 16:05:53 UTC (rev 1308)
@@ -465,7 +465,7 @@
      * @return an object that allows the scope of the lock to be defined
      */
     public LockScope<LockTimeout<Conjunction<Graph>>> lock( Property firstIdProperty,
-                                               Property... additionalIdProperties ) {
+                                                            Property... additionalIdProperties ) {
         return lock(Location.create(firstIdProperty, additionalIdProperties));
     }
 
@@ -2006,13 +2006,15 @@
                             }
 
                             public List<Location> under( Location at ) {
-                                return requests.readBlockOfChildren(at, getCurrentWorkspaceName(), startingIndex, blockSize).getChildren();
+                                return requests.readBlockOfChildren(at, getCurrentWorkspaceName(), startingIndex, blockSize)
+                                               .getChildren();
                             }
                         };
                     }
 
                     public List<Location> startingAfter( final Location previousSibling ) {
-                        return requests.readNextBlockOfChildren(previousSibling, getCurrentWorkspaceName(), blockSize).getChildren();
+                        return requests.readNextBlockOfChildren(previousSibling, getCurrentWorkspaceName(), blockSize)
+                                       .getChildren();
                     }
 
                     public List<Location> startingAfter( String pathOfPreviousSibling ) {
@@ -2666,7 +2668,7 @@
          * @return an object that allows the scope of the lock to be defined
          */
         public LockScope<LockTimeout<BatchConjunction>> lock( Property firstIdProperty,
-                                                 Property... additionalIdProperties ) {
+                                                              Property... additionalIdProperties ) {
             return lock(Location.create(firstIdProperty, additionalIdProperties));
         }
 
@@ -4740,6 +4742,7 @@
                                      org.jboss.dna.graph.request.LockBranchRequest.LockScope lockScope,
                                      long lockTimeoutInMillis );
 
+        @SuppressWarnings( "synthetic-access" )
         public LockTimeout<T> andItsDescendants() {
             return new LockTimeout<T>() {
 
@@ -4756,6 +4759,7 @@
             };
         }
 
+        @SuppressWarnings( "synthetic-access" )
         public LockTimeout<T> only() {
             return new LockTimeout<T>() {
 
@@ -6424,7 +6428,10 @@
         }
 
         public SubgraphNode getNode( Name relativePath ) {
-            Path path = getGraph().getContext().getValueFactories().getPathFactory().create(getLocation().getPath(), relativePath);
+            Path path = getGraph().getContext()
+                                  .getValueFactories()
+                                  .getPathFactory()
+                                  .create(getLocation().getPath(), relativePath);
             path = path.getNormalizedPath();
             return getNode(path);
         }


From dna-commits at lists.jboss.org  Tue Oct 27 12:37:53 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 27 Oct 2009 12:37:53 -0400
Subject: [dna-commits] DNA SVN: r1309 -
	trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views.
Message-ID: <200910271637.n9RGbr4s027163@svn01.web.mwc.hst.phx2.redhat.com>

Author: elvisisking
Date: 2009-10-27 12:37:53 -0400 (Tue, 27 Oct 2009)
New Revision: 1309

Modified:
   trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/DnaContentProvider.java
Log:
Changed server retry wait time from 10 secs to 2 secs. This is the time to wait before trying to connect again.

Modified: trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/DnaContentProvider.java
===================================================================
--- trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/DnaContentProvider.java	2009-10-26 16:05:53 UTC (rev 1308)
+++ trunk/tools/org.jboss.dna.eclipse.jcr.rest.client/src/org/jboss/dna/eclipse/jcr/rest/client/views/DnaContentProvider.java	2009-10-27 16:37:53 UTC (rev 1309)
@@ -73,7 +73,7 @@
     /**
      * If a server connection cannot be established, wait this amount of time before trying again.
      */
-    private static final long RETRY_DURATION = 10000;
+    private static final long RETRY_DURATION = 2000;
 
     // ===========================================================================================================================
     // Class Methods


From dna-commits at lists.jboss.org  Tue Oct 27 15:12:46 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 27 Oct 2009 15:12:46 -0400
Subject: [dna-commits] DNA SVN: r1310 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation.
Message-ID: <200910271912.n9RJCk4Q030525@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-27 15:12:46 -0400 (Tue, 27 Oct 2009)
New Revision: 1310

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
Log:
DNA-457 Added support for the new LockBranchRequest and UnlockBranchRequest classes in the federation connector.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java	2009-10-27 16:37:53 UTC (rev 1309)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java	2009-10-27 19:12:46 UTC (rev 1310)
@@ -66,18 +66,23 @@
 import org.jboss.dna.graph.request.GetWorkspacesRequest;
 import org.jboss.dna.graph.request.InvalidRequestException;
 import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.jboss.dna.graph.request.LockBranchRequest;
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadBlockOfChildrenRequest;
 import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest;
 import org.jboss.dna.graph.request.ReadNodeRequest;
 import org.jboss.dna.graph.request.ReadPropertyRequest;
 import org.jboss.dna.graph.request.RemovePropertyRequest;
 import org.jboss.dna.graph.request.RenameNodeRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
 import org.jboss.dna.graph.request.UnsupportedRequestException;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.UpdateValuesRequest;
 import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
 import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -839,6 +844,26 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBlockOfChildrenRequest)
+     */
+    @Override
+    public void process( ReadBlockOfChildrenRequest request ) {
+        super.process(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest)
+     */
+    @Override
+    public void process( ReadNextBlockOfChildrenRequest request ) {
+        super.process(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
      */
     @Override
@@ -1129,6 +1154,42 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdateValuesRequest)
+     */
+    @Override
+    public void process( UpdateValuesRequest request ) {
+        // Figure out where this request is projected ...
+        ProjectedNode projectedNode = project(request.on(), request.inWorkspace(), request, true);
+        if (projectedNode == null) return;
+
+        // Create the federated request ...
+        FederatedRequest federatedRequest = new FederatedRequest(request);
+
+        // Any non-read request should be submitted to the first ProxyNode ...
+        while (projectedNode != null) {
+            if (projectedNode.isProxy()) {
+                ProxyNode proxy = projectedNode.asProxy();
+                // Create and submit a request for the projection ...
+                UpdateValuesRequest pushDownRequest = new UpdateValuesRequest(proxy.workspaceName(), proxy.location(),
+                                                                              request.property(), request.addedValues(),
+                                                                              request.removedValues());
+                federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+
+                // Submit the requests for processing and then STOP ...
+                submit(federatedRequest);
+                return;
+            }
+            assert projectedNode.isPlaceholder();
+            projectedNode = projectedNode.next();
+        }
+        // Unable to perform this update ...
+        String msg = GraphI18n.unableToUpdatePlaceholder.text(readable(request.on()), request.inWorkspace(), getSourceName());
+        request.setError(new UnsupportedRequestException(msg));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.SetPropertyRequest)
      */
     @Override
@@ -1498,6 +1559,75 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.LockBranchRequest)
+     */
+    @Override
+    public void process( LockBranchRequest request ) {
+        // Figure out where this request is projected ...
+        ProjectedNode projectedNode = project(request.at(), request.inWorkspace(), request, true);
+        if (projectedNode == null) return;
+
+        // Create the federated request ...
+        FederatedRequest federatedRequest = new FederatedRequest(request);
+
+        // Any non-read request should be submitted to the first ProxyNode ...
+        while (projectedNode != null) {
+            if (projectedNode.isProxy()) {
+                ProxyNode proxy = projectedNode.asProxy();
+                // Create and submit a request for the projection ...
+                LockBranchRequest pushDownRequest = new LockBranchRequest(proxy.location(), proxy.workspaceName(),
+                                                                          request.lockScope(), request.lockTimeoutInMillis());
+                federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+
+                // Submit the requests for processing and then STOP ...
+                submit(federatedRequest);
+                return;
+            }
+            assert projectedNode.isPlaceholder();
+            projectedNode = projectedNode.next();
+        }
+        // Unable to perform this update ...
+        String msg = GraphI18n.unableToUpdatePlaceholder.text(readable(request.at()), request.inWorkspace(), getSourceName());
+        request.setError(new UnsupportedRequestException(msg));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UnlockBranchRequest)
+     */
+    @Override
+    public void process( UnlockBranchRequest request ) {
+        // Figure out where this request is projected ...
+        ProjectedNode projectedNode = project(request.at(), request.inWorkspace(), request, true);
+        if (projectedNode == null) return;
+
+        // Create the federated request ...
+        FederatedRequest federatedRequest = new FederatedRequest(request);
+
+        // Any non-read request should be submitted to the first ProxyNode ...
+        while (projectedNode != null) {
+            if (projectedNode.isProxy()) {
+                ProxyNode proxy = projectedNode.asProxy();
+                // Create and submit a request for the projection ...
+                UnlockBranchRequest pushDownRequest = new UnlockBranchRequest(proxy.location(), proxy.workspaceName());
+                federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+
+                // Submit the requests for processing and then STOP ...
+                submit(federatedRequest);
+                return;
+            }
+            assert projectedNode.isPlaceholder();
+            projectedNode = projectedNode.next();
+        }
+        // Unable to perform this update ...
+        String msg = GraphI18n.unableToUpdatePlaceholder.text(readable(request.at()), request.inWorkspace(), getSourceName());
+        request.setError(new UnsupportedRequestException(msg));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java	2009-10-27 16:37:53 UTC (rev 1309)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java	2009-10-27 19:12:46 UTC (rev 1310)
@@ -39,8 +39,10 @@
 import org.jboss.dna.graph.request.CreateNodeRequest;
 import org.jboss.dna.graph.request.CreateWorkspaceRequest;
 import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DeleteChildrenRequest;
 import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
 import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.LockBranchRequest;
 import org.jboss.dna.graph.request.MoveBranchRequest;
 import org.jboss.dna.graph.request.ReadAllChildrenRequest;
 import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
@@ -53,7 +55,9 @@
 import org.jboss.dna.graph.request.RenameNodeRequest;
 import org.jboss.dna.graph.request.Request;
 import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.UpdateValuesRequest;
 import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
 import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
 import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -306,6 +310,20 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdateValuesRequest)
+     */
+    @Override
+    public void process( UpdateValuesRequest request ) {
+        UpdateValuesRequest source = (UpdateValuesRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+        if (checkErrorOrCancel(request, source)) return;
+        request.setActualLocation(source.getActualLocationOfNode(),
+                                  request.getActualAddedValues(),
+                                  request.getActualRemovedValues());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
      */
     @Override
@@ -343,6 +361,18 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteChildrenRequest)
+     */
+    @Override
+    public void process( DeleteChildrenRequest request ) {
+        DeleteChildrenRequest source = (DeleteChildrenRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+        if (checkErrorOrCancel(request, source)) return;
+        request.setActualLocationOfNode(source.getActualLocationOfNode());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
      */
     @Override
@@ -355,6 +385,30 @@
     /**
      * {@inheritDoc}
      * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.LockBranchRequest)
+     */
+    @Override
+    public void process( LockBranchRequest request ) {
+        LockBranchRequest source = (LockBranchRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+        if (checkErrorOrCancel(request, source)) return;
+        request.setActualLocation(source.getActualLocation());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UnlockBranchRequest)
+     */
+    @Override
+    public void process( UnlockBranchRequest request ) {
+        UnlockBranchRequest source = (UnlockBranchRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+        if (checkErrorOrCancel(request, source)) return;
+        request.setActualLocation(source.getActualLocation());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
      */
     @Override


From dna-commits at lists.jboss.org  Tue Oct 27 16:02:40 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 27 Oct 2009 16:02:40 -0400
Subject: [dna-commits] DNA SVN: r1311 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor.
Message-ID: <200910272002.n9RK2e18007829@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-10-27 16:02:40 -0400 (Tue, 27 Oct 2009)
New Revision: 1311

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
Log:
DNA-457 Add JSR-170 Locking Optional Feature

The attached patch (DNA-457_fixed_RequestProcessor.patch) corrects the default RequestProcessor implementation to populate the actual location on the request, looking up the path for the node if necessary.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-10-27 19:12:46 UTC (rev 1310)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-10-27 20:02:40 UTC (rev 1311)
@@ -866,6 +866,21 @@
      * @param request the request
      */
     public void process( LockBranchRequest request ) {
+        Location actualLocation = request.at();
+        if (!actualLocation.hasPath()) {
+            VerifyNodeExistsRequest nodeExists = new VerifyNodeExistsRequest(request.at(), request.inWorkspace());
+
+            process(nodeExists);
+
+            if (nodeExists.hasError()) {
+                request.setError(nodeExists.getError());
+                return;
+            }
+
+            actualLocation = nodeExists.getActualLocationOfNode();
+        }
+
+        request.setActualLocation(actualLocation);
     }
 
     /**
@@ -879,6 +894,21 @@
      * @param request the request
      */
     public void process( UnlockBranchRequest request ) {
+        Location actualLocation = request.at();
+        if (!actualLocation.hasPath()) {
+            VerifyNodeExistsRequest nodeExists = new VerifyNodeExistsRequest(request.at(), request.inWorkspace());
+
+            process(nodeExists);
+
+            if (nodeExists.hasError()) {
+                request.setError(nodeExists.getError());
+                return;
+            }
+
+            actualLocation = nodeExists.getActualLocationOfNode();
+        }
+
+        request.setActualLocation(actualLocation);
     }
 
     /**


From dna-commits at lists.jboss.org  Tue Oct 27 20:41:28 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Tue, 27 Oct 2009 20:41:28 -0400
Subject: [dna-commits] DNA SVN: r1312 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/request.
Message-ID: <200910280041.n9S0fSvY027518@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-10-27 20:41:28 -0400 (Tue, 27 Oct 2009)
New Revision: 1312

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
Log:
Corrected implementation of changedLocation to be consistent with usage in CreateNodeRequest.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-10-27 20:02:40 UTC (rev 1311)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/LockBranchRequest.java	2009-10-28 00:41:28 UTC (rev 1312)
@@ -167,7 +167,7 @@
      */
     @Override
     public Location changedLocation() {
-        return at;
+        return actualLocation;
     }
 
     /**

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-10-27 20:02:40 UTC (rev 1311)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UnlockBranchRequest.java	2009-10-28 00:41:28 UTC (rev 1312)
@@ -133,7 +133,7 @@
      */
     @Override
     public Location changedLocation() {
-        return at;
+        return actualLocation;
     }
 
     /**


From dna-commits at lists.jboss.org  Wed Oct 28 10:43:18 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 28 Oct 2009 10:43:18 -0400
Subject: [dna-commits] DNA SVN: r1313 - in trunk:
	dna-graph/src/main/java/org/jboss/dna/graph/request/processor
	and 3 other directories.
Message-ID: <200910281443.n9SEhI5f023401@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-28 10:43:17 -0400 (Wed, 28 Oct 2009)
New Revision: 1313

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencingService.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/StreamSequencerAdapterTest.java
Log:
DNA-456 Changed the NetChangeObserver functionality so that there is a NetChanges object that corresponds to the Changes object, where the NetChanges contains all of the individual NetChange objects corresponding to the ChangeRequest objects in the Changes object.  This means that we still keep the notion of atomic set of changes.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java	2009-10-28 00:41:28 UTC (rev 1312)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java	2009-10-28 14:43:17 UTC (rev 1313)
@@ -24,7 +24,7 @@
 package org.jboss.dna.graph.observe;
 
 import java.io.Serializable;
-import java.util.Iterator;
+import java.util.Collections;
 import java.util.List;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.SecurityContext;
@@ -35,15 +35,15 @@
  * A set of changes that were made atomically. Each change is in the form of a frozen {@link ChangeRequest}.
  */
 @Immutable
-public final class Changes implements Iterable<ChangeRequest>, Comparable<Changes>, Serializable {
+public class Changes implements Comparable<Changes>, Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    private final String processId;
-    private final String userName;
-    private final String sourceName;
-    private final DateTime timestamp;
-    private final List<ChangeRequest> changeRequests;
+    protected final String processId;
+    protected final String userName;
+    protected final String sourceName;
+    protected final DateTime timestamp;
+    protected final List<ChangeRequest> changeRequests;
 
     public Changes( String userName,
                     String sourceName,
@@ -57,13 +57,33 @@
                     String sourceName,
                     DateTime timestamp,
                     List<ChangeRequest> requests ) {
+        assert requests != null;
+        assert !requests.isEmpty();
         this.userName = userName;
         this.sourceName = sourceName;
         this.timestamp = timestamp;
-        this.changeRequests = requests;
+        this.changeRequests = Collections.unmodifiableList(requests);
         this.processId = processId != null ? processId : "";
+        assert this.userName != null;
+        assert this.sourceName != null;
+        assert this.timestamp != null;
+        assert this.changeRequests != null;
+        assert this.processId != null;
     }
 
+    protected Changes( Changes changes ) {
+        this.userName = changes.userName;
+        this.sourceName = changes.sourceName;
+        this.timestamp = changes.timestamp;
+        this.changeRequests = changes.changeRequests;
+        this.processId = changes.processId;
+        assert this.userName != null;
+        assert this.sourceName != null;
+        assert this.timestamp != null;
+        assert this.changeRequests != null;
+        assert this.processId != null;
+    }
+
     /**
      * Get the user that made these changes.
      * 
@@ -102,12 +122,12 @@
     }
 
     /**
-     * {@inheritDoc}
+     * Get the list of changes.
      * 
-     * @see java.lang.Iterable#iterator()
+     * @return the immutable list of change requests; never null and never empty
      */
-    public Iterator<ChangeRequest> iterator() {
-        return this.changeRequests.iterator();
+    public List<ChangeRequest> getChangeRequests() {
+        return changeRequests;
     }
 
     /**
@@ -148,4 +168,18 @@
         }
         return false;
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        if (processId.length() != 0) {
+            return getTimestamp() + " @" + getUserName() + " [" + getSourceName() + "] - " + changeRequests.size() + " events";
+        }
+        return getTimestamp() + " @" + getUserName() + " #" + getProcessId() + " [" + getSourceName() + "] - "
+               + changeRequests.size() + " events";
+    }
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java	2009-10-28 00:41:28 UTC (rev 1312)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java	2009-10-28 14:43:17 UTC (rev 1313)
@@ -27,6 +27,8 @@
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
@@ -42,8 +44,10 @@
 import org.jboss.dna.graph.request.CreateNodeRequest;
 import org.jboss.dna.graph.request.DeleteBranchRequest;
 import org.jboss.dna.graph.request.DeleteChildrenRequest;
+import org.jboss.dna.graph.request.LockBranchRequest;
 import org.jboss.dna.graph.request.RemovePropertyRequest;
 import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
 import org.jboss.dna.graph.request.UpdatePropertiesRequest;
 
 /**
@@ -74,7 +78,7 @@
     public void notify( Changes changes ) {
         Map<String, Map<Location, NetChangeDetails>> detailsByLocationByWorkspace = new HashMap<String, Map<Location, NetChangeDetails>>();
         // Process each of the events, extracting the node path and property details for each ...
-        for (ChangeRequest change : changes) {
+        for (ChangeRequest change : changes.getChangeRequests()) {
             Location location = change.changedLocation();
             String workspace = change.changedWorkspace();
 
@@ -129,37 +133,84 @@
                     }
                     childDetails.addEventType(ChangeType.NODE_REMOVED);
                 }
+            } else if (change instanceof LockBranchRequest) {
+                details.setLockAction(LockAction.LOCKED);
+            } else if (change instanceof UnlockBranchRequest) {
+                details.setLockAction(LockAction.UNLOCKED);
             }
         }
 
         // Walk through the net changes ...
-        String sourceName = changes.getSourceName();
+        List<NetChange> netChanges = new LinkedList<NetChange>();
         for (Map.Entry<String, Map<Location, NetChangeDetails>> byWorkspaceEntry : detailsByLocationByWorkspace.entrySet()) {
             String workspaceName = byWorkspaceEntry.getKey();
             // Iterate over the entries. Since we've used a TreeSet, we'll get these with the lower paths first ...
             for (Map.Entry<Location, NetChangeDetails> entry : byWorkspaceEntry.getValue().entrySet()) {
                 Location location = entry.getKey();
                 NetChangeDetails details = entry.getValue();
-                notify(new NetChange(sourceName, workspaceName, location, details.getEventTypes(),
-                                     details.getModifiedProperties(), details.getRemovedProperties()));
+                netChanges.add(new NetChange(workspaceName, location, details.getEventTypes(), details.getModifiedProperties(),
+                                             details.getRemovedProperties()));
             }
         }
+        // Now notify of all of the changes ...
+        notify(new NetChanges(changes, netChanges));
     }
 
     /**
-     * Method that is called for each net change.
+     * Method that is called for the set of net changes.
      * 
-     * @param change the net change; never null
+     * @param netChanges the net changes; never null
      */
-    protected abstract void notify( NetChange change );
+    protected abstract void notify( NetChanges netChanges );
 
     /**
+     * A set of net changes that were made atomically. Each change is in the form of a frozen {@link ChangeRequest}, and the net
+     * change is in the form.
+     */
+    @Immutable
+    public static final class NetChanges extends Changes {
+        private static final long serialVersionUID = 1L;
+
+        private final List<NetChange> netChanges;
+
+        public NetChanges( Changes changes,
+                           List<NetChange> netChanges ) {
+            super(changes);
+            assert netChanges != null;
+            assert !netChanges.isEmpty();
+            this.netChanges = Collections.unmodifiableList(netChanges);
+        }
+
+        /**
+         * Get the list of net changes.
+         * 
+         * @return the immutable list of net changes; never null and never empty
+         */
+        public List<NetChange> getNetChanges() {
+            return this.netChanges;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            if (processId.length() != 0) {
+                return getTimestamp() + " @" + getUserName() + " [" + getSourceName() + "] - " + netChanges.size() + " changes";
+            }
+            return getTimestamp() + " @" + getUserName() + " #" + getProcessId() + " [" + getSourceName() + "] - "
+                   + netChanges.size() + " changes";
+        }
+    }
+
+    /**
      * A notification of changes to a node.
      */
     @Immutable
-    public static class NetChange {
+    public static final class NetChange {
 
-        private final String sourceName;
         private final String workspaceName;
         private final Location location;
         private final EnumSet<ChangeType> eventTypes;
@@ -167,16 +218,13 @@
         private final Set<Name> removedProperties;
         private final int hc;
 
-        public NetChange( String sourceName,
-                          String workspaceName,
+        public NetChange( String workspaceName,
                           Location location,
                           EnumSet<ChangeType> eventTypes,
                           Set<Property> modifiedProperties,
                           Set<Name> removedProperties ) {
-            assert sourceName != null;
             assert workspaceName != null;
             assert location != null;
-            this.sourceName = sourceName;
             this.workspaceName = workspaceName;
             this.location = location;
             this.hc = HashCode.compute(this.workspaceName, this.location);
@@ -195,13 +243,6 @@
         }
 
         /**
-         * @return repositorySourceName
-         */
-        public String getRepositorySourceName() {
-            return this.sourceName;
-        }
-
-        /**
          * @return repositoryWorkspaceName
          */
         public String getRepositoryWorkspaceName() {
@@ -308,6 +349,12 @@
         }
     }
 
+    private enum LockAction {
+        NO_CHANGE,
+        LOCKED,
+        UNLOCKED;
+    }
+
     /**
      * Internal utility class used in the computation of the net changes.
      */
@@ -317,10 +364,26 @@
         private final Set<Property> modifiedProperties = new HashSet<Property>();
         private final Set<Name> removedProperties = new HashSet<Name>();
         private EnumSet<ChangeType> eventTypes = EnumSet.noneOf(ChangeType.class);
+        private LockAction lockAction = LockAction.NO_CHANGE;
 
         protected NetChangeDetails() {
         }
 
+        public void setLockAction( LockAction lockAction ) {
+            switch (lockAction) {
+                case LOCKED:
+                    // always mark as locked
+                    this.lockAction = lockAction;
+                    break;
+                case UNLOCKED:
+                    // mark as unlock or unchanged if previously locked ...
+                    this.lockAction = this.lockAction == LockAction.LOCKED ? LockAction.UNLOCKED : lockAction;
+                    break;
+                case NO_CHANGE:
+                    break;
+            }
+        }
+
         public void addEventType( ChangeType eventType ) {
             this.eventTypes.add(eventType);
         }
@@ -341,6 +404,13 @@
         }
 
         /**
+         * @return lockAction
+         */
+        public LockAction getLockAction() {
+            return lockAction;
+        }
+
+        /**
          * @return nodeAction
          */
         public EnumSet<ChangeType> getEventTypes() {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-10-28 00:41:28 UTC (rev 1312)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java	2009-10-28 14:43:17 UTC (rev 1313)
@@ -847,6 +847,7 @@
         // Publish any changes ...
         if (observer != null && !this.changes.isEmpty()) {
             String userName = context.getSecurityContext() != null ? context.getSecurityContext().getUserName() : null;
+            if (userName == null) userName = "";
             Changes changes = new Changes(userName, getSourceName(), getNowInUtc(), this.changes);
             observer.notify(changes);
         }

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java	2009-10-28 00:41:28 UTC (rev 1312)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java	2009-10-28 14:43:17 UTC (rev 1313)
@@ -446,44 +446,47 @@
     }
 
     protected class ConfigurationChangeObserver extends NetChangeObserver {
+
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.graph.observe.NetChangeObserver#notify(org.jboss.dna.graph.observe.NetChangeObserver.NetChange)
+         * @see org.jboss.dna.graph.observe.NetChangeObserver#notify(org.jboss.dna.graph.observe.NetChangeObserver.NetChanges)
          */
         @Override
-        protected void notify( NetChange change ) {
-            if (!getConfigurationSourceName().equals(change.getRepositorySourceName())) return;
-            if (!getConfigurationWorkspaceName().equals(change.getRepositoryWorkspaceName())) return;
-            Path changedPath = change.getPath();
-            Path configPath = getPathToConfigurationRoot();
-            if (!changedPath.isAtOrBelow(getPathToConfigurationRoot())) return;
-            boolean changedNodeIsPotentiallySource = configPath.size() + 1 == changedPath.size();
+        protected void notify( NetChanges netChanges ) {
+            if (!getConfigurationSourceName().equals(netChanges.getSourceName())) return;
+            for (NetChange change : netChanges.getNetChanges()) {
+                if (!getConfigurationWorkspaceName().equals(change.getRepositoryWorkspaceName())) return;
+                Path changedPath = change.getPath();
+                Path configPath = getPathToConfigurationRoot();
+                if (!changedPath.isAtOrBelow(getPathToConfigurationRoot())) return;
+                boolean changedNodeIsPotentiallySource = configPath.size() + 1 == changedPath.size();
 
-            // At this point, we know that something inside the configuration changed, so figure out what happened ...
-            if (changedNodeIsPotentiallySource && change.includes(ChangeType.NODE_REMOVED)) {
-                // Then potentially a source with the supplied name has been removed ...
-                String sourceName = changedPath.getLastSegment().getName().getLocalName();
-                getRepositoryLibrary().removeSource(sourceName);
-            } else {
-                // The add/change/remove is either at or below a source, so try to create a new source for it ...
-                Path sourcePath = changedNodeIsPotentiallySource ? changedPath : changedPath.subpath(0, configPath.size() + 1);
-                Problems problems = new SimpleProblems();
-                // Now read the node and create the source ...
-                Graph graph = Graph.create(getConfigurationSourceName(), getRepositoryLibrary(), getExecutionEnvironment());
-                try {
-                    String workspaceName = getConfigurationWorkspaceName();
-                    if (workspaceName != null) graph.useWorkspace(workspaceName);
-                    Map<Name, Property> properties = graph.getPropertiesByName().on(sourcePath);
-                    RepositorySource source = createRepositorySource(sourcePath, properties, problems);
-                    if (source != null) {
-                        // It was the config for a source, so try to add or replace an existing source ...
-                        getRepositoryLibrary().addSource(source, true);
-                    }
-                } catch (PathNotFoundException e) {
-                    // No source was found, and this is okay (since it may just been deleted)...
+                // At this point, we know that something inside the configuration changed, so figure out what happened ...
+                if (changedNodeIsPotentiallySource && change.includes(ChangeType.NODE_REMOVED)) {
+                    // Then potentially a source with the supplied name has been removed ...
                     String sourceName = changedPath.getLastSegment().getName().getLocalName();
                     getRepositoryLibrary().removeSource(sourceName);
+                } else {
+                    // The add/change/remove is either at or below a source, so try to create a new source for it ...
+                    Path sourcePath = changedNodeIsPotentiallySource ? changedPath : changedPath.subpath(0, configPath.size() + 1);
+                    Problems problems = new SimpleProblems();
+                    // Now read the node and create the source ...
+                    Graph graph = Graph.create(getConfigurationSourceName(), getRepositoryLibrary(), getExecutionEnvironment());
+                    try {
+                        String workspaceName = getConfigurationWorkspaceName();
+                        if (workspaceName != null) graph.useWorkspace(workspaceName);
+                        Map<Name, Property> properties = graph.getPropertiesByName().on(sourcePath);
+                        RepositorySource source = createRepositorySource(sourcePath, properties, problems);
+                        if (source != null) {
+                            // It was the config for a source, so try to add or replace an existing source ...
+                            getRepositoryLibrary().addSource(source, true);
+                        }
+                    } catch (PathNotFoundException e) {
+                        // No source was found, and this is okay (since it may just been deleted)...
+                        String sourceName = changedPath.getLastSegment().getName().getLocalName();
+                        getRepositoryLibrary().removeSource(sourceName);
+                    }
                 }
             }
         }

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencingService.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencingService.java	2009-10-28 00:41:28 UTC (rev 1312)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencer/SequencingService.java	2009-10-28 14:43:17 UTC (rev 1313)
@@ -23,9 +23,9 @@
  */
 package org.jboss.dna.repository.sequencer;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -49,7 +49,9 @@
 import org.jboss.dna.graph.connector.RepositorySource;
 import org.jboss.dna.graph.observe.ChangeObserver;
 import org.jboss.dna.graph.observe.NetChangeObserver;
+import org.jboss.dna.graph.observe.NetChangeObserver.ChangeType;
 import org.jboss.dna.graph.observe.NetChangeObserver.NetChange;
+import org.jboss.dna.graph.observe.NetChangeObserver.NetChanges;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.property.Path;
 import org.jboss.dna.graph.property.Property;
@@ -388,95 +390,103 @@
      * Do the work of processing by sequencing the node. This method is called by the {@link #executorService executor service}
      * when it performs it's work on the enqueued {@link NetChange NetChange runnable objects}.
      * 
-     * @param change the change describing the node to be processed.
+     * @param changes the change describing the node to be processed.
      */
-    protected void processChange( NetChange change ) {
+    protected void processChange( NetChanges changes ) {
         final ExecutionContext context = this.getExecutionContext();
         final Logger logger = context.getLogger(getClass());
         assert logger != null;
 
         try {
-            final String repositorySourceName = change.getRepositorySourceName();
-            final String repositoryWorkspaceName = change.getRepositoryWorkspaceName();
+            List<Sequencer> allSequencers = null;
+            final String repositorySourceName = changes.getSourceName();
+            for (NetChange change : changes.getNetChanges()) {
+                // Go through each net change, and only process node/property adds and property changes ...
+                if (change.includes(ChangeType.NODE_ADDED, ChangeType.PROPERTY_ADDED, ChangeType.PROPERTY_CHANGED)) {
+                    final String repositoryWorkspaceName = change.getRepositoryWorkspaceName();
 
-            // Figure out which sequencers accept this path,
-            // and track which output nodes should be passed to each sequencer...
-            final Path nodePath = change.getPath();
-            final String nodePathStr = context.getValueFactories().getStringFactory().create(nodePath);
-            Map<SequencerCall, Set<RepositoryNodePath>> sequencerCalls = new HashMap<SequencerCall, Set<RepositoryNodePath>>();
-            List<Sequencer> allSequencers = this.sequencerLibrary.getInstances();
-            List<Sequencer> sequencers = new ArrayList<Sequencer>(allSequencers.size());
-            for (Sequencer sequencer : allSequencers) {
-                final SequencerConfig config = sequencer.getConfiguration();
-                for (SequencerPathExpression pathExpression : config.getPathExpressions()) {
-                    for (Property property : change.getModifiedProperties()) {
-                        Name propertyName = property.getName();
-                        String propertyNameStr = context.getValueFactories().getStringFactory().create(propertyName);
-                        String path = nodePathStr + "/@" + propertyNameStr;
-                        SequencerPathExpression.Matcher matcher = pathExpression.matcher(path);
-                        if (matcher.matches()) {
-                            // String selectedPath = matcher.getSelectedPath();
-                            RepositoryNodePath outputPath = RepositoryNodePath.parse(matcher.getOutputPath(),
-                                                                                     repositorySourceName,
-                                                                                     repositoryWorkspaceName);
-                            SequencerCall call = new SequencerCall(sequencer, propertyNameStr);
-                            // Record the output path ...
-                            Set<RepositoryNodePath> outputPaths = sequencerCalls.get(call);
-                            if (outputPaths == null) {
-                                outputPaths = new HashSet<RepositoryNodePath>();
-                                sequencerCalls.put(call, outputPaths);
+                    // Figure out which sequencers accept this path,
+                    // and track which output nodes should be passed to each sequencer...
+                    final Path nodePath = change.getPath();
+                    final String nodePathStr = context.getValueFactories().getStringFactory().create(nodePath);
+                    Map<SequencerCall, Set<RepositoryNodePath>> sequencerCalls = new HashMap<SequencerCall, Set<RepositoryNodePath>>();
+                    if (allSequencers == null) {
+                        allSequencers = this.sequencerLibrary.getInstances();
+                    }
+                    List<Sequencer> sequencers = new LinkedList<Sequencer>();
+                    for (Sequencer sequencer : allSequencers) {
+                        final SequencerConfig config = sequencer.getConfiguration();
+                        for (SequencerPathExpression pathExpression : config.getPathExpressions()) {
+                            for (Property property : change.getModifiedProperties()) {
+                                Name propertyName = property.getName();
+                                String propertyNameStr = context.getValueFactories().getStringFactory().create(propertyName);
+                                String path = nodePathStr + "/@" + propertyNameStr;
+                                SequencerPathExpression.Matcher matcher = pathExpression.matcher(path);
+                                if (matcher.matches()) {
+                                    // String selectedPath = matcher.getSelectedPath();
+                                    RepositoryNodePath outputPath = RepositoryNodePath.parse(matcher.getOutputPath(),
+                                                                                             repositorySourceName,
+                                                                                             repositoryWorkspaceName);
+                                    SequencerCall call = new SequencerCall(sequencer, propertyNameStr);
+                                    // Record the output path ...
+                                    Set<RepositoryNodePath> outputPaths = sequencerCalls.get(call);
+                                    if (outputPaths == null) {
+                                        outputPaths = new HashSet<RepositoryNodePath>();
+                                        sequencerCalls.put(call, outputPaths);
+                                    }
+                                    outputPaths.add(outputPath);
+                                    sequencers.add(sequencer);
+                                    break;
+                                }
                             }
-                            outputPaths.add(outputPath);
-                            sequencers.add(sequencer);
-                            break;
                         }
                     }
-                }
-            }
 
-            RepositorySource source = repositoryLibrary.getSource(repositorySourceName);
-            Graph graph = Graph.create(source, context);
-            Node node = null;
-            if (!sequencers.isEmpty()) {
+                    RepositorySource source = repositoryLibrary.getSource(repositorySourceName);
+                    Graph graph = Graph.create(source, context);
+                    Node node = null;
+                    if (!sequencers.isEmpty()) {
 
-                // Find the changed node ...
-                node = graph.getNodeAt(nodePath);
+                        // Find the changed node ...
+                        node = graph.getNodeAt(nodePath);
 
-                // Figure out which sequencers should run ...
-                sequencers = this.sequencerSelector.selectSequencers(sequencers, node, change);
-            }
-            if (sequencers.isEmpty()) {
-                this.statistics.recordNodeSkipped();
-                if (logger.isDebugEnabled()) {
-                    logger.trace("Skipping '{0}': no sequencers matched this condition", change);
-                }
-            } else {
-                // Run each of those sequencers ...
-                for (Map.Entry<SequencerCall, Set<RepositoryNodePath>> entry : sequencerCalls.entrySet()) {
+                        // Figure out which sequencers should run ...
+                        sequencers = this.sequencerSelector.selectSequencers(sequencers, node, change);
+                    }
+                    if (sequencers.isEmpty()) {
+                        this.statistics.recordNodeSkipped();
+                        if (logger.isDebugEnabled()) {
+                            logger.trace("Skipping '{0}': no sequencers matched this condition", change);
+                        }
+                    } else {
+                        // Run each of those sequencers ...
+                        for (Map.Entry<SequencerCall, Set<RepositoryNodePath>> entry : sequencerCalls.entrySet()) {
 
-                    final SequencerCall sequencerCall = entry.getKey();
-                    final Set<RepositoryNodePath> outputPaths = entry.getValue();
-                    final Sequencer sequencer = sequencerCall.getSequencer();
-                    final String sequencerName = sequencer.getConfiguration().getName();
-                    final String propertyName = sequencerCall.getSequencedPropertyName();
+                            final SequencerCall sequencerCall = entry.getKey();
+                            final Set<RepositoryNodePath> outputPaths = entry.getValue();
+                            final Sequencer sequencer = sequencerCall.getSequencer();
+                            final String sequencerName = sequencer.getConfiguration().getName();
+                            final String propertyName = sequencerCall.getSequencedPropertyName();
 
-                    // Get the paths to the nodes where the sequencer should write it's output ...
-                    assert outputPaths != null && outputPaths.size() != 0;
+                            // Get the paths to the nodes where the sequencer should write it's output ...
+                            assert outputPaths != null && outputPaths.size() != 0;
 
-                    // Create a new execution context for each sequencer
-                    final SimpleProblems problems = new SimpleProblems();
-                    SequencerContext sequencerContext = new SequencerContext(context, graph);
-                    try {
-                        sequencer.execute(node, propertyName, change, outputPaths, sequencerContext, problems);
-                        sequencerContext.getDestination().submit();
-                    } catch (SequencerException e) {
-                        logger.error(e, RepositoryI18n.errorWhileSequencingNode, sequencerName, change);
+                            // Create a new execution context for each sequencer
+                            final SimpleProblems problems = new SimpleProblems();
+                            SequencerContext sequencerContext = new SequencerContext(context, graph);
+                            try {
+                                sequencer.execute(node, propertyName, change, outputPaths, sequencerContext, problems);
+                                sequencerContext.getDestination().submit();
+                            } catch (SequencerException e) {
+                                logger.error(e, RepositoryI18n.errorWhileSequencingNode, sequencerName, change);
+                            }
+                        }
+                        this.statistics.recordNodeSequenced();
                     }
                 }
-                this.statistics.recordNodeSequenced();
             }
         } catch (Throwable e) {
-            logger.error(e, RepositoryI18n.errorFindingSequencersToRunAgainstNode, change);
+            logger.error(e, RepositoryI18n.errorFindingSequencersToRunAgainstNode, changes);
         }
     }
 
@@ -594,22 +604,19 @@
         /**
          * {@inheritDoc}
          * 
-         * @see org.jboss.dna.graph.observe.NetChangeObserver#notify(org.jboss.dna.graph.observe.NetChangeObserver.NetChange)
+         * @see org.jboss.dna.graph.observe.NetChangeObserver#notify(org.jboss.dna.graph.observe.NetChangeObserver.NetChanges)
          */
         @Override
-        protected void notify( final NetChange change ) {
-            // Only care about new nodes or nodes that have new/changed properies ...
-            if (change.includes(ChangeType.NODE_ADDED, ChangeType.PROPERTY_ADDED, ChangeType.PROPERTY_CHANGED)) {
-                try {
-                    getExecutorService().execute(new Runnable() {
+        protected void notify( final NetChanges netChanges ) {
+            try {
+                getExecutorService().execute(new Runnable() {
 
-                        public void run() {
-                            processChange(change);
-                        }
-                    });
-                } catch (RejectedExecutionException e) {
-                    // The executor service has been shut down, so do nothing with this set of changes
-                }
+                    public void run() {
+                        processChange(netChanges);
+                    }
+                });
+            } catch (RejectedExecutionException e) {
+                // The executor service has been shut down, so do nothing with this set of changes
             }
         }
     }

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/StreamSequencerAdapterTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/StreamSequencerAdapterTest.java	2009-10-28 00:41:28 UTC (rev 1312)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencer/StreamSequencerAdapterTest.java	2009-10-28 14:43:17 UTC (rev 1313)
@@ -131,9 +131,8 @@
 
         Location location = Location.create(context.getValueFactories().getPathFactory().create("/a/b/c"));
         Property sequencedProperty = inputNode.getProperty("sequencedProperty");
-        NetChange nodeChange = new NetChange(repositorySourceName, repositoryWorkspaceName, location,
-                                             EnumSet.of(ChangeType.PROPERTY_CHANGED), Collections.singleton(sequencedProperty),
-                                             null);
+        NetChange nodeChange = new NetChange(repositoryWorkspaceName, location, EnumSet.of(ChangeType.PROPERTY_CHANGED),
+                                             Collections.singleton(sequencedProperty), null);
         Set<RepositoryNodePath> outputPaths = new HashSet<RepositoryNodePath>();
         outputPaths.add(new RepositoryNodePath(repositorySourceName, repositoryWorkspaceName, "/d/e"));
         sequencerOutput.setProperty("alpha/beta", "isSomething", true);
@@ -190,9 +189,8 @@
         // Set up the node changes ...
         Location location = Location.create(context.getValueFactories().getPathFactory().create("/a/b/c"));
         Property sequencedProperty = nodeC.getProperty("sequencedProperty");
-        NetChange nodeChange = new NetChange(repositorySourceName, repositoryWorkspaceName, location,
-                                             EnumSet.of(ChangeType.PROPERTY_CHANGED), Collections.singleton(sequencedProperty),
-                                             null);
+        NetChange nodeChange = new NetChange(repositoryWorkspaceName, location, EnumSet.of(ChangeType.PROPERTY_CHANGED),
+                                             Collections.singleton(sequencedProperty), null);
 
         // Set up the output directory ...
         Set<RepositoryNodePath> outputPaths = new HashSet<RepositoryNodePath>();
@@ -226,9 +224,8 @@
         // Set up the node changes ...
         Location location = Location.create(context.getValueFactories().getPathFactory().create("/a/b/c"));
         Property sequencedProperty = nodeC.getProperty("sequencedProperty");
-        NetChange nodeChange = new NetChange(repositorySourceName, repositoryWorkspaceName, location,
-                                             EnumSet.of(ChangeType.PROPERTY_CHANGED), Collections.singleton(sequencedProperty),
-                                             null);
+        NetChange nodeChange = new NetChange(repositoryWorkspaceName, location, EnumSet.of(ChangeType.PROPERTY_CHANGED),
+                                             Collections.singleton(sequencedProperty), null);
 
         // Set up the output directory ...
         Set<RepositoryNodePath> outputPaths = new HashSet<RepositoryNodePath>();
@@ -262,9 +259,8 @@
         // Set up the node changes ...
         Location location = Location.create(context.getValueFactories().getPathFactory().create("/a/b/c"));
         Property sequencedProperty = nodeC.getProperty("sequencedProperty");
-        NetChange nodeChange = new NetChange(repositorySourceName, repositoryWorkspaceName, location,
-                                             EnumSet.of(ChangeType.PROPERTY_CHANGED), Collections.singleton(sequencedProperty),
-                                             null);
+        NetChange nodeChange = new NetChange(repositoryWorkspaceName, location, EnumSet.of(ChangeType.PROPERTY_CHANGED),
+                                             Collections.singleton(sequencedProperty), null);
 
         // Set up the output directory ...
         Set<RepositoryNodePath> outputPaths = new HashSet<RepositoryNodePath>();
@@ -303,9 +299,8 @@
         // Set up the node changes ...
         Location location = Location.create(context.getValueFactories().getPathFactory().create("/a/b/c"));
         Property sequencedProperty = nodeC.getProperty("sequencedProperty");
-        NetChange nodeChange = new NetChange(repositorySourceName, repositoryWorkspaceName, location,
-                                             EnumSet.of(ChangeType.PROPERTY_CHANGED), Collections.singleton(sequencedProperty),
-                                             null);
+        NetChange nodeChange = new NetChange(repositoryWorkspaceName, location, EnumSet.of(ChangeType.PROPERTY_CHANGED),
+                                             Collections.singleton(sequencedProperty), null);
 
         // Set up the output directory ...
         Set<RepositoryNodePath> outputPaths = new HashSet<RepositoryNodePath>();
@@ -348,9 +343,8 @@
         // Set up the node changes ...
         Location location = Location.create(context.getValueFactories().getPathFactory().create("/a/b/c"));
         Property sequencedProperty = nodeC.getProperty("sequencedProperty");
-        NetChange nodeChange = new NetChange(repositorySourceName, repositoryWorkspaceName, location,
-                                             EnumSet.of(ChangeType.PROPERTY_CHANGED), Collections.singleton(sequencedProperty),
-                                             null);
+        NetChange nodeChange = new NetChange(repositoryWorkspaceName, location, EnumSet.of(ChangeType.PROPERTY_CHANGED),
+                                             Collections.singleton(sequencedProperty), null);
 
         // Set up the output directory ...
         Set<RepositoryNodePath> outputPaths = new HashSet<RepositoryNodePath>();


From dna-commits at lists.jboss.org  Wed Oct 28 15:27:45 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Wed, 28 Oct 2009 15:27:45 -0400
Subject: [dna-commits] DNA SVN:  r1314 - trunk.
Message-ID: <200910281927.n9SJRjTI021280@svn01.web.mwc.hst.phx2.redhat.com>

Author: steve.jacobs
Date: 2009-10-28 15:27:45 -0400 (Wed, 28 Oct 2009)
New Revision: 1314

Modified:
   trunk/.gitignore
Log:
added Eclipse bin directory to the .gitignore file

Modified: trunk/.gitignore
===================================================================
--- trunk/.gitignore	2009-10-28 14:43:17 UTC (rev 1313)
+++ trunk/.gitignore	2009-10-28 19:27:45 UTC (rev 1314)
@@ -62,3 +62,5 @@
 /extensions/dna-sequencer-msoffice/target
 /extensions/dna-sequencer-xml/target
 /extensions/dna-sequencer-zip/target
+
+/tools/org.jboss.dna.eclipse.jcr.rest.client/bin


From dna-commits at lists.jboss.org  Thu Oct 29 17:25:06 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Thu, 29 Oct 2009 17:25:06 -0400
Subject: [dna-commits] DNA SVN: r1315 -
	trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe.
Message-ID: <200910292125.n9TLP66A004240@svn01.web.mwc.hst.phx2.redhat.com>

Author: rhauch
Date: 2009-10-29 17:25:06 -0400 (Thu, 29 Oct 2009)
New Revision: 1315

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java
Log:
DNA-456 Corrected behavior of NetChangeObserver and how it records lock and unlock state in the NetChange objects

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java	2009-10-28 19:27:45 UTC (rev 1314)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/NetChangeObserver.java	2009-10-29 21:25:06 UTC (rev 1315)
@@ -63,7 +63,9 @@
         NODE_REMOVED,
         PROPERTY_ADDED,
         PROPERTY_REMOVED,
-        PROPERTY_CHANGED;
+        PROPERTY_CHANGED,
+        NODE_LOCKED,
+        NODE_UNLOCKED;
     }
 
     protected NetChangeObserver() {
@@ -350,7 +352,6 @@
     }
 
     private enum LockAction {
-        NO_CHANGE,
         LOCKED,
         UNLOCKED;
     }
@@ -364,7 +365,6 @@
         private final Set<Property> modifiedProperties = new HashSet<Property>();
         private final Set<Name> removedProperties = new HashSet<Name>();
         private EnumSet<ChangeType> eventTypes = EnumSet.noneOf(ChangeType.class);
-        private LockAction lockAction = LockAction.NO_CHANGE;
 
         protected NetChangeDetails() {
         }
@@ -372,15 +372,16 @@
         public void setLockAction( LockAction lockAction ) {
             switch (lockAction) {
                 case LOCKED:
-                    // always mark as locked
-                    this.lockAction = lockAction;
+                    // always mark as locked and remove any unlocked state
+                    eventTypes.add(ChangeType.NODE_LOCKED);
+                    eventTypes.remove(ChangeType.NODE_UNLOCKED);
                     break;
                 case UNLOCKED:
-                    // mark as unlock or unchanged if previously locked ...
-                    this.lockAction = this.lockAction == LockAction.LOCKED ? LockAction.UNLOCKED : lockAction;
+                    if (!eventTypes.remove(ChangeType.NODE_LOCKED)) {
+                        // It was not previously locked by this change set, so we should unlock it ...
+                        eventTypes.add(ChangeType.NODE_UNLOCKED);
+                    }
                     break;
-                case NO_CHANGE:
-                    break;
             }
         }
 
@@ -404,13 +405,6 @@
         }
 
         /**
-         * @return lockAction
-         */
-        public LockAction getLockAction() {
-            return lockAction;
-        }
-
-        /**
          * @return nodeAction
          */
         public EnumSet<ChangeType> getEventTypes() {


From dna-commits at lists.jboss.org  Fri Oct 30 04:22:04 2009
From: dna-commits at lists.jboss.org (© VIAGRA ® Official Site)
Date: Fri, 30 Oct 2009 04:22:04 -0400
Subject: [dna-commits] Dear dna-commits@lists.jboss.org 73% 0FF on Pfizer !
Message-ID: <200910300822.n9U8M4CG008316@lists01.dmz-a.mwc.hst.phx2.redhat.com>

An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/dna-commits/attachments/20091030/afd8cf87/attachment.html 

From dna-commits at lists.jboss.org  Fri Oct 30 17:34:28 2009
From: dna-commits at lists.jboss.org (dna-commits at lists.jboss.org)
Date: Fri, 30 Oct 2009 17:34:28 -0400
Subject: [dna-commits] DNA SVN: r1316 - in trunk:
	dna-integration-tests/src/test/java/org/jboss/dna/test/integration
	and 3 other directories.
Message-ID: <200910302134.n9ULYSCW013222@svn01.web.mwc.hst.phx2.redhat.com>

Author: bcarothers
Date: 2009-10-30 17:34:28 -0400 (Fri, 30 Oct 2009)
New Revision: 1316

Modified:
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
   trunk/dna-integration-tests/src/test/java/org/jboss/dna/test/integration/JpaRepositoryTckTest.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/dna_builtins.cnd
   trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
Log:
DNA-518 JPA connector fails to properly clone or update from another workspace

The previous patch fixed the originally declared issue, but exposed a few regressions. The committed patch fixes most of those regressions and corrects a significant design flaw in the original locking implementation (using jcr:uuid to refer to another nodes uuid).

However, this patch does not correct two key defects. JCR imports (session and workspace) that include collisions with existing nodes that share a UUID will fail if the removeExisting flag is set to true. There is also a minor defect with moving locked nodes on this connector.

Since the original defect is addressed, this defect can be resolved and two new defects can be opened for the other regressions.

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java	2009-10-29 21:25:06 UTC (rev 1315)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WritableConnectorTest.java	2009-10-30 21:34:28 UTC (rev 1316)
@@ -40,6 +40,7 @@
 import org.jboss.dna.common.util.IoUtil;
 import org.jboss.dna.graph.DnaLexicon;
 import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.JcrLexicon;
 import org.jboss.dna.graph.Node;
 import org.jboss.dna.graph.Subgraph;
 import org.jboss.dna.graph.connector.RepositorySource;
@@ -1020,7 +1021,16 @@
     }
 
     private UUID uuidFor( Node node ) {
-        return (UUID)node.getProperty(DnaLexicon.UUID).getFirstValue();
+        UUID uuid = null;
+        if (node.getLocation().getUuid() != null) {
+            uuid = node.getLocation().getUuid();
+        }
+        
+        if (uuid == null && (node.getProperty(JcrLexicon.UUID) != null)) {
+            uuid = (UUID)node.getProperty(JcrLexicon.UUID).getFirstValue();
+        }
+        
+        return uuid;
     }
 
     protected void assertReference( String fromNodePath,

Modified: trunk/dna-integration-tests/src/test/java/org/jboss/dna/test/integration/JpaRepositoryTckTest.java
===================================================================
--- trunk/dna-integration-tests/src/test/java/org/jboss/dna/test/integration/JpaRepositoryTckTest.java	2009-10-29 21:25:06 UTC (rev 1315)
+++ trunk/dna-integration-tests/src/test/java/org/jboss/dna/test/integration/JpaRepositoryTckTest.java	2009-10-30 21:34:28 UTC (rev 1316)
@@ -24,10 +24,147 @@
 package org.jboss.dna.test.integration;
 
 import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.jackrabbit.test.api.AddNodeTest;
+import org.apache.jackrabbit.test.api.CheckPermissionTest;
+import org.apache.jackrabbit.test.api.ImpersonateTest;
+import org.apache.jackrabbit.test.api.NamespaceRegistryTest;
+import org.apache.jackrabbit.test.api.NodeAddMixinTest;
+import org.apache.jackrabbit.test.api.NodeCanAddMixinTest;
+import org.apache.jackrabbit.test.api.NodeItemIsModifiedTest;
+import org.apache.jackrabbit.test.api.NodeItemIsNewTest;
+import org.apache.jackrabbit.test.api.NodeOrderableChildNodesTest;
+import org.apache.jackrabbit.test.api.NodeRemoveMixinTest;
+import org.apache.jackrabbit.test.api.NodeTest;
+import org.apache.jackrabbit.test.api.PropertyItemIsModifiedTest;
+import org.apache.jackrabbit.test.api.PropertyItemIsNewTest;
+import org.apache.jackrabbit.test.api.PropertyTest;
+import org.apache.jackrabbit.test.api.RepositoryLoginTest;
+import org.apache.jackrabbit.test.api.SerializationTest;
+import org.apache.jackrabbit.test.api.SessionTest;
+import org.apache.jackrabbit.test.api.SetPropertyAssumeTypeTest;
+import org.apache.jackrabbit.test.api.SetPropertyBooleanTest;
+import org.apache.jackrabbit.test.api.SetPropertyCalendarTest;
+import org.apache.jackrabbit.test.api.SetPropertyConstraintViolationExceptionTest;
+import org.apache.jackrabbit.test.api.SetPropertyDoubleTest;
+import org.apache.jackrabbit.test.api.SetPropertyInputStreamTest;
+import org.apache.jackrabbit.test.api.SetPropertyLongTest;
+import org.apache.jackrabbit.test.api.SetPropertyNodeTest;
+import org.apache.jackrabbit.test.api.SetPropertyStringTest;
+import org.apache.jackrabbit.test.api.SetPropertyValueTest;
+import org.apache.jackrabbit.test.api.SetValueBinaryTest;
+import org.apache.jackrabbit.test.api.SetValueBooleanTest;
+import org.apache.jackrabbit.test.api.SetValueConstraintViolationExceptionTest;
+import org.apache.jackrabbit.test.api.SetValueDateTest;
+import org.apache.jackrabbit.test.api.SetValueDoubleTest;
+import org.apache.jackrabbit.test.api.SetValueLongTest;
+import org.apache.jackrabbit.test.api.SetValueReferenceTest;
+import org.apache.jackrabbit.test.api.SetValueStringTest;
+import org.apache.jackrabbit.test.api.SetValueValueFormatExceptionTest;
+import org.apache.jackrabbit.test.api.SetValueVersionExceptionTest;
+import org.apache.jackrabbit.test.api.ValueFactoryTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneSameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneTest;
+import org.apache.jackrabbit.test.api.WorkspaceCloneVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesSameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopySameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyTest;
+import org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveReferenceableTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveSameNameSibsTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveTest;
+import org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest;
 
 public class JpaRepositoryTckTest {
 
     public static Test suite() {
-        return AbstractRepositoryTckTest.readWriteRepositorySuite("jpa");
+        TestSuite suite = AbstractRepositoryTckTest.readOnlyRepositorySuite("jpa");
+        suite.addTest(new LevelTwoFeatureTests());
+        // suite.addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite());
+
+        return suite;
+
     }
+
+    private static class LevelTwoFeatureTests extends TestSuite {
+        protected LevelTwoFeatureTests() {
+            super("JCR Level 2 API Tests");
+            // We currently don't pass the tests in those suites that are commented out
+            // See https://jira.jboss.org/jira/browse/DNA-285
+
+            // level 2 tests
+            addTestSuite(AddNodeTest.class);
+            addTestSuite(NamespaceRegistryTest.class);
+            // addTestSuite(ReferencesTest.class);
+            addTestSuite(SessionTest.class);
+            // addTestSuite(SessionUUIDTest.class);
+            addTestSuite(NodeTest.class);
+            // addTestSuite(NodeUUIDTest.class);
+            addTestSuite(NodeOrderableChildNodesTest.class);
+            addTestSuite(PropertyTest.class);
+
+            addTestSuite(SetValueBinaryTest.class);
+            addTestSuite(SetValueBooleanTest.class);
+            addTestSuite(SetValueDateTest.class);
+            addTestSuite(SetValueDoubleTest.class);
+            addTestSuite(SetValueLongTest.class);
+            addTestSuite(SetValueReferenceTest.class);
+            addTestSuite(SetValueStringTest.class);
+            addTestSuite(SetValueConstraintViolationExceptionTest.class);
+            addTestSuite(SetValueValueFormatExceptionTest.class);
+            addTestSuite(SetValueVersionExceptionTest.class);
+
+            addTestSuite(SetPropertyBooleanTest.class);
+            addTestSuite(SetPropertyCalendarTest.class);
+            addTestSuite(SetPropertyDoubleTest.class);
+            addTestSuite(SetPropertyInputStreamTest.class);
+            addTestSuite(SetPropertyLongTest.class);
+            addTestSuite(SetPropertyNodeTest.class);
+            addTestSuite(SetPropertyStringTest.class);
+            addTestSuite(SetPropertyValueTest.class);
+            addTestSuite(SetPropertyConstraintViolationExceptionTest.class);
+            addTestSuite(SetPropertyAssumeTypeTest.class);
+
+            addTestSuite(NodeItemIsModifiedTest.class);
+            addTestSuite(NodeItemIsNewTest.class);
+            addTestSuite(PropertyItemIsModifiedTest.class);
+            addTestSuite(PropertyItemIsNewTest.class);
+
+            addTestSuite(NodeAddMixinTest.class);
+            addTestSuite(NodeCanAddMixinTest.class);
+            addTestSuite(NodeRemoveMixinTest.class);
+
+            addTestSuite(WorkspaceCloneReferenceableTest.class);
+            addTestSuite(WorkspaceCloneSameNameSibsTest.class);
+            addTestSuite(WorkspaceCloneTest.class);
+            addTestSuite(WorkspaceCloneVersionableTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesReferenceableTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesSameNameSibsTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesTest.class);
+            addTestSuite(WorkspaceCopyBetweenWorkspacesVersionableTest.class);
+            addTestSuite(WorkspaceCopyReferenceableTest.class);
+            addTestSuite(WorkspaceCopySameNameSibsTest.class);
+            addTestSuite(WorkspaceCopyTest.class);
+            addTestSuite(WorkspaceCopyVersionableTest.class);
+            addTestSuite(WorkspaceMoveReferenceableTest.class);
+            addTestSuite(WorkspaceMoveSameNameSibsTest.class);
+            addTestSuite(WorkspaceMoveTest.class);
+            addTestSuite(WorkspaceMoveVersionableTest.class);
+
+            addTestSuite(RepositoryLoginTest.class);
+            addTestSuite(ImpersonateTest.class);
+            addTestSuite(CheckPermissionTest.class);
+
+            // addTestSuite(DocumentViewImportTest.class);
+            addTestSuite(SerializationTest.class);
+
+            addTestSuite(ValueFactoryTest.class);
+        }
+    }
+
 }

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-10-29 21:25:06 UTC (rev 1315)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaLexicon.java	2009-10-30 21:34:28 UTC (rev 1316)
@@ -37,7 +37,7 @@
     public static final Name IS_HELD_BY_SESSION = new BasicName(Namespace.URI, "isHeldBySession");
     public static final Name IS_SESSION_SCOPED = new BasicName(Namespace.URI, "isSessionScoped");    
     public static final Name LOCK = new BasicName(Namespace.URI, "lock");    
-    public static final Name LOCKED_NODE = new BasicName(Namespace.URI, "lockedNode");    
+    public static final Name LOCKED_UUID = new BasicName(Namespace.URI, "lockedUuid");
     public static final Name LOCKS = new BasicName(Namespace.URI, "locks");    
     public static final Name NAMESPACE = new BasicName(Namespace.URI, "namespace");
     public static final Name NODE_TYPES = new BasicName(Namespace.URI, "nodeTypes");

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-29 21:25:06 UTC (rev 1315)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/WorkspaceLockManager.java	2009-10-30 21:34:28 UTC (rev 1316)
@@ -94,9 +94,8 @@
 
         batch.create(pathFactory.create(locksPath, pathFactory.createSegment(lockUuid.toString())),
                      propFactory.create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.LOCK),
-                     propFactory.create(JcrLexicon.UUID, nodeUuid.toString()),
                      propFactory.create(DnaLexicon.WORKSPACE, workspaceName),
-                     propFactory.create(DnaLexicon.LOCKED_NODE, nodeUuid.toString()),
+                     propFactory.create(DnaLexicon.LOCKED_UUID, nodeUuid.toString()),
                      propFactory.create(DnaLexicon.IS_SESSION_SCOPED, isSessionScoped),
                      // This gets set after the lock succeeds and the lock token gets added to the session
                      propFactory.create(DnaLexicon.IS_HELD_BY_SESSION, false),

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/dna_builtins.cnd
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/dna_builtins.cnd	2009-10-29 21:25:06 UTC (rev 1315)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/dna_builtins.cnd	2009-10-30 21:34:28 UTC (rev 1316)
@@ -44,9 +44,8 @@
 + * (nt:nodeType) = nt:nodeType protected version
 
 [dna:lock] > nt:base
-- dna:lockedNode (string) protected ignore
+- dna:lockedUuid (string) protected ignore
 - jcr:lockOwner (string) protected ignore
-- jcr:uuid (string) protected ignore
 - dna:sessionScope (boolean) protected ignore
 - jcr:isDeep (boolean) protected ignore
 - dna:isHeldBySession (boolean) protected ignore

Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java	2009-10-29 21:25:06 UTC (rev 1315)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java	2009-10-30 21:34:28 UTC (rev 1316)
@@ -228,6 +228,16 @@
                 }
             }
 
+            if (uuid == null) {
+                for (Property property : request.properties()) {
+                    if (property.getName().equals(JcrLexicon.UUID)) {
+                        uuid = uuidFactory.create(property.getFirstValue());
+                        uuidString = stringFactory.create(property.getFirstValue());
+                        break;
+                    }
+                }
+            }
+
             switch (request.conflictBehavior()) {
                 case DO_NOT_REPLACE:
                 case UPDATE:
@@ -1154,7 +1164,7 @@
                                      Name desiredName,
                                      Path.Segment desiredSegment,
                                      Map<String, String> oldUuidsToNewUuids,
-                                     Map<String, ChildEntity> addedLocations,
+                                     Map<Location, ChildEntity> addedLocations,
                                      Map<String, Location> deletedLocations ) {
         assert fromWorkspace != null;
         assert intoWorkspace != null;
@@ -1186,9 +1196,12 @@
             case REPLACE_EXISTING_NODE:
                 try {
                     if (desiredSegment != null) {
-                        existingLocation = getActualLocation(intoWorkspace,
-                                                             Location.create(pathFactory.create(actualNewParent.location.getPath(),
-                                                                                                desiredSegment)));
+                        Location nLocation = Location.create(pathFactory.create(actualNewParent.location.getPath(),
+                                                                                desiredSegment));
+                        existingLocation = getActualLocation(intoWorkspace, nLocation);
+                        nLocation = Location.create(nLocation.getPath(), UUID.fromString(existingLocation.uuid));
+                        assert nLocation.getUuid() != null;
+                        deletedLocations.putAll(computeDeletedLocations(intoWorkspace, nLocation, true));
                         newUuid = existingLocation.childEntity.getId().getChildUuidString();
 
                     } else {
@@ -1225,11 +1238,12 @@
         } else {
             // Now add the new copy of the original ...
             boolean allowSnS = original.getAllowsSameNameChildren();
+            if (desiredName == null) desiredName = desiredSegment.getName();
             newLocation = addNewChild(intoWorkspace.getId(), actualNewParent, newUuid, desiredName, allowSnS);
         }
 
         assert newLocation != null;
-        addedLocations.put(newLocation.uuid, newLocation.childEntity);
+        addedLocations.put(newLocation.location, newLocation.childEntity);
 
         return newLocation;
     }
@@ -1278,7 +1292,7 @@
                 // Walk through the original nodes, creating new ChildEntity object (i.e., copy) for each original ...
                 List<ChildEntity> originalNodes = query.getNodes(true, true);
                 Iterator<ChildEntity> originalIter = originalNodes.iterator();
-                Map<String, ChildEntity> addedLocations = new HashMap<String, ChildEntity>();
+                Map<Location, ChildEntity> addedLocations = new HashMap<Location, ChildEntity>();
                 Map<String, Location> deletedLocations = new HashMap<String, Location>();
 
                 // Start with the original (top-level) node first, since we need to add it to the list of children ...
@@ -1475,7 +1489,7 @@
                 // Walk through the original nodes, creating new ChildEntity object (i.e., copy) for each original ...
                 List<ChildEntity> originalNodes = query.getNodes(true, true);
                 Iterator<ChildEntity> originalIter = originalNodes.iterator();
-                Map<String, ChildEntity> addedLocations = new HashMap<String, ChildEntity>();
+                Map<Location, ChildEntity> addedLocations = new HashMap<Location, ChildEntity>();
                 Map<String, Location> deletedLocations = new HashMap<String, Location>();
 
                 // Start with the original (top-level) node first, since we need to add it to the list of children ...
@@ -1618,17 +1632,18 @@
                     * Now we need to clean up any nodes that were descendants of the old copies of the
                     * nodes but are not descendants of the new copies.
                     */
-                    deletedLocations.keySet().removeAll(addedLocations.keySet());
+                    Map<String, Location> netDeletedLocations = new HashMap<String, Location>(deletedLocations);
+                    netDeletedLocations.values().removeAll(addedLocations.keySet());
 
-                    if (deletedLocations.size() > 0) {
+                    if (netDeletedLocations.size() > 0) {
                         // Verify referential integrity: that none of the deleted nodes are referenced by nodes not being deleted.
                         List<ReferenceEntity> invalidReferences = ReferenceEntity.getReferencesToUuids(intoWorkspace.getId(),
-                                                                                                       deletedLocations.keySet(),
+                                                                                                       netDeletedLocations.keySet(),
                                                                                                        entities);
 
                         for (Iterator<ReferenceEntity> iter = invalidReferences.iterator(); iter.hasNext();) {
                             ReferenceEntity invalidRef = iter.next();
-                            if (deletedLocations.keySet().contains(invalidRef.getId().getFromUuidString())) {
+                            if (netDeletedLocations.keySet().contains(invalidRef.getId().getFromUuidString())) {
                                 iter.remove();
                             }
                         }
@@ -1657,14 +1672,21 @@
                         /*
                          * This list of values that were deleted is expected to be fairly small
                          */
-                        for (Location location : deletedLocations.values()) {
+                        for (Location location : netDeletedLocations.values()) {
                             ActualLocation node = getActualLocation(intoWorkspace, location);
-                            entities.remove(node.childEntity);
+                            ChildEntity entity = node.childEntity;
+
+                            if (entity != null) {
+                                entities.remove(node.childEntity);
+                            }
                             PropertiesEntity.deletePropertiesFor(intoWorkspace.getId(), node.uuid, entities);
 
                         }
                         // Remove from the cache of children locations all entries for deleted nodes ...
-                        cache.removeBranch(intoWorkspaceId, deletedLocations.values());
+                        cache.removeBranch(intoWorkspaceId, netDeletedLocations.values());
+                    }
+
+                    if (!deletedLocations.isEmpty()) {
                         removedLocations = Collections.unmodifiableSet(new HashSet<Location>(deletedLocations.values()));
                     }
                     LargeValueEntity.deleteUnused(entities);
@@ -2077,6 +2099,11 @@
 
             Location fromLocation = request.from();
             ActualLocation actualLocation = getActualLocation(workspace, fromLocation);
+            if (actualLocation.childEntity == null) {
+                actualLocation = new ActualLocation(actualLocation.location, actualLocation.uuid, findNode(workspaceId,
+                                                                                                           actualLocation.uuid));
+            }
+
             actualOldLocation = actualLocation.location;
             Path oldPath = actualOldLocation.getPath();
 


