DNA SVN: r1237 - branches/eclipse.
by dna-commits@lists.jboss.org
Author: elvisisking
Date: 2009-09-21 17:41:15 -0400 (Mon, 21 Sep 2009)
New Revision: 1237
Added:
branches/eclipse/org.jboss.dna.eclipse.jcr.rest.client/
Removed:
branches/eclipse/org.jboss.dna.publish.ui.swt/
Log:
Changing project name to match upcoming java packaging renaming.
Copied: branches/eclipse/org.jboss.dna.eclipse.jcr.rest.client (from rev 1236, branches/eclipse/org.jboss.dna.publish.ui.swt)
14 years, 7 months
DNA SVN: r1235 - in branches/eclipse/org.jboss.dna.publish.ui.swt: META-INF and 1 other directory.
by dna-commits@lists.jboss.org
Author: elvisisking
Date: 2009-09-21 17:35:43 -0400 (Mon, 21 Sep 2009)
New Revision: 1235
Added:
branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.7-SNAPSHOT-jar-with-dependencies.jar
branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.7-SNAPSHOT-sources.jar
Removed:
branches/eclipse/org.jboss.dna.publish.ui.swt/dna-common-0.6-SNAPSHOT-sources.jar
branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.6-SNAPSHOT-jar-with-dependencies.jar
branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.6-SNAPSHOT-sources.jar
Modified:
branches/eclipse/org.jboss.dna.publish.ui.swt/.classpath
branches/eclipse/org.jboss.dna.publish.ui.swt/META-INF/MANIFEST.MF
branches/eclipse/org.jboss.dna.publish.ui.swt/build.properties
Log:
Updated to use the 0.7 client snapshot.
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/.classpath
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/.classpath 2009-09-21 20:03:40 UTC (rev 1234)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/.classpath 2009-09-21 21:35:43 UTC (rev 1235)
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry exported="true" kind="lib" path="dna-web-jcr-rest-client-0.6-SNAPSHOT-jar-with-dependencies.jar" sourcepath="dna-web-jcr-rest-client-0.6-SNAPSHOT-sources.jar"/>
+ <classpathentry exported="true" kind="lib" path="dna-web-jcr-rest-client-0.7-SNAPSHOT-jar-with-dependencies.jar" sourcepath="dna-web-jcr-rest-client-0.7-SNAPSHOT-sources.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/META-INF/MANIFEST.MF
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/META-INF/MANIFEST.MF 2009-09-21 20:03:40 UTC (rev 1234)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/META-INF/MANIFEST.MF 2009-09-21 21:35:43 UTC (rev 1235)
@@ -14,5 +14,5 @@
org.eclipse.debug.ui
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
-Bundle-ClassPath: dna-web-jcr-rest-client-0.6-SNAPSHOT-jar-with-dependencies.jar,
- dnaPublishUi.jar
+Bundle-ClassPath: dna-web-jcr-rest-client-0.7-SNAPSHOT-jar-with-dependencies.jar,
+ jcrRestClient.jar
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/build.properties
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/build.properties 2009-09-21 20:03:40 UTC (rev 1234)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/build.properties 2009-09-21 21:35:43 UTC (rev 1235)
@@ -3,23 +3,23 @@
# See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
#
# See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
-source.dnaPublishUi.jar = src/
-output.dnaPublishUi.jar = bin/
bin.includes = META-INF/,\
- dnaPublishUi.jar,\
plugin.properties,\
icons/,\
plugin.xml,\
LEGAL.txt,\
AUTHORS.txt,\
- dna-web-jcr-rest-client-0.6-SNAPSHOT-jar-with-dependencies.jar,\
src/log4j.properties,\
docs/helpContexts.xml,\
docs/,\
about.ini,\
about.mappings,\
about.properties,\
- feature.png
+ feature.png,\
+ dna-web-jcr-rest-client-0.7-SNAPSHOT-jar-with-dependencies.jar,\
+ jcrRestClient.jar
src.includes = src/,\
LEGAL.txt
-source.dnaPublishUi.jar = src/
+jars.compile.order = jcrRestClient.jar
+source.jcrRestClient.jar = src/
+output.jcrRestClient.jar = bin/
Deleted: branches/eclipse/org.jboss.dna.publish.ui.swt/dna-common-0.6-SNAPSHOT-sources.jar
===================================================================
(Binary files differ)
Deleted: branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.6-SNAPSHOT-jar-with-dependencies.jar
===================================================================
(Binary files differ)
Deleted: branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.6-SNAPSHOT-sources.jar
===================================================================
(Binary files differ)
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.7-SNAPSHOT-jar-with-dependencies.jar
===================================================================
(Binary files differ)
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.7-SNAPSHOT-jar-with-dependencies.jar
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.7-SNAPSHOT-sources.jar
===================================================================
(Binary files differ)
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.7-SNAPSHOT-sources.jar
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
14 years, 7 months
DNA SVN: r1234 - in trunk: dna-cnd/src/test/java/org/jboss/dna/cnd and 41 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-09-21 16:03:40 -0400 (Mon, 21 Sep 2009)
New Revision: 1234
Added:
trunk/dna-common/src/main/java/org/jboss/dna/common/text/SecureHashTextEncoder.java
trunk/dna-common/src/main/java/org/jboss/dna/common/util/ObjectUtil.java
trunk/dna-common/src/test/java/org/jboss/dna/common/text/SecureHashTextEncoderTest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/InvalidQueryException.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/QueryContext.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/QueryResults.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/
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/Command.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/Constraint.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/DynamicOperand.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/JoinCondition.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinType.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LanguageObject.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/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/Not.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/Or.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Order.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/QueryCommand.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Readable.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/SelectorName.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/Source.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/StaticOperand.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/model/Visitable.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/
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/ChooseJoinAlgorithm.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/Optimizer.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/OptimizerRule.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/RightOuterToLeftOuterJoins.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/
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/ColumnExpression.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.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/plan/
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/JoinAlgorithm.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/plan/Planner.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DelegatingComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctOfSortedComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ExceptComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/IntersectComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/JoinComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/LimitComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/MergeJoinComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NestedLoopJoinComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NoResultsComponent.java
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/Processor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProjectComponent.java
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/QueryResultColumns.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SelectComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SetOperationComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortLocationsComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortValuesComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/UnionComponent.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableKey.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/test/java/org/jboss/dna/graph/query/
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/AbstractQueryObjectTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/QueryTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/SetQueryTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.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/parse/
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/FullTextSearchParserTest.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/parse/SqlTokenizerTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/
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/PlanHintsTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/PlanNodeTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/
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/DistinctComponentTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultsTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
trunk/dna-search/
trunk/dna-search/.classpath
trunk/dna-search/.project
trunk/dna-search/pom.xml
trunk/dna-search/src/
trunk/dna-search/src/main/
trunk/dna-search/src/main/java/
trunk/dna-search/src/main/java/org/
trunk/dna-search/src/main/java/org/jboss/
trunk/dna-search/src/main/java/org/jboss/dna/
trunk/dna-search/src/main/java/org/jboss/dna/search/
trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java
trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java
trunk/dna-search/src/main/java/org/jboss/dna/search/EncodingNamespaceRegistry.java
trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java
trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java
trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java
trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java
trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java
trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
trunk/dna-search/src/main/resources/
trunk/dna-search/src/main/resources/org/
trunk/dna-search/src/main/resources/org/jboss/
trunk/dna-search/src/main/resources/org/jboss/dna/
trunk/dna-search/src/main/resources/org/jboss/dna/search/
trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties
trunk/dna-search/src/test/
trunk/dna-search/src/test/java/
trunk/dna-search/src/test/java/org/
trunk/dna-search/src/test/java/org/jboss/
trunk/dna-search/src/test/java/org/jboss/dna/
trunk/dna-search/src/test/java/org/jboss/dna/search/
trunk/dna-search/src/test/java/org/jboss/dna/search/EncodingNamespaceRegistryTest.java
trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java
trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
trunk/dna-search/src/test/java/org/jboss/dna/search/SearchI18nTest.java
trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
trunk/dna-search/src/test/resources/
trunk/dna-search/src/test/resources/aircraft.xml
trunk/dna-search/src/test/resources/cars.xml
trunk/dna-search/src/test/resources/log4j.properties
Modified:
trunk/.gitignore
trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndTokenizerTest.java
trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java
trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java
trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties
trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
trunk/pom.xml
Log:
DNA-467 Add search/query support to the graph API
Added an initial version of the query engine functionality. This design provides a way to define and supply queries (in a various languages) and have the engine parse them into a single Abstract Query Model (equivalent to the abstract syntax tree for a query), plan, validate, optimize and then process the portions atomic portions of the query plan. This whole system was designed to be easily reused as-is or extended and customized to provide the desired behavior. But because this is a generalized query engine capable of query over a 'graph', the actual processing of the atomic portions of the queries must be provided when the engine is used. Part of this commit includes a new 'dna-search' project containing a specialization of the query engine with a processor capable of using a set of Lucene search indexes, along with utility and management methods to populate and update the indexes (by indexing the entire content and/or by updating the content based upon events).
A number of packages were added to 'dna-graph', including: an abstract query model (AQM) based upon the JSR-283 specification in 'o.j.dna.graph.query.model'; a query engine component in 'o.j.dna.graph.query', a query planning module in 'o.j.dna.graph.query.plan'; an extensible rule-based optimization module in 'o.j.dna.graph.query.optimize'; a simple way to define the schemata that is being queried in 'o.j.dna.query.validate'; a flexible processing plan model and execution framework in 'o.j.dna.graph.query.process'; and a framework for different query language parsers (including a JCR-SQL2 parser) in 'o.j.dna.query.parse'. This entire query engine framework was designed to be reused and/or extended in multiple places, and so includes a way to accept and execute queries from a number of different an abstraction of the actual processing of the low-level atomic queries. Numerous unit tests were added to test each of the components, including a large number of tests for the SQ!
L parser.
A new 'dna-search' project was created and the initial Lucene-based query engine functionality was added. Quite a few tests were added to verify the desired behavior.
At this point, the general query engine and the Lucene-based specialization are for the most part complete and thoroughly tested, but these components need to be integrated into the larger connector framework and JCR implementation. All of the Lucene index generation and management needs to be coordinated and integrated with the administration and lifecycle of the DNA connectors and JCR engine. Additionally, while there are methods to create/update the indexes, the ability to extract text from binary property values still needs to be added. In short, there still is a lot of outstanding work.
Modified: trunk/.gitignore
===================================================================
--- trunk/.gitignore 2009-09-21 18:44:47 UTC (rev 1233)
+++ trunk/.gitignore 2009-09-21 20:03:40 UTC (rev 1234)
@@ -15,6 +15,9 @@
/dna-integration-tests/.settings
/dna-integration-tests/target
+/dna-search/.settings
+/dna-search/target
+
/dna-jcr/.settings
/dna-jcr/target
Modified: trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndTokenizerTest.java
===================================================================
--- trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndTokenizerTest.java 2009-09-21 18:44:47 UTC (rev 1233)
+++ trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndTokenizerTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -112,7 +112,6 @@
public void shouldNotIncludeColonInListOfSymbolsSinceTheyCanAppearInNames() {
tokenizer = new CndTokenizer(true, true);
String content = "dna:someName";
- int numSymbols = content.length();
tokenize(content);
assertNextTokenIs(0, content.length(), CndTokenizer.WORD);
assertNoMoreTokens();
@@ -122,7 +121,6 @@
public void shouldCreateVendorExtensionToken() {
tokenizer = new CndTokenizer(true, true);
String content = "{vendor extension}";
- int numSymbols = content.length();
tokenize(content);
assertNextTokenIs(0, content.length(), CndTokenizer.VENDOR_EXTENSION);
assertNoMoreTokens();
@@ -132,7 +130,6 @@
public void shouldNotCreateVendorExtensionTokenIfTokenizerIsNotUsingThem() {
tokenizer = new CndTokenizer(true, false);
String content = "{vendor extension}";
- int numSymbols = content.length();
tokenize(content);
assertNoMoreTokens();
}
Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java 2009-09-21 18:44:47 UTC (rev 1233)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/CommonI18n.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -101,6 +101,9 @@
public static I18n startMethodMustBeCalledBeforeConsumingOrMatching;
public static I18n noMatchingDoubleQuoteFound;
public static I18n noMatchingSingleQuoteFound;
+ public static I18n expectingValidIntegerAtLineAndColumn;
+ public static I18n expectingValidLongAtLineAndColumn;
+ public static I18n expectingValidBooleanAtLineAndColumn;
static {
try {
Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java 2009-09-21 18:44:47 UTC (rev 1233)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -10,8 +10,8 @@
private final int line;
private final int column;
- protected Position( int line,
- int column ) {
+ public Position( int line,
+ int column ) {
this.line = line;
this.column = column;
}
Added: trunk/dna-common/src/main/java/org/jboss/dna/common/text/SecureHashTextEncoder.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/SecureHashTextEncoder.java (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/SecureHashTextEncoder.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,98 @@
+/*
+ * 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.common.text;
+
+import java.security.NoSuchAlgorithmException;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.SecureHash;
+import org.jboss.dna.common.util.SecureHash.Algorithm;
+import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
+
+/**
+ * A text encoder that performs a secure hash of the input text and returns that hash as the encoded text. This encoder can be
+ * configured to use different secure hash algorithsm and to return a fixed set of characters of the hash.
+ */
+public class SecureHashTextEncoder implements TextEncoder {
+
+ private final Algorithm algorithm;
+ private final int maxLength;
+
+ /**
+ * Create an encoder that uses the supplied algorithm and returns only the supplied number of characters in the hash.
+ *
+ * @param algorithm the algorithm that should be used
+ * @throws IllegalArgumentException if the algorithm is null
+ */
+ public SecureHashTextEncoder( Algorithm algorithm ) {
+ this(algorithm, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Create an encoder that uses the supplied algorithm and returns only the supplied number of characters in the hash.
+ *
+ * @param algorithm the algorithm that should be used
+ * @param maxLength the maximumLength, or a non-positive number (or {@link Integer#MAX_VALUE}) if the full hash should be used
+ * @throws IllegalArgumentException if the algorithm is null
+ */
+ public SecureHashTextEncoder( Algorithm algorithm,
+ int maxLength ) {
+ CheckArg.isNotNull(algorithm, "algorithm");
+ this.algorithm = algorithm;
+ this.maxLength = maxLength < 1 ? Integer.MAX_VALUE : maxLength;
+ }
+
+ /**
+ * Get the maximum length of the encoded string, or {@link Integer#MAX_VALUE} if there is no maximum.
+ *
+ * @return the maximum encoded string length; always positive
+ */
+ public int getMaxLength() {
+ return maxLength;
+ }
+
+ /**
+ * Return the secure hash algorithm used by this encoder.
+ *
+ * @return the algorithm; never null
+ */
+ public Algorithm getAlgorithm() {
+ return algorithm;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.common.text.TextEncoder#encode(java.lang.String)
+ */
+ public String encode( String text ) {
+ try {
+ byte[] hash = SecureHash.getHash(algorithm, text.getBytes());
+ String result = Base64.encode(hash);
+ return result.length() < maxLength ? result : result.substring(0, maxLength);
+ } catch (NoSuchAlgorithmException e) {
+ return text;
+ }
+ }
+
+}
Property changes on: trunk/dna-common/src/main/java/org/jboss/dna/common/text/SecureHashTextEncoder.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
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-09-21 18:44:47 UTC (rev 1233)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -444,6 +444,72 @@
}
/**
+ * Convert the value of this token to an integer, return it, and move to the next token.
+ *
+ * @return the current token's value, converted to an integer
+ * @throws ParsingException if there is no such token to consume, or if the token cannot be converted to an integer
+ * @throws IllegalStateException if this method was called before the stream was {@link #start() started}
+ */
+ public int consumeInteger() throws ParsingException, IllegalStateException {
+ if (completed) throwNoMoreContent();
+ // Get the value from the current token ...
+ String value = currentToken().value();
+ try {
+ int result = Integer.parseInt(value);
+ moveToNextToken();
+ return result;
+ } catch (NumberFormatException e) {
+ Position position = currentToken().position();
+ String msg = CommonI18n.expectingValidIntegerAtLineAndColumn.text(value, position.getLine(), position.getColumn());
+ throw new ParsingException(position, msg);
+ }
+ }
+
+ /**
+ * Convert the value of this token to a long, return it, and move to the next token.
+ *
+ * @return the current token's value, converted to an integer
+ * @throws ParsingException if there is no such token to consume, or if the token cannot be converted to a long
+ * @throws IllegalStateException if this method was called before the stream was {@link #start() started}
+ */
+ public long consumeLong() throws ParsingException, IllegalStateException {
+ if (completed) throwNoMoreContent();
+ // Get the value from the current token ...
+ String value = currentToken().value();
+ try {
+ long result = Long.parseLong(value);
+ moveToNextToken();
+ return result;
+ } catch (NumberFormatException e) {
+ Position position = currentToken().position();
+ String msg = CommonI18n.expectingValidLongAtLineAndColumn.text(value, position.getLine(), position.getColumn());
+ throw new ParsingException(position, msg);
+ }
+ }
+
+ /**
+ * Convert the value of this token to an integer, return it, and move to the next token.
+ *
+ * @return the current token's value, converted to an integer
+ * @throws ParsingException if there is no such token to consume, or if the token cannot be converted to an integer
+ * @throws IllegalStateException if this method was called before the stream was {@link #start() started}
+ */
+ public boolean consumeBoolean() throws ParsingException, IllegalStateException {
+ if (completed) throwNoMoreContent();
+ // Get the value from the current token ...
+ String value = currentToken().value();
+ try {
+ boolean result = Boolean.parseBoolean(value);
+ moveToNextToken();
+ return result;
+ } catch (NumberFormatException e) {
+ Position position = currentToken().position();
+ String msg = CommonI18n.expectingValidBooleanAtLineAndColumn.text(value, position.getLine(), position.getColumn());
+ throw new ParsingException(position, msg);
+ }
+ }
+
+ /**
* Return the value of this token and move to the next token.
*
* @return the value of the current token
@@ -451,16 +517,19 @@
* @throws IllegalStateException if this method was called before the stream was {@link #start() started}
*/
public String consume() throws ParsingException, IllegalStateException {
- if (completed) {
- String msg = CommonI18n.noMoreContent.text();
- throw new ParsingException(tokens.get(tokens.size() - 1).position(), msg);
- }
+ if (completed) throwNoMoreContent();
// Get the value from the current token ...
String result = currentToken().value();
moveToNextToken();
return result;
}
+ protected void throwNoMoreContent() throws ParsingException {
+ String msg = CommonI18n.noMoreContent.text();
+ Position pos = tokens.isEmpty() ? new Position(1, 0) : tokens.get(tokens.size() - 1).position();
+ throw new ParsingException(pos, msg);
+ }
+
/**
* Attempt to consume this current token as long as it matches the expected value, or throw an exception if the token does not
* match.
@@ -1278,6 +1347,14 @@
boolean isNextWhitespace();
/**
+ * Determine if the next character on the sream is a {@link Character#isLetterOrDigit(char) letter or digit}. This method
+ * does <i>not</i> advance the stream.
+ *
+ * @return true if there is a {@link #next() next} character and it is a letter or digit, or false otherwise
+ */
+ boolean isNextLetterOrDigit();
+
+ /**
* Determine if the next character on the sream is a {@link XmlCharacters#isValid(int) valid XML character}. This method
* does <i>not</i> advance the stream.
*
@@ -1647,7 +1724,7 @@
private int lastIndex = -1;
private final int maxIndex;
private int lineNumber = 1;
- private int columnNumber = 1;
+ private int columnNumber = 0;
private boolean nextCharMayBeLineFeed;
public CharacterArrayStream( char[] content ) {
@@ -1692,6 +1769,7 @@
throw new NoSuchElementException();
}
char result = content[++lastIndex];
+ ++columnNumber;
if (result == '\r') {
nextCharMayBeLineFeed = true;
++lineNumber;
@@ -1785,6 +1863,16 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.common.text.TokenStream.CharacterStream#isNextLetterOrDigit()
+ */
+ public boolean isNextLetterOrDigit() {
+ int nextIndex = lastIndex + 1;
+ return nextIndex <= maxIndex && Character.isLetterOrDigit(content[nextIndex]);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.common.text.TokenStream.CharacterStream#isNextValidXmlCharacter()
*/
public boolean isNextValidXmlCharacter() {
Added: trunk/dna-common/src/main/java/org/jboss/dna/common/util/ObjectUtil.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/ObjectUtil.java (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/ObjectUtil.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.
+ */
+package org.jboss.dna.common.util;
+
+/**
+ * Common utility methods for general objects.
+ */
+public class ObjectUtil {
+
+ public static <Type> boolean isEqualNoNulls( Type reference1,
+ Type reference2 ) {
+ return reference1.equals(reference2);
+ }
+
+ public static <Type> boolean isEqualWithNulls( Type reference1,
+ Type reference2 ) {
+ return reference1 == null ? reference2 == null : reference1.equals(reference2);
+ }
+}
Property changes on: trunk/dna-common/src/main/java/org/jboss/dna/common/util/ObjectUtil.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties
===================================================================
--- trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties 2009-09-21 18:44:47 UTC (rev 1233)
+++ trunk/dna-common/src/main/resources/org/jboss/dna/common/CommonI18n.properties 2009-09-21 20:03:40 UTC (rev 1234)
@@ -90,3 +90,6 @@
startMethodMustBeCalledBeforeConsumingOrMatching = The 'start()' method must be called before tokens can be consumed or matched
noMatchingDoubleQuoteFound = No matching closing double quote found for the one at line {0}, column {1}
noMatchingSingleQuoteFound = No matching closing single quote found for the one at line {0}, column {1}
+expectingValidIntegerAtLineAndColumn = Expecting a valid integer value but found '{0}' at line {1}, column {2}
+expectingValidLongAtLineAndColumn = Expecting a valid long value but found '{0}' at line {1}, column {2}
+expectingValidBooleanAtLineAndColumn = Expecting a valid boolean value but found '{0}' at line {1}, column {2}
Added: trunk/dna-common/src/test/java/org/jboss/dna/common/text/SecureHashTextEncoderTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/text/SecureHashTextEncoderTest.java (rev 0)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/text/SecureHashTextEncoderTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,99 @@
+/*
+ * 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.common.text;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import java.util.HashSet;
+import java.util.Set;
+import org.jboss.dna.common.util.SecureHash.Algorithm;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class SecureHashTextEncoderTest {
+
+ private SecureHashTextEncoder encoder = new SecureHashTextEncoder(Algorithm.SHA_1);
+ private SecureHashTextEncoder shortEncoder = new SecureHashTextEncoder(Algorithm.SHA_1, 4);
+ private SecureHashTextEncoder md5Encoder = new SecureHashTextEncoder(Algorithm.MD5);
+ private Set<String> alreadyEncoded = new HashSet<String>();
+ private Set<String> alreadyEncodedShort = new HashSet<String>();
+ private Set<String> alreadyEncodedMd5 = new HashSet<String>();
+
+ @Before
+ public void beforeEach() {
+
+ }
+
+ protected void checkEncoding( String input ) {
+ assertThat(alreadyEncoded.add(checkEncoding(encoder, input)), is(true));
+ assertThat(alreadyEncodedShort.add(checkEncoding(shortEncoder, input)), is(true));
+ assertThat(alreadyEncodedMd5.add(checkEncoding(md5Encoder, input)), is(true));
+ }
+
+ protected String checkEncoding( SecureHashTextEncoder encoder,
+ String input ) {
+ String output = encoder.encode(input);
+ assertThat(output, is(notNullValue()));
+ assertThat(output.length() <= encoder.getMaxLength(), is(true));
+ return output;
+ }
+
+ @Test
+ public void shouldEncodeAlphabeticCharacters() {
+ checkEncoding("abcdefghijklmnopqrstuvwxyz");
+ checkEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ }
+
+ @Test
+ public void shouldEncodeNumericCharacters() {
+ checkEncoding("0123456789");
+ }
+
+ @Test
+ public void shouldEncodePunctuationCharacters() {
+ checkEncoding("~`!@#$%^&()-_+={}\\;\"'<,>.?");
+ }
+
+ @Test
+ public void shouldEncodeUrlsAndHaveNoDuplicates() {
+ checkEncoding("http://www.jboss.org");
+ checkEncoding("http://www.jboss.org/");
+ checkEncoding("http://www.jboss.org/dna");
+ checkEncoding("http://www.jboss.org/dna/1.0");
+ checkEncoding("http://www.jboss.org/dna/internal/1.0");
+ checkEncoding("http://www.jcp.org/jcr/1.0");
+ checkEncoding("http://www.jcp.org/jcr/nt/1.0");
+ checkEncoding("http://www.jcp.org/jcr/mix/1.0");
+ checkEncoding("http://www.jcp.org/jcr/sv/1.0");
+ checkEncoding("http://www.acme.com/this/is/a/really/long/url/this/is/a/really/long/url/t...");
+ checkEncoding("http://www.acme.com/this/is/a/really/long/url/this/is/a/really/long/url/t...");
+ // System.out.println(alreadyEncoded);
+ // System.out.println(alreadyEncodedShort);
+ // System.out.println(alreadyEncodedMd5);
+ }
+}
Property changes on: trunk/dna-common/src/test/java/org/jboss/dna/common/text/SecureHashTextEncoderTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java 2009-09-21 18:44:47 UTC (rev 1233)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -57,6 +57,8 @@
@Immutable
public class ExecutionContext implements ClassLoaderFactory, Cloneable {
+ public static final ExecutionContext DEFAULT_CONTEXT = new ExecutionContext();
+
private final ClassLoaderFactory classLoaderFactory;
private final PropertyFactory propertyFactory;
private final ValueFactories valueFactories;
@@ -210,7 +212,7 @@
*
* @return the property factory; never <code>null</code>
*/
- public PropertyFactory getPropertyFactory() {
+ public final PropertyFactory getPropertyFactory() {
return this.propertyFactory;
}
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-09-21 18:44:47 UTC (rev 1233)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -127,6 +127,35 @@
public static I18n unableToMoveNodeToBeChildOfDecendent;
public static I18n childNotFound;
+ /* Query */
+ public static I18n tableDoesNotExist;
+ public static I18n columnDoesNotExistOnTable;
+ public static I18n columnDoesNotExistInQuery;
+ public static I18n selectorDoesNotExistInQuery;
+ public static I18n propertyOnSelectorIsNotUsedInQuery;
+ public static I18n errorResolvingNodesFromLocationsUsingSourceAndWorkspace;
+ public static I18n queryHasNoResults;
+ public static I18n schemataKeyReferencesNonExistingColumn;
+ public static I18n nextMethodMustBeCalledBeforeGettingValue;
+ public static I18n expectingValidName;
+ public static I18n expectingValidPath;
+ public static I18n columnMustBeScoped;
+ public static I18n expectingValidNameAtLineAndColumn;
+ public static I18n expectingValidPathAtLineAndColumn;
+ public static I18n mustBeScopedAtLineAndColumn;
+ public static I18n unexpectedToken;
+ public static I18n secondValueInLimitRangeCannotBeLessThanFirst;
+ public static I18n expectingComparisonOperator;
+ public static I18n expectingConstraintCondition;
+ public static I18n functionIsAmbiguous;
+ public static I18n bindVariableMustConformToNcName;
+ public static I18n invalidPropertyType;
+ public static I18n valueCannotBeCastToSpecifiedType;
+ public static I18n noMatchingBracketFound;
+ public static I18n expectingLiteralAndUnableToParseAsLong;
+ public static I18n expectingLiteralAndUnableToParseAsDouble;
+ public static I18n expectingLiteralAndUnableToParseAsDate;
+
static {
try {
I18n.initialize(GraphI18n.class);
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2009-09-21 18:44:47 UTC (rev 1233)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -45,7 +46,32 @@
@Immutable
public abstract class Location implements Iterable<Property>, Comparable<Location> {
+ private static final Comparator<Location> COMPARATOR = new Comparator<Location>() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ public int compare( Location o1,
+ Location o2 ) {
+ if (o1 == o2) return 0;
+ if (o1 == null) return -1;
+ if (o2 == null) return 1;
+ return o1.compareTo(o2);
+ }
+ };
+
/**
+ * Get a {@link Comparator} that can be used to compare two Location objects. Note that Location implements {@link Comparable}
+ * .
+ *
+ * @return the comparator; never null
+ */
+ public static final Comparator<Location> comparator() {
+ return COMPARATOR;
+ }
+
+ /**
* Simple shared iterator instance that is used when there are no properties.
*/
protected static final Iterator<Property> NO_ID_PROPERTIES_ITERATOR = new Iterator<Property>() {
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/InvalidQueryException.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.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);
+
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/InvalidQueryException.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,1361 @@
+/*
+ * 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 java.util.LinkedList;
+import java.util.List;
+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.Name;
+import org.jboss.dna.graph.property.Path;
+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.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.Join;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.Limit;
+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.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.Not;
+import org.jboss.dna.graph.query.model.Operator;
+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.Query;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.SameNode;
+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.SetQuery;
+import org.jboss.dna.graph.query.model.Source;
+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;
+
+/**
+ * A component that can be used to programmatically create {@link QueryCommand} objects. Simply call methods to build the selector
+ * clause, from clause, join criteria, where criteria, limits, and ordering, and then {@link #query() obtain the query}. This
+ * builder should be adequate for most queries; however, any query that cannot be expressed by this builder can always be
+ * constructed by directly creating the Abstract Query Model classes.
+ * <p>
+ * This builder is stateful and therefore should only be used by one thread at a time. However, once a query has been built, the
+ * builder can be {@link #clear() cleared} and used to create another query.
+ * </p>
+ * <p>
+ * The order in which the methods are called are (for the most part) important. Simply call the methods in the same order that
+ * would be most natural in a normal SQL query. For example, the following code creates a Query object that is equivalent to "
+ * <code>SELECT * FROM table</code>":
+ *
+ * <pre>
+ * QueryCommand query = builder.selectStar().from("table").query();
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * Here are a few other examples:
+ * <table border="1" cellspacing="0" cellpadding="3" summary="">
+ * <tr>
+ * <th>SQL Statement</th>
+ * <th>QueryBuilder code</th>
+ * </tr>
+ * <tr>
+ * <td>
+ *
+ * <pre>
+ * SELECT * FROM table1
+ * INNER JOIN table2
+ * ON table2.c0 = table1.c0
+ * </pre>
+ *
+ * </td>
+ * <td>
+ *
+ * <pre>
+ * query = builder.selectStar().from("table1").join("table2").on("table2.c0=table1.c0").query();
+ * </pre>
+ *
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>
+ *
+ * <pre>
+ * SELECT * FROM table1 AS t1
+ * INNER JOIN table2 AS t2
+ * ON t1.c0 = t2.c0
+ * </pre>
+ *
+ * </td>
+ * <td>
+ *
+ * <pre>
+ * query = builder.selectStar().from("table1 AS t1").join("table2 AS t2").on("t1.c0=t2.c0").query();
+ * </pre>
+ *
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>
+ *
+ * <pre>
+ * SELECT * FROM table1 AS t1
+ * INNER JOIN table2 AS t2
+ * ON t1.c0 = t2.c0
+ * INNER JOIN table3 AS t3
+ * ON t1.c1 = t3.c1
+ * </pre>
+ *
+ * </td>
+ * <td>
+ *
+ * <pre>
+ * query = builder.selectStar()
+ * .from("table1 AS t1")
+ * .innerJoin("table2 AS t2")
+ * .on("t1.c0=t2.c0")
+ * .innerJoin("table3 AS t3")
+ * .on("t1.c1=t3.c1")
+ * .query();
+ * </pre>
+ *
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>
+ *
+ * <pre>
+ * SELECT * FROM table1
+ * UNION
+ * SELECT * FROM table2
+ * </pre>
+ *
+ * </td>
+ * <td>
+ *
+ * <pre>
+ * query = builder.selectStar().from("table1").union().selectStar().from("table2").query();
+ * </pre>
+ *
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>
+ *
+ * <pre>
+ * SELECT t1.c1,t1.c2,t2.c3 FROM table1 AS t1
+ * INNER JOIN table2 AS t2
+ * ON t1.c0 = t2.c0
+ * UNION ALL
+ * SELECT t3.c1,t3.c2,t4.c3 FROM table3 AS t3
+ * INNER JOIN table4 AS t4
+ * ON t3.c0 = t4.c0
+ * </pre>
+ *
+ * </td>
+ * <td>
+ *
+ * <pre>
+ * query = builder.select("t1.c1","t1.c2","t2.c3",)
+ * .from("table1 AS t1")
+ * .innerJoin("table2 AS t2")
+ * .on("t1.c0=t2.c0")
+ * .union()
+ * .select("t3.c1","t3.c2","t4.c3",)
+ * .from("table3 AS t3")
+ * .innerJoin("table4 AS t4")
+ * .on("t3.c0=t4.c0")
+ * .query();
+ * </pre>
+ *
+ * </td>
+ * </tr>
+ * </table>
+ * </pre>
+ */
+@NotThreadSafe
+public class QueryBuilder {
+
+ protected final ExecutionContext context;
+ protected Source source = new AllNodes();
+ protected Constraint constraint;
+ protected List<Column> columns = new LinkedList<Column>();
+ protected List<Ordering> orderings = new LinkedList<Ordering>();
+ protected Limit limit = Limit.NONE;
+ protected boolean distinct;
+ protected QueryCommand firstQuery;
+ protected Operation firstQuerySetOperation;
+ protected boolean firstQueryAll;
+
+ /**
+ * Create a new builder that uses the supplied execution context.
+ *
+ * @param context the execution context
+ * @throws IllegalArgumentException if the context is null
+ */
+ public QueryBuilder( ExecutionContext context ) {
+ CheckArg.isNotNull(context, "context");
+ this.context = context;
+ }
+
+ /**
+ * Clear this builder completely to start building a new query.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder clear() {
+ return clear(true);
+ }
+
+ /**
+ * Utility method that does all the work of the clear, but with a flag that defines whether to clear the first query. This
+ * method is used by {@link #clear()} as well as the {@link #union() many} {@link #intersect() set} {@link #except()
+ * operations}.
+ *
+ * @param clearFirstQuery true if the first query should be cleared, or false if the first query should be retained
+ * @return this builder object, for convenience in method chaining
+ */
+ protected QueryBuilder clear( boolean clearFirstQuery ) {
+ source = new AllNodes();
+ constraint = null;
+ columns = new LinkedList<Column>();
+ orderings = new LinkedList<Ordering>();
+ limit = Limit.NONE;
+ distinct = false;
+ if (clearFirstQuery) {
+ this.firstQuery = null;
+ this.firstQuerySetOperation = null;
+ }
+ return this;
+ }
+
+ /**
+ * Convenience method that creates a selector name object using the supplied string.
+ *
+ * @param name the name of the selector; may not be null
+ * @return the selector name; never null
+ */
+ protected SelectorName selector( String name ) {
+ return new SelectorName(name.trim());
+ }
+
+ /**
+ * Convenience method that creates a {@link NamedSelector} object given a string that contains the selector name and
+ * optionally an alias. The format of the string parameter is <code>name [AS alias]</code>. Leading and trailing whitespace
+ * are trimmed.
+ *
+ * @param nameWithOptionalAlias the name and optional alias; may not be null
+ * @return the named selector object; never null
+ */
+ protected NamedSelector namedSelector( String nameWithOptionalAlias ) {
+ String[] parts = nameWithOptionalAlias.split("\\sAS\\s");
+ if (parts.length == 2) {
+ return new NamedSelector(selector(parts[0]), selector(parts[1]));
+ }
+ return new NamedSelector(selector(parts[0]));
+ }
+
+ /**
+ * Convenience method that creates a {@link Name} object given the supplied string. Leading and trailing whitespace are
+ * trimmed.
+ *
+ * @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
+ */
+ protected Name name( String name ) {
+ try {
+ return context.getValueFactories().getNameFactory().create(name.trim());
+ } catch (ValueFormatException e) {
+ throw new InvalidQueryException(GraphI18n.expectingValidName.text(name));
+ }
+ }
+
+ /**
+ * Convenience method that creates a {@link Path} object given the supplied string. Leading and trailing whitespace are
+ * trimmed.
+ *
+ * @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
+ */
+ protected Path path( String path ) {
+ try {
+ return context.getValueFactories().getPathFactory().create(path.trim());
+ } catch (ValueFormatException e) {
+ throw new InvalidQueryException(GraphI18n.expectingValidPath.text(path));
+ }
+ }
+
+ /**
+ * Create a {@link Column} given the supplied expression. The expression 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 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
+ */
+ protected Column column( String nameExpression ) {
+ String[] parts = nameExpression.split("(?<!\\\\)\\."); // a . not preceded by an escaping slash
+ for (int i = 0; i != parts.length; ++i) {
+ parts[i] = parts[i].trim();
+ }
+ SelectorName name = null;
+ Name propertyName = null;
+ String columnName = null;
+ if (parts.length == 2) {
+ name = selector(parts[0]);
+ propertyName = name(parts[1]);
+ columnName = parts[1];
+ } else {
+ if (source instanceof Selector) {
+ Selector selector = (Selector)source;
+ name = selector.hasAlias() ? selector.getAlias() : selector.getName();
+ propertyName = name(parts[0]);
+ columnName = parts[0];
+ } else {
+ throw new InvalidQueryException(GraphI18n.columnMustBeScoped.text(parts[0]));
+ }
+ }
+ return new Column(name, propertyName, columnName);
+ }
+
+ /**
+ * Select all of the single-valued columns.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder selectStar() {
+ columns.clear();
+ return this;
+ }
+
+ /**
+ * 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.
+ *
+ * @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
+ */
+ public QueryBuilder select( String... columnNames ) {
+ columns.clear();
+ for (String expression : columnNames) {
+ columns.add(column(expression));
+ }
+ return this;
+ }
+
+ /**
+ * Select all of the distinct values from the single-valued columns.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder selectDistinctStar() {
+ distinct = true;
+ return selectStar();
+ }
+
+ /**
+ * Select the distinct values from 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
+ */
+ public QueryBuilder selectDistinct( String... columnNames ) {
+ distinct = true;
+ return select(columnNames);
+ }
+
+ /**
+ * Specify that the query should select from the "__ALLNODES__" built-in table.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder fromAllNodes() {
+ this.source = new AllNodes();
+ return this;
+ }
+
+ /**
+ * Specify that the query should select from the "__ALLNODES__" built-in table using the supplied alias.
+ *
+ * @param alias the alias for the "__ALL_NODES" table; may not be null
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder fromAllNodesAs( String alias ) {
+ AllNodes allNodes = new AllNodes(selector(alias));
+ SelectorName oldName = this.source instanceof Selector ? ((Selector)source).getName() : null;
+ // Go through the columns and change the selector name to use the new alias ...
+ for (int i = 0; i != columns.size(); ++i) {
+ Column old = columns.get(i);
+ if (old.getSelectorName().equals(oldName)) {
+ columns.set(i, new Column(allNodes.getAliasOrName(), old.getPropertyName(), old.getColumnName()));
+ }
+ }
+ this.source = allNodes;
+ return this;
+ }
+
+ /**
+ * Specify the name of the table from which tuples should be selected. The supplied string is of the form "
+ * <code>tableName [AS alias]</code>".
+ *
+ * @param tableNameWithOptionalAlias the name of the table, optionally including the alias
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder from( String tableNameWithOptionalAlias ) {
+ Selector selector = namedSelector(tableNameWithOptionalAlias);
+ SelectorName oldName = this.source instanceof Selector ? ((Selector)source).getName() : null;
+ // Go through the columns and change the selector name to use the new alias ...
+ for (int i = 0; i != columns.size(); ++i) {
+ Column old = columns.get(i);
+ if (old.getSelectorName().equals(oldName)) {
+ columns.set(i, new Column(selector.getAliasOrName(), old.getPropertyName(), old.getColumnName()));
+ }
+ }
+ this.source = selector;
+ return this;
+ }
+
+ /**
+ * Begin the WHERE clause for this query by obtaining the constraint builder. When completed, be sure to call
+ * {@link ConstraintBuilder#end() end()} on the resulting constraint builder, or else the constraint will not be applied to
+ * the current query.
+ *
+ * @return the constraint builder that can be used to specify the criteria; never null
+ */
+ public ConstraintBuilder where() {
+ return new ConstraintBuilder(null);
+ }
+
+ /**
+ * Perform an inner join between the already defined source with the supplied table. The supplied string is of the form "
+ * <code>tableName [AS alias]</code>".
+ *
+ * @param tableName the name of the table, optionally including the alias
+ * @return the component that must be used to complete the join specification; never null
+ */
+ public JoinClause join( String tableName ) {
+ return innerJoin(tableName);
+ }
+
+ /**
+ * Perform an inner join between the already defined source with the supplied table. The supplied string is of the form "
+ * <code>tableName [AS alias]</code>".
+ *
+ * @param tableName the name of the table, optionally including the alias
+ * @return the component that must be used to complete the join specification; never null
+ */
+ public JoinClause innerJoin( String tableName ) {
+ // Expect there to be a source already ...
+ return new JoinClause(namedSelector(tableName), JoinType.INNER);
+ }
+
+ /**
+ * Perform a cross join between the already defined source with the supplied table. The supplied string is of the form "
+ * <code>tableName [AS alias]</code>". 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 tableName the name of the table, optionally including the alias
+ * @return the component that must be used to complete the join specification; never null
+ */
+ public JoinClause crossJoin( String tableName ) {
+ // Expect there to be a source already ...
+ return new JoinClause(namedSelector(tableName), JoinType.CROSS);
+ }
+
+ /**
+ * Perform a full outer join between the already defined source with the supplied table. The supplied string is of the form "
+ * <code>tableName [AS alias]</code>".
+ *
+ * @param tableName the name of the table, optionally including the alias
+ * @return the component that must be used to complete the join specification; never null
+ */
+ public JoinClause fullOuterJoin( String tableName ) {
+ // Expect there to be a source already ...
+ return new JoinClause(namedSelector(tableName), JoinType.FULL_OUTER);
+ }
+
+ /**
+ * Perform a left outer join between the already defined source with the supplied table. The supplied string is of the form "
+ * <code>tableName [AS alias]</code>".
+ *
+ * @param tableName the name of the table, optionally including the alias
+ * @return the component that must be used to complete the join specification; never null
+ */
+ public JoinClause leftOuterJoin( String tableName ) {
+ // Expect there to be a source already ...
+ return new JoinClause(namedSelector(tableName), JoinType.LEFT_OUTER);
+ }
+
+ /**
+ * Perform a right outer join between the already defined source with the supplied table. The supplied string is of the form "
+ * <code>tableName [AS alias]</code>".
+ *
+ * @param tableName the name of the table, optionally including the alias
+ * @return the component that must be used to complete the join specification; never null
+ */
+ public JoinClause rightOuterJoin( String tableName ) {
+ // Expect there to be a source already ...
+ return new JoinClause(namedSelector(tableName), 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
+ * @return this builder object, for convenience in method chaining
+ * @throws IllegalArgumentException if the row limit is not a positive integer
+ */
+ public QueryBuilder limit( int rowLimit ) {
+ this.limit.withRowLimit(rowLimit);
+ return this;
+ }
+
+ /**
+ * Specify the number of rows that results are to skip. The default offset is '0'.
+ *
+ * @param offset the number of rows before the results are to begin
+ * @return this builder object, for convenience in method chaining
+ * @throws IllegalArgumentException if the row limit is a negative integer
+ */
+ public QueryBuilder offset( int offset ) {
+ this.limit.withOffset(offset);
+ return this;
+ }
+
+ /**
+ * Perform a UNION between the query as defined prior to this method and the query that will be defined following this method.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder union() {
+ this.firstQuery = query();
+ this.firstQuerySetOperation = Operation.UNION;
+ this.firstQueryAll = false;
+ clear(false);
+ return this;
+ }
+
+ /**
+ * Perform a UNION ALL between the query as defined prior to this method and the query that will be defined following this
+ * method.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder unionAll() {
+ this.firstQuery = query();
+ this.firstQuerySetOperation = Operation.UNION;
+ this.firstQueryAll = true;
+ clear(false);
+ return this;
+ }
+
+ /**
+ * Perform an INTERSECT between the query as defined prior to this method and the query that will be defined following this
+ * method.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder intersect() {
+ this.firstQuery = query();
+ this.firstQuerySetOperation = Operation.INTERSECT;
+ this.firstQueryAll = false;
+ clear(false);
+ return this;
+ }
+
+ /**
+ * Perform an INTERSECT ALL between the query as defined prior to this method and the query that will be defined following
+ * this method.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder intersectAll() {
+ this.firstQuery = query();
+ this.firstQuerySetOperation = Operation.INTERSECT;
+ this.firstQueryAll = true;
+ clear(false);
+ return this;
+ }
+
+ /**
+ * Perform an EXCEPT between the query as defined prior to this method and the query that will be defined following this
+ * method.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder except() {
+ this.firstQuery = query();
+ this.firstQuerySetOperation = Operation.EXCEPT;
+ this.firstQueryAll = false;
+ clear(false);
+ return this;
+ }
+
+ /**
+ * Perform an EXCEPT ALL between the query as defined prior to this method and the query that will be defined following this
+ * method.
+ *
+ * @return this builder object, for convenience in method chaining
+ */
+ public QueryBuilder exceptAll() {
+ this.firstQuery = query();
+ this.firstQuerySetOperation = Operation.EXCEPT;
+ this.firstQueryAll = true;
+ clear(false);
+ return this;
+ }
+
+ /**
+ * Return a {@link QueryCommand} representing the currently-built query.
+ *
+ * @return the resulting query command; never null
+ * @see #clear()
+ */
+ public QueryCommand query() {
+ QueryCommand result = new Query(source, constraint, orderings, columns, limit, distinct);
+ if (this.firstQuery != null) {
+ // EXCEPT has a higher precedence than INTERSECT or UNION, so if the first query is
+ // an INTERSECT or UNION SetQuery, the result should be applied to the RHS of the previous set ...
+ if (firstQuery instanceof SetQuery && firstQuerySetOperation == Operation.EXCEPT) {
+ SetQuery setQuery = (SetQuery)firstQuery;
+ QueryCommand left = setQuery.getLeft();
+ QueryCommand right = setQuery.getRight();
+ SetQuery exceptQuery = new SetQuery(right, Operation.EXCEPT, result, firstQueryAll);
+ result = new SetQuery(left, setQuery.getOperation(), exceptQuery, setQuery.isAll());
+ } else {
+ result = new SetQuery(this.firstQuery, this.firstQuerySetOperation, result, this.firstQueryAll);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Class used to specify a join clause of a query.
+ *
+ * @see QueryBuilder#join(String)
+ * @see QueryBuilder#innerJoin(String)
+ * @see QueryBuilder#leftOuterJoin(String)
+ * @see QueryBuilder#rightOuterJoin(String)
+ * @see QueryBuilder#fullOuterJoin(String)
+ */
+ public class JoinClause {
+ private final NamedSelector rightSource;
+ private final JoinType type;
+
+ protected JoinClause( NamedSelector rightTable,
+ JoinType type ) {
+ this.rightSource = rightTable;
+ this.type = type;
+ }
+
+ /**
+ * Walk the current source or the 'rightSource' to find the named selector with the supplied name or alias
+ *
+ * @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
+ */
+ protected SelectorName nameOf( String tableName ) {
+ final SelectorName name = new SelectorName(tableName);
+ // Look at the right source ...
+ if (rightSource.getAliasOrName().equals(name)) return name;
+ // Look through the left source ...
+ final AtomicBoolean notFound = new AtomicBoolean(true);
+ Visitors.visitAll(source, new Visitors.AbstractVisitor() {
+ @Override
+ public void visit( AllNodes selector ) {
+ if (notFound.get() && selector.getAliasOrName().equals(name)) notFound.set(false);
+ }
+
+ @Override
+ public void visit( NamedSelector selector ) {
+ if (notFound.get() && selector.getAliasOrName().equals(name)) notFound.set(false);
+ }
+ });
+ if (notFound.get()) {
+ throw new InvalidQueryException("Expected \"" + tableName + "\" to be a valid table name or alias");
+ }
+ return name;
+ }
+
+ /**
+ * Define the join as using an equi-join criteria by specifying the expression equating two columns. Each column reference
+ * must be qualified with the appropriate table name or alias.
+ *
+ * @param columnEqualExpression the equality expression between the two tables; may not be null
+ * @return the query builder instance, for method chaining purposes
+ */
+ public QueryBuilder on( String columnEqualExpression ) {
+ String[] parts = columnEqualExpression.split("=");
+ if (parts.length != 2) {
+ throw new InvalidQueryException("Expected equality expression for columns, but found \"" + columnEqualExpression
+ + "\"");
+ }
+ return createJoin(new EquiJoinCondition(column(parts[0]), column(parts[1])));
+ }
+
+ /**
+ * Define the join criteria to require the two tables represent the same node. The supplied tables must be a valid name or
+ * alias.
+ *
+ * @param table1 the name or alias of the first table
+ * @param table2 the name or alias of the second table
+ * @return the query builder instance, for method chaining purposes
+ */
+ public QueryBuilder onSameNode( String table1,
+ String table2 ) {
+ return createJoin(new SameNodeJoinCondition(nameOf(table1), nameOf(table2)));
+ }
+
+ /**
+ * Define the join criteria to require the node in one table is a descendant of the node in another table. The supplied
+ * tables must be a valid name or alias.
+ *
+ * @param ancestorTable the name or alias of the table containing the ancestor node
+ * @param descendantTable the name or alias of the table containing the descendant node
+ * @return the query builder instance, for method chaining purposes
+ */
+ public QueryBuilder onDescendant( String ancestorTable,
+ String descendantTable ) {
+ return createJoin(new DescendantNodeJoinCondition(nameOf(ancestorTable), nameOf(descendantTable)));
+ }
+
+ /**
+ * Define the join criteria to require the node in one table is a child of the node in another table. The supplied tables
+ * must be a valid name or alias.
+ *
+ * @param parentTable the name or alias of the table containing the parent node
+ * @param childTable the name or alias of the table containing the child node
+ * @return the query builder instance, for method chaining purposes
+ */
+ public QueryBuilder onChildNode( String parentTable,
+ String childTable ) {
+ return createJoin(new DescendantNodeJoinCondition(nameOf(parentTable), nameOf(childTable)));
+ }
+
+ protected QueryBuilder createJoin( JoinCondition condition ) {
+ // CROSS joins have a higher precedence, so we may need to adjust the existing left side in this case...
+ if (type == JoinType.CROSS && source instanceof Join && ((Join)source).getType() != JoinType.CROSS) {
+ // A CROSS join follows a non-CROSS join, so the CROSS join becomes precendent ...
+ Join left = (Join)source;
+ Join cross = new Join(left.getRight(), type, rightSource, condition);
+ source = new Join(left.getLeft(), left.getType(), cross, left.getJoinCondition());
+ } else {
+ // Otherwise, just create using usual precedence ...
+ source = new Join(source, type, rightSource, condition);
+ }
+ return QueryBuilder.this;
+ }
+ }
+
+ /**
+ * Interface that defines a dynamic operand portion of a criteria.
+ */
+ public interface DynamicOperandBuilder {
+ /**
+ * Constrains the nodes in the the supplied table such that they must have a property value whose length matches the
+ * criteria.
+ *
+ * @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
+ * @param property the name of the property; may not be null and must refer to a valid property name
+ * @return the interface for completing the value portion of the criteria specification; never null
+ */
+ public ComparisonBuilder length( String table,
+ String property );
+
+ /**
+ * Constrains the nodes in the the supplied table such that they must have a matching 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
+ * @param property the name of the property; may not be null and must refer to a valid property name
+ * @return the interface for completing the value portion of the criteria specification; never null
+ */
+ public ComparisonBuilder propertyValue( String table,
+ String property );
+
+ /**
+ * Constrains the nodes in the the supplied table such that they must satisfy the supplied full-text search on the nodes'
+ * property values.
+ *
+ * @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 fullTextSearchScore( String table );
+
+ /**
+ * Constrains the nodes in the the supplied table such that they must have a matching node 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.
+ *
+ * @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 nodeName( String table );
+
+ /**
+ * Begin a constraint against the uppercase form of a dynamic operand.
+ *
+ * @return the interface for completing the criteria specification; never null
+ */
+ public DynamicOperandBuilder upperCaseOf();
+
+ /**
+ * Begin a constraint against the lowercase form of a dynamic operand.
+ *
+ * @return the interface for completing the criteria specification; never null
+ */
+ public DynamicOperandBuilder lowerCaseOf();
+
+ }
+
+ public class ConstraintBuilder implements DynamicOperandBuilder {
+ private final ConstraintBuilder parent;
+ /** Used for the current operations */
+ private Constraint constraint;
+ /** Set when a logical criteria is started */
+ private Constraint left;
+ private boolean and;
+ private boolean negateConstraint;
+
+ protected ConstraintBuilder( ConstraintBuilder parent ) {
+ this.parent = parent;
+ }
+
+ /**
+ * Complete this constraint specification.
+ *
+ * @return the query builder, for method chaining purposes
+ */
+ public QueryBuilder end() {
+ buildLogicalConstraint();
+ QueryBuilder.this.constraint = constraint;
+ return QueryBuilder.this;
+ }
+
+ /**
+ * Simulate the use of an open parenthesis in the constraint. The resulting builder should be used to define the
+ * constraint within the parenthesis, and should always be terminated with a {@link #closeParen()}.
+ *
+ * @return the constraint builder that should be used to define the portion of the constraint within the parenthesis;
+ * never null
+ * @see #closeParen()
+ */
+ public ConstraintBuilder openParen() {
+ return new ConstraintBuilder(this);
+ }
+
+ /**
+ * Complete the specification of a constraint clause, and return the builder for the parent constraint clause.
+ *
+ * @return the constraint builder that was used to create this parenthetical constraint clause builder; never null
+ */
+ public ConstraintBuilder closeParen() {
+ assert parent != null;
+ buildLogicalConstraint();
+ return parent.setConstraint(constraint);
+ }
+
+ /**
+ * Signal that the previous constraint clause be AND-ed together with another constraint clause that will be defined
+ * immediately after this method call.
+ *
+ * @return the constraint builder for the remaining constraint clause; never null
+ */
+ public ConstraintBuilder and() {
+ buildLogicalConstraint();
+ left = constraint;
+ constraint = null;
+ and = true;
+ return this;
+ }
+
+ /**
+ * Signal that the previous constraint clause be OR-ed together with another constraint clause that will be defined
+ * immediately after this method call.
+ *
+ * @return the constraint builder for the remaining constraint clause; never null
+ */
+ public ConstraintBuilder or() {
+ buildLogicalConstraint();
+ left = constraint;
+ constraint = null;
+ and = false;
+ return this;
+ }
+
+ /**
+ * Signal that the next constraint clause (defined immediately after this method) should be negated.
+ *
+ * @return the constraint builder for the constraint clause that is to be negated; never null
+ */
+ public ConstraintBuilder not() {
+ negateConstraint = true;
+ return this;
+ }
+
+ protected ConstraintBuilder buildLogicalConstraint() {
+ if (negateConstraint && constraint != null) {
+ constraint = new Not(constraint);
+ negateConstraint = false;
+ }
+ if (left != null && constraint != null) {
+ if (and) {
+ // If the left constraint is an OR, we need to rearrange things since AND is higher precedence ...
+ if (left instanceof Or) {
+ Or previous = (Or)left;
+ constraint = new Or(previous.getLeft(), new And(previous.getRight(), constraint));
+ } else {
+ constraint = new And(left, constraint);
+ }
+ } else {
+ constraint = new Or(left, constraint);
+ }
+ left = null;
+ }
+ return this;
+ }
+
+ /**
+ * Define a constraint clause that the node within the named table is the same node as that appearing at the supplied
+ * 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
+ * @param asNodeAtPath the path to the node
+ * @return the constraint builder that was used to create this clause; never null
+ */
+ 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)));
+ }
+
+ /**
+ * Define a constraint clause that the node within the named table is the child of the node at the supplied path.
+ *
+ * @param childTable 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
+ * @param parentPath the path to the parent node
+ * @return the constraint builder that was used to create this clause; never null
+ */
+ 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)));
+ }
+
+ /**
+ * Define a constraint clause that the node within the named table is a descendant of the node at the supplied path.
+ *
+ * @param descendantTable 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
+ * @param ancestorPath the path to the ancestor node
+ * @return the constraint builder that was used to create this clause; never null
+ */
+ 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)));
+ }
+
+ /**
+ * Define a constraint clause that the node within the named table has at least one value for the named property. 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
+ * @param propertyName the name of the property
+ * @return the constraint builder that was used to create this clause; never null
+ */
+ 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)));
+ }
+
+ /**
+ * Define a constraint clause that the node within the named table have at least one property that satisfies the full-text
+ * search expression.
+ *
+ * @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
+ * @param searchExpression the full-text search expression
+ * @return the constraint builder that was used to create this clause; never null
+ */
+ 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));
+ }
+
+ /**
+ * Define a constraint clause that the node within the named table have a value for the named property that satisfies the
+ * full-text search expression.
+ *
+ * @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
+ * @param propertyName the name of the property to be searched
+ * @param searchExpression the full-text search expression
+ * @return the constraint builder that was used to create this clause; never null
+ */
+ 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));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#length(java.lang.String, java.lang.String)
+ */
+ public ComparisonBuilder length( String table,
+ String property ) {
+ return new ComparisonBuilder(this, new Length(new PropertyValue(selector(table), name(property))));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#propertyValue(String, String)
+ */
+ public ComparisonBuilder propertyValue( String table,
+ String property ) {
+ return new ComparisonBuilder(this, new PropertyValue(selector(table), name(property)));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#fullTextSearchScore(String)
+ */
+ public ComparisonBuilder fullTextSearchScore( String table ) {
+ return new ComparisonBuilder(this, new FullTextSearchScore(selector(table)));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#nodeLocalName(String)
+ */
+ public ComparisonBuilder nodeLocalName( String table ) {
+ return new ComparisonBuilder(this, new NodeLocalName(selector(table)));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#nodeName(String)
+ */
+ public ComparisonBuilder nodeName( String table ) {
+ return new ComparisonBuilder(this, new NodeName(selector(table)));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#upperCaseOf()
+ */
+ public DynamicOperandBuilder upperCaseOf() {
+ return new UpperCaser(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryBuilder.DynamicOperandBuilder#lowerCaseOf()
+ */
+ public DynamicOperandBuilder lowerCaseOf() {
+ return new LowerCaser(this);
+ }
+
+ protected ConstraintBuilder setConstraint( Constraint constraint ) {
+ if (this.constraint != null && this.left == null) {
+ and();
+ }
+ this.constraint = constraint;
+ return buildLogicalConstraint();
+ }
+ }
+
+ /**
+ * A specialized form of the {@link ConstraintBuilder} that always wraps the generated constraint in a {@link UpperCase}
+ * instance.
+ */
+ protected class UpperCaser extends ConstraintBuilder {
+ private final ConstraintBuilder delegate;
+
+ protected UpperCaser( ConstraintBuilder delegate ) {
+ super(null);
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected ConstraintBuilder setConstraint( Constraint constraint ) {
+ Comparison comparison = (Comparison)constraint;
+ return delegate.setConstraint(new Comparison(new UpperCase(comparison.getOperand1()), comparison.getOperator(),
+ comparison.getOperand2()));
+ }
+ }
+
+ /**
+ * A specialized form of the {@link ConstraintBuilder} that always wraps the generated constraint in a {@link LowerCase}
+ * instance.
+ */
+ protected class LowerCaser extends ConstraintBuilder {
+ private final ConstraintBuilder delegate;
+
+ protected LowerCaser( ConstraintBuilder delegate ) {
+ super(null);
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected ConstraintBuilder setConstraint( Constraint constraint ) {
+ Comparison comparison = (Comparison)constraint;
+ return delegate.setConstraint(new Comparison(new LowerCase(comparison.getOperand1()), comparison.getOperator(),
+ comparison.getOperand2()));
+ }
+ }
+
+ /**
+ * An interface used to set the right-hand side of a constraint.
+ */
+ public class ComparisonBuilder {
+ private final DynamicOperand left;
+ private final ConstraintBuilder constraintBuilder;
+
+ protected ComparisonBuilder( ConstraintBuilder constraintBuilder,
+ DynamicOperand left ) {
+ this.left = left;
+ this.constraintBuilder = constraintBuilder;
+ }
+
+ protected ConstraintBuilder isVariable( Operator operator,
+ String variableName ) {
+ assert operator != null;
+ return this.constraintBuilder.setConstraint(new Comparison(left, operator, new BindVariableName(variableName)));
+ }
+
+ protected ConstraintBuilder is( Operator operator,
+ Object literal ) {
+ assert operator != null;
+ return this.constraintBuilder.setConstraint(new Comparison(left, operator, new Literal(literal)));
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be equivalent to the value of the supplied variable.
+ *
+ * @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 isEqualToVariable( String variableName ) {
+ return isVariable(Operator.EQUAL_TO, variableName);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be greater than the value of the supplied variable.
+ *
+ * @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 isGreaterThanVariable( String variableName ) {
+ return isVariable(Operator.GREATER_THAN, variableName);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be greater than or equal to the value of the supplied variable.
+ *
+ * @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 isGreaterThanOrEqualToVariable( String variableName ) {
+ return isVariable(Operator.GREATER_THAN_OR_EQUAL_TO, variableName);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be less than the value of the supplied variable.
+ *
+ * @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 isLessThanVariable( String variableName ) {
+ return isVariable(Operator.LESS_THAN, variableName);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be less than or equal to the value of the supplied variable.
+ *
+ * @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 isLessThanOrEqualToVariable( String variableName ) {
+ return isVariable(Operator.LESS_THAN_OR_EQUAL_TO, variableName);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be LIKE the value of the supplied variable.
+ *
+ * @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 isLikeVariable( String variableName ) {
+ return isVariable(Operator.LIKE, variableName);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be not equal to the value of the supplied variable.
+ *
+ * @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 isNotEqualToVariable( String variableName ) {
+ return isVariable(Operator.NOT_EQUAL_TO, variableName);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be equivalent to the supplied literal value.
+ *
+ * @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 isEqualTo( Object literal ) {
+ return is(Operator.EQUAL_TO, literal);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be greater than the supplied literal value.
+ *
+ * @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 isGreaterThan( Object literal ) {
+ return is(Operator.GREATER_THAN, literal);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be greater than or equal to the supplied literal value.
+ *
+ * @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 isGreaterThanOrEqualTo( Object literal ) {
+ return is(Operator.GREATER_THAN_OR_EQUAL_TO, literal);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be less than the supplied literal value.
+ *
+ * @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 isLessThan( Object literal ) {
+ return is(Operator.LESS_THAN, literal);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be less than or equal to the supplied literal value.
+ *
+ * @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 isLessThanOrEqualTo( Object literal ) {
+ return is(Operator.LESS_THAN_OR_EQUAL_TO, literal);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be LIKE the supplied literal value.
+ *
+ * @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 isLike( Object literal ) {
+ return is(Operator.LIKE, literal);
+ }
+
+ /**
+ * Define the right-hand-side of the constraint to be not equal to the supplied literal value.
+ *
+ * @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 isNotEqualTo( Object literal ) {
+ return is(Operator.NOT_EQUAL_TO, literal);
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryBuilder.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,110 @@
+/*
+ * 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 java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.collection.SimpleProblems;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.model.BindVariableName;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.validate.Schemata;
+
+/**
+ *
+ */
+public final class QueryContext {
+ private final ExecutionContext context;
+ private final PlanHints hints;
+ private final Schemata schemata;
+ private final Problems problems;
+ private final Map<String, Object> variables;
+
+ public QueryContext( ExecutionContext context,
+ PlanHints hints,
+ Schemata schemata,
+ Problems problems,
+ Map<String, Object> variables ) {
+ CheckArg.isNotNull(context, "context");
+ this.context = context;
+ this.hints = hints != null ? hints : new PlanHints();
+ this.schemata = schemata;
+ this.problems = problems != null ? problems : new SimpleProblems();
+ this.variables = variables != null ? Collections.<String, Object>unmodifiableMap(new HashMap<String, Object>(variables)) : Collections.<String, Object>emptyMap();
+ }
+
+ public QueryContext( ExecutionContext context,
+ PlanHints hints,
+ Schemata schemata,
+ Problems problems ) {
+ this(context, hints, schemata, problems, null);
+ }
+
+ public QueryContext( ExecutionContext context,
+ PlanHints hints,
+ Schemata schemata ) {
+ this(context, hints, schemata, null, null);
+ }
+
+ /**
+ * @return context
+ */
+ public final ExecutionContext getExecutionContext() {
+ return context;
+ }
+
+ /**
+ * @return hints
+ */
+ public final PlanHints getHints() {
+ return hints;
+ }
+
+ /**
+ * @return problems
+ */
+ public final Problems getProblems() {
+ return problems;
+ }
+
+ /**
+ * @return schemata
+ */
+ public Schemata getSchemata() {
+ return schemata;
+ }
+
+ /**
+ * Get the variables that are to be substituted into the {@link BindVariableName} used in the query.
+ *
+ * @return immutable map of variable values keyed by their name; never null
+ */
+ public Map<String, Object> getVariables() {
+ return variables;
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.QueryResults.Statistics;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.FullTextSearch;
+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.optimize.Optimizer;
+import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+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.Planner;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Traversal;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.process.Processor;
+import org.jboss.dna.graph.query.process.QueryResultColumns;
+import org.jboss.dna.graph.query.validate.Schemata;
+
+/**
+ * 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 {
+
+ private final Planner planner;
+ private final Optimizer optimizer;
+ private final Processor processor;
+ private final Schemata schemata;
+
+ public QueryEngine( Planner planner,
+ Optimizer optimizer,
+ Processor processor,
+ Schemata schemata ) {
+ CheckArg.isNotNull(processor, "processor");
+ this.planner = planner != null ? planner : new CanonicalPlanner();
+ this.optimizer = optimizer != null ? optimizer : new RuleBasedOptimizer();
+ this.processor = processor;
+ this.schemata = schemata != null ? schemata : new Schemata() {
+ public Table getTable( SelectorName name ) {
+ // This won't allow the query engine to do anything (or much of anything),
+ // but it is legal and will result in meaningful problems
+ return null;
+ }
+ };
+ }
+
+ /**
+ * 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.
+ *
+ * @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
+ */
+ public QueryResults execute( ExecutionContext context,
+ QueryCommand query,
+ PlanHints hints ) {
+ QueryContext queryContext = new QueryContext(context, hints, schemata);
+
+ // Create the canonical plan ...
+ long start = System.nanoTime();
+ PlanNode plan = planner.createPlan(queryContext, query);
+ long duration = System.nanoTime() - start;
+ Statistics stats = new Statistics(duration);
+
+ QueryResultColumns resultColumns = QueryResultColumns.empty();
+ if (!queryContext.getProblems().hasErrors()) {
+ // Optimize the plan ...
+ start = System.nanoTime();
+ PlanNode optimizedPlan = optimizer.optimize(queryContext, plan);
+ duration = System.nanoTime() - start;
+ stats = stats.withOptimizationTime(duration);
+
+ // Find the query result columns ...
+ start = System.nanoTime();
+ resultColumns = determineQueryResultColumns(optimizedPlan);
+ duration = System.nanoTime() - start;
+ stats = stats.withOptimizationTime(duration);
+
+ if (!queryContext.getProblems().hasErrors()) {
+ // Execute the plan ...
+ try {
+ start = System.nanoTime();
+ return processor.execute(queryContext, query, stats, optimizedPlan);
+ } finally {
+ duration = System.nanoTime() - start;
+ stats = stats.withOptimizationTime(duration);
+ }
+ }
+ }
+ // There were problems somewhere ...
+ return new org.jboss.dna.graph.query.process.QueryResults(queryContext, query, resultColumns, stats);
+ }
+
+ protected QueryResultColumns determineQueryResultColumns( PlanNode optimizedPlan ) {
+ // Look for which columns to include in the results; this will be defined by the highest PROJECT node ...
+ PlanNode project = optimizedPlan.findAtOrBelow(Traversal.LEVEL_ORDER, Type.PROJECT);
+ if (project != null) {
+ List<Column> columns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+ // Determine whether to include the full-text search scores in the results ...
+ final AtomicBoolean includeFullTextSearchScores = new AtomicBoolean(false);
+ for (PlanNode select : optimizedPlan.findAllAtOrBelow(Type.SELECT)) {
+ Constraint constraint = select.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+ if (constraint != null) {
+ Visitors.visitAll(constraint, new Visitors.AbstractVisitor() {
+ @Override
+ public void visit( FullTextSearch obj ) {
+ includeFullTextSearchScores.set(true);
+ }
+ });
+ }
+ if (includeFullTextSearchScores.get()) break;
+ }
+ return new QueryResultColumns(columns, includeFullTextSearchScores.get());
+ }
+ return QueryResultColumns.empty();
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,651 @@
+/*
+ * 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 java.text.DecimalFormat;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.TimeUnit;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.QueryCommand;
+
+/**
+ * The resulting output of a query.
+ */
+@Immutable
+public interface QueryResults {
+
+ /**
+ * Get the execution context in which the query was evaluated.
+ *
+ * @return the execution context; never null
+ */
+ public ExecutionContext getContext();
+
+ /**
+ * Get the original query command.
+ *
+ * @return the query; never null
+ */
+ public QueryCommand getCommand();
+
+ /**
+ * Get the description of the columns contained in these results. These columns can be used to discover the indexes of the
+ * corresponding values from the arrays representing the {@link #getTuples() tuples}.
+ *
+ * @return the column descriptions; never null
+ */
+ public Columns getColumns();
+
+ /**
+ * Get a cursor that can be used to walk through the results.
+ *
+ * @return the cursor; never null, though possibly empty (meaning {@link Cursor#hasNext()} may return true)
+ */
+ public Cursor getCursor();
+
+ /**
+ * Get the actual tuples that contain the results. Each element in the list represents a tuple, and each tuple corresponds to
+ * the column definitions.
+ *
+ * @return the list of tuples; never null but possibly empty
+ */
+ public List<Object[]> getTuples();
+
+ /**
+ * Get the problems encountered during execution.
+ *
+ * @return the problems; never null but possibly empty
+ */
+ public Problems getProblems();
+
+ /**
+ * Return true if there is at least one error recorded in the {@link #getProblems() problems}.
+ *
+ * @return true if there is one or more errors associated with the query, or false otherwise
+ */
+ public boolean hasErrors();
+
+ /**
+ * Return true if there is at least one warning recorded in the {@link #getProblems() problems}.
+ *
+ * @return true if there is one or more warnings associated with the query, or false otherwise
+ */
+ public boolean hasWarnings();
+
+ /**
+ * Get the statistics that describe the time metrics for this query.
+ *
+ * @return the statistics; never null
+ */
+ public Statistics getStatistics();
+
+ /**
+ * An interface used to walk through the results.
+ */
+ public interface Cursor {
+ /**
+ * Determine whether this cursor can be moved from its current position to the next row.
+ *
+ * @return true if there is another row, or false otherwise
+ */
+ boolean hasNext();
+
+ /**
+ * Move this cursor position to the next row. obtained for
+ *
+ * @throws NoSuchElementException if there is no next row that the cursor can point to.
+ */
+ void next();
+
+ /**
+ * Get the 0-based index of the current row.
+ *
+ * @return the index of the current row; never negative
+ * @exception IllegalStateException if the <tt>next()</tt> method has not yet been called
+ */
+ int getRowIndex();
+
+ /**
+ * Get from the current row the location of the node from which the value in the given column was taken.
+ *
+ * @param columnNumber the column number (0-based)
+ * @return the location of the node; never null
+ * @throws IndexOutOfBoundsException if the column number is negative or larger than the number of columns
+ * @throws IllegalStateException if the <tt>next()</tt> method has not yet been called, or if there are no results
+ */
+ Location getLocation( int columnNumber );
+
+ /**
+ * Get from the current row the location of the node that was produced by the named selector.
+ *
+ * @param selectorName the name of the selector that resulted in a node appearing in the current row
+ * @return the location of the node; or null if there is no node corresponding to the named selector for the current row
+ * @throws NoSuchElementException if the selector name does not correspond to an available selector
+ * @throws IllegalStateException if the <tt>next()</tt> method has not yet been called, or if there are no results
+ */
+ Location getLocation( String selectorName );
+
+ /**
+ * Get from the current row the value in the given column.
+ *
+ * @param columnNumber the column number (0-based)
+ * @return the value; possibly null
+ * @throws IndexOutOfBoundsException if the column number is negative or larger than the number of columns
+ * @throws IllegalStateException if the <tt>next()</tt> method has not yet been called, or if there are no results
+ */
+ Object getValue( int columnNumber );
+
+ /**
+ * Get the value in the named column.
+ *
+ * @param columnName the name of the column
+ * @return the value; possibly null
+ * @throws NoSuchElementException if the column name does not correspond to an available column
+ * @throws IllegalStateException if the <tt>next()</tt> method has not yet been called, or if there are no results
+ */
+ Object getValue( String columnName );
+ }
+
+ /**
+ * Definition of the columns that are available in the results, which outline the structure of the
+ * {@link QueryResults#getTuples() tuples} in the results, and which can be used to access the individual values in each of
+ * the tuples.
+ */
+ public interface Columns {
+ /**
+ * Get the columns.
+ *
+ * @return the immutable list of columns, with size equal to {@link #getColumnCount()}; never null
+ */
+ public List<Column> getColumns();
+
+ /**
+ * Get the names of the columns.
+ *
+ * @return the immutable list of column names, with size equal to {@link #getColumnCount()}; never null
+ */
+ public List<String> getColumnNames();
+
+ /**
+ * Get the number of columns in each tuple.
+ *
+ * @return the number of columns; always positive
+ */
+ public int getColumnCount();
+
+ /**
+ * Get the number of {@link Location} objects in each tuple.
+ *
+ * @return the number of Location objects; always positive
+ */
+ public int getLocationCount();
+
+ /**
+ * Get the names of the selectors that are associated with these results. These results contain a single {@link Location}
+ * object for each of the selectors.
+ *
+ * @return the immutable list of selector names, with size equal to {@link #getLocationCount()}; never null
+ */
+ public List<String> getSelectorNames();
+
+ /**
+ * Get the size of the tuple arrays.
+ *
+ * @return the length of each tuple array
+ */
+ public int getTupleSize();
+
+ /**
+ * Get the names of the all of the tuple values.
+ *
+ * @return the immutable list of names
+ */
+ public List<String> getTupleValueNames();
+
+ /**
+ * Get the index of a tuple's correct Location object given the column index.
+ *
+ * @param columnIndex the column index
+ * @return the Location index that corresponds to the supplied column; never negative
+ * @throws IndexOutOfBoundsException if the column index is invalid
+ */
+ public int getLocationIndexForColumn( int columnIndex );
+
+ /**
+ * Get the index of a tuple's correct Location object given the column index.
+ *
+ * @param columnName the column name
+ * @return the Location index that corresponds to the supplied column; never negative
+ * @throws NoSuchElementException if the column name is invalid
+ */
+ public int getLocationIndexForColumn( String columnName );
+
+ /**
+ * Get the index of a tuple's correct Location object given the name of the selector used in the query.
+ *
+ * @param selectorName the selector name
+ * @return the Location index that corresponds to the supplied column; never negative
+ * @throws NoSuchElementException if the selector name is invalid
+ */
+ public int getLocationIndex( String selectorName );
+
+ /**
+ * Determine if these results contain values from the selector with the supplied name.
+ *
+ * @param selectorName the selector name
+ * @return true if the results have values from the supplied selector, or false otherwise
+ */
+ public boolean hasSelector( String selectorName );
+
+ /**
+ * Get the name of the property that corresponds to the supplied column in each tuple.
+ *
+ * @param columnIndex the column index
+ * @return the property name; never null
+ * @throws IndexOutOfBoundsException if the column index is invalid
+ */
+ public Name getPropertyNameForColumn( int columnIndex );
+
+ /**
+ * Get the name of the property that corresponds to the supplied column in each tuple.
+ *
+ * @param columnName the column name
+ * @return the property name; never null
+ * @throws NoSuchElementException if the column name is invalid or doesn't match an existing column
+ */
+ public Name getPropertyNameForColumn( String columnName );
+
+ /**
+ * Get the index of the column given the column name.
+ *
+ * @param columnName the column name
+ * @return the column index
+ * @throws NoSuchElementException if the column name is invalid or doesn't match an existing column
+ */
+ public int getColumnIndexForName( String columnName );
+
+ /**
+ * Get the index of the column given the name of the selector and the property name from where the column should be
+ * obtained.
+ *
+ * @param selectorName the selector name
+ * @param propertyName the name of the property
+ * @return the column index that corresponds to the supplied column; never negative
+ * @throws NoSuchElementException if the selector name or the property name are invalid
+ */
+ public int getColumnIndexForProperty( String selectorName,
+ Name propertyName );
+
+ /**
+ * Get the index of the tuple value containing the full-text search score for the node taken from the named selector.
+ *
+ * @param selectorName the selector name
+ * @return the index that corresponds to the {@link Double} full-text search score, or -1 if there is no full-text search
+ * score for the named selector
+ * @throws NoSuchElementException if the selector name is invalid
+ */
+ public int getFullTextSearchScoreIndexFor( String selectorName );
+
+ /**
+ * Determine whether these results include full-text search scores.
+ *
+ * @return true if the full-text search scores are included in the results, or false otherwise
+ */
+ public boolean hasFullTextSearchScores();
+
+ /**
+ * Determine whether this mapping includes all of the columns (and locations) in the supplied mapping.
+ *
+ * @param other the other mapping; may not be null
+ * @return true if all of the other mapping's columns and locations are included in this mapping, or false otherwise
+ */
+ public boolean includes( Columns other );
+
+ /**
+ * Determine whether this column and the other are <i>union-compatible</i> (that is, having the same columns).
+ *
+ * @param other the other mapping; may not be null
+ * @return true if this and the supplied columns definition are union-compatible, or false if they are not
+ */
+ public boolean isUnionCompatible( Columns other );
+
+ /**
+ * Obtain a new definition for the query results that can be used to reference the same tuples that use this columns
+ * definition, but that defines a subset of the columns in this definition. This is useful in a PROJECT operation, since
+ * that reduces the number of columns.
+ *
+ * @param columns the new columns, which must be a subset of the columns in this definition; may not be null
+ * @return the new columns definition; never null
+ */
+ public Columns subSelect( List<Column> columns );
+
+ /**
+ * Obtain a new definition for the query results that can be used to reference the same tuples that use this columns
+ * definition, but that defines a subset of the columns in this definition. This is useful in a PROJECT operation, since
+ * that reduces the number of columns.
+ *
+ * @param columns the new columns, which must be a subset of the columns in this definition; may not be null
+ * @return the new columns definition; never null
+ */
+ public Columns subSelect( Column... columns );
+ }
+
+ public static class Statistics implements Comparable<Statistics> {
+ protected static final Statistics EMPTY_STATISTICS = new Statistics();
+
+ private final long planningNanos;
+ private final long optimizationNanos;
+ private final long resultFormulationNanos;
+ private final long executionNanos;
+
+ private Statistics() {
+ this(0L, 0L, 0L, 0L);
+ }
+
+ protected Statistics( long planningNanos ) {
+ this(planningNanos, 0L, 0L, 0L);
+ }
+
+ protected Statistics( long planningNanos,
+ long optimizationNanos,
+ long resultFormulationNanos,
+ long executionNanos ) {
+ this.planningNanos = planningNanos;
+ this.optimizationNanos = optimizationNanos;
+ this.resultFormulationNanos = resultFormulationNanos;
+ this.executionNanos = executionNanos;
+ }
+
+ /**
+ * Get the time required to come up with the canonical plan.
+ *
+ * @param unit the time unit that should be used
+ * @return the time to plan, in the desired units
+ * @throws IllegalArgumentException if the unit is null
+ */
+ public long getPlanningTime( TimeUnit unit ) {
+ CheckArg.isNotNull(unit, "unit");
+ return unit.convert(planningNanos, TimeUnit.NANOSECONDS);
+ }
+
+ /**
+ * Get the time required to determine or select a (more) optimal plan.
+ *
+ * @param unit the time unit that should be used
+ * @return the time to determine an optimal plan, in the desired units
+ * @throws IllegalArgumentException if the unit is null
+ */
+ public long getOptimizationTime( TimeUnit unit ) {
+ CheckArg.isNotNull(unit, "unit");
+ return unit.convert(optimizationNanos, TimeUnit.NANOSECONDS);
+ }
+
+ /**
+ * Get the time required to formulate the structure of the results.
+ *
+ * @param unit the time unit that should be used
+ * @return the time to formulate the results, in the desired units
+ * @throws IllegalArgumentException if the unit is null
+ */
+ public long getResultFormulationTime( TimeUnit unit ) {
+ CheckArg.isNotNull(unit, "unit");
+ return unit.convert(resultFormulationNanos, TimeUnit.NANOSECONDS);
+ }
+
+ /**
+ * Get the time required to execute the query.
+ *
+ * @param unit the time unit that should be used
+ * @return the time to execute the query, in the desired units
+ * @throws IllegalArgumentException if the unit is null
+ */
+ public long getExecutionTime( TimeUnit unit ) {
+ return unit.convert(executionNanos, TimeUnit.NANOSECONDS);
+ }
+
+ /**
+ * Get the time required to execute the query.
+ *
+ * @param unit the time unit that should be used
+ * @return the time to execute the query, in the desired units
+ * @throws IllegalArgumentException if the unit is null
+ */
+ public long getTotalTime( TimeUnit unit ) {
+ return unit.convert(totalTime(), TimeUnit.NANOSECONDS);
+ }
+
+ protected long totalTime() {
+ return planningNanos + optimizationNanos + resultFormulationNanos + executionNanos;
+ }
+
+ /**
+ * Create a new statistics object that has the supplied planning time.
+ *
+ * @param planningNanos the number of nanoseconds required by planning
+ * @return the new statistics object; never null
+ * @throws IllegalArgumentException if the time value is negative
+ */
+ public Statistics withPlanningTime( long planningNanos ) {
+ CheckArg.isNonNegative(planningNanos, "planningNanos");
+ return new Statistics(planningNanos, optimizationNanos, resultFormulationNanos, executionNanos);
+ }
+
+ /**
+ * Create a new statistics object that has the supplied optimization time.
+ *
+ * @param optimizationNanos the number of nanoseconds required by optimization
+ * @return the new statistics object; never null
+ * @throws IllegalArgumentException if the time value is negative
+ */
+ public Statistics withOptimizationTime( long optimizationNanos ) {
+ CheckArg.isNonNegative(optimizationNanos, "optimizationNanos");
+ return new Statistics(planningNanos, optimizationNanos, resultFormulationNanos, executionNanos);
+ }
+
+ /**
+ * Create a new statistics object that has the supplied result formulation time.
+ *
+ * @param resultFormulationNanos the number of nanoseconds required by result formulation
+ * @return the new statistics object; never null
+ * @throws IllegalArgumentException if the time value is negative
+ */
+ public Statistics withResultsFormulationTime( long resultFormulationNanos ) {
+ CheckArg.isNonNegative(resultFormulationNanos, "resultFormulationNanos");
+ return new Statistics(planningNanos, optimizationNanos, resultFormulationNanos, executionNanos);
+ }
+
+ /**
+ * Create a new statistics object that has the supplied execution time.
+ *
+ * @param executionNanos the number of nanoseconds required to execute the query
+ * @return the new statistics object; never null
+ * @throws IllegalArgumentException if the time value is negative
+ */
+ public Statistics withExecutionTime( long executionNanos ) {
+ CheckArg.isNonNegative(executionNanos, "executionNanos");
+ return new Statistics(planningNanos, optimizationNanos, resultFormulationNanos, executionNanos);
+ }
+
+ /**
+ * Create a new statistics object that has the supplied planning time.
+ *
+ * @param planning the time required to plan the query
+ * @param unit the time unit
+ * @return the new statistics object; never null
+ * @throws IllegalArgumentException if the unit is null or if the time value is negative
+ */
+ public Statistics withPlanningTime( long planning,
+ TimeUnit unit ) {
+ CheckArg.isNonNegative(planning, "planning");
+ CheckArg.isNotNull(unit, "unit");
+ long planningNanos = TimeUnit.NANOSECONDS.convert(planning, unit);
+ return new Statistics(planningNanos, optimizationNanos, resultFormulationNanos, executionNanos);
+ }
+
+ /**
+ * Create a new statistics object that has the supplied optimization time.
+ *
+ * @param optimization the time required by optimization
+ * @param unit the time unit
+ * @return the new statistics object; never null
+ * @throws IllegalArgumentException if the unit is null or if the time value is negative
+ */
+ public Statistics withOptimizationTime( long optimization,
+ TimeUnit unit ) {
+ CheckArg.isNonNegative(optimization, "optimization");
+ CheckArg.isNotNull(unit, "unit");
+ long optimizationNanos = TimeUnit.NANOSECONDS.convert(optimization, unit);
+ return new Statistics(planningNanos, optimizationNanos, resultFormulationNanos, executionNanos);
+ }
+
+ /**
+ * Create a new statistics object that has the supplied result formulation time.
+ *
+ * @param resultFormulation the time required to formulate the results
+ * @param unit the time unit
+ * @return the new statistics object; never null
+ * @throws IllegalArgumentException if the unit is null or if the time value is negative
+ */
+ public Statistics withResultsFormulationTime( long resultFormulation,
+ TimeUnit unit ) {
+ CheckArg.isNonNegative(resultFormulation, "resultFormulation");
+ CheckArg.isNotNull(unit, "unit");
+ long resultFormulationNanos = TimeUnit.NANOSECONDS.convert(resultFormulation, unit);
+ return new Statistics(planningNanos, optimizationNanos, resultFormulationNanos, executionNanos);
+ }
+
+ /**
+ * Create a new statistics object that has the supplied execution time.
+ *
+ * @param execution the time required to execute the query
+ * @param unit the time unit
+ * @return the new statistics object; never null
+ * @throws IllegalArgumentException if the unit is null or if the time value is negative
+ */
+ public Statistics withExecutionTime( long execution,
+ TimeUnit unit ) {
+ CheckArg.isNonNegative(execution, "execution");
+ CheckArg.isNotNull(unit, "unit");
+ long executionNanos = TimeUnit.NANOSECONDS.convert(execution, unit);
+ return new Statistics(planningNanos, optimizationNanos, resultFormulationNanos, executionNanos);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo( Statistics that ) {
+ if (that == this) return 0;
+ long diff = this.totalTime() - that.totalTime();
+ if (diff < 0) return -1;
+ if (diff > 0) return 1;
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ readable(totalTime(), sb);
+ boolean first = false;
+ if (planningNanos != 0L) {
+ sb.append(" (plan=");
+ readable(planningNanos, sb);
+ first = false;
+ }
+ if (optimizationNanos != 0L) {
+ if (first) {
+ first = false;
+ sb.append(" (");
+ } else {
+ sb.append(" ,");
+ }
+ sb.append("opt=");
+ readable(optimizationNanos, sb);
+ }
+ if (resultFormulationNanos != 0L) {
+ if (first) {
+ first = false;
+ sb.append(" (");
+ } else {
+ sb.append(" ,");
+ }
+ sb.append("res=");
+ readable(resultFormulationNanos, sb);
+ }
+ if (executionNanos != 0L) {
+ if (first) {
+ first = false;
+ sb.append(" (");
+ } else {
+ sb.append(" ,");
+ }
+ sb.append("exec=");
+ readable(executionNanos, sb);
+ }
+ if (!first) sb.append(')');
+ return sb.toString();
+ }
+
+ protected void readable( long nanos,
+ StringBuilder sb ) {
+ // 3210987654321
+ // XXXXXXXXXXXXX nanos
+ // XXXXXXXXXX micros
+ // XXXXXXX millis
+ // XXXX seconds
+ if (nanos < 1000) {
+ sb.append(nanos).append(" ns");
+ } else if (nanos < 1000000) {
+ double value = nanos / 1000d;
+ sb.append(FORMATTER.get().format(value)).append(" usec");
+ } else if (nanos < 1000000000) {
+ double value = nanos / 1000000d;
+ sb.append(FORMATTER.get().format(value)).append(" ms");
+ } else {
+ double value = nanos / 1000000000d;
+ sb.append(FORMATTER.get().format(value)).append(" sec");
+ }
+ }
+ }
+
+ static ThreadLocal<DecimalFormat> FORMATTER = new ThreadLocal<DecimalFormat>() {
+ @Override
+ protected synchronized DecimalFormat initialValue() {
+ return new DecimalFormat("###,###,##0.0##");
+ }
+ };
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryResults.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,83 @@
+/*
+ * 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.ObjectUtil;
+
+/**
+ * A selector that represents a source that returns all nodes.
+ */
+@Immutable
+public class AllNodes extends Selector {
+
+ public static final SelectorName ALL_NODES_NAME = new SelectorName("__ALLNODES__");
+
+ public AllNodes() {
+ super(ALL_NODES_NAME);
+ }
+
+ /**
+ * Create a selector with the supplied alias.
+ *
+ * @param alias the alias for this selector; may be null
+ */
+ public AllNodes( SelectorName alias ) {
+ super(ALL_NODES_NAME, alias);
+ }
+
+ /**
+ * {@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 AllNodes) {
+ AllNodes that = (AllNodes)obj;
+ return ObjectUtil.isEqualWithNulls(this.getAlias(), that.getAlias());
+ }
+ 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/AllNodes.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,93 @@
+/*
+ * 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 constraint that evaluates to true when <i>both</i> of the other constraints evaluate to true.
+ */
+@Immutable
+public class And extends Constraint {
+
+ private final Constraint left;
+ private final Constraint right;
+
+ public And( Constraint left,
+ Constraint right ) {
+ CheckArg.isNotNull(left, "left");
+ CheckArg.isNotNull(right, "right");
+ this.left = left;
+ this.right = right;
+ }
+
+ /**
+ * @return left
+ */
+ public final Constraint getLeft() {
+ return left;
+ }
+
+ /**
+ * @return right
+ */
+ public final Constraint getRight() {
+ return right;
+ }
+
+ /**
+ * {@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 And) {
+ And that = (And)obj;
+ return left.equals(that.left) && right.equals(that.right);
+ }
+ 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/And.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/BindVariableName.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,82 @@
+/*
+ * 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 value bound to a variable name used in a {@link Comparison} constraint.
+ */
+@Immutable
+public class BindVariableName extends StaticOperand {
+
+ private final String variableName;
+
+ public BindVariableName( String variableName ) {
+ CheckArg.isNotNull(variableName, "variableName");
+ this.variableName = variableName;
+ }
+
+ /**
+ * @return variableName
+ */
+ public final String getVariableName() {
+ return variableName;
+ }
+
+ /**
+ * {@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 BindVariableName) {
+ BindVariableName that = (BindVariableName)obj;
+ return this.variableName.equals(that.variableName);
+ }
+ 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/BindVariableName.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,102 @@
+/*
+ * 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;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * A constraint requiring that the selected node is a child of the node reachable by the supplied absolute path
+ */
+@Immutable
+public class ChildNode extends Constraint {
+ private final SelectorName selectorName;
+ private final Path parentPath;
+
+ /**
+ * Create a constraint requiring that the node identified by the selector is a child of the node reachable by the supplied
+ * absolute path.
+ *
+ * @param selectorName the name of the selector
+ * @param parentPath the absolute path to the parent
+ */
+ public ChildNode( SelectorName selectorName,
+ Path parentPath ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ CheckArg.isNotNull(parentPath, "parentPath");
+ this.selectorName = selectorName;
+ this.parentPath = parentPath;
+ }
+
+ /**
+ * @return selectorName
+ */
+ public final SelectorName getSelectorName() {
+ return selectorName;
+ }
+
+ /**
+ * @return parentPath
+ */
+ public final Path getParentPath() {
+ return parentPath;
+ }
+
+ /**
+ * {@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 ChildNode) {
+ ChildNode that = (ChildNode)obj;
+ if (!this.selectorName.equals(that.selectorName)) return false;
+ if (!this.parentPath.equals(that.parentPath)) 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/ChildNode.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,101 @@
+/*
+ * 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 join condition that evaluates to true only when the named child node is indeed a child of the named parent node.
+ */
+@Immutable
+public class ChildNodeJoinCondition extends JoinCondition {
+ private final SelectorName childSelectorName;
+ private final SelectorName parentSelectorName;
+
+ /**
+ * Create a join condition that determines whether the node identified by the child selector is a child of the node identified
+ * by the parent selector.
+ *
+ * @param parentSelectorName the first selector
+ * @param childSelectorName the second selector
+ */
+ public ChildNodeJoinCondition( SelectorName parentSelectorName,
+ SelectorName childSelectorName ) {
+ CheckArg.isNotNull(childSelectorName, "childSelectorName");
+ CheckArg.isNotNull(parentSelectorName, "parentSelectorName");
+ this.childSelectorName = childSelectorName;
+ this.parentSelectorName = parentSelectorName;
+ }
+
+ /**
+ * @return childSelectorName
+ */
+ public final SelectorName getChildSelectorName() {
+ return childSelectorName;
+ }
+
+ /**
+ * @return parentSelectorName
+ */
+ public final SelectorName getParentSelectorName() {
+ return parentSelectorName;
+ }
+
+ /**
+ * {@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 ChildNodeJoinCondition) {
+ ChildNodeJoinCondition that = (ChildNodeJoinCondition)obj;
+ if (!this.childSelectorName.equals(that.childSelectorName)) return false;
+ if (!this.parentSelectorName.equals(that.parentSelectorName)) 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/ChildNodeJoinCondition.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,128 @@
+/*
+ * 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;
+import org.jboss.dna.common.util.ObjectUtil;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ *
+ */
+@Immutable
+public class Column implements LanguageObject {
+
+ private final SelectorName selectorName;
+ private final Name propertyName;
+ private final String columnName;
+
+ /**
+ * Include a column for each of the single-valued, accessible properties on the node identified by the selector.
+ *
+ * @param selectorName the selector name
+ */
+ public Column( SelectorName selectorName ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ this.selectorName = selectorName;
+ this.propertyName = null;
+ this.columnName = null;
+ }
+
+ /**
+ * A column with the given name representing the named property on the node identified by the selector.
+ *
+ * @param selectorName the selector name
+ * @param propertyName the name of the property
+ * @param columnName the name of the column
+ */
+ public Column( SelectorName selectorName,
+ Name propertyName,
+ String columnName ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ CheckArg.isNotNull(propertyName, "propertyName");
+ CheckArg.isNotNull(columnName, "columnName");
+ this.selectorName = selectorName;
+ this.propertyName = propertyName;
+ this.columnName = columnName;
+ }
+
+ /**
+ * @return selectorName
+ */
+ public final SelectorName getSelectorName() {
+ return selectorName;
+ }
+
+ /**
+ * @return propertyName
+ */
+ public final Name getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * @return columnName
+ */
+ public final String getColumnName() {
+ return columnName;
+ }
+
+ /**
+ * {@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 Column) {
+ Column that = (Column)obj;
+ if (!this.selectorName.equals(that.selectorName)) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.propertyName, that.propertyName)) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.columnName, that.columnName)) 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/Column.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Command.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Command.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Command.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+/**
+ * Represents the abstract base class for all top-level language objects that are the root of a language object tree.
+ */
+public abstract class Command implements LanguageObject {
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Command.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.model;
+
+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.
+ */
+@Immutable
+public class Comparison extends Constraint {
+
+ private final DynamicOperand operand1;
+ private final StaticOperand operand2;
+ private final Operator operator;
+
+ public Comparison( DynamicOperand operand1,
+ Operator operator,
+ StaticOperand operand2 ) {
+ CheckArg.isNotNull(operand1, "operand1");
+ CheckArg.isNotNull(operator, "operator");
+ CheckArg.isNotNull(operand2, "operand2");
+ this.operand1 = operand1;
+ this.operand2 = operand2;
+ this.operator = operator;
+ }
+
+ /**
+ * @return operand1
+ */
+ public final DynamicOperand getOperand1() {
+ return operand1;
+ }
+
+ /**
+ * @return operand2
+ */
+ public final StaticOperand getOperand2() {
+ return operand2;
+ }
+
+ /**
+ * @return operator
+ */
+ public final Operator getOperator() {
+ return operator;
+ }
+
+ /**
+ * {@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 Comparison) {
+ Comparison that = (Comparison)obj;
+ if (!this.operator.equals(that.operator)) return false;
+ if (!this.operand1.equals(that.operand1)) return false;
+ if (!this.operand2.equals(that.operand2)) 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/Comparison.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Constraint.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Constraint.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Constraint.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.
+ */
+package org.jboss.dna.graph.query.model;
+
+import net.jcip.annotations.Immutable;
+
+/**
+ *
+ */
+@Immutable
+public abstract class Constraint implements LanguageObject {
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Constraint.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,102 @@
+/*
+ * 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;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * A constraint requiring that the selected node is a descendant of the node reachable by the supplied absolute path
+ */
+@Immutable
+public class DescendantNode extends Constraint {
+ private final SelectorName selectorName;
+ private final Path ancestorPath;
+
+ /**
+ * Create a constraint requiring that the node identified by the selector is a descendant of the node reachable by the
+ * supplied absolute path.
+ *
+ * @param selectorName the name of the selector
+ * @param ancestorPath the absolute path to the ancestor
+ */
+ public DescendantNode( SelectorName selectorName,
+ Path ancestorPath ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ CheckArg.isNotNull(ancestorPath, "ancestorPath");
+ this.selectorName = selectorName;
+ this.ancestorPath = ancestorPath;
+ }
+
+ /**
+ * @return selectorName
+ */
+ public final SelectorName getSelectorName() {
+ return selectorName;
+ }
+
+ /**
+ * @return ancestorPath
+ */
+ public final Path getAncestorPath() {
+ return ancestorPath;
+ }
+
+ /**
+ * {@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 DescendantNode) {
+ DescendantNode that = (DescendantNode)obj;
+ if (!this.selectorName.equals(that.selectorName)) return false;
+ if (!this.ancestorPath.equals(that.ancestorPath)) 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/DescendantNode.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,101 @@
+/*
+ * 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 join condition that evaluates to true only when the named node is a descendant of another named node.
+ */
+@Immutable
+public class DescendantNodeJoinCondition extends JoinCondition {
+ private final SelectorName descendantSelectorName;
+ private final SelectorName ancestorSelectorName;
+
+ /**
+ * Create a join condition that determines whether the node identified by the descendant selector is indeed a descendant of
+ * the node identified by the ancestor selector.
+ *
+ * @param ancestorSelectorName the name of the ancestor selector
+ * @param descendantSelectorName the name of the descendant selector
+ */
+ public DescendantNodeJoinCondition( SelectorName ancestorSelectorName,
+ SelectorName descendantSelectorName ) {
+ CheckArg.isNotNull(descendantSelectorName, "descendantSelectorName");
+ CheckArg.isNotNull(ancestorSelectorName, "ancestorSelectorName");
+ this.descendantSelectorName = descendantSelectorName;
+ this.ancestorSelectorName = ancestorSelectorName;
+ }
+
+ /**
+ * @return descendantSelectorName
+ */
+ public final SelectorName getDescendantSelectorName() {
+ return descendantSelectorName;
+ }
+
+ /**
+ * @return ancestorSelectorName
+ */
+ public final SelectorName getAncestorSelectorName() {
+ return ancestorSelectorName;
+ }
+
+ /**
+ * {@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 DescendantNodeJoinCondition) {
+ DescendantNodeJoinCondition that = (DescendantNodeJoinCondition)obj;
+ if (!this.descendantSelectorName.equals(that.descendantSelectorName)) return false;
+ if (!this.ancestorSelectorName.equals(that.ancestorSelectorName)) 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/DescendantNodeJoinCondition.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DynamicOperand.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DynamicOperand.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DynamicOperand.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.
+ */
+package org.jboss.dna.graph.query.model;
+
+import net.jcip.annotations.Immutable;
+
+/**
+ * A dynamic operand used in a {@link Comparison} constraint.
+ */
+@Immutable
+public abstract class DynamicOperand implements LanguageObject {
+
+ /**
+ * Get the selector symbol to which this operand applies.
+ *
+ * @return the selector name; never null
+ */
+ public abstract SelectorName getSelectorName();
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DynamicOperand.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,130 @@
+/*
+ * 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;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * A join condition that tests whether a property on a node is equal to a property on another node. A node-tuple satisfies the
+ * constraint only if:
+ * <ul>
+ * <li>the {@code selector1Name} node has a property named {@code property1Name}, and</li>
+ * <li>the {@code selector2Name} node has a property named {@code property2Name}, and</li>
+ * <li>the value of property {@code property1Name} is equal to the value of property {@code property2Name}</li>
+ * </ul>
+ */
+@Immutable
+public class EquiJoinCondition extends JoinCondition {
+ private final SelectorName selector1Name;
+ private final Name property1Name;
+ private final SelectorName selector2Name;
+ private final Name property2Name;
+
+ public EquiJoinCondition( SelectorName selector1Name,
+ Name property1Name,
+ SelectorName selector2Name,
+ Name property2Name ) {
+ CheckArg.isNotNull(selector1Name, "selector1Name");
+ CheckArg.isNotNull(property1Name, "property1Name");
+ CheckArg.isNotNull(selector2Name, "selector2Name");
+ CheckArg.isNotNull(property2Name, "property2Name");
+ this.selector1Name = selector1Name;
+ this.property1Name = property1Name;
+ this.selector2Name = selector2Name;
+ this.property2Name = property2Name;
+ }
+
+ public EquiJoinCondition( Column column1,
+ Column column2 ) {
+ this(column1.getSelectorName(), column1.getPropertyName(), column2.getSelectorName(), column2.getPropertyName());
+ }
+
+ /**
+ * @return selector1Name
+ */
+ public final SelectorName getSelector1Name() {
+ return selector1Name;
+ }
+
+ /**
+ * @return property1Name
+ */
+ public final Name getProperty1Name() {
+ return property1Name;
+ }
+
+ /**
+ * @return selector2Name
+ */
+ public final SelectorName getSelector2Name() {
+ return selector2Name;
+ }
+
+ /**
+ * @return property2Name
+ */
+ public final Name getProperty2Name() {
+ return property2Name;
+ }
+
+ /**
+ * {@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 EquiJoinCondition) {
+ EquiJoinCondition that = (EquiJoinCondition)obj;
+ if (!this.selector1Name.equals(that.selector1Name)) return false;
+ if (!this.selector2Name.equals(that.selector2Name)) return false;
+ if (!this.property1Name.equals(that.property1Name)) return false;
+ if (!this.property2Name.equals(that.property2Name)) 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/EquiJoinCondition.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,388 @@
+/*
+ * 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.Iterator;
+import java.util.List;
+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.ObjectUtil;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.parse.FullTextSearchParser;
+
+/**
+ * A constraint that evaluates to true only when a full-text search applied to the search scope results in positive findings. If a
+ * property name is supplied, then the search is limited to the value(s) of the named property on the node(s) in the search scope.
+ */
+@Immutable
+public class FullTextSearch extends Constraint {
+ private final SelectorName selectorName;
+ private final Name propertyName;
+ private final String fullTextSearchExpression;
+ private Term term;
+
+ /**
+ * Create a constraint defining a full-text search against the property values on node within the search scope.
+ *
+ * @param selectorName the name of the node selector defining the search scope
+ * @param propertyName the name of the property to be searched; may be null if all property values are to be searched
+ * @param fullTextSearchExpression the search expression
+ * @param term the term representation, if it is known; may be null
+ */
+ public FullTextSearch( SelectorName selectorName,
+ Name propertyName,
+ String fullTextSearchExpression,
+ Term term ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ CheckArg.isNotEmpty(fullTextSearchExpression, "fullTextSearchExpression");
+ this.selectorName = selectorName;
+ this.propertyName = propertyName;
+ this.fullTextSearchExpression = fullTextSearchExpression;
+ }
+
+ /**
+ * Create a constraint defining a full-text search against the property values on node within the search scope.
+ *
+ * @param selectorName the name of the node selector defining the search scope
+ * @param propertyName the name of the property to be searched; may be null if all property values are to be searched
+ * @param fullTextSearchExpression the search expression
+ */
+ public FullTextSearch( SelectorName selectorName,
+ Name propertyName,
+ String fullTextSearchExpression ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ CheckArg.isNotEmpty(fullTextSearchExpression, "fullTextSearchExpression");
+ this.selectorName = selectorName;
+ this.propertyName = propertyName;
+ this.fullTextSearchExpression = fullTextSearchExpression;
+ this.term = null;
+ }
+
+ /**
+ * Create a constraint defining a full-text search against the node within the search scope.
+ *
+ * @param selectorName the name of the node selector defining the search scope
+ * @param fullTextSearchExpression the search expression
+ */
+ public FullTextSearch( SelectorName selectorName,
+ String fullTextSearchExpression ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ CheckArg.isNotEmpty(fullTextSearchExpression, "fullTextSearchExpression");
+ this.selectorName = selectorName;
+ this.propertyName = null;
+ this.fullTextSearchExpression = fullTextSearchExpression;
+ }
+
+ /**
+ * @return selectorName
+ */
+ public final SelectorName getSelectorName() {
+ return selectorName;
+ }
+
+ /**
+ * @return propertyName
+ */
+ public final Name getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * @return fullTextSearchExpression
+ */
+ public final String getFullTextSearchExpression() {
+ return fullTextSearchExpression;
+ }
+
+ /**
+ * Get the formal {@link Term} representation of the expression.
+ *
+ * @return the term representing this search; never null
+ * @throws ParsingException if there is an error producing the term representation
+ */
+ public Term getTerm() {
+ // Idempotent, so okay to not lock/synchronize ...
+ if (term == null) {
+ term = new FullTextSearchParser().parse(fullTextSearchExpression);
+ }
+ return term;
+ }
+
+ /**
+ * {@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 FullTextSearch) {
+ FullTextSearch that = (FullTextSearch)obj;
+ 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;
+ 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);
+ }
+
+ /**
+ * The general notion of a term that makes up a full-text search.
+ */
+ public static interface Term {
+ }
+
+ /**
+ * A {@link Term} that represents a single search term. The term may be comprised of multiple words.
+ */
+ public static class SimpleTerm implements Term {
+ private final String value;
+ private final boolean excluded;
+ private final boolean quoted;
+
+ /**
+ * Create a simple term with the value and whether the term is excluded or included.
+ *
+ * @param value the value that makes up the term
+ * @param excluded true if the term should not appear, or false if the term is required
+ */
+ public SimpleTerm( String value,
+ boolean excluded ) {
+ assert value != null;
+ assert value.trim().length() > 0;
+ this.value = value;
+ this.excluded = excluded;
+ this.quoted = this.value.indexOf(' ') != -1;
+ }
+
+ /**
+ * Get the value of this term. Note that this is the actual value that is to be searched for, and will not include the
+ * {@link #isQuotingRequired() quotes}.
+ *
+ * @return the value; never null
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Get whether or not this term is expected to appear in the results.
+ *
+ * @return true if the term is expected to not appear, or false if the term is expected to appear
+ */
+ public boolean isExcluded() {
+ return excluded;
+ }
+
+ /**
+ * Get whether this term needs to be quoted because it consists of multiple words.
+ *
+ * @return true if the term needs to be quoted, or false otherwise
+ */
+ public boolean isQuotingRequired() {
+ return quoted;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof SimpleTerm) {
+ SimpleTerm that = (SimpleTerm)obj;
+ if (this.isExcluded() != that.isExcluded()) return false;
+ return this.getValue().equals(that.getValue());
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ String value = quoted ? "\"" + this.value + "\"" : this.value;
+ return excluded ? "-" + value : value;
+ }
+ }
+
+ /**
+ * A list of {@link Term}s.
+ */
+ public static abstract class CompoundTerm implements Term, Iterable<Term> {
+ private final List<Term> terms;
+
+ /**
+ * Create a compound term of the supplied terms.
+ *
+ * @param terms the terms; may not be null or empty
+ */
+ protected CompoundTerm( List<Term> terms ) {
+ this.terms = terms;
+ }
+
+ /**
+ * Get the terms that make up this compound term.
+ *
+ * @return the terms in the disjunction; never null and never empty
+ */
+ public List<Term> getTerms() {
+ return terms;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Iterable#iterator()
+ */
+ public Iterator<Term> iterator() {
+ return terms.iterator();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return terms.hashCode();
+ }
+
+ /**
+ * {@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)) {
+ CompoundTerm that = (CompoundTerm)obj;
+ return this.getTerms().equals(that.getTerms());
+ }
+ return false;
+ }
+
+ protected String toString( String delimiter ) {
+ if (terms.size() == 1) return terms.iterator().next().toString();
+ StringBuilder sb = new StringBuilder();
+ sb.append("( ");
+ boolean first = true;
+ for (Term term : terms) {
+ if (first) first = false;
+ else sb.append(' ').append(delimiter).append(' ');
+ sb.append(term);
+ }
+ sb.append(" )");
+ return sb.toString();
+ }
+ }
+
+ /**
+ * A set of {@link Term}s that are ANDed together.
+ */
+ public static class Disjunction extends CompoundTerm {
+
+ /**
+ * Create a disjunction of the supplied terms.
+ *
+ * @param terms the terms to be ORed together; may not be null or empty
+ */
+ public Disjunction( List<Term> terms ) {
+ super(terms);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return toString("OR");
+ }
+ }
+
+ /**
+ * A set of {@link Term}s that are ANDed together.
+ */
+ public static class Conjunction extends CompoundTerm {
+
+ /**
+ * Create a conjunction of the supplied terms.
+ *
+ * @param terms the terms to be ANDed together; may not be null or empty
+ */
+ public Conjunction( List<Term> terms ) {
+ super(terms);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return toString("AND");
+ }
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearchScore.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,91 @@
+/*
+ * 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 full-text search score of a node given by a selector, used in a {@link Comparison}
+ * constraint.
+ */
+@Immutable
+public class FullTextSearchScore extends DynamicOperand {
+ private final SelectorName selectorName;
+
+ /**
+ * Create a dynamic operand that evaluates to the full-text search score of the node identified by the selector.
+ *
+ * @param selectorName the name of the selector
+ */
+ public FullTextSearchScore( SelectorName selectorName ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ this.selectorName = selectorName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.DynamicOperand#getSelectorName()
+ */
+ @Override
+ public 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 FullTextSearchScore) {
+ FullTextSearchScore that = (FullTextSearchScore)obj;
+ if (!this.selectorName.equals(that.selectorName)) 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/FullTextSearchScore.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,128 @@
+/*
+ * 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;
+
+/**
+ *
+ */
+@Immutable
+public class Join extends Source {
+
+ private final Source left;
+ private final Source right;
+ private final JoinType type;
+ private final JoinCondition joinCondition;
+
+ /**
+ * Create a join of the left and right sources, using the supplied join condition. The outputs of the left and right sources
+ * are expected to be equivalent.
+ *
+ * @param left the left source being joined
+ * @param type the type of join
+ * @param right the right source being joined
+ * @param joinCondition the join condition
+ */
+ public Join( Source left,
+ JoinType type,
+ Source right,
+ JoinCondition joinCondition ) {
+ CheckArg.isNotNull(left, "left");
+ CheckArg.isNotNull(right, "right");
+ CheckArg.isNotNull(type, "type");
+ CheckArg.isNotNull(joinCondition, "joinCondition");
+ this.left = left;
+ this.right = right;
+ this.type = type;
+ this.joinCondition = joinCondition;
+ }
+
+ /**
+ * @return left
+ */
+ public final Source getLeft() {
+ return left;
+ }
+
+ /**
+ * @return right
+ */
+ public final Source getRight() {
+ return right;
+ }
+
+ /**
+ * @return type
+ */
+ public final JoinType getType() {
+ return type;
+ }
+
+ /**
+ * @return joinCondition
+ */
+ public final JoinCondition getJoinCondition() {
+ return joinCondition;
+ }
+
+ /**
+ * {@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 Join) {
+ Join that = (Join)obj;
+ if (!this.type.equals(that.type)) return false;
+ if (!this.left.equals(that.left)) return false;
+ if (!this.right.equals(that.right)) return false;
+ if (!this.joinCondition.equals(that.joinCondition)) 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/Join.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinCondition.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinCondition.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.
+ */
+package org.jboss.dna.graph.query.model;
+
+import net.jcip.annotations.Immutable;
+
+/**
+ * The condition used for a join between two sources.
+ */
+@Immutable
+public abstract class JoinCondition implements LanguageObject {
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinCondition.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinType.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinType.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinType.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,100 @@
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+
+public enum JoinType implements Readable {
+ INNER("INNER JOIN"),
+ LEFT_OUTER("LEFT OUTER JOIN"),
+ RIGHT_OUTER("RIGHT OUTER JOIN"),
+ FULL_OUTER("FULL OUTER JOIN"),
+ CROSS("CROSS JOIN");
+
+ private static final Map<String, JoinType> TYPE_BY_SYMBOL;
+ static {
+ Map<String, JoinType> typesBySymbol = new HashMap<String, JoinType>();
+ for (JoinType type : JoinType.values()) {
+ typesBySymbol.put(type.getSymbol().toUpperCase(), type);
+ }
+ TYPE_BY_SYMBOL = Collections.unmodifiableMap(typesBySymbol);
+ }
+
+ private final String symbol;
+
+ private JoinType( String symbol ) {
+ this.symbol = symbol;
+ }
+
+ /**
+ * @return symbol
+ */
+ public String getSymbol() {
+ return symbol;
+ }
+
+ /**
+ * Check if this join type is an outer join.
+ *
+ * @return true if left/right/full outer, or false otherwise
+ */
+ public boolean isOuter() {
+ return this.equals(LEFT_OUTER) || this.equals(FULL_OUTER) || this.equals(RIGHT_OUTER);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString() {
+ return symbol;
+ }
+
+ /**
+ * Attempt to find the JoinType given a symbol. The matching is done independent of case.
+ *
+ * @param symbol the symbol
+ * @return the JoinType having the supplied symbol, or null if there is no JoinType with the supplied symbol
+ * @throws IllegalArgumentException if the symbol is null
+ */
+ public static JoinType forSymbol( String symbol ) {
+ CheckArg.isNotNull(symbol, "symbol");
+ return TYPE_BY_SYMBOL.get(symbol.toUpperCase());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Readable#getString(org.jboss.dna.graph.ExecutionContext)
+ */
+ public String getString( ExecutionContext context ) {
+ return getSymbol();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/JoinType.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LanguageObject.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LanguageObject.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LanguageObject.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/**
+ * A common interface for all query language objects.
+ */
+public interface LanguageObject extends Visitable {
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LanguageObject.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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 net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A dynamic operand that evaluates to the length of the supplied propety values, used in a {@link Comparison} constraint.
+ */
+@Immutable
+public class Length extends DynamicOperand {
+ private final PropertyValue propertyValue;
+
+ /**
+ * Create a dynamic operand that evaluates to the length of the supplied property values.
+ *
+ * @param propertyValue the property value operand
+ */
+ public Length( PropertyValue propertyValue ) {
+ CheckArg.isNotNull(propertyValue, "propertyValue");
+ this.propertyValue = propertyValue;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.DynamicOperand#getSelectorName()
+ */
+ @Override
+ public SelectorName getSelectorName() {
+ return propertyValue.getSelectorName();
+ }
+
+ /**
+ * @return propertyValue
+ */
+ public final PropertyValue getPropertyValue() {
+ return propertyValue;
+ }
+
+ /**
+ * {@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 Length) {
+ Length that = (Length)obj;
+ return this.propertyValue.equals(that.propertyValue);
+ }
+ 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/Length.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,139 @@
+/*
+ * 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;
+
+/**
+ *
+ */
+@Immutable
+public class Limit implements LanguageObject {
+
+ public static final Limit NONE = new Limit(Integer.MAX_VALUE, 0);
+
+ private final int offset;
+ private final int rowLimit;
+
+ public Limit( int rowLimit ) {
+ CheckArg.isPositive(rowLimit, "rowLimit");
+ this.rowLimit = rowLimit;
+ this.offset = 0;
+ }
+
+ public Limit( int rowLimit,
+ int offset ) {
+ CheckArg.isPositive(rowLimit, "rowLimit");
+ CheckArg.isNonNegative(offset, "offset");
+ this.rowLimit = rowLimit;
+ this.offset = offset;
+ }
+
+ /**
+ * @return offset
+ */
+ public final int getOffset() {
+ return offset;
+ }
+
+ /**
+ * Get the maximum number of rows that are to be returned.
+ *
+ * @return the maximum number of rows; always positive, or equal to {@link Integer#MAX_VALUE} if there is no limit
+ */
+ public final int getRowLimit() {
+ return rowLimit;
+ }
+
+ /**
+ * Determine whether this limit clause is necessary.
+ *
+ * @return true if the number of rows is not limited and there is no offset, or false otherwise
+ */
+ public final boolean isUnlimited() {
+ return rowLimit == Integer.MAX_VALUE && offset == 0;
+ }
+
+ /**
+ * Determine whether this limit clause defines a maximum limit
+ *
+ * @return true if the number of rows are limited, or false if there is no limit to the number of rows
+ */
+ public final boolean hasRowLimited() {
+ return rowLimit != Integer.MAX_VALUE;
+ }
+
+ /**
+ * Determine whether this limit clause defines an offset.
+ *
+ * @return true if there is an offset, or false if there is no offset
+ */
+ public final boolean isOffset() {
+ return offset > 0;
+ }
+
+ /**
+ * {@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 Limit) {
+ Limit that = (Limit)obj;
+ return this.offset == that.offset && this.rowLimit == that.rowLimit;
+ }
+ 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);
+ }
+
+ public Limit withRowLimit( int rowLimit ) {
+ return new Limit(rowLimit, offset);
+ }
+
+ public Limit withOffset( int offset ) {
+ return new Limit(rowLimit, offset);
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,82 @@
+/*
+ * 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 literal value used in a {@link Comparison} constraint.
+ */
+@Immutable
+public class Literal extends StaticOperand {
+
+ private final Object value;
+
+ public Literal( Object value ) {
+ CheckArg.isNotNull(value, "value");
+ this.value = value;
+ }
+
+ /**
+ * @return value
+ */
+ public final Object getValue() {
+ return value;
+ }
+
+ /**
+ * {@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 Literal) {
+ Literal that = (Literal)obj;
+ return this.value.equals(that.value);
+ }
+ 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/Literal.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,97 @@
+/*
+ * 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 lower-case representation of the supplied operand, used in a {@link Comparison}
+ * constraint.
+ */
+@Immutable
+public class LowerCase extends DynamicOperand {
+ private final DynamicOperand operand;
+
+ /**
+ * Create a dynamic operand that evaluates to the lower-case representation of the supplied operand.
+ *
+ * @param operand the operand that is to be lower-cased
+ */
+ public LowerCase( DynamicOperand operand ) {
+ CheckArg.isNotNull(operand, "operand");
+ this.operand = operand;
+ }
+
+ /**
+ * @return operand
+ */
+ public final DynamicOperand getOperand() {
+ return operand;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.DynamicOperand#getSelectorName()
+ */
+ @Override
+ public SelectorName getSelectorName() {
+ return operand.getSelectorName();
+ }
+
+ /**
+ * {@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 LowerCase) {
+ LowerCase that = (LowerCase)obj;
+ return this.operand.equals(that.operand);
+ }
+ 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/LowerCase.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,92 @@
+/*
+ * 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.ObjectUtil;
+
+/**
+ *
+ */
+@Immutable
+public class NamedSelector extends Selector {
+
+ /**
+ * Create a selector with a name.
+ *
+ * @param name the name for this selector
+ * @throws IllegalArgumentException if the selector name is null
+ */
+ public NamedSelector( SelectorName name ) {
+ super(name);
+ }
+
+ /**
+ * Create a selector with the supplied name and alias.
+ *
+ * @param name the name for this selector
+ * @param alias the alias for this selector; may be null
+ * @throws IllegalArgumentException if the selector name is null
+ */
+ public NamedSelector( SelectorName name,
+ SelectorName alias ) {
+ super(name, alias);
+ }
+
+ /**
+ * {@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 NamedSelector) {
+ NamedSelector that = (NamedSelector)obj;
+ if (!this.getName().equals(that.getName())) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.getAlias(), that.getAlias())) 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/NamedSelector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.model;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A dynamic operand that evaluates to the local name of a node given by a selector, used in a {@link Comparison} constraint.
+ */
+@Immutable
+public class NodeLocalName extends DynamicOperand {
+ private final SelectorName selectorName;
+
+ /**
+ * Create a dynamic operand that evaluates to the local name of the node identified by the selector.
+ *
+ * @param selectorName the name of the selector
+ */
+ public NodeLocalName( 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 NodeLocalName) {
+ NodeLocalName that = (NodeLocalName)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);
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.model;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A dynamic operand that evaluates to the qualified name of a node given by a selector, used in a {@link Comparison} constraint.
+ */
+@Immutable
+public class NodeName extends DynamicOperand {
+ private final SelectorName selectorName;
+
+ /**
+ * Create a dynamic operand that evaluates to the qualified name of the node identified by the selector.
+ *
+ * @param selectorName the name of the selector
+ */
+ public NodeName( 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 NodeName) {
+ NodeName that = (NodeName)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);
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Not.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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 constraint that negates another constraint.
+ */
+@Immutable
+public class Not extends Constraint {
+
+ private final Constraint constraint;
+
+ /**
+ * Create a constraint that negates another constraint.
+ *
+ * @param constraint the constraint that is being negated
+ * @throws IllegalArgumentException if the supplied constraint is null
+ */
+ public Not( Constraint constraint ) {
+ CheckArg.isNotNull(constraint, "constraint");
+ this.constraint = constraint;
+ }
+
+ /**
+ * The constraint being negated.
+ *
+ * @return the constraint; never null
+ */
+ public final Constraint getConstraint() {
+ return constraint;
+ }
+
+ /**
+ * {@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 Not) {
+ Not that = (Not)obj;
+ return this.constraint.equals(that.constraint);
+ }
+ 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/Not.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,87 @@
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ *
+ */
+public enum Operator {
+ EQUAL_TO("="),
+ NOT_EQUAL_TO("!="),
+ LESS_THAN("<"),
+ LESS_THAN_OR_EQUAL_TO("<="),
+ GREATER_THAN(">"),
+ GREATER_THAN_OR_EQUAL_TO(">="),
+ LIKE("LIKE");
+
+ private static final Map<String, Operator> OPERATORS_BY_SYMBOL;
+ static {
+ Map<String, Operator> opsBySymbol = new HashMap<String, Operator>();
+ for (Operator operator : Operator.values()) {
+ opsBySymbol.put(operator.getSymbol().toUpperCase(), operator);
+ }
+ opsBySymbol.put("<>", NOT_EQUAL_TO);
+ OPERATORS_BY_SYMBOL = Collections.unmodifiableMap(opsBySymbol);
+ }
+
+ private final String symbol;
+
+ private Operator( String symbol ) {
+ this.symbol = symbol;
+ }
+
+ /**
+ * @return symbol
+ */
+ public String getSymbol() {
+ return symbol;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString() {
+ return symbol;
+ }
+
+ /**
+ * Attempt to find the Operator given a symbol. The matching is done independent of case.
+ *
+ * @param symbol the symbol
+ * @return the Operator having the supplied symbol, or null if there is no Operator with the supplied symbol
+ * @throws IllegalArgumentException if the symbol is null
+ */
+ public static Operator forSymbol( String symbol ) {
+ CheckArg.isNotNull(symbol, "symbol");
+ return OPERATORS_BY_SYMBOL.get(symbol.toUpperCase());
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Operator.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Or.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,104 @@
+/*
+ * 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 constraint that evaluates to true when either of the other constraints evaluates to true.
+ */
+@Immutable
+public class Or extends Constraint {
+
+ private final Constraint left;
+ private final Constraint right;
+
+ /**
+ * Create a constraint that evaluates to true if either of the two supplied constraints evaluates to true.
+ *
+ * @param left the left constraint
+ * @param right the right constraint
+ * @throws IllegalArgumentException if the left or right constraints are null
+ */
+ public Or( Constraint left,
+ Constraint right ) {
+ CheckArg.isNotNull(left, "left");
+ CheckArg.isNotNull(right, "right");
+ this.left = left;
+ this.right = right;
+ }
+
+ /**
+ * Get the left-hand constraint.
+ *
+ * @return the left-hand constraint; never null
+ */
+ public final Constraint getLeft() {
+ return left;
+ }
+
+ /**
+ * Get the right-hand constraint.
+ *
+ * @return the right-hand constraint; never null
+ */
+ public final Constraint getRight() {
+ return right;
+ }
+
+ /**
+ * {@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 Or) {
+ Or that = (Or)obj;
+ return this.left.equals(that.left) && this.right.equals(that.right);
+ }
+ 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/Or.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Order.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Order.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Order.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,81 @@
+/*
+ * 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 org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+
+/**
+ *
+ */
+public enum Order implements Readable {
+ ASCENDING("ASC"),
+ DESCENDING("DESC");
+
+ private final String symbol;
+
+ private Order( String symbol ) {
+ this.symbol = symbol;
+ }
+
+ /**
+ * @return symbol
+ */
+ public String getSymbol() {
+ return symbol;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString() {
+ return symbol;
+ }
+
+ /**
+ * Attempt to find the Order given a symbol. The matching is done independent of case.
+ *
+ * @param symbol the symbol
+ * @return the Order having the supplied symbol, or null if there is no Order with the supplied symbol
+ * @throws IllegalArgumentException if the symbol is null
+ */
+ public static Order forSymbol( String symbol ) {
+ CheckArg.isNotNull(symbol, "symbol");
+ if (ASCENDING.getSymbol().equalsIgnoreCase(symbol)) return ASCENDING;
+ if (DESCENDING.getSymbol().equalsIgnoreCase(symbol)) return DESCENDING;
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Readable#getString(org.jboss.dna.graph.ExecutionContext)
+ */
+ public String getString( ExecutionContext context ) {
+ return getSymbol();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Order.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Ordering.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,105 @@
+/*
+ * 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 specification of the ordering for the results.
+ */
+@Immutable
+public class Ordering implements LanguageObject {
+
+ private final DynamicOperand operand;
+ private final Order order;
+
+ /**
+ * Create a new ordering specification, given the supplied operand and order.
+ *
+ * @param operand the operand being ordered
+ * @param order the order type
+ * @throws IllegalArgumentException if the operand or order type is null
+ */
+ public Ordering( DynamicOperand operand,
+ Order order ) {
+ CheckArg.isNotNull(operand, "operand");
+ CheckArg.isNotNull(order, "order");
+ this.operand = operand;
+ this.order = order;
+ }
+
+ /**
+ * Get the operand being ordered.
+ *
+ * @return the operand; never null
+ */
+ public final DynamicOperand getOperand() {
+ return operand;
+ }
+
+ /**
+ * The order type.
+ *
+ * @return the type; never null
+ */
+ public final Order getOrder() {
+ return order;
+ }
+
+ /**
+ * {@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 Ordering) {
+ Ordering that = (Ordering)obj;
+ if (this.order != that.order) return false;
+ return this.operand.equals(that.operand);
+ }
+ 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/Ordering.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,103 @@
+/*
+ * 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;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * A constraint that evaluates to true only when a named property exists on a node.
+ */
+@Immutable
+public class PropertyExistence extends Constraint {
+ private final SelectorName selectorName;
+ private final Name propertyName;
+
+ /**
+ * Create a constraint requiring that a property exist on a node.
+ *
+ * @param selectorName the name of the node selector
+ * @param propertyName the name of the property that must exist
+ */
+ public PropertyExistence( SelectorName selectorName,
+ Name propertyName ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ CheckArg.isNotNull(propertyName, "propertyName");
+ this.selectorName = selectorName;
+ this.propertyName = propertyName;
+ }
+
+ /**
+ * Get the name of the selector.
+ *
+ * @return the selector name; never null
+ */
+ public final SelectorName getSelectorName() {
+ return selectorName;
+ }
+
+ /**
+ * Get the name of the property.
+ *
+ * @return the property name; never null
+ */
+ public final Name getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * {@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 PropertyExistence) {
+ PropertyExistence that = (PropertyExistence)obj;
+ return this.selectorName.equals(that.selectorName) && this.propertyName.equals(that.propertyName);
+ }
+ 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/PropertyExistence.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,105 @@
+/*
+ * 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;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * A dynamic operand that evaluates to the value(s) of a property on a selector, used in a {@link Comparison} constraint.
+ */
+@Immutable
+public class PropertyValue extends DynamicOperand {
+ private final SelectorName selectorName;
+ private final Name propertyName;
+
+ /**
+ * Create a dynamic operand that evaluates to the property values of the node identified by the selector.
+ *
+ * @param selectorName the name of the selector
+ * @param propertyName the name of the property
+ * @throws IllegalArgumentException if the selector name or property name are null
+ */
+ public PropertyValue( SelectorName selectorName,
+ Name propertyName ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ CheckArg.isNotNull(propertyName, "propertyName");
+ this.selectorName = selectorName;
+ this.propertyName = propertyName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.DynamicOperand#getSelectorName()
+ */
+ @Override
+ public final SelectorName getSelectorName() {
+ return selectorName;
+ }
+
+ /**
+ * Get the name of the property.
+ *
+ * @return the property name; never null
+ */
+ public final Name getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * {@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 PropertyValue) {
+ PropertyValue that = (PropertyValue)obj;
+ return this.selectorName.equals(that.selectorName) && this.propertyName.equals(that.propertyName);
+ }
+ 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/PropertyValue.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,218 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.ObjectUtil;
+
+/**
+ *
+ */
+@Immutable
+public class Query extends QueryCommand {
+
+ public static final boolean IS_DISTINCT_DEFAULT = false;
+
+ private final Source source;
+ private final Constraint constraint;
+ private final List<Column> columns;
+ private final boolean distinct;
+
+ /**
+ * Create a new query that uses the supplied source.
+ *
+ * @param source the source
+ * @throws IllegalArgumentException if the source is null
+ */
+ public Query( Source source ) {
+ super();
+ CheckArg.isNotNull(source, "source");
+ this.source = source;
+ this.constraint = null;
+ this.columns = Collections.<Column>emptyList();
+ this.distinct = IS_DISTINCT_DEFAULT;
+ }
+
+ /**
+ * Create a new query that uses the supplied source, constraint, orderings, columns and limits.
+ *
+ * @param source the source
+ * @param constraint the constraint (or composite constraint), or null or empty if there are no constraints
+ * @param orderings the specifications of how the results are to be ordered, or null if the order is to be implementation
+ * determined
+ * @param columns the columns to be included in the results, or null or empty if there are no explicit columns and the actual
+ * result columns are to be implementation determiend
+ * @param limit the limit for the results, or null if all of the results are to be included
+ * @param isDistinct true if duplicates are to be removed from the results
+ * @throws IllegalArgumentException if the source is null
+ */
+ public Query( Source source,
+ Constraint constraint,
+ List<Ordering> orderings,
+ List<Column> columns,
+ Limit limit,
+ boolean isDistinct ) {
+ super(orderings, limit);
+ CheckArg.isNotNull(source, "source");
+ this.source = source;
+ this.constraint = constraint;
+ this.columns = columns != null ? columns : Collections.<Column>emptyList();
+ this.distinct = isDistinct;
+ }
+
+ /**
+ * Get the source for the results.
+ *
+ * @return the query source; never null
+ */
+ public final Source getSource() {
+ return source;
+ }
+
+ /**
+ * Get the constraints, if there are any.
+ *
+ * @return the constraint; may be null
+ */
+ public final Constraint getConstraint() {
+ return constraint;
+ }
+
+ /**
+ * Return the columns defining the query results. If there are no columns, then the columns are implementation determined.
+ *
+ * @return the list of columns; never null
+ */
+ public final List<Column> getColumns() {
+ return columns;
+ }
+
+ /**
+ * Determine whether this query is to return only distinct values.
+ *
+ * @return true if the query is to remove duplicate tuples, or false otherwise
+ */
+ public boolean isDistinct() {
+ return distinct;
+ }
+
+ public Query distinct() {
+ return new Query(source, constraint, getOrderings(), columns, getLimits(), true);
+ }
+
+ public Query noDistinct() {
+ return new Query(source, constraint, getOrderings(), columns, getLimits(), false);
+ }
+
+ public Query constrainedBy( Constraint constraint ) {
+ return new Query(source, constraint, getOrderings(), columns, getLimits(), distinct);
+ }
+
+ public Query orderedBy( List<Ordering> orderings ) {
+ return new Query(source, constraint, orderings, columns, getLimits(), distinct);
+ }
+
+ public Query withLimit( int rowLimit ) {
+ return new Query(source, constraint, getOrderings(), columns, getLimits().withRowLimit(rowLimit), distinct);
+ }
+
+ public Query withOffset( int offset ) {
+ return new Query(source, constraint, getOrderings(), columns, getLimits().withOffset(offset), distinct);
+ }
+
+ public Query returning( List<Column> columns ) {
+ return new Query(source, constraint, getOrderings(), columns, getLimits(), distinct);
+ }
+
+ public Query adding( Ordering... orderings ) {
+ List<Ordering> newOrderings = null;
+ if (this.getOrderings() != null) {
+ newOrderings = new ArrayList<Ordering>(getOrderings());
+ for (Ordering ordering : orderings) {
+ newOrderings.add(ordering);
+ }
+ } else {
+ newOrderings = Arrays.asList(orderings);
+ }
+ return new Query(source, constraint, newOrderings, columns, getLimits(), distinct);
+ }
+
+ public Query adding( Column... columns ) {
+ List<Column> newColumns = null;
+ if (this.columns != null) {
+ newColumns = new ArrayList<Column>(this.columns);
+ for (Column column : columns) {
+ newColumns.add(column);
+ }
+ } else {
+ newColumns = Arrays.asList(columns);
+ }
+ return new Query(source, constraint, getOrderings(), newColumns, getLimits(), distinct);
+ }
+
+ /**
+ * {@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 Query) {
+ Query that = (Query)obj;
+ if (this.distinct != that.distinct) return false;
+ if (!this.source.equals(that.source)) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.getLimits(), that.getLimits())) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.constraint, that.constraint)) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.columns, that.columns)) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.getOrderings(), that.getOrderings())) 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/Query.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/QueryCommand.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/QueryCommand.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/QueryCommand.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,74 @@
+/*
+ * 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.Collections;
+import java.util.List;
+
+/**
+ * Represents the abstract base class for all query commands. Subclasses include {@link Query} and {@link SetQuery}.
+ */
+public abstract class QueryCommand extends Command {
+
+ private final List<Ordering> orderings;
+ private final Limit limits;
+
+ /**
+ * Create a new query command.
+ */
+ protected QueryCommand() {
+ this(null, null);
+ }
+
+ /**
+ * Create a new query command that uses the supplied orderings and limits.
+ *
+ * @param orderings the specifications of how the results are to be ordered, or null if the order is to be implementation
+ * determined result columns are to be implementation determiend
+ * @param limit the limit for the results, or null if all of the results are to be included
+ */
+ protected QueryCommand( List<Ordering> orderings,
+ Limit limit ) {
+ this.orderings = orderings != null ? orderings : Collections.<Ordering>emptyList();
+ this.limits = limit != null ? limit : Limit.NONE;
+ }
+
+ /**
+ * Return the orderings for this query.
+ *
+ * @return the list of orderings; never null
+ */
+ public final List<Ordering> getOrderings() {
+ return orderings;
+ }
+
+ /**
+ * Get the limits associated with this query.
+ *
+ * @return the limits; never null but possibly {@link Limit#isUnlimited() unlimited}
+ */
+ public final Limit getLimits() {
+ return limits;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/QueryCommand.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Readable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Readable.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Readable.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.
+ */
+package org.jboss.dna.graph.query.model;
+
+import org.jboss.dna.graph.ExecutionContext;
+
+/**
+ *
+ */
+public interface Readable {
+ /**
+ * Get the string representation of this query object.
+ *
+ * @param context the execution context in which the conversion is to take place
+ * @return the string representation; never null
+ */
+ String getString( ExecutionContext context );
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Readable.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,106 @@
+/*
+ * 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;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * A constraint requiring that the selected node is reachable by the supplied absolute path
+ */
+@Immutable
+public class SameNode extends Constraint {
+ private final SelectorName selectorName;
+ private final Path path;
+
+ /**
+ * Create a constraint requiring that the node identified by the selector is reachable by the supplied absolute path.
+ *
+ * @param selectorName the name of the selector
+ * @param path the absolute path
+ * @throws IllegalArgumentException if the selector name or path are null
+ */
+ public SameNode( SelectorName selectorName,
+ Path path ) {
+ CheckArg.isNotNull(selectorName, "selectorName");
+ CheckArg.isNotNull(path, "path");
+ this.selectorName = selectorName;
+ this.path = path;
+ }
+
+ /**
+ * Get the name of the selector.
+ *
+ * @return the selector name; never null
+ */
+ public final SelectorName getSelectorName() {
+ return selectorName;
+ }
+
+ /**
+ * Get the absolute path for the node
+ *
+ * @return the absolute path; never null
+ */
+ public final Path getPath() {
+ return path;
+ }
+
+ /**
+ * {@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 SameNode) {
+ SameNode that = (SameNode)obj;
+ if (!this.selectorName.equals(that.selectorName)) return false;
+ if (!this.path.equals(that.path)) 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/SameNode.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,141 @@
+/*
+ * 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;
+import org.jboss.dna.common.util.ObjectUtil;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * A join condition that tests whether two nodes are the same nodes (that is, have the same identifier or have the same relative
+ * path from the nearest ancestor with an identifiers).
+ */
+@Immutable
+public class SameNodeJoinCondition extends JoinCondition {
+ private final SelectorName selector1Name;
+ private final SelectorName selector2Name;
+ private final Path selector2Path;
+
+ /**
+ * Create a join condition that determines whether the node identified by the first selector is the same as the node at the
+ * given path relative to the node identified by the second selector.
+ *
+ * @param selector1Name the name of the first selector
+ * @param selector2Name the name of the second selector
+ * @param selector2Path the relative path from the second selector locating the node being compared with the first selector
+ * @throws IllegalArgumentException if the path or either selector name is null
+ */
+ public SameNodeJoinCondition( SelectorName selector1Name,
+ SelectorName selector2Name,
+ Path selector2Path ) {
+ CheckArg.isNotNull(selector1Name, "selector1Name");
+ CheckArg.isNotNull(selector2Name, "selector2Name");
+ CheckArg.isNotNull(selector2Path, "selector2Path");
+ this.selector1Name = selector1Name;
+ this.selector2Name = selector2Name;
+ this.selector2Path = selector2Path;
+ }
+
+ /**
+ * Create a join condition that determines whether the node identified by the first selector is the same as the node
+ * identified by the second selector.
+ *
+ * @param selector1Name the name of the first selector
+ * @param selector2Name the name of the second selector
+ * @throws IllegalArgumentException if either selector name is null
+ */
+ public SameNodeJoinCondition( SelectorName selector1Name,
+ SelectorName selector2Name ) {
+ CheckArg.isNotNull(selector1Name, "selector1Name");
+ CheckArg.isNotNull(selector2Name, "selector2Name");
+ this.selector1Name = selector1Name;
+ this.selector2Name = selector2Name;
+ this.selector2Path = null;
+ }
+
+ /**
+ * Get the selector name for the first side of the join condition.
+ *
+ * @return the name of the first selector; never null
+ */
+ public final SelectorName getSelector1Name() {
+ return selector1Name;
+ }
+
+ /**
+ * Get the selector name for the second side of the join condition.
+ *
+ * @return the name of the second selector; never null
+ */
+ public final SelectorName getSelector2Name() {
+ return selector2Name;
+ }
+
+ /**
+ * Get the path for the node being used, relative to the second selector.
+ *
+ * @return the relative path to the node; may be null if the second selector is the node being used
+ */
+ public final Path getSelector2Path() {
+ return selector2Path;
+ }
+
+ /**
+ * {@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 SameNodeJoinCondition) {
+ SameNodeJoinCondition that = (SameNodeJoinCondition)obj;
+ 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;
+ 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/SameNodeJoinCondition.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+/**
+ *
+ */
+@Immutable
+public abstract class Selector extends Source {
+
+ private final SelectorName name;
+ private final SelectorName alias;
+
+ /**
+ * Create a selector with a name.
+ *
+ * @param name the name for this selector
+ * @throws IllegalArgumentException if the selector name is null
+ */
+ protected Selector( SelectorName name ) {
+ CheckArg.isNotNull(name, "name");
+ this.name = name;
+ this.alias = null;
+ }
+
+ /**
+ * Create a selector with the supplied name and alias.
+ *
+ * @param name the name for this selector
+ * @param alias the alias for this selector; may be null
+ * @throws IllegalArgumentException if the selector name is null
+ */
+ protected Selector( SelectorName name,
+ SelectorName alias ) {
+ CheckArg.isNotNull(name, "name");
+ this.name = name;
+ this.alias = alias;
+ }
+
+ /**
+ * Get the name for this selector.
+ *
+ * @return the selector name; never null
+ */
+ public SelectorName getName() {
+ return name;
+ }
+
+ /**
+ * Get the alias name for this source, if there is one.
+ *
+ * @return the alias name, or null if there is none.
+ */
+ public SelectorName getAlias() {
+ return alias;
+ }
+
+ /**
+ * Get the alias if this selector has one, or the name.
+ *
+ * @return the alias or name; never null
+ */
+ public SelectorName getAliasOrName() {
+ return alias != null ? alias : name;
+ }
+
+ /**
+ * Determine if this selector has an alias.
+ *
+ * @return true if this selector has an alias, or false otherwise.
+ */
+ public boolean hasAlias() {
+ return alias != null;
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SelectorName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SelectorName.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SelectorName.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,95 @@
+/*
+ * 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;
+import org.jboss.dna.graph.ExecutionContext;
+
+/**
+ * A representation of a qualified or expanded name.
+ */
+@Immutable
+public class SelectorName implements Readable {
+
+ private final String name;
+
+ public SelectorName( String name ) {
+ CheckArg.isNotEmpty(name, "name");
+ this.name = name;
+ }
+
+ /**
+ * The raw name of the selector.
+ *
+ * @return the raw name; never null and never empty
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Readable#getString(ExecutionContext)
+ */
+ public String getString( ExecutionContext context ) {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return this.name.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof SelectorName) {
+ SelectorName that = (SelectorName)obj;
+ return this.name.equals(that.getName());
+ }
+ return false;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SelectorName.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,230 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.ObjectUtil;
+import org.jboss.dna.graph.ExecutionContext;
+
+/**
+ * This object acts as a Set operator on multiple {@link QueryCommand queries}, such as performing UNION, INTERSECT, and EXCEPT
+ * operations.
+ * <p>
+ * The two {@link QueryCommand queries} are expected to have the same number and order of columns, and the corresponding columns
+ * types must be compatible.
+ * </p>
+ */
+@Immutable
+public class SetQuery extends QueryCommand {
+
+ public enum Operation implements Readable {
+ UNION("UNION"),
+ INTERSECT("INTERSECT"),
+ EXCEPT("EXCEPT");
+
+ private static final Map<String, Operation> OPERATIONS_BY_SYMBOL;
+ static {
+ Map<String, Operation> opsBySymbol = new HashMap<String, Operation>();
+ for (Operation op : Operation.values()) {
+ opsBySymbol.put(op.getSymbol(), op);
+ }
+ OPERATIONS_BY_SYMBOL = Collections.unmodifiableMap(opsBySymbol);
+ }
+
+ private final String symbol;
+
+ private Operation( String symbol ) {
+ this.symbol = symbol;
+ }
+
+ /**
+ * @return symbol
+ */
+ public String getSymbol() {
+ return symbol;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString() {
+ return symbol;
+ }
+
+ public static Operation forSymbol( String symbol ) {
+ CheckArg.isNotNull(symbol, "symbol");
+ return OPERATIONS_BY_SYMBOL.get(symbol.toUpperCase());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Readable#getString(org.jboss.dna.graph.ExecutionContext)
+ */
+ public String getString( ExecutionContext context ) {
+ return getSymbol();
+ }
+ }
+
+ private final QueryCommand left;
+ private final QueryCommand right;
+ private final Operation operation;
+ private final boolean all;
+
+ public SetQuery( QueryCommand left,
+ Operation operation,
+ QueryCommand right,
+ boolean all ) {
+ super();
+ CheckArg.isNotNull(left, "left");
+ CheckArg.isNotNull(right, "right");
+ CheckArg.isNotNull(operation, "operation");
+ this.left = left;
+ this.right = right;
+ this.operation = operation;
+ this.all = all;
+ }
+
+ public SetQuery( QueryCommand left,
+ Operation operation,
+ QueryCommand right,
+ boolean all,
+ List<Ordering> orderings,
+ Limit limit ) {
+ super(orderings, limit);
+ CheckArg.isNotNull(left, "left");
+ CheckArg.isNotNull(right, "right");
+ CheckArg.isNotNull(operation, "operation");
+ this.left = left;
+ this.right = right;
+ this.operation = operation;
+ this.all = all;
+ }
+
+ /**
+ * Get the left-hand query.
+ *
+ * @return the left-hand query; never null
+ */
+ public final QueryCommand getLeft() {
+ return left;
+ }
+
+ /**
+ * Get the right-hand query.
+ *
+ * @return the right-hand query; never null
+ */
+ public final QueryCommand getRight() {
+ return right;
+ }
+
+ /**
+ * Get the set operation for this query.
+ *
+ * @return the operation; never null
+ */
+ public final Operation getOperation() {
+ return operation;
+ }
+
+ /**
+ * Return whether this set query is a 'UNION ALL' or 'INTERSECT ALL' or 'EXCEPT ALL' query.
+ *
+ * @return true if this is an 'ALL' query, or false otherwise
+ */
+ public final boolean isAll() {
+ return all;
+ }
+
+ /**
+ * {@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 SetQuery) {
+ SetQuery that = (SetQuery)obj;
+ if (this.operation != that.operation) return false;
+ if (!this.left.equals(that.left)) return false;
+ if (!this.right.equals(that.right)) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.getLimits(), that.getLimits())) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.getOrderings(), that.getOrderings())) 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);
+ }
+
+ public SetQuery withLimit( int rowLimit ) {
+ return new SetQuery(left, operation, right, all, getOrderings(), getLimits().withRowLimit(rowLimit));
+ }
+
+ public SetQuery withOffset( int offset ) {
+ return new SetQuery(left, operation, right, all, getOrderings(), getLimits().withOffset(offset));
+ }
+
+ public SetQuery adding( Ordering... orderings ) {
+ List<Ordering> newOrderings = null;
+ if (this.getOrderings() != null) {
+ newOrderings = new ArrayList<Ordering>(getOrderings());
+ for (Ordering ordering : orderings) {
+ newOrderings.add(ordering);
+ }
+ } else {
+ newOrderings = Arrays.asList(orderings);
+ }
+ return new SetQuery(left, operation, right, all, newOrderings, getLimits());
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Source.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Source.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Source.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.
+ */
+package org.jboss.dna.graph.query.model;
+
+import net.jcip.annotations.Immutable;
+
+/**
+ * The source that represents a set of node tuples. The {@link Selector} and {@link Join} are the two concrete types of sources.
+ */
+@Immutable
+public abstract class Source implements LanguageObject {
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Source.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/StaticOperand.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/StaticOperand.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/StaticOperand.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.
+ */
+package org.jboss.dna.graph.query.model;
+
+import net.jcip.annotations.Immutable;
+
+/**
+ * A static operand used in a {@link Comparison} constraint.
+ */
+@Immutable
+public abstract class StaticOperand implements LanguageObject {
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/StaticOperand.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/UpperCase.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,99 @@
+/*
+ * 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 upper-case representation of the supplied operand, used in a {@link Comparison}
+ * constraint.
+ */
+@Immutable
+public class UpperCase extends DynamicOperand {
+ private final DynamicOperand operand;
+
+ /**
+ * Create a dynamic operand that evaluates to the upper-case representation of the supplied operand.
+ *
+ * @param operand the operand that is to be lower-cased
+ */
+ public UpperCase( DynamicOperand operand ) {
+ CheckArg.isNotNull(operand, "operand");
+ this.operand = operand;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.DynamicOperand#getSelectorName()
+ */
+ @Override
+ public SelectorName getSelectorName() {
+ return operand.getSelectorName();
+ }
+
+ /**
+ * Get the operand that is being uppercased.
+ *
+ * @return the operand being uppercased; never null
+ */
+ public final DynamicOperand getOperand() {
+ return operand;
+ }
+
+ /**
+ * {@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 UpperCase) {
+ UpperCase that = (UpperCase)obj;
+ return this.operand.equals(that.operand);
+ }
+ 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/UpperCase.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitable.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitable.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.
+ */
+package org.jboss.dna.graph.query.model;
+
+import net.jcip.annotations.Immutable;
+
+/**
+ *
+ */
+@Immutable
+public interface Visitable {
+ void accept( Visitor visitor );
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitable.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+/**
+ * The basic interface for all query visitor implementations.
+ */
+public interface Visitor {
+
+ void visit( AllNodes obj );
+
+ void visit( And obj );
+
+ void visit( BindVariableName obj );
+
+ void visit( ChildNode obj );
+
+ void visit( ChildNodeJoinCondition obj );
+
+ void visit( Column obj );
+
+ void visit( Comparison obj );
+
+ void visit( DescendantNode obj );
+
+ void visit( DescendantNodeJoinCondition obj );
+
+ void visit( EquiJoinCondition obj );
+
+ void visit( FullTextSearch obj );
+
+ void visit( FullTextSearchScore obj );
+
+ void visit( Join obj );
+
+ void visit( Length obj );
+
+ void visit( Limit limit );
+
+ void visit( Literal obj );
+
+ void visit( LowerCase obj );
+
+ void visit( NodeLocalName obj );
+
+ void visit( NodeName obj );
+
+ void visit( NamedSelector obj );
+
+ void visit( Not obj );
+
+ void visit( Or obj );
+
+ void visit( Ordering obj );
+
+ void visit( PropertyExistence obj );
+
+ void visit( PropertyValue obj );
+
+ void visit( Query obj );
+
+ void visit( SameNode obj );
+
+ void visit( SameNodeJoinCondition obj );
+
+ void visit( SetQuery obj );
+
+ void visit( UpperCase obj );
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,1284 @@
+/*
+ * 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.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * A set of common visitors that can be reused or extended, and methods that provide easy construction and calling of visitors.
+ */
+public class Visitors {
+
+ /**
+ * Visit all objects in the supplied {@link Visitable object} using a {@link NavigationVisitor} (specifically a
+ * {@link WalkAllVisitor}), and with each of these visited objects calling the appropriate {@code visit(...)} method on the
+ * supplied {@link Visitor}.
+ *
+ * @param <StrategyVisitor> the type of strategy visitor
+ * @param visitable the top-level object to be visited
+ * @param strategyVisitor the visitor that is to be called for each visited objects, but that does <i>not</i> call
+ * {@link Visitable#accept(Visitor)}
+ * @return the strategy visitor, allowing the caller to easily invoke operations on the visitor after visitation has completed
+ */
+ public static <StrategyVisitor extends Visitor> StrategyVisitor visitAll( Visitable visitable,
+ StrategyVisitor strategyVisitor ) {
+ visitable.accept(new WalkAllVisitor(strategyVisitor));
+ return strategyVisitor;
+ }
+
+ /**
+ * Visit the supplied {@link Visitable object} using the supplied {@link Visitor}, which must be responsible for navigation as
+ * well as any business logic.
+ *
+ * @param <GeneralVisitor> the type of visitor
+ * @param visitable the top-level object to be visited
+ * @param visitor the visitor that is to be used
+ * @return the visitor, allowing the caller to easily invoke operations on the visitor after visitation has completed
+ */
+ public static <GeneralVisitor extends Visitor> GeneralVisitor visit( Visitable visitable,
+ GeneralVisitor visitor ) {
+ visitable.accept(visitor);
+ return visitor;
+ }
+
+ /**
+ * Using a visitor, obtain the readable string representation of the supplied {@link Visitable object}
+ *
+ * @param visitable the visitable
+ * @param context the execution context in which the representation should be produced, or null if there is none
+ * @return the string representation
+ */
+ public static String readable( Visitable visitable,
+ ExecutionContext context ) {
+ return visit(visitable, new ReadableVisitor(context)).getString();
+ }
+
+ /**
+ * Using a visitor, obtain the readable string representation of the supplied {@link Visitable object}
+ *
+ * @param visitable the visitable
+ * @return the string representation
+ */
+ public static String readable( Visitable visitable ) {
+ return visit(visitable, new ReadableVisitor()).getString();
+ }
+
+ /**
+ * Get the names of the selectors referenced by the visitable object.
+ *
+ * @param visitable the object to be visited
+ * @return the set of selector names referenced in some way by the visitable; never null
+ */
+ public static Set<SelectorName> getSelectorsReferencedBy( Visitable visitable ) {
+ final Set<SelectorName> symbols = new HashSet<SelectorName>();
+ // Walk the entire structure, so only supply a StrategyVisitor (that does no navigation) ...
+ visitAll(visitable, new AbstractVisitor() {
+ @Override
+ public void visit( AllNodes allNodes ) {
+ if (allNodes.hasAlias()) {
+ symbols.add(allNodes.getAlias());
+ } else {
+ symbols.add(allNodes.getName());
+ }
+ }
+
+ @Override
+ public void visit( ChildNode childNode ) {
+ symbols.add(childNode.getSelectorName());
+ }
+
+ @Override
+ public void visit( ChildNodeJoinCondition joinCondition ) {
+ symbols.add(joinCondition.getChildSelectorName());
+ symbols.add(joinCondition.getParentSelectorName());
+ }
+
+ @Override
+ public void visit( Column column ) {
+ symbols.add(column.getSelectorName());
+ }
+
+ @Override
+ public void visit( DescendantNode descendant ) {
+ symbols.add(descendant.getSelectorName());
+ }
+
+ @Override
+ public void visit( DescendantNodeJoinCondition joinCondition ) {
+ symbols.add(joinCondition.getAncestorSelectorName());
+ symbols.add(joinCondition.getDescendantSelectorName());
+ }
+
+ @Override
+ public void visit( EquiJoinCondition joinCondition ) {
+ symbols.add(joinCondition.getSelector1Name());
+ symbols.add(joinCondition.getSelector2Name());
+ }
+
+ @Override
+ public void visit( FullTextSearch fullTextSearch ) {
+ symbols.add(fullTextSearch.getSelectorName());
+ }
+
+ @Override
+ public void visit( Length length ) {
+ symbols.add(length.getSelectorName());
+ }
+
+ @Override
+ public void visit( NodeLocalName node ) {
+ symbols.add(node.getSelectorName());
+ }
+
+ @Override
+ public void visit( NodeName node ) {
+ symbols.add(node.getSelectorName());
+ }
+
+ @Override
+ public void visit( NamedSelector node ) {
+ if (node.hasAlias()) {
+ symbols.add(node.getAlias());
+ } else {
+ symbols.add(node.getName());
+ }
+ }
+
+ @Override
+ public void visit( PropertyExistence prop ) {
+ symbols.add(prop.getSelectorName());
+ }
+
+ @Override
+ public void visit( PropertyValue prop ) {
+ symbols.add(prop.getSelectorName());
+ }
+
+ @Override
+ public void visit( SameNode node ) {
+ symbols.add(node.getSelectorName());
+ }
+
+ @Override
+ public void visit( SameNodeJoinCondition joinCondition ) {
+ symbols.add(joinCondition.getSelector1Name());
+ symbols.add(joinCondition.getSelector2Name());
+ }
+ });
+ return symbols;
+ }
+
+ /**
+ * A common base class for all visitors, which provides no-op implementations for all {@code visit(...)} methods. Visitor
+ * implementations can subclass and implement only those methods that they need to implement.
+ * <p>
+ * This is often an excellent base class for <i>strategy visitors</i>, which simply are {@link Visitor} implementations that
+ * are responsible only for visiting the supplied object but that never call {@link Visitable#accept(Visitor)}. Such strategy
+ * visitors can be used in conjunction with separate <i>{@link NavigationVisitor navigation visitors}</i> that do the job of
+ * navigating the Visitable objects and, for each, delegating to the strategy visitor. See
+ * {@link Visitors#visitAll(Visitable, Visitor)} for an example.
+ * </p>
+ */
+ public static class AbstractVisitor implements Visitor {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.AllNodes)
+ */
+ public void visit( AllNodes obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.And)
+ */
+ public void visit( And obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.BindVariableName)
+ */
+ public void visit( BindVariableName obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.ChildNode)
+ */
+ public void visit( ChildNode obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.ChildNodeJoinCondition)
+ */
+ public void visit( ChildNodeJoinCondition obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Column)
+ */
+ public void visit( Column obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Comparison)
+ */
+ public void visit( Comparison obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.DescendantNode)
+ */
+ public void visit( DescendantNode obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.DescendantNodeJoinCondition)
+ */
+ public void visit( DescendantNodeJoinCondition obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.EquiJoinCondition)
+ */
+ public void visit( EquiJoinCondition obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.FullTextSearch)
+ */
+ public void visit( FullTextSearch obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.FullTextSearchScore)
+ */
+ public void visit( FullTextSearchScore obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Join)
+ */
+ public void visit( Join obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Length)
+ */
+ public void visit( Length obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Limit)
+ */
+ public void visit( Limit limit ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Literal)
+ */
+ public void visit( Literal obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.LowerCase)
+ */
+ public void visit( LowerCase obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeName)
+ */
+ public void visit( NodeName obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeLocalName)
+ */
+ public void visit( NodeLocalName obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NamedSelector)
+ */
+ public void visit( NamedSelector obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Not)
+ */
+ public void visit( Not obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Or)
+ */
+ public void visit( Or obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Ordering)
+ */
+ public void visit( Ordering obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.PropertyExistence)
+ */
+ public void visit( PropertyExistence obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.PropertyValue)
+ */
+ public void visit( PropertyValue obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Query)
+ */
+ public void visit( Query obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SameNode)
+ */
+ public void visit( SameNode obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SameNodeJoinCondition)
+ */
+ public void visit( SameNodeJoinCondition obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SetQuery)
+ */
+ public void visit( SetQuery obj ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.UpperCase)
+ */
+ public void visit( UpperCase obj ) {
+ }
+ }
+
+ /**
+ * An abstract visitor implementation that performs navigation of the query object.
+ * <p>
+ * Subclasses should always implement the {@code visit(T object)} methods by performing the following actions:
+ * <ol>
+ * <li>Call <code>strategy.visit(object);</code></li>
+ * <li>Add any children of {@code object} that are to be visited using {@link #enqueue(Visitable)}</li>
+ * <li>Call {@link #visitNext()}</code></li>
+ * </ol>
+ * </p>
+ */
+ public static abstract class NavigationVisitor implements Visitor {
+ protected final Visitor strategy;
+ private final LinkedList<? super Visitable> itemQueue = new LinkedList<Visitable>();
+
+ /**
+ * Create a visitor that walks all query objects.
+ *
+ * @param strategy the visitor that should be called at every node.
+ */
+ protected NavigationVisitor( Visitor strategy ) {
+ assert strategy != null;
+ this.strategy = strategy;
+ }
+
+ protected final void enqueue( Visitable objectToBeVisited ) {
+ itemQueue.add(objectToBeVisited);
+ }
+
+ protected final void enqueue( Iterable<? extends Visitable> objectsToBeVisited ) {
+ for (Visitable objectToBeVisited : objectsToBeVisited) {
+ itemQueue.add(objectToBeVisited);
+ }
+ }
+
+ protected final void visitNext() {
+ if (!itemQueue.isEmpty()) {
+ Visitable first = (Visitable)itemQueue.removeFirst();
+ first.accept(this);
+ }
+ }
+ }
+
+ /**
+ * A visitor implementation that walks the entire query object tree and delegates to another supplied visitor to do the actual
+ * work.
+ */
+ public static class WalkAllVisitor extends NavigationVisitor {
+
+ /**
+ * Create a visitor that walks all query objects.
+ *
+ * @param strategy the visitor that should be called at every node.
+ */
+ protected WalkAllVisitor( Visitor strategy ) {
+ super(strategy);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.AllNodes)
+ */
+ public void visit( AllNodes allNodes ) {
+ strategy.visit(allNodes);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.And)
+ */
+ public void visit( And and ) {
+ strategy.visit(and);
+ enqueue(and.getLeft());
+ enqueue(and.getRight());
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.BindVariableName)
+ */
+ public void visit( BindVariableName variableName ) {
+ strategy.visit(variableName);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.ChildNode)
+ */
+ public void visit( ChildNode child ) {
+ strategy.visit(child);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.ChildNodeJoinCondition)
+ */
+ public void visit( ChildNodeJoinCondition joinCondition ) {
+ strategy.visit(joinCondition);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Column)
+ */
+ public void visit( Column column ) {
+ strategy.visit(column);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Comparison)
+ */
+ public void visit( Comparison comparison ) {
+ strategy.visit(comparison);
+ enqueue(comparison.getOperand1());
+ enqueue(comparison.getOperand2());
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.DescendantNode)
+ */
+ public void visit( DescendantNode descendant ) {
+ strategy.visit(descendant);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.DescendantNodeJoinCondition)
+ */
+ public void visit( DescendantNodeJoinCondition condition ) {
+ strategy.visit(condition);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.EquiJoinCondition)
+ */
+ public void visit( EquiJoinCondition condition ) {
+ strategy.visit(condition);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.FullTextSearch)
+ */
+ public void visit( FullTextSearch fullTextSearch ) {
+ strategy.visit(fullTextSearch);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.FullTextSearchScore)
+ */
+ public void visit( FullTextSearchScore score ) {
+ strategy.visit(score);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Join)
+ */
+ public void visit( Join join ) {
+ strategy.visit(join);
+ enqueue(join.getLeft());
+ enqueue(join.getJoinCondition());
+ enqueue(join.getRight());
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Length)
+ */
+ public void visit( Length length ) {
+ strategy.visit(length);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Limit)
+ */
+ public void visit( Limit limit ) {
+ strategy.visit(limit);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Literal)
+ */
+ public void visit( Literal literal ) {
+ strategy.visit(literal);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.LowerCase)
+ */
+ public void visit( LowerCase lowerCase ) {
+ strategy.visit(lowerCase);
+ enqueue(lowerCase.getOperand());
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeName)
+ */
+ public void visit( NodeName nodeName ) {
+ strategy.visit(nodeName);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeLocalName)
+ */
+ public void visit( NodeLocalName nodeLocalName ) {
+ strategy.visit(nodeLocalName);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NamedSelector)
+ */
+ public void visit( NamedSelector selector ) {
+ strategy.visit(selector);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Not)
+ */
+ public void visit( Not not ) {
+ strategy.visit(not);
+ enqueue(not.getConstraint());
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Or)
+ */
+ public void visit( Or or ) {
+ strategy.visit(or);
+ enqueue(or.getLeft());
+ enqueue(or.getRight());
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Ordering)
+ */
+ public void visit( Ordering ordering ) {
+ strategy.visit(ordering);
+ enqueue(ordering.getOperand());
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.PropertyExistence)
+ */
+ public void visit( PropertyExistence existence ) {
+ strategy.visit(existence);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.PropertyValue)
+ */
+ public void visit( PropertyValue propertyValue ) {
+ strategy.visit(propertyValue);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Query)
+ */
+ public void visit( Query query ) {
+ strategy.visit(query);
+ enqueue(query.getSource());
+ enqueue(query.getColumns());
+ enqueue(query.getConstraint());
+ enqueue(query.getOrderings());
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SameNode)
+ */
+ public void visit( SameNode sameNode ) {
+ strategy.visit(sameNode);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SameNodeJoinCondition)
+ */
+ public void visit( SameNodeJoinCondition condition ) {
+ strategy.visit(condition);
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SetQuery)
+ */
+ public void visit( SetQuery setQuery ) {
+ strategy.visit(setQuery);
+ enqueue(setQuery.getLeft());
+ enqueue(setQuery.getRight());
+ visitNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.UpperCase)
+ */
+ public void visit( UpperCase upperCase ) {
+ strategy.visit(upperCase);
+ enqueue(upperCase.getOperand());
+ visitNext();
+ }
+ }
+
+ public static class ReadableVisitor implements Visitor {
+ private final StringBuilder sb = new StringBuilder();
+ private final ExecutionContext context;
+ private final NamespaceRegistry registry;
+
+ public ReadableVisitor( ExecutionContext context ) {
+ CheckArg.isNotNull(context, "context");
+ this.context = context;
+ this.registry = context == null ? null : context.getNamespaceRegistry();
+ }
+
+ public ReadableVisitor() {
+ this.context = null;
+ this.registry = null;
+ }
+
+ protected final ReadableVisitor append( String string ) {
+ sb.append(string);
+ return this;
+ }
+
+ protected final ReadableVisitor append( char character ) {
+ sb.append(character);
+ return this;
+ }
+
+ protected final ReadableVisitor append( int value ) {
+ sb.append(value);
+ return this;
+ }
+
+ protected final ReadableVisitor append( SelectorName name ) {
+ sb.append(name.getString(context));
+ return this;
+ }
+
+ protected final ReadableVisitor append( Name name ) {
+ sb.append(name.getString(registry, null, null));
+ return this;
+ }
+
+ protected final ReadableVisitor append( Path path ) {
+ sb.append('\'');
+ sb.append(path.getString(registry));
+ sb.append('\'');
+ return this;
+ }
+
+ /**
+ * @return context
+ */
+ public final ExecutionContext getContext() {
+ return context;
+ }
+
+ /**
+ * Get the string representation of the visited objects.
+ *
+ * @return the string representation
+ */
+ public final String getString() {
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.AllNodes)
+ */
+ public void visit( AllNodes allNodes ) {
+ append(allNodes.getName());
+ if (allNodes.hasAlias()) {
+ append(" AS ").append(allNodes.getAlias());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.And)
+ */
+ public void visit( And and ) {
+ append('(');
+ and.getLeft().accept(this);
+ append(" AND ");
+ and.getRight().accept(this);
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.BindVariableName)
+ */
+ public void visit( BindVariableName variable ) {
+ append('$').append(variable.getVariableName());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.ChildNode)
+ */
+ public void visit( ChildNode child ) {
+ append("ISCHILDNODE(");
+ append(child.getSelectorName());
+ append(',');
+ append(child.getParentPath());
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.ChildNodeJoinCondition)
+ */
+ public void visit( ChildNodeJoinCondition condition ) {
+ append("ISCHILDNODE(");
+ append(condition.getChildSelectorName());
+ append(',');
+ append(condition.getParentSelectorName());
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Column)
+ */
+ public void visit( Column column ) {
+ append(column.getSelectorName());
+ if (column.getPropertyName() == null) {
+ append(".*");
+ } else {
+ Name propertyName = column.getPropertyName();
+ String propName = propertyName.getString(registry, null, null);
+ append('.').append(propName);
+ if (!propName.equals(column.getColumnName()) && !propertyName.getLocalName().equals(column.getColumnName())) {
+ append(" AS ").append(column.getColumnName());
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Comparison)
+ */
+ public void visit( Comparison comparison ) {
+ comparison.getOperand1().accept(this);
+ append(' ').append(comparison.getOperator().getSymbol()).append(' ');
+ comparison.getOperand2().accept(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.DescendantNode)
+ */
+ public void visit( DescendantNode descendant ) {
+ append("ISDESCENDANTNODE(");
+ append(descendant.getSelectorName());
+ append(',');
+ append(descendant.getAncestorPath());
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.DescendantNodeJoinCondition)
+ */
+ public void visit( DescendantNodeJoinCondition condition ) {
+ append("ISDESCENDANTNODE(");
+ append(condition.getDescendantSelectorName());
+ append(',');
+ append(condition.getAncestorSelectorName());
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.EquiJoinCondition)
+ */
+ public void visit( EquiJoinCondition condition ) {
+ append(condition.getSelector1Name()).append('.').append(condition.getProperty1Name());
+ append(" = ");
+ append(condition.getSelector2Name()).append('.').append(condition.getProperty2Name());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.FullTextSearch)
+ */
+ public void visit( FullTextSearch fullText ) {
+ append("CONTAINS(").append(fullText.getSelectorName());
+ if (fullText.getPropertyName() != null) {
+ append('.').append(fullText.getPropertyName());
+ }
+ sb.append(",'").append(fullText.getFullTextSearchExpression()).append("')");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.FullTextSearchScore)
+ */
+ public void visit( FullTextSearchScore score ) {
+ append("SCORE(").append(score.getSelectorName()).append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Join)
+ */
+ public void visit( Join join ) {
+ join.getLeft().accept(this);
+ // if (join.getType() != JoinType.INNER) {
+ sb.append(' ').append(join.getType().getSymbol());
+ // } else {
+ // sb.append(',');
+ // }
+ append(' ');
+ join.getRight().accept(this);
+ append(" ON ");
+ join.getJoinCondition().accept(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Length)
+ */
+ public void visit( Length length ) {
+ append("LENGTH(");
+ length.getPropertyValue().accept(this);
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Limit)
+ */
+ public void visit( Limit limit ) {
+ append("LIMIT ").append(limit.getRowLimit());
+ if (limit.getOffset() != 0) {
+ append(" OFFSET ").append(limit.getOffset());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @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()));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.LowerCase)
+ */
+ public void visit( LowerCase lowerCase ) {
+ append("LOWER(");
+ lowerCase.getOperand().accept(this);
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeLocalName)
+ */
+ public void visit( NodeLocalName name ) {
+ append("LOCALNAME(").append(name.getSelectorName()).append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NodeName)
+ */
+ public void visit( NodeName name ) {
+ append("NAME(").append(name.getSelectorName()).append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.NamedSelector)
+ */
+ public void visit( NamedSelector selector ) {
+ append(selector.getName());
+ if (selector.hasAlias()) {
+ append(" AS ").append(selector.getAlias());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Not)
+ */
+ public void visit( Not not ) {
+ append('(');
+ append("NOT ");
+ not.getConstraint().accept(this);
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Or)
+ */
+ public void visit( Or or ) {
+ append('(');
+ or.getLeft().accept(this);
+ append(" OR ");
+ or.getRight().accept(this);
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Ordering)
+ */
+ public void visit( Ordering ordering ) {
+ ordering.getOperand().accept(this);
+ append(' ').append(ordering.getOrder().getSymbol());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.PropertyExistence)
+ */
+ public void visit( PropertyExistence existence ) {
+ append(existence.getSelectorName()).append('.').append(existence.getPropertyName()).append(" IS NOT NULL");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.PropertyValue)
+ */
+ public void visit( PropertyValue value ) {
+ append(value.getSelectorName()).append('.').append(value.getPropertyName());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.Query)
+ */
+ public void visit( Query query ) {
+ append("SELECT ");
+ if (query.isDistinct()) append("DISTINCT ");
+ if (query.getColumns().isEmpty()) {
+ append('*');
+ } else {
+ boolean isFirst = true;
+ for (Column column : query.getColumns()) {
+ if (isFirst) isFirst = false;
+ else append(',');
+ column.accept(this);
+ }
+ }
+ append(" FROM ");
+ query.getSource().accept(this);
+ if (query.getConstraint() != null) {
+ append(" WHERE ");
+ query.getConstraint().accept(this);
+ }
+ if (!query.getOrderings().isEmpty()) {
+ append(" ORDER BY ");
+ boolean isFirst = true;
+ for (Ordering ordering : query.getOrderings()) {
+ if (isFirst) isFirst = false;
+ else append(',');
+ ordering.accept(this);
+ }
+ }
+ if (!query.getLimits().isUnlimited()) {
+ append(' ');
+ query.getLimits().accept(this);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SameNode)
+ */
+ public void visit( SameNode sameNode ) {
+ append("ISSAMENODE(").append(sameNode.getSelectorName()).append(',').append(sameNode.getPath()).append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SameNodeJoinCondition)
+ */
+ public void visit( SameNodeJoinCondition condition ) {
+ append("ISSAMENODE(").append(condition.getSelector1Name()).append(',').append(condition.getSelector2Name());
+ if (condition.getSelector2Path() != null) {
+ append(',').append(condition.getSelector2Path());
+ }
+ append(')');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.SetQuery)
+ */
+ public void visit( SetQuery query ) {
+ query.getLeft().accept(this);
+ append(' ').append(query.getOperation().getSymbol()).append(' ');
+ if (query.isAll()) append("ALL ");
+ query.getRight().accept(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Visitor#visit(org.jboss.dna.graph.query.model.UpperCase)
+ */
+ public void visit( UpperCase upperCase ) {
+ append("UPPER(");
+ upperCase.getOperand().accept(this);
+ append(')');
+ }
+
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Visitors.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AddAccessNodes.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.optimize;
+
+import java.util.LinkedList;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.QueryContext;
+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.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
+ * {@link RuleBasedOptimizer#populateRuleStack(LinkedList, PlanHints)}.
+ * <p>
+ * Before:
+ *
+ * <pre>
+ * ...
+ * |
+ * SOURCE
+ * </pre>
+ *
+ * After:
+ *
+ * <pre>
+ * ...
+ * |
+ * ACCESS
+ * |
+ * SOURCE
+ * </pre>
+ *
+ * </p>
+ */
+@Immutable
+public class AddAccessNodes implements OptimizerRule {
+
+ public static final AddAccessNodes INSTANCE = new AddAccessNodes();
+
+ /**
+ * {@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 ) {
+ // On each of the source nodes ...
+ for (PlanNode source : plan.findAllAtOrBelow(Type.SOURCE)) {
+ // The source node should have no children ...
+ if (source.getChildCount() != 0) continue;
+
+ // Create the ACCESS node, set the selectors, and insert above the source node ...
+ PlanNode access = new PlanNode(Type.ACCESS);
+ access.addSelectors(source.getSelectors());
+ source.insertAsParent(access);
+ }
+ 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/AddAccessNodes.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithm.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithm.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithm.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,242 @@
+/*
+ * 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.LinkedList;
+import java.util.List;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
+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.JoinCondition;
+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.SameNodeJoinCondition;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.JoinAlgorithm;
+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 choose the appropriate join algorithm and sets up any prerequisites, based upon
+ * the {@link JoinCondition}.
+ * <p>
+ * There are two static instances that can be used (or the equivalent can be instantiated or subclassed using the constructor):
+ * one that only uses {@link JoinAlgorithm#NESTED_LOOP nested-loop}, and another that will attempt to use
+ * {@link JoinAlgorithm#MERGE merge} where possible. Both instances ignore any existing {@link Property#JOIN_ALGORITHM} property
+ * value set on the JOIN node.
+ * </p>
+ * <p>
+ * For example, the {@link #USE_ONLY_NESTED_JOIN_ALGORITHM} instance will convert this simple tree:
+ *
+ * <pre>
+ * ...
+ * |
+ * JOIN
+ * / \
+ * ... ...
+ * </pre>
+ *
+ * into this:
+ *
+ * <pre>
+ * ...
+ * |
+ * JOIN ({@link Property#JOIN_ALGORITHM JOIN_ALGORITHM}={@link JoinAlgorithm#NESTED_LOOP NESTED_LOOP})
+ * / \
+ * ... ...
+ * </pre>
+ *
+ * On the other hand, the {@link #USE_BEST_JOIN_ALGORITHM} instance will do a couple of different things, depending upon the input
+ * plan.
+ * <ol>
+ * <li>If the condition is a {@link DescendantNodeJoinCondition}, then the join algorithm will always be
+ * {@link JoinAlgorithm#NESTED_LOOP}.</li>
+ * <li>Otherwise, the rule will use the {@link JoinAlgorithm#MERGE} algorithm and will change this structure:
+ *
+ * <pre>
+ * ...
+ * |
+ * JOIN
+ * / \
+ * ... ...
+ * </pre>
+ *
+ * into this:
+ *
+ * <pre>
+ * ...
+ * |
+ * JOIN ({@link Property#JOIN_ALGORITHM JOIN_ALGORITHM}={@link JoinAlgorithm#MERGE MERGE})
+ * / \
+ * / \
+ * DUP_REMOVE DUP_REMOVE
+ * | |
+ * SORT SORT
+ * | |
+ * ... ...
+ * </pre>
+ *
+ * </li>
+ * </ol>
+ * </p>
+ */
+@Immutable
+public class ChooseJoinAlgorithm implements OptimizerRule {
+
+ public static final ChooseJoinAlgorithm USE_ONLY_NESTED_JOIN_ALGORITHM = new ChooseJoinAlgorithm(true);
+ public static final ChooseJoinAlgorithm USE_BEST_JOIN_ALGORITHM = new ChooseJoinAlgorithm(false);
+
+ private final boolean useOnlyNested;
+
+ protected ChooseJoinAlgorithm( boolean useOnlyNested ) {
+ this.useOnlyNested = useOnlyNested;
+ }
+
+ /**
+ * {@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 ) {
+ // For each of the JOIN nodes ...
+ for (PlanNode joinNode : plan.findAllAtOrBelow(Type.JOIN)) {
+ JoinCondition condition = joinNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+ if (useOnlyNested) {
+ joinNode.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
+ break;
+ }
+
+ if (condition instanceof DescendantNodeJoinCondition) {
+ // It has to be a nest-loop join ...
+ joinNode.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
+ } else {
+ joinNode.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.MERGE);
+ assert joinNode.getChildCount() == 2;
+
+ // We can try to use the merge join, but we need to sort and remove remove duplicates ...
+ // on the left and right children of the join ...
+ Set<SelectorName> leftSelectors = joinNode.getFirstChild().getSelectors();
+ Set<SelectorName> rightSelectors = joinNode.getLastChild().getSelectors();
+ List<Object> leftSortBy = new LinkedList<Object>();
+ List<Object> rightSortBy = new LinkedList<Object>();
+ createOrderBysForJoinCondition(condition, leftSelectors, leftSortBy, rightSelectors, rightSortBy);
+
+ PlanNode leftSort = new PlanNode(Type.SORT, leftSelectors);
+ leftSort.setProperty(Property.SORT_ORDER_BY, leftSortBy);
+ joinNode.getFirstChild().insertAsParent(leftSort);
+ if (joinNode.getFirstChild().findAllAtOrBelow(Type.DUP_REMOVE).isEmpty()) {
+ // There is no duplicate removal below the left-hand side of the join, so insert it ...
+ PlanNode leftDupRemoval = new PlanNode(Type.DUP_REMOVE, leftSelectors);
+ joinNode.getFirstChild().insertAsParent(leftDupRemoval);
+ }
+
+ // There is no sort below the right-hand side of the join, so insert it ...
+ PlanNode rightSort = new PlanNode(Type.SORT, rightSelectors);
+ rightSort.setProperty(Property.SORT_ORDER_BY, rightSortBy);
+ joinNode.getLastChild().insertAsParent(rightSort);
+ if (joinNode.getLastChild().findAllAtOrBelow(Type.DUP_REMOVE).isEmpty()) {
+ // There is no duplicate removal below the right-hand side of the join, so insert it ...
+ PlanNode rightDupRemoval = new PlanNode(Type.DUP_REMOVE, rightSelectors);
+ joinNode.getLastChild().insertAsParent(rightDupRemoval);
+ }
+ }
+ }
+ return plan;
+ }
+
+ protected void createOrderBysForJoinCondition( JoinCondition condition,
+ Set<SelectorName> leftSelectors,
+ List<Object> leftSortBy,
+ Set<SelectorName> rightSelectors,
+ List<Object> rightSortBy ) {
+ if (condition instanceof SameNodeJoinCondition) {
+ SameNodeJoinCondition joinCondition = (SameNodeJoinCondition)condition;
+ SelectorName name1 = joinCondition.getSelector1Name();
+ SelectorName name2 = joinCondition.getSelector2Name();
+ if (leftSelectors.contains(name1)) {
+ leftSortBy.add(name1);
+ rightSortBy.add(name2);
+ } else {
+ leftSortBy.add(name2);
+ rightSortBy.add(name1);
+ }
+ } else if (condition instanceof ChildNodeJoinCondition) {
+ ChildNodeJoinCondition joinCondition = (ChildNodeJoinCondition)condition;
+ SelectorName childName = joinCondition.getChildSelectorName();
+ SelectorName parentName = joinCondition.getParentSelectorName();
+ if (leftSelectors.contains(childName)) {
+ leftSortBy.add(childName);
+ rightSortBy.add(parentName);
+ } else {
+ leftSortBy.add(parentName);
+ rightSortBy.add(childName);
+ }
+ } else if (condition instanceof EquiJoinCondition) {
+ EquiJoinCondition joinCondition = (EquiJoinCondition)condition;
+ SelectorName selector1 = joinCondition.getSelector1Name();
+ SelectorName selector2 = joinCondition.getSelector2Name();
+ Name property1 = joinCondition.getProperty1Name();
+ Name property2 = joinCondition.getProperty2Name();
+
+ // Create the Ordering for the first selector/property pair ...
+ DynamicOperand operand1 = new PropertyValue(selector1, property1);
+ Ordering ordering1 = new Ordering(operand1, Order.ASCENDING);
+ // Create the Ordering for the second selector/property pair ...
+ DynamicOperand operand2 = new PropertyValue(selector2, property2);
+ Ordering ordering2 = new Ordering(operand2, Order.ASCENDING);
+
+ if (leftSelectors.contains(selector1)) {
+ leftSortBy.add(ordering1);
+ rightSortBy.add(ordering2);
+ } else {
+ leftSortBy.add(ordering2);
+ rightSortBy.add(ordering1);
+ }
+ } else {
+ assert false;
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * {@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/ChooseJoinAlgorithm.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/Optimizer.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/Optimizer.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/Optimizer.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,44 @@
+/*
+ * 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 org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.plan.PlanNode;
+
+/**
+ * Interface for an optimizer.
+ */
+public interface Optimizer {
+
+ /**
+ * Optimize the supplied query plan and produce an executable processor plan.
+ *
+ * @param context the context in which the query is being optimized
+ * @param plan the query plan to be optimized
+ * @return the optimized query plan; never null
+ */
+ PlanNode optimize( QueryContext context,
+ PlanNode plan );
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/Optimizer.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/OptimizerRule.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/OptimizerRule.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/OptimizerRule.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,49 @@
+/*
+ * 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.LinkedList;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.plan.PlanNode;
+
+/**
+ * Interface that defines an {@link Optimizer} rule.
+ */
+@Immutable
+public interface OptimizerRule {
+
+ /**
+ * Optimize the supplied plan using the supplied context, hints, and yet-to-be-run rules.
+ *
+ * @param context the context in which the query is being optimized; never null
+ * @param plan the plan to be optimized; never null
+ * @param ruleStack the stack of rules that will be run after this rule; never null
+ * @return the optimized plan; never null
+ */
+ PlanNode execute( QueryContext context,
+ PlanNode plan,
+ LinkedList<OptimizerRule> ruleStack );
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/OptimizerRule.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,381 @@
+/*
+ * 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.EnumSet;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+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.JoinType;
+import org.jboss.dna.graph.query.model.SelectorName;
+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 attempts to push the criteria nodes in a canonical plan down as far as possible.
+ * <p>
+ * For example, here is a single-access plan 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
+ * SELECTn
+ * |
+ * ACCESS
+ * |
+ * SOURCE
+ * </pre>
+ *
+ * And after:
+ *
+ * <pre>
+ * ...
+ * |
+ * 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
+ * | together
+ * SELECTn
+ * |
+ * SOURCE
+ * </pre>
+ *
+ * Here is another case, where multiple SELECT nodes above a simple JOIN and where each SELECT node applies to one or more of the
+ * SOURCE nodes (via the named selectors). Each SELECT node that applies to a single selector will get pushed toward that source,
+ * but will have the same order relative to other SELECT nodes also pushed toward that SOURCE. However, this rules does not push
+ * SELECT nodes that apply to multiple selectors.
+ * </p>
+ * <p>
+ * Before:
+ *
+ * <pre>
+ * ...
+ * |
+ * PROJECT ('s1','s2') with the list of columns being SELECTed (from 's1' and 's2' selectors)
+ * |
+ * SELECT1 ('s1')
+ * | One or more SELECT plan nodes that each have
+ * SELECT2 ('s2') a single non-join constraint that are then all AND-ed
+ * | together, and that each have the selector(s) they apply to
+ * SELECT3 ('s1','s2')
+ * |
+ * SELECT4 ('s1')
+ * |
+ * JOIN ('s1','s2')
+ * / \
+ * / \
+ * ACCESS ACCESS
+ * ('s1') ('s2')
+ * | |
+ * SOURCE SOURCE
+ * ('s1') ('s2')
+ * </pre>
+ *
+ * And after:
+ *
+ * <pre>
+ * ...
+ * |
+ * PROJECT ('s1','s2') with the list of columns being SELECTed (from 's1' and 's2' selectors)
+ * |
+ * SELECT3 ('s1','s2') Any SELECT plan nodes that apply to multiple selectors are left above
+ * | the ACCESS nodes.
+ * JOIN ('s1','s2')
+ * / \
+ * / \
+ * ACCESS ACCESS
+ * ('s1') ('s2')
+ * | |
+ * SELECT1 SELECT2
+ * ('s1') ('s2')
+ * | |
+ * SELECT4 SOURCE
+ * ('s1') ('s2')
+ * |
+ * SOURCE
+ * ('s1')
+ * </pre>
+ *
+ * </p>
+ */
+@Immutable
+public class PushSelectCriteria implements OptimizerRule {
+
+ public static final PushSelectCriteria INSTANCE = new PushSelectCriteria();
+ private static final Set<Type> ORIGINATING_TYPES = Collections.unmodifiableSet(EnumSet.of(Type.NULL,
+ Type.SOURCE,
+ Type.JOIN,
+ Type.SET_OPERATION));
+
+ /**
+ * {@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 ) {
+ // Create set of nodes that no longer need to be considered
+ Set<PlanNode> deadNodes = new HashSet<PlanNode>();
+
+ // Loop while criteria nodes are still being moved ...
+ boolean movedSomeNode = true;
+ while (movedSomeNode) {
+
+ // Reset flag to false for this iteration
+ movedSomeNode = false;
+
+ // Find all of the criteria (SELECT) nodes that can be pushed ...
+ List<PlanNode> criteriaNodes = plan.findAllAtOrBelow(Type.SELECT);
+
+ // Find all of the NULL, SOURCE, SET_OPERATION or JOIN nodes, ordered correctly; we'll use this
+ // to look for the node on which each criteria can apply ...
+ List<PlanNode> originatingNodes = plan.findAllAtOrBelow(ORIGINATING_TYPES);
+
+ // Starting with the lowest one first ...
+ Collections.reverse(criteriaNodes);
+ for (PlanNode criteriaNode : criteriaNodes) {
+ // Skip any node we've already tried and failed to move ...
+ if (deadNodes.contains(criteriaNode)) continue;
+
+ // Find the first originating node that has all of the required selectors for this criteria ...
+ PlanNode originatingNode = findOriginatingNode(criteriaNode, originatingNodes);
+ if (originatingNode == null || originatingNode == criteriaNode) {
+ deadNodes.add(criteriaNode);
+ continue;
+ }
+
+ // Try to push the criteria node down ...
+ if (!pushTowardsOriginatingNode(criteriaNode, originatingNode)) {
+ // criteria node could not be moved ...
+ deadNodes.add(criteriaNode);
+ continue;
+ }
+
+ // The criteria node was indeed moved, but it may need to be adjusted ...
+ boolean adjusted = false;
+ switch (originatingNode.getType()) {
+ case SOURCE:
+
+ break;
+ case JOIN:
+ if (!criteriaNode.hasAncestorOfType(Type.ACCESS)) {
+ // Try to push down the join criteria (only when above ACCESS nodes) ...
+ adjusted = pushDownJoinCriteria(criteriaNode, originatingNode);
+ }
+ break;
+ default:
+ // Nothing to change ...
+ }
+ if (adjusted) {
+ // We changed something, so make sure we go through the loop again ...
+ movedSomeNode = true;
+ } else {
+ // Nothing was changed from the push-down, so consider this criteria node as processed ...
+ deadNodes.add(criteriaNode);
+ }
+ }
+ }
+ return plan;
+ }
+
+ /**
+ * Attempt to push down criteria that applies to the JOIN as additional constraints on the JOIN itself.
+ *
+ * @param criteriaNode the SELECT node; may not be null
+ * @param joinNode the JOIN node; may not be null
+ * @return true if the criteria was pushed down, or false otherwise
+ */
+ protected boolean pushDownJoinCriteria( PlanNode criteriaNode,
+ PlanNode joinNode ) {
+ JoinType joinType = (JoinType)joinNode.getProperty(Property.JOIN_TYPE);
+
+ switch (joinType) {
+ case CROSS:
+ joinNode.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+ moveCriteriaIntoOnClause(criteriaNode, joinNode);
+ break;
+ case INNER:
+ moveCriteriaIntoOnClause(criteriaNode, joinNode);
+ break;
+ default:
+ // This is where we could attempt to optimize the join type ...
+ // if (optimizeJoinType(criteriaNode, joinNode) == JoinType.INNER) {
+ // // The join type has changed ...
+ // moveCriteriaIntoOnClause(criteriaNode, joinNode);
+ // return true; // since the join type has changed ...
+ // }
+ }
+ return false;
+ }
+
+ /**
+ * Move the criteria that applies to the join to be included in the actual join criteria.
+ *
+ * @param criteriaNode the SELECT node; may not be null
+ * @param joinNode the JOIN node; may not be null
+ */
+ private void moveCriteriaIntoOnClause( PlanNode criteriaNode,
+ PlanNode joinNode ) {
+ List<Constraint> constraints = joinNode.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
+ Constraint criteria = criteriaNode.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+
+ // since the parser uses EMPTY_LIST, check for size 0 also
+ if (constraints == null || constraints.isEmpty()) {
+ constraints = new LinkedList<Constraint>();
+ joinNode.setProperty(Property.JOIN_CONSTRAINTS, constraints);
+ }
+
+ if (!constraints.contains(criteria)) {
+ constraints.add(criteria);
+ if (criteriaNode.hasBooleanProperty(Property.IS_DEPENDENT)) {
+ joinNode.setProperty(Property.IS_DEPENDENT, Boolean.TRUE);
+ }
+ }
+ criteriaNode.extractFromParent();
+ }
+
+ /**
+ * Find the first node that has all of the same {@link PlanNode#getSelectors() selectors} as the supplied criteria.
+ *
+ * @param criteriaNode the criteria
+ * @param originatingNodes the list of nodes to search
+ * @return the first (highest) node that is uses all of the same selectors as the criteria, or null if no such node could be
+ * found
+ */
+ protected PlanNode findOriginatingNode( PlanNode criteriaNode,
+ List<PlanNode> originatingNodes ) {
+ Set<SelectorName> requiredSelectors = criteriaNode.getSelectors();
+ if (requiredSelectors.isEmpty()) return criteriaNode;
+
+ // first look for originating nodes that exactly match the required selectors ...
+ for (PlanNode originatingNode : originatingNodes) {
+ if (originatingNode.getSelectors().equals(requiredSelectors)) return originatingNode;
+ }
+
+ // Nothing matched exactly, so can we push down to a node that contain all of the required selectors ...
+ for (PlanNode originatingNode : originatingNodes) {
+ if (originatingNode.getSelectors().containsAll(requiredSelectors)) return originatingNode;
+ }
+ return null;
+ }
+
+ /**
+ * Push the criteria node as close to the originating node as possible. In general, the criteria node usually ends up being
+ * moved to be a parent of the supplied originating node, except in a couple of cases:
+ * <ul>
+ * <li>There are already criteria nodes immediately above the originating node; in this case, the supplied criteria node is
+ * placed above all these existing criteria nodes.</li>
+ * <li>The originating node is below a JOIN node that is itself below an ACCESS node; in this case, the criteria node is
+ * placed immediately above the JOIN node.</li>
+ * <li>The originating node is below a LIMIT with a single SORT child node; in this case, the criteria node is placed
+ * immediately above the LIMIT node.</li>
+ * </ul>
+ *
+ * @param criteriaNode the criteria node that is to be pushed down; may not be null
+ * @param originatingNode the target node that represents the node above which the criteria node should be inserted; may not
+ * be null
+ * @return true if the criteria node was pushed down, or false if the criteria node could not be pushed down
+ */
+ protected boolean pushTowardsOriginatingNode( PlanNode criteriaNode,
+ PlanNode originatingNode ) {
+ // To keep things stable, 'originatingNode' should be the top-most SELECT (criteria) node above a SOURCE ...
+ while (originatingNode.getParent().getType() == Type.SELECT) {
+ originatingNode = originatingNode.getParent();
+ if (originatingNode == criteriaNode) return false;
+ }
+
+ // Find out the best node above which the criteria node should be placed ...
+ PlanNode bestChild = findBestChildForCriteria(criteriaNode, originatingNode);
+ if (bestChild == criteriaNode) return false;
+ criteriaNode.extractFromParent();
+ bestChild.insertAsParent(criteriaNode);
+ assert atBoundary(criteriaNode, originatingNode);
+ return true;
+ }
+
+ protected PlanNode findBestChildForCriteria( PlanNode criteriaNode,
+ PlanNode originatingNode ) {
+ // Walk the nodes, from the criteria node down to the originating node ...
+ for (PlanNode node : criteriaNode.getPathTo(originatingNode)) {
+ // Check the node to see if there is any reason why the node cannot be pushed
+ if (node.getType() == Type.JOIN) {
+ // Pushing below a JOIN is not necessary under an ACCESS node
+ if (node.hasAncestorOfType(Type.ACCESS)) return node;
+ } else if (node.getType() == Type.LIMIT) {
+ // Don't push below a LIMIT above a SORT ...
+ if (node.getChildCount() == 1 && node.getFirstChild().getType() == Type.SORT) {
+ return node;
+ }
+ }
+ }
+ return originatingNode;
+ }
+
+ /**
+ * Determine whether all of the nodes between the criteria node and its originating node are criteria (SELECT) nodes.
+ *
+ * @param criteriaNode the criteria node; may not be null
+ * @param originatingNode the originating node
+ * @return true if all nodes between the criteria and originating nodes are SELECT nodes
+ */
+ protected boolean atBoundary( PlanNode criteriaNode,
+ PlanNode originatingNode ) {
+ // Walk from source node to critNode to check each intervening node
+ PlanNode currentNode = originatingNode.getParent();
+ while (currentNode != criteriaNode) {
+ if (currentNode.getType() != Type.SELECT) return false;
+ currentNode = currentNode.getParent();
+ }
+ return true;
+ }
+
+ /**
+ * {@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/PushSelectCriteria.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,454 @@
+/*
+ * 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.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.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+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.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;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that rewrites JOIN nodes that have {@link EquiJoinCondition equi-join criteria} where
+ * the columns involved in the equi-join are all identity columns (that is, they form a {@link Schemata.Table#getKeys() key} for
+ * the table). This rewrite only happens when the left and right children of the JOIN node are both SOURCE nodes.
+ * <p>
+ * The basic idea is that in these identity equi-join cases, the following structure:
+ *
+ * <pre>
+ * ...
+ * |
+ * JOIN
+ * / \
+ * / \
+ * SOURCE SOURCE
+ * </pre>
+ *
+ * is transformed into a simple SOURCE node:
+ *
+ * <pre>
+ * ...
+ * |
+ * SOURCE
+ * </pre>
+ *
+ * Note that this rewriting removes a selector, and thus the nodes above the JOIN node that made use of the removed selector are
+ * also modified to reference the remaining selector.
+ * </p>
+ */
+@Immutable
+public class RewriteIdentityJoins implements OptimizerRule {
+
+ public static final RewriteIdentityJoins INSTANCE = new RewriteIdentityJoins();
+
+ /**
+ * {@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 ) {
+ // For each of the JOIN nodes ...
+ Map<SelectorName, SelectorName> rewrittenSelectors = null;
+ for (PlanNode joinNode : plan.findAllAtOrBelow(Type.JOIN)) {
+ JoinCondition condition = joinNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+ if (condition instanceof EquiJoinCondition) {
+ PlanNode leftNode = joinNode.getFirstChild();
+ PlanNode rightNode = joinNode.getLastChild();
+ 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);
+ }
+ }
+ }
+ }
+
+ if (rewrittenSelectors != null && !rewrittenSelectors.isEmpty()) {
+ // We re-wrote at least one JOIN, but since this only applies to JOIN nodes that meet certain criteria,
+ // the rewriting may have changed JOIN nodes that did not meet this criteria into nodes that now meet
+ // this criteria, so we need to re-run this rule...
+ ruleStack.addFirst(this);
+
+ // Now rewrite the various portions of the plan that make use of the now-removed selectors ...
+ 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;
+ }
+ 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();
+
+ // Replace the join node with the left source node ...
+ PlanNode leftSource = joinNode.getFirstChild();
+ joinNode.extractFromParent();
+
+ // 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);
+ SelectorName leftTableAlias = leftSource.getProperty(Property.SOURCE_ALIAS, SelectorName.class);
+ if (leftTableAlias != null) {
+ if (rightTableName != null) rewrittenSelectors.put(rightTableName, leftTableAlias);
+ if (rightTableAlias != null) rewrittenSelectors.put(rightTableAlias, leftTableAlias);
+ } else {
+ SelectorName leftTableName = leftSource.getProperty(Property.SOURCE_NAME, SelectorName.class);
+ assert leftTableName != null;
+ if (rightTableName != null) rewrittenSelectors.put(rightTableName, leftTableName);
+ if (rightTableAlias != null) rewrittenSelectors.put(rightTableAlias, leftTableName);
+ }
+ }
+
+ 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 Length(new PropertyValue(replacement, value.getPropertyName()));
+ }
+ 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}
+ *
+ * @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/RewriteIdentityJoins.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoins.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoins.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoins.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,76 @@
+/*
+ * 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.LinkedList;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.JoinType;
+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 converts {@link JoinType#RIGHT_OUTER right outer joins} into
+ * {@link JoinType#LEFT_OUTER left outer joins}.
+ */
+@Immutable
+public class RightOuterToLeftOuterJoins implements OptimizerRule {
+
+ public static final RightOuterToLeftOuterJoins INSTANCE = new RightOuterToLeftOuterJoins();
+
+ /**
+ * {@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 ) {
+ // For each of the JOIN nodes ...
+ for (PlanNode joinNode : plan.findAllAtOrBelow(Type.JOIN)) {
+ if (JoinType.RIGHT_OUTER == joinNode.getProperty(Property.JOIN_TYPE, JoinType.class)) {
+ // Swap the information ...
+ PlanNode left = joinNode.getFirstChild();
+ left.removeFromParent(); // right is now the first child ...
+ left.setParent(joinNode);
+ joinNode.setProperty(Property.JOIN_TYPE, JoinType.LEFT_OUTER);
+ // The JoinCondition and Constraints don't need to be changed
+ }
+ }
+ 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/RightOuterToLeftOuterJoins.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,81 @@
+/*
+ * 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.LinkedList;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+
+/**
+ * Optimizer implementation that optimizes a query using a stack of rules. Subclasses can override the
+ * {@link #populateRuleStack(LinkedList, PlanHints)} method to define the stack of rules they'd like to use, including the use of
+ * custom rules.
+ */
+@Immutable
+public class RuleBasedOptimizer implements Optimizer {
+
+ private final Logger logger = Logger.getLogger(getClass());
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.optimize.Optimizer#optimize(QueryContext, PlanNode)
+ */
+ public PlanNode optimize( QueryContext context,
+ PlanNode plan ) {
+ LinkedList<OptimizerRule> rules = new LinkedList<OptimizerRule>();
+ populateRuleStack(rules, context.getHints());
+
+ Problems problems = context.getProblems();
+ while (rules.peek() != null && !problems.hasErrors()) {
+ OptimizerRule nextRule = rules.poll();
+ logger.debug("Running query optimizer rule {0}", nextRule);
+ plan = nextRule.execute(context, plan, rules);
+ }
+
+ return plan;
+ }
+
+ /**
+ * Method that is used to create the initial rule stack. This method can be overridden by subclasses
+ *
+ * @param ruleStack the stack where the rules should be placed; never null
+ * @param hints the plan hints
+ */
+ protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
+ PlanHints hints ) {
+ if (hints.hasJoin) {
+ ruleStack.addFirst(ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM);
+ }
+ if (hints.hasCriteria) {
+ ruleStack.addFirst(PushSelectCriteria.INSTANCE);
+ }
+ ruleStack.addFirst(AddAccessNodes.INSTANCE);
+ ruleStack.addFirst(RightOuterToLeftOuterJoins.INSTANCE);
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/ColumnExpression.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/ColumnExpression.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/ColumnExpression.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,130 @@
+/*
+ * 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 net.jcip.annotations.Immutable;
+import org.jboss.dna.common.text.Position;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.ObjectUtil;
+import org.jboss.dna.graph.query.model.SelectorName;
+
+/**
+ * A representation of a column as expressed in the SQL query. Note that the selector name may not have been explicitly specified.
+ */
+@Immutable
+class ColumnExpression {
+
+ private final SelectorName selectorName;
+ private final String propertyName;
+ private final String columnName;
+ private final Position position;
+
+ /**
+ * A column with the given name representing the named property on the node identified by the selector.
+ *
+ * @param selectorName the selector name; may be null if no selector was explicitly used in the query
+ * @param propertyName the name of the property
+ * @param columnName the name of the column
+ * @param position the position of the column in the query
+ */
+ ColumnExpression( SelectorName selectorName,
+ String propertyName,
+ String columnName,
+ Position position ) {
+ CheckArg.isNotNull(propertyName, "propertyName");
+ CheckArg.isNotNull(columnName, "columnName");
+ CheckArg.isNotNull(position, "position");
+ this.selectorName = selectorName;
+ this.propertyName = propertyName;
+ this.columnName = columnName;
+ this.position = position;
+ }
+
+ /**
+ * Get the column's position in the query.
+ *
+ * @return the column's position; never null
+ */
+ public Position getPosition() {
+ return position;
+ }
+
+ /**
+ * @return the name of the selector; may be null if no selector was explicitly used in the query
+ */
+ public final SelectorName getSelectorName() {
+ return selectorName;
+ }
+
+ /**
+ * @return propertyName
+ */
+ public final String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * @return columnName
+ */
+ public final String getColumnName() {
+ return columnName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ColumnExpression) {
+ ColumnExpression that = (ColumnExpression)obj;
+ if (!ObjectUtil.isEqualWithNulls(this.selectorName, that.selectorName)) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.propertyName, that.propertyName)) return false;
+ if (!ObjectUtil.isEqualWithNulls(this.columnName, that.columnName)) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (selectorName != null) {
+ sb.append(selectorName.getName());
+ sb.append('.');
+ }
+ sb.append(propertyName);
+ if (columnName != null) {
+ sb.append(" AS ").append(columnName);
+ }
+ return sb.toString();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/ColumnExpression.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,106 @@
+/*
+ * 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.ArrayList;
+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.common.util.CheckArg;
+import org.jboss.dna.graph.query.model.FullTextSearch.Conjunction;
+import org.jboss.dna.graph.query.model.FullTextSearch.Disjunction;
+import org.jboss.dna.graph.query.model.FullTextSearch.SimpleTerm;
+import org.jboss.dna.graph.query.model.FullTextSearch.Term;
+
+/**
+ * A {@link QueryParser} implementation that parses a full-text search expression. This grammar is based on the full-text search
+ * grammar as defined by the JCR 2.0 specification.
+ */
+public class FullTextSearchParser {
+
+ /**
+ * Parse the full-text search criteria given in the supplied string.
+ *
+ * @param fullTextSearchExpression the full-text search expression; may not be null
+ * @return the term representation of the full-text search, or null if there are no terms
+ * @throws ParsingException if there is an error parsing the supplied string
+ * @throws IllegalArgumentException if the expression is null
+ */
+ public Term parse( String fullTextSearchExpression ) {
+ CheckArg.isNotNull(fullTextSearchExpression, "fullTextSearchExpression");
+ Tokenizer tokenizer = TokenStream.basicTokenizer(false);
+ TokenStream stream = new TokenStream(fullTextSearchExpression, tokenizer, false);
+ return parse(stream.start());
+ }
+
+ /**
+ * Parse the full-text search criteria from the supplied token stream. This method is useful when the full-text search
+ * expression is included in other content.
+ *
+ * @param tokens the token stream containing the full-text search starting on the next token
+ * @return the term representation of the full-text search, or null if there are no terms
+ * @throws ParsingException if there is an error parsing the supplied string
+ * @throws IllegalArgumentException if the token stream is null
+ */
+ public Term parse( TokenStream tokens ) {
+ CheckArg.isNotNull(tokens, "tokens");
+ List<Term> terms = new ArrayList<Term>();
+ do {
+ Term term = parseDisjunctedTerms(tokens);
+ if (term == null) break;
+ terms.add(term);
+ } while (tokens.canConsume("OR"));
+ if (terms.isEmpty()) return null;
+ return terms.size() > 1 ? new Disjunction(terms) : terms.iterator().next();
+ }
+
+ protected Term parseDisjunctedTerms( TokenStream tokens ) {
+ List<Term> terms = new ArrayList<Term>();
+ do {
+ Term term = parseTerm(tokens);
+ if (term == null) break;
+ terms.add(term);
+ } while (tokens.hasNext() && !tokens.matches("OR"));
+ if (terms.isEmpty()) return null;
+ return terms.size() > 1 ? new Conjunction(terms) : terms.iterator().next();
+ }
+
+ protected Term parseTerm( TokenStream tokens ) {
+ if (tokens.canConsume('-')) {
+ return new SimpleTerm(removeQuotes(tokens.consume()), true);
+ }
+ return new SimpleTerm(removeQuotes(tokens.consume()), false);
+ }
+
+ /**
+ * Remove any leading and trailing single- 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 quotes
+ */
+ protected String removeQuotes( String text ) {
+ return text.replaceFirst("^['\"]+", "").replaceAll("['\"]+$", "");
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,44 @@
+/*
+ * 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.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}.
+ */
+public interface QueryParser {
+
+ /**
+ * 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
+ */
+ QueryCommand parseQuery( String query,
+ ExecutionContext context );
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/QueryParser.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,954 @@
+/*
+ * 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.jboss.dna.common.text.TokenStream.ANY_VALUE;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+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.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+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;
+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.Join;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.Limit;
+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.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.Not;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.Or;
+import org.jboss.dna.graph.query.model.Order;
+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.Query;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.SameNode;
+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.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.FullTextSearch.Term;
+import org.jboss.dna.graph.query.model.SetQuery.Operation;
+
+/**
+ * A {@link QueryParser} implementation that parses a subset of SQL select and set queries.
+ * <p>
+ * This grammar is based on the SQL grammar as defined by the JCR 2.0 specification, with some useful additions:
+ * <ul>
+ * <li>(UNION|INTERSECT|EXCEPT) [ALL]</li>
+ * <li>SELECT DISTINCT</li>
+ * <li>LIMIT clauses</li>
+ * </ul>
+ * </p>
+ */
+public class SqlQueryParser implements QueryParser {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.parse.QueryParser#parseQuery(String, ExecutionContext)
+ */
+ public QueryCommand parseQuery( String query,
+ ExecutionContext context ) {
+ Tokenizer tokenizer = new SqlTokenizer(false);
+ TokenStream tokens = new TokenStream(query, tokenizer, false);
+ tokens.start();
+ QueryCommand command = null;
+ if (tokens.matches("SELECT")) {
+ command = parseQuery(tokens, context);
+ while (tokens.hasNext()) {
+ if (tokens.matchesAnyOf("UNION", "INTERSECT", "EXCEPT")) {
+ command = parseSetQuery(tokens, command, context);
+ } else {
+ Position pos = tokens.previousPosition();
+ String msg = GraphI18n.unexpectedToken.text(tokens.consume(), pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ }
+ }
+ return command;
+ }
+
+ protected Query parseQuery( TokenStream tokens,
+ ExecutionContext context ) {
+ AtomicBoolean isDistinct = new AtomicBoolean(false);
+ List<ColumnExpression> columnExpressions = parseSelect(tokens, isDistinct, context);
+ Source source = parseFrom(tokens, context);
+ Constraint constraint = parseWhere(tokens, context, source);
+ // Parse the order by and limit (can be in any order) ...
+ List<Ordering> orderings = parseOrderBy(tokens, context, source);
+ Limit limit = parseLimit(tokens);
+ if (orderings == null) parseOrderBy(tokens, context, source);
+
+ // Convert the column expressions to columns ...
+ List<Column> columns = new ArrayList<Column>(columnExpressions.size());
+ for (ColumnExpression expression : columnExpressions) {
+ SelectorName selectorName = expression.getSelectorName();
+ Name propertyName = nameFrom(expression.getPropertyName(), expression.getPosition(), context);
+ if (selectorName == null) {
+ if (source instanceof Selector) {
+ selectorName = ((Selector)source).getName();
+ } else {
+ Position pos = expression.getPosition();
+ String msg = GraphI18n.mustBeScopedAtLineAndColumn.text(expression, pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ }
+ columns.add(new Column(selectorName, propertyName, expression.getColumnName()));
+ }
+ // Now create the query ...
+ return new Query(source, constraint, orderings, columns, limit, isDistinct.get());
+ }
+
+ protected SetQuery parseSetQuery( TokenStream tokens,
+ QueryCommand leftHandSide,
+ ExecutionContext context ) {
+ Operation operation = null;
+ if (tokens.canConsume("UNION")) {
+ operation = Operation.UNION;
+ } else if (tokens.canConsume("INTERSECT")) {
+ operation = Operation.INTERSECT;
+ } else {
+ tokens.consume("EXCEPT");
+ operation = Operation.EXCEPT;
+ }
+ boolean all = tokens.canConsume("ALL");
+ // Parse the next select
+ QueryCommand rightQuery = parseQuery(tokens, context);
+ return new SetQuery(leftHandSide, operation, rightQuery, all);
+ }
+
+ protected List<ColumnExpression> parseSelect( TokenStream tokens,
+ AtomicBoolean isDistinct,
+ ExecutionContext context ) {
+ tokens.consume("SELECT");
+ if (tokens.canConsume("DISTINCT")) isDistinct.set(true);
+ if (tokens.canConsume('*')) {
+ return Collections.emptyList();
+ }
+ List<ColumnExpression> columns = new ArrayList<ColumnExpression>();
+ do {
+ Position position = tokens.nextPosition();
+ String propertyName = 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();
+ }
+ String alias = propertyName;
+ if (tokens.canConsume("AS")) alias = removeBracketsAndQuotes(tokens.consume());
+ columns.add(new ColumnExpression(selectorName, propertyName, alias, position));
+ } while (tokens.canConsume(','));
+ return columns;
+ }
+
+ protected Source parseFrom( TokenStream tokens,
+ ExecutionContext context ) {
+ Source source = null;
+ tokens.consume("FROM");
+ source = parseNamedSelector(tokens);
+ while (tokens.hasNext()) {
+ JoinType joinType = null;
+ if (tokens.canConsume("JOIN") || tokens.canConsume("INNER", "JOIN")) {
+ joinType = JoinType.INNER;
+ } else if (tokens.canConsume("OUTER", "JOIN") || tokens.canConsume("LEFT", "JOIN")
+ || tokens.canConsume("LEFT", "OUTER", "JOIN")) {
+ joinType = JoinType.LEFT_OUTER;
+ } else if (tokens.canConsume("RIGHT", "OUTER", "JOIN")) {
+ joinType = JoinType.RIGHT_OUTER;
+ } else if (tokens.canConsume("FULL", "OUTER", "JOIN")) {
+ joinType = JoinType.FULL_OUTER;
+ } else if (tokens.canConsume("CROSS", "JOIN")) {
+ joinType = JoinType.CROSS;
+ }
+ if (joinType == null) break;
+ // Read the name of the selector on the right side of the join ...
+ NamedSelector right = parseNamedSelector(tokens);
+ // Read the join condition ...
+ JoinCondition joinCondition = parseJoinCondition(tokens, context);
+ // Create the join ...
+ source = new Join(source, joinType, right, joinCondition);
+ }
+ return source;
+ }
+
+ protected JoinCondition parseJoinCondition( TokenStream tokens,
+ ExecutionContext context ) {
+ tokens.consume("ON");
+ if (tokens.canConsume("ISSAMENODE", "(")) {
+ SelectorName selector1Name = parseSelectorName(tokens);
+ tokens.consume(',');
+ SelectorName selector2Name = parseSelectorName(tokens);
+ if (tokens.canConsume('.')) {
+ Path path = parsePath(tokens, context);
+ tokens.consume(')');
+ return new SameNodeJoinCondition(selector1Name, selector2Name, path);
+ }
+ tokens.consume(')');
+ return new SameNodeJoinCondition(selector1Name, selector2Name);
+ }
+ if (tokens.canConsume("ISCHILDNODE", "(")) {
+ SelectorName child = parseSelectorName(tokens);
+ tokens.consume(',');
+ SelectorName parent = parseSelectorName(tokens);
+ tokens.consume(')');
+ return new ChildNodeJoinCondition(parent, child);
+ }
+ if (tokens.canConsume("ISDESCENDANTNODE", "(")) {
+ SelectorName descendant = parseSelectorName(tokens);
+ tokens.consume(',');
+ SelectorName ancestor = parseSelectorName(tokens);
+ tokens.consume(')');
+ return new DescendantNodeJoinCondition(ancestor, descendant);
+ }
+ SelectorName selector1 = parseSelectorName(tokens);
+ tokens.consume('.');
+ Name property1 = parseName(tokens, context);
+ tokens.consume('=');
+ SelectorName selector2 = parseSelectorName(tokens);
+ tokens.consume('.');
+ Name property2 = parseName(tokens, context);
+ return new EquiJoinCondition(selector1, property1, selector2, property2);
+ }
+
+ protected Constraint parseWhere( TokenStream tokens,
+ ExecutionContext context,
+ Source source ) {
+ if (tokens.canConsume("WHERE")) {
+ return parseConstraint(tokens, context, source);
+ }
+ return null;
+ }
+
+ protected Constraint parseConstraint( TokenStream tokens,
+ ExecutionContext context,
+ Source source ) {
+ Constraint constraint = null;
+ Position pos = tokens.nextPosition();
+ if (tokens.canConsume("(")) {
+ constraint = parseConstraint(tokens, context, source);
+ tokens.consume(")");
+ } else if (tokens.canConsume("NOT")) {
+ tokens.canConsume('(');
+ constraint = new Not(parseConstraint(tokens, context, source));
+ tokens.canConsume(')');
+ } else if (tokens.canConsume("CONTAINS", "(")) {
+ // Either 'selectorName.propertyName', or 'selectorName.*' or 'propertyName' ...
+ String first = tokens.consume();
+ SelectorName selectorName = null;
+ Name propertyName = null;
+ if (tokens.canConsume(".", "*")) {
+ selectorName = new SelectorName(removeBracketsAndQuotes(first));
+ } else if (tokens.canConsume('.')) {
+ selectorName = new SelectorName(removeBracketsAndQuotes(first));
+ propertyName = parseName(tokens, context);
+ } else {
+ if (!(source instanceof Selector)) {
+ String msg = GraphI18n.functionIsAmbiguous.text("CONTEXT()", pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ selectorName = ((Selector)source).getName();
+ propertyName = nameFrom(first, pos, context);
+ }
+ tokens.consume(',');
+
+ // Followed by the full text search expression ...
+ String expression = removeBracketsAndQuotes(tokens.consume());
+ Term term = parseFullTextSearchExpression(expression, tokens.previousPosition());
+ tokens.consume(")");
+ constraint = new FullTextSearch(selectorName, propertyName, expression, term);
+ } else if (tokens.canConsume("ISSAMENODE", "(")) {
+ SelectorName selectorName = null;
+ if (tokens.matches(ANY_VALUE, ")")) {
+ if (!(source instanceof Selector)) {
+ String msg = GraphI18n.functionIsAmbiguous.text("ISSAMENODE()", pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ selectorName = ((Selector)source).getName();
+ } else {
+ selectorName = parseSelectorName(tokens);
+ tokens.consume(',');
+ }
+ Path path = parsePath(tokens, context);
+ tokens.consume(')');
+ constraint = new SameNode(selectorName, path);
+ } else if (tokens.canConsume("ISCHILDNODE", "(")) {
+ SelectorName selectorName = null;
+ if (tokens.matches(ANY_VALUE, ")")) {
+ if (!(source instanceof Selector)) {
+ String msg = GraphI18n.functionIsAmbiguous.text("ISCHILDNODE()", pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ selectorName = ((Selector)source).getName();
+ } else {
+ selectorName = parseSelectorName(tokens);
+ tokens.consume(',');
+ }
+ Path path = parsePath(tokens, context);
+ tokens.consume(')');
+ constraint = new ChildNode(selectorName, path);
+ } else if (tokens.canConsume("ISDESCENDANTNODE", "(")) {
+ SelectorName selectorName = null;
+ if (tokens.matches(ANY_VALUE, ")")) {
+ if (!(source instanceof Selector)) {
+ String msg = GraphI18n.functionIsAmbiguous.text("ISDESCENDANTNODE()", pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ selectorName = ((Selector)source).getName();
+ } else {
+ selectorName = parseSelectorName(tokens);
+ tokens.consume(',');
+ }
+ Path path = parsePath(tokens, context);
+ tokens.consume(')');
+ constraint = new DescendantNode(selectorName, path);
+ } else {
+ // First try a property existance ...
+ Position pos2 = tokens.nextPosition();
+ constraint = parsePropertyExistance(tokens, context, source);
+ if (constraint == null) {
+ // Try to parse as a dynamic operand ...
+ DynamicOperand left = parseDynamicOperand(tokens, context, source);
+ if (left != null) {
+ if (tokens.matches('(') && left instanceof PropertyValue) {
+ // This was probably a bad function that we parsed as the start of a dynamic operation ...
+ String name = ((PropertyValue)left).getPropertyName().getLocalName(); // this may be the function name
+ 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);
+ }
+ // else continue ...
+ }
+ }
+ if (constraint == null) {
+ String msg = GraphI18n.expectingConstraintCondition.text(tokens.consume(), pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ // AND has higher precedence than OR, so we need to evaluate it first ...
+ while (tokens.canConsume("AND")) {
+ constraint = new And(constraint, parseConstraint(tokens, context, source));
+ }
+ while (tokens.canConsume("OR")) {
+ constraint = new Or(constraint, parseConstraint(tokens, context, source));
+ }
+ return constraint;
+ }
+
+ protected Term parseFullTextSearchExpression( String expression,
+ Position position ) {
+ try {
+ return new FullTextSearchParser().parse(expression);
+ } catch (ParsingException e) {
+ // Convert the position in the exception into a position in the query.
+ Position exprPos = e.getPosition();
+ int line = position.getLine() + exprPos.getLine() - 1;
+ int column = exprPos.getLine() == 1 ? exprPos.getColumn() + position.getColumn() : exprPos.getColumn();
+ Position queryPos = new Position(line, column);
+ throw new ParsingException(queryPos, e.getMessage());
+ }
+ }
+
+ protected Operator parseComparisonOperator( TokenStream tokens ) {
+ if (tokens.canConsume("=")) return Operator.EQUAL_TO;
+ if (tokens.canConsume("LIKE")) return Operator.LIKE;
+ if (tokens.canConsume("!", "=")) return Operator.NOT_EQUAL_TO;
+ if (tokens.canConsume("<", ">")) return Operator.NOT_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;
+ Position pos = tokens.nextPosition();
+ String msg = GraphI18n.expectingComparisonOperator.text(tokens.consume(), pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+
+ protected List<Ordering> parseOrderBy( TokenStream tokens,
+ ExecutionContext context,
+ Source source ) {
+ if (tokens.canConsume("ORDER", "BY")) {
+ List<Ordering> orderings = new ArrayList<Ordering>();
+ do {
+ orderings.add(parseOrdering(tokens, context, source));
+ } while (tokens.canConsume(','));
+ return orderings;
+ }
+ return null;
+ }
+
+ protected Ordering parseOrdering( TokenStream tokens,
+ ExecutionContext context,
+ Source source ) {
+ DynamicOperand operand = parseDynamicOperand(tokens, context, source);
+ Order order = Order.ASCENDING;
+ if (tokens.canConsume("DESC")) order = Order.DESCENDING;
+ if (tokens.canConsume("ASC")) order = Order.ASCENDING;
+ return new Ordering(operand, order);
+ }
+
+ protected Constraint parsePropertyExistance( TokenStream tokens,
+ ExecutionContext context,
+ Source source ) {
+ if (tokens.matches(ANY_VALUE, ".", ANY_VALUE, "IS", "NOT", "NULL")
+ || tokens.matches(ANY_VALUE, ".", ANY_VALUE, "IS", "NULL") || tokens.matches(ANY_VALUE, "IS", "NOT", "NULL")
+ || tokens.matches(ANY_VALUE, "IS", "NULL")) {
+ Position pos = tokens.nextPosition();
+ String firstWord = tokens.consume();
+ SelectorName selectorName = null;
+ Name propertyName = null;
+ if (tokens.canConsume('.')) {
+ // We actually read the selector name, so now read the property name ...
+ selectorName = new SelectorName(firstWord);
+ propertyName = parseName(tokens, context);
+ } else {
+ // Otherwise the source should be a single named selector
+ if (!(source instanceof Selector)) {
+ String msg = GraphI18n.mustBeScopedAtLineAndColumn.text(firstWord, pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ selectorName = ((Selector)source).getName();
+ propertyName = nameFrom(firstWord, pos, context);
+ }
+ if (tokens.canConsume("IS", "NOT", "NULL")) {
+ return new PropertyExistence(selectorName, propertyName);
+ }
+ tokens.consume("IS", "NULL");
+ return new Not(new PropertyExistence(selectorName, propertyName));
+ }
+ return null;
+ }
+
+ protected StaticOperand parseStaticOperand( TokenStream tokens,
+ ExecutionContext context ) {
+ if (tokens.canConsume('$')) {
+ // The variable name must conform to a valid prefix, which is defined as a valid NCName ...
+ String value = tokens.consume();
+ if (!XmlCharacters.isValidNcName(value)) {
+ Position pos = tokens.previousPosition();
+ String msg = GraphI18n.bindVariableMustConformToNcName.text(value, pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ return new BindVariableName(value);
+ }
+ return parseLiteral(tokens, context);
+ }
+
+ protected Literal parseLiteral( TokenStream tokens,
+ ExecutionContext context ) {
+ if (tokens.canConsume("CAST", "(")) {
+ // Get the value that is to be cast ...
+ Position pos = tokens.nextPosition();
+ String value = parseLiteralValue(tokens, context);
+ // Figure out the type we're supposed to cast to ...
+ ValueFactories factories = context.getValueFactories();
+ ValueFactory<?> factory = factories.getStringFactory();
+ tokens.consume("AS");
+ if (tokens.canConsume("STRING")) factory = factories.getStringFactory();
+ else if (tokens.canConsume("BINARY")) factory = factories.getBinaryFactory();
+ else if (tokens.canConsume("DATE")) factory = factories.getDateFactory();
+ else if (tokens.canConsume("LONG")) factory = factories.getLongFactory();
+ else if (tokens.canConsume("DOUBLE")) factory = factories.getDoubleFactory();
+ else if (tokens.canConsume("DECIMAL")) factory = factories.getDecimalFactory();
+ else if (tokens.canConsume("BOOLEAN")) factory = factories.getBooleanFactory();
+ else if (tokens.canConsume("NAME")) factory = factories.getNameFactory();
+ else if (tokens.canConsume("PATH")) factory = factories.getPathFactory();
+ else if (tokens.canConsume("REFERENCE")) factory = factories.getReferenceFactory();
+ else if (tokens.canConsume("WEAKREFERENCE")) factory = factories.getPathFactory();
+ else if (tokens.canConsume("URI")) factory = factories.getUriFactory();
+ else {
+ Position typePos = tokens.nextPosition();
+ String msg = GraphI18n.invalidPropertyType.text(tokens.consume(), typePos.getLine(), typePos.getColumn());
+ throw new ParsingException(typePos, msg);
+ }
+ // Convert the supplied value to the desired value ...
+ tokens.consume(')');
+ try {
+ Object literal = factory.create(value);
+ if (literal instanceof DateTime) {
+ // Convert the timestamp to UTC, since that's how everything should be stored ...
+ literal = ((DateTime)literal).toUtcTimeZone();
+ }
+ return new Literal(literal);
+ } catch (ValueFormatException e) {
+ String msg = GraphI18n.valueCannotBeCastToSpecifiedType.text(value,
+ pos.getLine(),
+ pos.getColumn(),
+ factory.getPropertyType().name(),
+ e.getMessage());
+ throw new ParsingException(pos, msg);
+ }
+ }
+ // Just create a literal out of the supplied value ...
+ return new Literal(parseLiteralValue(tokens, context));
+ }
+
+ protected String parseLiteralValue( TokenStream tokens,
+ ExecutionContext context ) {
+ if (tokens.matches(SqlTokenizer.QUOTED_STRING)) {
+ return removeBracketsAndQuotes(tokens.consume());
+ }
+ ValueFactory<String> stringFactory = context.getValueFactories().getStringFactory();
+ if (tokens.canConsume("TRUE")) return stringFactory.create(Boolean.TRUE);
+ if (tokens.canConsume("FALSE")) return stringFactory.create(Boolean.FALSE);
+
+ // Otherwise it is an unquoted literal value ...
+ Position pos = tokens.nextPosition();
+ String sign = "";
+ if (tokens.canConsume('-')) sign = "-";
+ else if (tokens.canConsume('+')) sign = "";
+
+ // Try to parse this value as a number ...
+ String integral = tokens.consume();
+ String decimal = null;
+ if (tokens.canConsume('.')) {
+ decimal = tokens.consume();
+ String value = sign + integral + "." + decimal;
+ if (decimal.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 ...
+ return stringFactory.create(context.getValueFactories().getDoubleFactory().create(value));
+ } catch (ValueFormatException e) {
+ String msg = GraphI18n.expectingLiteralAndUnableToParseAsDouble.text(value, pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ }
+ if (tokens.canConsume('-')) {
+ // Looks like a date (see Section 3.6.4.3 of the JCR 2.0 specification) ...
+ // sYYYY-MM-DDThh:mm:ss.sssTZD
+ String year = integral;
+ String month = tokens.consume();
+ tokens.consume('-');
+ String dateAndHour = tokens.consume();
+ tokens.consume(':');
+ String minutes = tokens.consume();
+ tokens.consume(':');
+ String seconds = tokens.consume();
+ tokens.consume('.');
+ String subSeconds = tokens.consume(); // should contain 'T' separator and possibly the TZ name and (if no +/-)
+ // hours
+ String tzSign = "+";
+ String tzHours = "00";
+ String tzMinutes = "00";
+ String tzDelim = ":";
+ if (tokens.canConsume('+')) {
+ // the fractionalSeconds did NOT contain the tzHours ...
+ tzHours = tokens.consume();
+ if (tokens.canConsume(':')) tzMinutes = tokens.consume();
+ } else if (tokens.canConsume('-')) {
+ // the fractionalSeconds did NOT contain the tzHours ...
+ tzSign = "-";
+ tzHours = tokens.consume();
+ if (tokens.canConsume(':')) tzMinutes = tokens.consume();
+ } else if (tokens.canConsume(':')) {
+ // fractionalSeconds DID contain the TZ hours (without + or -)
+ tzHours = tzSign = "";
+ if (tokens.canConsume(':')) tzMinutes = tokens.consume();
+ } else if (subSeconds.endsWith("Z")) {
+ tzSign = tzMinutes = tzDelim = tzHours = "";
+ } else if (subSeconds.endsWith("UTC")) {
+ subSeconds = subSeconds.length() > 3 ? subSeconds.substring(0, subSeconds.length() - 3) : subSeconds;
+ }
+ String value = sign + year + "-" + month + "-" + dateAndHour + ":" + minutes + ":" + seconds + "." + subSeconds
+ + tzSign + tzHours + tzDelim + tzMinutes;
+ try {
+ // Convert to a date and then back to a string to get canonical form ...
+ DateTime dateTime = context.getValueFactories().getDateFactory().create(value);
+ dateTime = dateTime.toUtcTimeZone();
+ return stringFactory.create(dateTime);
+ } catch (ValueFormatException e) {
+ String msg = GraphI18n.expectingLiteralAndUnableToParseAsDate.text(value, pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ }
+ // try to parse an a long ...
+ String value = sign + integral;
+ try {
+ // Convert to a long and then back to a string to get canonical form ...
+ return stringFactory.create(context.getValueFactories().getLongFactory().create(value));
+ } catch (ValueFormatException e) {
+ String msg = GraphI18n.expectingLiteralAndUnableToParseAsLong.text(value, pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+
+ }
+
+ protected DynamicOperand parseDynamicOperand( TokenStream tokens,
+ ExecutionContext context,
+ Source source ) {
+ DynamicOperand result = null;
+ Position pos = tokens.nextPosition();
+ if (tokens.canConsume("LENGTH", "(")) {
+ result = new Length(parsePropertyValue(tokens, context, source));
+ tokens.consume(")");
+ } else if (tokens.canConsume("LOWER", "(")) {
+ result = new LowerCase(parseDynamicOperand(tokens, context, source));
+ tokens.consume(")");
+ } else if (tokens.canConsume("UPPER", "(")) {
+ result = new UpperCase(parseDynamicOperand(tokens, context, source));
+ tokens.consume(")");
+ } else if (tokens.canConsume("NAME", "(")) {
+ if (tokens.canConsume(")")) {
+ if (source instanceof Selector) {
+ return new NodeName(((Selector)source).getName());
+ }
+ String msg = GraphI18n.functionIsAmbiguous.text("NAME()", pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ result = new NodeName(parseSelectorName(tokens));
+ tokens.consume(")");
+ } else if (tokens.canConsume("LOCALNAME", "(")) {
+ if (tokens.canConsume(")")) {
+ if (source instanceof Selector) {
+ return new NodeLocalName(((Selector)source).getName());
+ }
+ String msg = GraphI18n.functionIsAmbiguous.text("LOCALNAME()", pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ result = new NodeLocalName(parseSelectorName(tokens));
+ tokens.consume(")");
+ } else if (tokens.canConsume("SCORE", "(")) {
+ if (tokens.canConsume(")")) {
+ if (source instanceof Selector) {
+ return new FullTextSearchScore(((Selector)source).getName());
+ }
+ String msg = GraphI18n.functionIsAmbiguous.text("SCORE()", pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ result = new FullTextSearchScore(parseSelectorName(tokens));
+ tokens.consume(")");
+ } else {
+ result = parsePropertyValue(tokens, context, source);
+ }
+ return result;
+ }
+
+ protected PropertyValue parsePropertyValue( TokenStream tokens,
+ ExecutionContext context,
+ Source source ) {
+ Position pos = tokens.nextPosition();
+ String firstWord = removeBracketsAndQuotes(tokens.consume());
+ SelectorName selectorName = null;
+ if (tokens.canConsume('.')) {
+ // We actually read the selector name, so now read the property name ...
+ selectorName = new SelectorName(firstWord);
+ Name propertyName = parseName(tokens, context);
+ return new PropertyValue(selectorName, propertyName);
+ }
+ // Otherwise the source should be a single named selector
+ if (source instanceof Selector) {
+ selectorName = ((Selector)source).getName();
+ return new PropertyValue(selectorName, nameFrom(firstWord, pos, context));
+ }
+ String msg = GraphI18n.mustBeScopedAtLineAndColumn.text(firstWord, pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+
+ protected Limit parseLimit( TokenStream tokens ) {
+ if (tokens.canConsume("LIMIT")) {
+ int first = tokens.consumeInteger();
+ if (tokens.canConsume(',')) {
+ // This is of the 'from,to' style ...
+ int to = tokens.consumeInteger();
+ int offset = to - first;
+ if (offset < 0) {
+ Position pos = tokens.previousPosition();
+ String msg = GraphI18n.secondValueInLimitRangeCannotBeLessThanFirst.text(first,
+ to,
+ pos.getLine(),
+ pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ return new Limit(offset, first);
+ }
+ if (tokens.canConsume("OFFSET")) {
+ int offset = tokens.consumeInteger();
+ return new Limit(first, offset);
+ }
+ // No offset
+ return new Limit(first, 0);
+ }
+ return null;
+ }
+
+ /**
+ * Remove any leading and trailing single-quotes, double-quotes, or square brackets from the supplied text.
+ *
+ * @param text the input text; may not be null
+ * @return the text without leading and trailing brackets and quotes, or <code>text</code> if there were no square brackets or
+ * quotes
+ */
+ protected String removeBracketsAndQuotes( String text ) {
+ return text.replaceFirst("^['\"\\[]+", "").replaceAll("['\"\\]]+$", "");
+ }
+
+ protected NamedSelector parseNamedSelector( TokenStream tokens ) {
+ SelectorName name = parseSelectorName(tokens);
+ SelectorName alias = null;
+ if (tokens.canConsume("AS")) alias = parseSelectorName(tokens);
+ return new NamedSelector(name, alias);
+ }
+
+ protected SelectorName parseSelectorName( TokenStream tokens ) {
+ return new SelectorName(removeBracketsAndQuotes(tokens.consume()));
+ }
+
+ protected Path parsePath( TokenStream tokens,
+ ExecutionContext context ) {
+ return pathFrom(removeBracketsAndQuotes(tokens.consume()), tokens.previousPosition(), context);
+ }
+
+ protected Name parseName( TokenStream tokens,
+ ExecutionContext context ) {
+ return nameFrom(removeBracketsAndQuotes(tokens.consume()), tokens.previousPosition(), context);
+ }
+
+ protected Path pathFrom( String name,
+ Position position,
+ ExecutionContext context ) {
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ try {
+ return pathFactory.create(name);
+ } catch (ValueFormatException e) {
+ String msg = GraphI18n.expectingValidPathAtLineAndColumn.text(name, position.getLine(), position.getColumn());
+ throw new ParsingException(position, msg);
+ }
+ }
+
+ protected Name nameFrom( String name,
+ Position position,
+ ExecutionContext context ) {
+ NameFactory nameFactory = context.getValueFactories().getNameFactory();
+ try {
+ return nameFactory.create(removeBracketsAndQuotes(name));
+ } catch (ValueFormatException e) {
+ String msg = GraphI18n.expectingValidNameAtLineAndColumn.text(name, position.getLine(), position.getColumn());
+ throw new ParsingException(position, msg);
+ }
+ }
+
+ /**
+ * A {@link TokenStream.Tokenizer} implementation that parses words, quoted phrases, comments, and symbols. Words are
+ * delimited by whitespace and consist only of alpha-number characters plus the underscore character. Quoted phrases are
+ * delimited by single-quote and double-quote characters (which may be escaped within the quote). Comments are the characters
+ * starting with '/*' and ending with '*/', or starting with '--' and ending with the next line terminator (or the end of
+ * the content).
+ */
+ public static class SqlTokenizer 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 WORD = 1;
+ /**
+ * 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;
+ /**
+ * The token type for tokens that consist of other characters.
+ */
+ public static final int OTHER = 3;
+ /**
+ * 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 = 4;
+ /**
+ * The token type for tokens that consist of all the characters between "/*" and "*/" or between "--" and the next
+ * line terminator (e.g., '\n', '\r' or "\r\n")
+ */
+ public static final int COMMENT = 6;
+
+ private final boolean useComments;
+
+ public SqlTokenizer( 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 ':':
+ tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+ break;
+ case '\'':
+ case '[':
+ case '\"':
+ int startIndex = input.index();
+ char closingChar = c == '[' ? ']' : c;
+ Position pos = input.position();
+ boolean foundClosingQuote = false;
+ while (input.hasNext()) {
+ c = input.next();
+ if (c == '\\' && input.isNext(closingChar)) {
+ c = input.next(); // consume the closingChar 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());
+ } else if (closingChar == ']') {
+ msg = GraphI18n.noMatchingBracketFound.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();
+ pos = input.position();
+ if (input.isNext('-')) {
+ // End-of-line comment ...
+ boolean foundLineTerminator = false;
+ while (input.hasNext()) {
+ c = input.next();
+ if (c == '\n' || c == '\r') {
+ foundLineTerminator = true;
+ break;
+ }
+ }
+ endIndex = input.index(); // the token won't include the '\n' or '\r' character(s)
+ if (!foundLineTerminator) ++endIndex; // must point beyond last char
+ if (c == '\r' && input.isNext('\n')) input.next();
+ if (useComments) {
+ tokens.addToken(pos, startIndex, endIndex, COMMENT);
+ }
+ } else {
+ tokens.addToken(input.position(), input.index(), input.index() + 1, SYMBOL);
+ break;
+ }
+ break;
+ case '/':
+ startIndex = input.index();
+ pos = input.position();
+ if (input.isNext('*')) {
+ // Multi-line comment ...
+ 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 quote 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 = (Character.isLetterOrDigit(c) || c == '_') ? WORD : OTHER;
+ while (input.isNextLetterOrDigit() || input.isNext('_')) {
+ c = input.next();
+ }
+ endIndex = input.index() + 1; // beyond last character that was included
+ tokens.addToken(pos, startIndex, endIndex, tokenType);
+ }
+ }
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,422 @@
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+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.query.QueryContext;
+import org.jboss.dna.graph.query.model.AllNodes;
+import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.FullTextSearch;
+import org.jboss.dna.graph.query.model.Join;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.model.Limit;
+import org.jboss.dna.graph.query.model.NamedSelector;
+import org.jboss.dna.graph.query.model.Ordering;
+import org.jboss.dna.graph.query.model.Query;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.Selector;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.SetQuery;
+import org.jboss.dna.graph.query.model.Source;
+import org.jboss.dna.graph.query.model.Visitors;
+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;
+
+/**
+ * The planner that produces a canonical query plan given a {@link QueryCommand query command}.
+ * <p>
+ * A canonical plan always has the same structure:
+ *
+ * <pre>
+ * LIMIT if row limit or offset are used
+ * |
+ * SORTING if 'ORDER BY' is used
+ * |
+ * DUP_REMOVE if 'SELECT DISTINCT' is used
+ * |
+ * 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 (see {@link #separateAndConstraints(Constraint, List)})
+ * SELECTn
+ * |
+ * SOURCE or JOIN A single SOURCE or JOIN node, depending upon the query
+ * / \
+ * / \
+ * SOJ SOJ A SOURCE or JOIN node for the left and right side of the JOIN
+ * </pre>
+ * <p>
+ * There leaves of the tree are always SOURCE nodes, so <i>conceptually</i> data always flows through this plan from the bottom
+ * SOURCE nodes, is adjusted/filtered as it trickles up through the plan, and is then ready to be used by the caller as it emerges
+ * from the top node of the plan.
+ * </p>
+ * <p>
+ * This canonical plan, however, is optimized and rearranged so that it performs faster.
+ * </p>
+ */
+public class CanonicalPlanner implements Planner {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.plan.Planner#createPlan(org.jboss.dna.graph.query.QueryContext,
+ * org.jboss.dna.graph.query.model.QueryCommand)
+ */
+ public PlanNode createPlan( QueryContext context,
+ QueryCommand query ) {
+ PlanNode plan = null;
+ if (query instanceof Query) {
+ plan = createCanonicalPlan(context, (Query)query);
+ } else {
+ plan = createCanonicalPlan(context, (SetQuery)query);
+ }
+ return plan;
+ }
+
+ /**
+ * Create a canonical query plan for the given query.
+ *
+ * @param context the context in which the query is being planned
+ * @param query the query to be planned
+ * @return the root node of the plan tree representing the canonical plan
+ */
+ protected PlanNode createCanonicalPlan( QueryContext context,
+ Query query ) {
+ PlanNode plan = null;
+
+ // Process the source of the query ...
+ Map<SelectorName, Table> usedSources = new HashMap<SelectorName, Table>();
+ plan = createPlanNode(context, query.getSource(), usedSources);
+
+ // Attach criteria (on top) ...
+ plan = attachCriteria(context, plan, query.getConstraint());
+
+ // Attach the project ...
+ plan = attachProject(context, plan, query.getColumns(), usedSources);
+
+ // Attach duplicate removal ...
+ if (query.isDistinct()) {
+ plan = attachDuplicateRemoval(context, plan);
+ }
+
+ // Process the orderings and limits ...
+ plan = attachSorting(context, plan, query.getOrderings());
+ plan = attachLimits(context, plan, query.getLimits());
+ return plan;
+ }
+
+ /**
+ * Create a canonical query plan for the given set query.
+ *
+ * @param context the context in which the query is being planned
+ * @param query the set query to be planned
+ * @return the root node of the plan tree representing the canonical plan
+ */
+ protected PlanNode createCanonicalPlan( QueryContext context,
+ SetQuery query ) {
+ // Process the left and right parts of the query ...
+ PlanNode left = createPlan(context, query.getLeft());
+ PlanNode right = createPlan(context, query.getRight());
+
+ // Wrap in a set operation node ...
+ PlanNode plan = new PlanNode(Type.SET_OPERATION);
+ plan.addChildren(left, right);
+ plan.setProperty(Property.SET_OPERATION, query.getOperation());
+ plan.setProperty(Property.SET_USE_ALL, query.isAll());
+
+ // Process the orderings and limits ...
+ plan = attachSorting(context, plan, query.getOrderings());
+ plan = attachLimits(context, plan, query.getLimits());
+ return plan;
+ }
+
+ /**
+ * Create a JOIN or SOURCE node that contain the source information.
+ *
+ * @param context the execution context
+ * @param source the source to be processed; may not be null
+ * @param usedSelectors the map of {@link SelectorName}s (aliases or names) used in the query.
+ * @return the new plan; never null
+ */
+ protected PlanNode createPlanNode( QueryContext context,
+ Source source,
+ Map<SelectorName, Table> usedSelectors ) {
+ if (source instanceof Selector) {
+ // No join required ...
+ assert source instanceof AllNodes || source instanceof NamedSelector;
+ Selector selector = (Selector)source;
+ PlanNode node = new PlanNode(Type.SOURCE);
+ if (selector.hasAlias()) {
+ node.addSelector(selector.getAlias());
+ node.setProperty(Property.SOURCE_ALIAS, selector.getAlias());
+ node.setProperty(Property.SOURCE_NAME, selector.getName());
+ } else {
+ node.addSelector(selector.getName());
+ node.setProperty(Property.SOURCE_NAME, selector.getName());
+ }
+ // 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 (usedSelectors.put(selector.getAliasOrName(), table) != null) {
+ // There was already a table with this alias or name ...
+ }
+ node.setProperty(Property.SOURCE_COLUMNS, table.getColumns());
+ }
+ return node;
+ }
+ if (source instanceof Join) {
+ Join join = (Join)source;
+ // Set up new join node corresponding to this join predicate
+ PlanNode node = new PlanNode(Type.JOIN);
+ node.setProperty(Property.JOIN_TYPE, join.getType());
+ node.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
+ node.setProperty(Property.JOIN_CONDITION, join.getJoinCondition());
+
+ context.getHints().hasJoin = true;
+ if (join.getType() == JoinType.LEFT_OUTER) {
+ context.getHints().hasOptionalJoin = true;
+ }
+
+ // 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));
+
+ // Add selectors to the joinNode
+ for (PlanNode child : node.getChildren()) {
+ node.addSelectors(child.getSelectors());
+ }
+ }
+ return node;
+ }
+ // should not get here; if we do, somebody added a new type of source
+ assert false;
+ return null;
+ }
+
+ /**
+ * Attach all criteria above the join nodes. The optimizer will push these criteria down to the appropriate source.
+ *
+ * @param context the context in which the query is being planned
+ * @param plan the existing plan, which joins all source groups
+ * @param constraint the criteria or constraint from the query
+ * @return the updated plan, or the existing plan if there were no constraints; never null
+ */
+ protected PlanNode attachCriteria( final QueryContext context,
+ PlanNode plan,
+ Constraint constraint ) {
+ if (constraint == null) return plan;
+ context.getHints().hasCriteria = true;
+
+ // Extract the list of Constraint objects that all must be satisfied ...
+ LinkedList<Constraint> andableConstraints = new LinkedList<Constraint>();
+ separateAndConstraints(constraint, andableConstraints);
+ assert !andableConstraints.isEmpty();
+
+ // For each of these constraints, create a criteria (SELECT) node above the supplied (JOIN or SOURCE) node.
+ // Do this in reverse order so that the top-most SELECT node corresponds to the first constraint.
+ while (!andableConstraints.isEmpty()) {
+ Constraint criteria = andableConstraints.removeLast();
+ // Create the select node ...
+ PlanNode criteriaNode = new PlanNode(Type.SELECT);
+ criteriaNode.setProperty(Property.SELECT_CRITERIA, criteria);
+
+ // Add selectors to the criteria node ...
+ criteriaNode.addSelectors(Visitors.getSelectorsReferencedBy(criteria));
+
+ // Is a full-text search of some kind included ...
+ Visitors.visitAll(criteria, new Visitors.AbstractVisitor() {
+ @Override
+ public void visit( FullTextSearch obj ) {
+ context.getHints().hasFullTextSearch = true;
+ }
+ });
+
+ plan = criteriaNode;
+ }
+ return plan;
+ }
+
+ /**
+ * Walk the supplied constraint to extract a list of the constraints that can be AND-ed together. For example, given the
+ * constraint tree ((C1 AND C2) AND (C3 OR C4)), this method would result in a list of three separate criteria: [C1,C2,(C3 OR
+ * C4)]. The resulting <code>andConstraints</code> list will contain Constraint objects that all must be true.
+ *
+ * @param constraint the input constraint
+ * @param andableConstraints the collection into which all non-{@link And AND} constraints should be placed
+ */
+ protected void separateAndConstraints( Constraint constraint,
+ List<Constraint> andableConstraints ) {
+ if (constraint == null) return;
+ assert andableConstraints != null;
+ if (constraint instanceof And) {
+ And and = (And)constraint;
+ separateAndConstraints(and.getLeft(), andableConstraints);
+ separateAndConstraints(and.getRight(), andableConstraints);
+ } else {
+ andableConstraints.add(constraint);
+ }
+ }
+
+ /**
+ * Attach SORT node at top of tree. The SORT may be pushed down to a source (or sources) if possible by the optimizer.
+ *
+ * @param context the context in which the query is being planned
+ * @param plan the existing plan
+ * @param orderings list of orderings from the query
+ * @return the updated plan, or the existing plan if there were no orderings; never null
+ */
+ protected PlanNode attachSorting( QueryContext context,
+ PlanNode plan,
+ List<Ordering> orderings ) {
+ if (orderings.isEmpty()) return plan;
+ PlanNode sortNode = new PlanNode(Type.SORT);
+
+ context.getHints().hasSort = true;
+ sortNode.setProperty(Property.SORT_ORDER_BY, orderings);
+
+ sortNode.addLastChild(plan);
+ return sortNode;
+ }
+
+ /**
+ * Attach a LIMIT node at the top of the plan tree.
+ *
+ * @param context the context in which the query is being planned
+ * @param plan the existing plan
+ * @param limit the limit definition; may be null
+ * @return the updated plan, or the existing plan if there were no limits
+ */
+ protected PlanNode attachLimits( QueryContext context,
+ PlanNode plan,
+ Limit limit ) {
+ if (limit.isUnlimited()) return plan;
+ context.getHints().hasLimit = true;
+ PlanNode limitNode = new PlanNode(Type.LIMIT);
+
+ boolean attach = false;
+ if (limit.getOffset() != 0) {
+ limitNode.setProperty(Property.LIMIT_COUNT, limit.getOffset());
+ attach = true;
+ }
+ if (!limit.isUnlimited()) {
+ limitNode.setProperty(Property.LIMIT_OFFSET, limit.getRowLimit());
+ attach = true;
+ }
+ if (attach) {
+ limitNode.addLastChild(plan);
+ plan = limitNode;
+ }
+ return plan;
+ }
+
+ /**
+ * Attach a PROJECT node at the top of the plan tree.
+ *
+ * @param context the context in which the query is being planned
+ * @param plan the existing plan
+ * @param columns the columns being projected; may be null
+ * @param selectors the selectors keyed by their alias or name
+ * @return the updated plan
+ */
+ protected PlanNode attachProject( QueryContext context,
+ PlanNode plan,
+ List<Column> columns,
+ Map<SelectorName, Table> selectors ) {
+ if (columns == null) columns = Collections.emptyList();
+ PlanNode projectNode = new PlanNode(Type.PROJECT);
+
+ if (columns.isEmpty()) {
+ columns = new LinkedList<Column>();
+ // SELECT *, so find all of the columns that are available from all the sources ...
+ NameFactory nameFactory = context.getExecutionContext().getValueFactories().getNameFactory();
+ for (Table table : selectors.values()) {
+ // Add the selector that is being used ...
+ projectNode.addSelector(table.getName());
+ // Compute the columns from this selector ...
+ for (Schemata.Column column : table.getColumns()) {
+ String columnName = column.getName();
+ Name propertyName = nameFactory.create(columnName);
+ columns.add(new Column(table.getName(), propertyName, columnName));
+ }
+ }
+ } else {
+ // Add the selector used by each column ...
+ for (Column column : columns) {
+ SelectorName tableName = column.getSelectorName();
+ // Add the selector that is being used ...
+ projectNode.addSelector(tableName);
+
+ // Verify that each column is available in the appropriate source ...
+ Table table = selectors.get(tableName);
+ if (table == null) {
+ context.getProblems().addError(GraphI18n.tableDoesNotExist,
+ tableName.getString(context.getExecutionContext()));
+ } else {
+ // Make sure that the column is in the table ...
+ if (table.getColumn(column.getColumnName()) == null) {
+ context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable,
+ column.getColumnName(),
+ tableName.getString(context.getExecutionContext()));
+ }
+ }
+ }
+ }
+ projectNode.setProperty(Property.PROJECT_COLUMNS, columns);
+ projectNode.addLastChild(plan);
+ return projectNode;
+ }
+
+ /**
+ * Attach DUP_REMOVE node at top of tree. The DUP_REMOVE may be pushed down to a source (or sources) if possible by the
+ * optimizer.
+ *
+ * @param context the context in which the query is being planned
+ * @param plan the existing plan
+ * @return the updated plan
+ */
+ protected PlanNode attachDuplicateRemoval( QueryContext context,
+ PlanNode plan ) {
+ PlanNode dupNode = new PlanNode(Type.DUP_REMOVE);
+ plan.setParent(dupNode);
+ return dupNode;
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/JoinAlgorithm.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/JoinAlgorithm.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/JoinAlgorithm.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * The type of join algorithms.
+ */
+public enum JoinAlgorithm {
+ // PARTITIONED_SORT,
+ NESTED_LOOP,
+ MERGE
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/JoinAlgorithm.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,74 @@
+/*
+ * 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 net.jcip.annotations.NotThreadSafe;
+
+@NotThreadSafe
+public final class PlanHints {
+
+ // This flag indicates that the plan has a criteria somewhere
+ public boolean hasCriteria = false;
+
+ // This flag indicates that the plan has a join somewhere
+ public boolean hasJoin = false;
+
+ // 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
+ public boolean hasSetQuery = false;
+
+ // flag indicating that the plan has a grouping node somewhere
+ // public boolean hasAggregates = false;
+
+ // List of groups that should not be dependent
+ // public List makeNotDepGroups = null;
+
+ public boolean hasLimit = false;
+
+ public boolean hasOptionalJoin = false;
+
+ public boolean hasFullTextSearch = false;
+
+ public PlanHints() {
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("PlanHints {");
+ sb.append(" hasCriteria=").append(hasCriteria);
+ sb.append(", hasJoin=").append(hasJoin);
+ sb.append(", hasSort=").append(hasSort);
+ sb.append(", hasSetQuery=").append(hasSetQuery);
+ sb.append(", hasLimit=").append(hasLimit);
+ sb.append(", hasOptionalJoin=").append(hasOptionalJoin);
+ sb.append(", hasFullTextSearch=").append(hasFullTextSearch);
+ sb.append('}');
+ return sb.toString();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanHints.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,1141 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+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.NotThreadSafe;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Command;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.model.Ordering;
+import org.jboss.dna.graph.query.model.Readable;
+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.SetQuery.Operation;
+import org.jboss.dna.graph.query.validate.Schemata;
+
+/**
+ * A representation of a single node within a plan tree.
+ */
+@NotThreadSafe
+public final class PlanNode implements Iterable<PlanNode>, Readable {
+
+ /**
+ * An enumeration dictating the type of plan tree nodes.
+ */
+ public enum Type {
+
+ /** A node that represents an access of the underlying storage. */
+ ACCESS("Access"),
+ /** A node that defines the removal of duplicate tuples. */
+ DUP_REMOVE("DupRemoval"),
+ /** A node that defines the join type, join criteria, and join strategy */
+ JOIN("Join"),
+ /** A node that defines the columns returned from the node. */
+ PROJECT("Project"),
+ /** A node that selects a filters the tuples by applying a criteria evaluation filter node (WHERE / HAVING) */
+ SELECT("Select"),
+ /** A node that defines the columns to sort on, the sort direction for each column, and whether to remove duplicates. */
+ SORT("Sort"),
+ /** A node that defines the 'table' from which the tuples are being obtained */
+ SOURCE("Source"),
+ /** A node that groups sets of rows into groups (and where aggregation would be performed) */
+ GROUP("Group"),
+ /** A node that produces no results */
+ NULL("Null"),
+ /** A node that limits the number of tuples returned */
+ LIMIT("Limit"),
+ /** A node the performs set operations on two sets of tuples, including UNION */
+ SET_OPERATION("SetOperation");
+
+ private static final Map<String, Type> TYPE_BY_SYMBOL;
+ static {
+ Map<String, Type> typesBySymbol = new HashMap<String, Type>();
+ for (Type type : Type.values()) {
+ typesBySymbol.put(type.getSymbol().toUpperCase(), type);
+ }
+ TYPE_BY_SYMBOL = Collections.unmodifiableMap(typesBySymbol);
+ }
+
+ private final String symbol;
+
+ private Type( String symbol ) {
+ this.symbol = symbol;
+ }
+
+ /**
+ * Get the symbol representation of this node type.
+ *
+ * @return the symbol; never null and never empty
+ */
+ public String getSymbol() {
+ return symbol;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString() {
+ return symbol;
+ }
+
+ /**
+ * Attempt to find the Type given a symbol. The matching is done independent of case.
+ *
+ * @param symbol the symbol
+ * @return the Type having the supplied symbol, or null if there is no Type with the supplied symbol
+ * @throws IllegalArgumentException if the symbol is null
+ */
+ public static Type forSymbol( String symbol ) {
+ CheckArg.isNotNull(symbol, "symbol");
+ return TYPE_BY_SYMBOL.get(symbol.toUpperCase().trim());
+ }
+ }
+
+ /**
+ * An enumeration dictating the type of plan tree nodes.
+ */
+ public enum Property {
+ /** For SELECT and JOIN nodes, a flag specifying whether the criteria is dependent. Value is a {@link Boolean} object. */
+ IS_DEPENDENT,
+
+ /** For SELECT nodes, the criteria object that is to be applied. Value is a {@link Constraint} object. */
+ SELECT_CRITERIA,
+
+ /** For SET_OPERATION nodes, the type of set operation to be performed. Value is a {@link Operation} object. */
+ SET_OPERATION,
+ /** For SET_OPERATION nodes, whether the 'all' clause is used. Value is a {@link Boolean} object. */
+ SET_USE_ALL,
+
+ /** For JOIN nodes, the type of join operation. Value is a {@link JoinType} object. */
+ JOIN_TYPE,
+ /** For JOIN nodes, the type of join algorithm. Value is a {@link JoinAlgorithm} object. */
+ JOIN_ALGORITHM,
+ /** For JOIN nodes, the join criteria (or join condition). Value is a {@link JoinCondition} object. */
+ JOIN_CONDITION,
+ /**
+ * For JOIN nodes, additional criteria that have been pushed down to the join. Value is a List of {@link Constraint}
+ * object.
+ */
+ JOIN_CONSTRAINTS,
+
+ /** For SOURCE nodes, the literal name of the selector. Value is a {@link SelectorName} object. */
+ SOURCE_NAME,
+ /** For SOURCE nodes, the alias name of the selector. Value is a {@link SelectorName} object. */
+ SOURCE_ALIAS,
+ /**
+ * For SOURCE nodes, the collection of columns that are available. Value is a Collection of {@link Schemata.Column}
+ * objects.
+ */
+ SOURCE_COLUMNS,
+
+ /** For PROJECT nodes, the ordered collection of columns being projected. Value is a Collection of {@link Column} objects. */
+ PROJECT_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).
+ */
+ SORT_ORDER_BY,
+
+ /** For LIMIT nodes, the maximum number of rows to return. Value is an {@link Integer} object. */
+ LIMIT_COUNT,
+ /** For LIMIT nodes, the offset value. Value is an {@link Integer} object. */
+ LIMIT_OFFSET,
+
+ /** For ACCESS nodes, the {@link Command} that is to be executed by the source */
+ // ACCESS_COMMAND,
+ BOGUS; // remove this
+ }
+
+ private Type type;
+ private PlanNode parent;
+ private LinkedList<PlanNode> children = new LinkedList<PlanNode>();
+ private List<PlanNode> childrenView = Collections.unmodifiableList(children);
+ private Map<Property, Object> nodeProperties;
+
+ /** The set of named selectors (e.g., tables) that this node deals with. */
+ private Set<SelectorName> selectors = new HashSet<SelectorName>();
+
+ /**
+ * Create a new plan node with the supplied initial type.
+ *
+ * @param type the type of the node; may not be null
+ */
+ public PlanNode( Type type ) {
+ assert type != null;
+ this.type = type;
+ }
+
+ /**
+ * Create a new plan node with the supplied initial type ad that is a child of the supplied parent.
+ *
+ * @param type the type of the node; may not be null
+ * @param parent the parent node, or null if there is no parent
+ */
+ public PlanNode( Type type,
+ PlanNode parent ) {
+ assert type != null;
+ this.type = type;
+ if (parent != null) {
+ this.parent = parent;
+ this.parent.children.add(this);
+ }
+ }
+
+ /**
+ * Create a new plan node with the supplied initial type ad that is a child of the supplied parent.
+ *
+ * @param type the type of the node; may not be null
+ * @param selectors the selectors that should be assigned to this node
+ */
+ public PlanNode( Type type,
+ SelectorName... selectors ) {
+ this(type);
+ for (SelectorName selector : selectors) {
+ addSelector(selector);
+ }
+ }
+
+ /**
+ * Create a new plan node with the supplied initial type ad that is a child of the supplied parent.
+ *
+ * @param type the type of the node; may not be null
+ * @param selectors the selectors that should be assigned to this node
+ */
+ public PlanNode( Type type,
+ Iterable<SelectorName> selectors ) {
+ this(type);
+ addSelectors(selectors);
+ }
+
+ /**
+ * Create a new plan node with the supplied initial type ad that is a child of the supplied parent.
+ *
+ * @param type the type of the node; may not be null
+ * @param parent the parent node, or null if there is no parent
+ * @param selectors the selectors that should be assigned to this node
+ */
+ public PlanNode( Type type,
+ PlanNode parent,
+ SelectorName... selectors ) {
+ this(type, parent);
+ for (SelectorName selector : selectors) {
+ addSelector(selector);
+ }
+ }
+
+ /**
+ * Create a new plan node with the supplied initial type ad that is a child of the supplied parent.
+ *
+ * @param type the type of the node; may not be null
+ * @param parent the parent node, or null if there is no parent
+ * @param selectors the selectors that should be assigned to this node
+ */
+ public PlanNode( Type type,
+ PlanNode parent,
+ Iterable<SelectorName> selectors ) {
+ this(type, parent);
+ addSelectors(selectors);
+ }
+
+ /**
+ * Get the type for this node.
+ *
+ * @return the node type, or null if there is no node type
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Set the type for this node.
+ *
+ * @param type Sets type to the specified value; may not be null
+ */
+ public void setType( Type type ) {
+ assert type != null;
+ this.type = type;
+ }
+
+ /**
+ * Get the parent of this node.
+ *
+ * @return the parent node, or null if this node has no parent
+ */
+ public PlanNode getParent() {
+ return parent;
+ }
+
+ /**
+ * Set the parent for this node. If this node already has a parent, this method will remove this node from the current parent.
+ * If the supplied parent is not null, then this node will be added to the supplied parent's children.
+ *
+ * @param parent the new parent, or null if this node is to have no parent
+ */
+ public void setParent( PlanNode parent ) {
+ removeFromParent();
+ if (parent != null) {
+ this.parent = parent;
+ this.parent.children.add(this);
+ }
+ }
+
+ /**
+ * Insert the supplied node into the plan node tree immediately above this node. If this node has a parent when this method is
+ * called, the new parent essentially takes the place of this node within the list of children of the old parent. This method
+ * does nothing if the supplied new parent is null.
+ * <p>
+ * For example, consider a plan node tree before this method is called:
+ *
+ * <pre>
+ * A
+ * / | \
+ * / | \
+ * B C D
+ * </pre>
+ *
+ * Then after this method is called with <code>c.insertAsParent(e)</code>, the resulting plan node tree will be:
+ *
+ * <pre>
+ * A
+ * / | \
+ * / | \
+ * B E D
+ * |
+ * |
+ * C
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * Also note that the node on which this method is called ('C' in the example above) will always be added as the
+ * {@link #addLastChild(PlanNode) last child} to the new parent. This allows the new parent to already have children before
+ * this method is called.
+ * </p>
+ *
+ * @param newParent the new parent; method does nothing if this is null
+ */
+ public void insertAsParent( PlanNode newParent ) {
+ if (newParent == null) return;
+ if (this.parent != null) {
+ this.parent.replaceChild(this, newParent);
+ }
+ newParent.addLastChild(this);
+ }
+
+ /**
+ * Remove this node from its parent, and return the node that used to be the parent of this node. Note that this method
+ * removes the entire subgraph under this node.
+ *
+ * @return the node that was the parent of this node, or null if this node had no parent
+ * @see #extractChild(PlanNode)
+ * @see #extractFromParent()
+ */
+ public PlanNode removeFromParent() {
+ PlanNode result = this.parent;
+ if (this.parent != null) {
+ // Remove this node from its current parent ...
+ this.parent.children.remove(this);
+ this.parent = null;
+ }
+ return result;
+ }
+
+ /**
+ * Get the unmodifiable list of child nodes. This list will immediately reflect any changes made to the children (via other
+ * methods), but this list cannot be used to add or remove children.
+ *
+ * @return the list of children, which immediately reflects changes but which cannot be modified directly; never null
+ */
+ public List<PlanNode> getChildren() {
+ return childrenView;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This iterator is immutable.
+ * </p>
+ *
+ * @see java.lang.Iterable#iterator()
+ */
+ public Iterator<PlanNode> iterator() {
+ return childrenView.iterator();
+ }
+
+ /**
+ * Remove all children from this node. All nodes immediately become orphaned. The resulting list will be mutable.
+ *
+ * @return a copy of all the of the children that were removed (and which have no parent); never null
+ */
+ public List<PlanNode> removeAllChildren() {
+ if (this.children.isEmpty()) {
+ return new ArrayList<PlanNode>(0);
+ }
+ List<PlanNode> copyOfChildren = new ArrayList<PlanNode>(this.children);
+ for (Iterator<PlanNode> childIter = this.children.iterator(); childIter.hasNext();) {
+ PlanNode child = childIter.next();
+ childIter.remove();
+ child.parent = null;
+ }
+ return copyOfChildren;
+ }
+
+ /**
+ * Replace the supplied child with another node. If the replacement is already a child of this node, this method effectively
+ * swaps the position of the child and replacement nodes.
+ *
+ * @param child the node that is already a child and that is to be replaced; may not be null and must be a child
+ * @param replacement the node that is to replace the 'child' node; may not be null
+ * @return true if the child was successfully replaced
+ */
+ public boolean replaceChild( PlanNode child,
+ PlanNode replacement ) {
+ assert child != null;
+ assert replacement != null;
+ if (child.parent == this) {
+ int i = this.children.indexOf(child);
+ if (replacement.parent == this) {
+ // Swapping the positions ...
+ int j = this.children.indexOf(replacement);
+ this.children.set(i, replacement);
+ this.children.set(j, child);
+ return true;
+ }
+ // The replacement is not yet a child ...
+ this.children.set(i, replacement);
+ replacement.removeFromParent();
+ replacement.parent = this;
+ child.parent = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the number of child nodes.
+ *
+ * @return the number of children; never negative
+ */
+ public int getChildCount() {
+ return this.children.size();
+ }
+
+ /**
+ * Get the first child.
+ *
+ * @return the first child, or null if there are no children
+ */
+ public PlanNode getFirstChild() {
+ return this.children.isEmpty() ? null : this.children.getFirst();
+ }
+
+ /**
+ * Get the last child.
+ *
+ * @return the last child, or null if there are no children
+ */
+ public PlanNode getLastChild() {
+ return this.children.isEmpty() ? null : this.children.getLast();
+ }
+
+ /**
+ * Get the child at the supplied index.
+ *
+ * @param index the index
+ * @return the child, or null if there are no children
+ * @throws IndexOutOfBoundsException if the index is not valid given the number of children
+ */
+ public PlanNode getChild( int index ) {
+ return this.children.isEmpty() ? null : this.children.get(index);
+ }
+
+ /**
+ * Add the supplied node to the front of the list of children.
+ *
+ * @param child the node that should be added as the first child; may not be null
+ */
+ public void addFirstChild( PlanNode child ) {
+ assert child != null;
+ this.children.addFirst(child);
+ child.removeFromParent();
+ child.parent = this;
+ }
+
+ /**
+ * Add the supplied node to the end of the list of children.
+ *
+ * @param child the node that should be added as the last child; may not be null
+ */
+ public void addLastChild( PlanNode child ) {
+ assert child != null;
+ this.children.addLast(child);
+ child.removeFromParent();
+ child.parent = this;
+ }
+
+ /**
+ * Add the supplied nodes at the end of the list of children.
+ *
+ * @param otherChildren the children to add; may not be null
+ */
+ public void addChildren( Iterable<PlanNode> otherChildren ) {
+ assert otherChildren != null;
+ for (PlanNode planNode : otherChildren) {
+ this.addLastChild(planNode);
+ }
+ }
+
+ /**
+ * Add the supplied nodes at the end of the list of children.
+ *
+ * @param first the first child to add
+ * @param second the second child to add
+ */
+ public void addChildren( PlanNode first,
+ PlanNode second ) {
+ if (first != null) this.addLastChild(first);
+ if (second != null) this.addLastChild(second);
+ }
+
+ /**
+ * Add the supplied nodes at the end of the list of children.
+ *
+ * @param first the first child to add
+ * @param second the second child to add
+ * @param third the third child to add
+ */
+ public void addChildren( PlanNode first,
+ PlanNode second,
+ PlanNode third ) {
+ if (first != null) this.addLastChild(first);
+ if (second != null) this.addLastChild(second);
+ if (third != null) this.addLastChild(third);
+ }
+
+ /**
+ * Remove the node from this node.
+ *
+ * @param child the child node; may not be null
+ * @return true if the child was removed from this node, or false if the supplied node was not a child of this node
+ */
+ public boolean removeChild( PlanNode child ) {
+ boolean result = this.children.remove(child);
+ if (result) {
+ child.parent = null;
+ }
+ return result;
+ }
+
+ /**
+ * Remove the child node from this node, and replace that child with its first child (if there is one).
+ *
+ * @param child the child to be extracted; may not be null and must have at most 1 child
+ * @see #extractFromParent()
+ */
+ public void extractChild( PlanNode child ) {
+ if (child.getChildCount() == 0) {
+ removeChild(child);
+ } else {
+ PlanNode grandChild = child.getFirstChild();
+ replaceChild(child, grandChild);
+ }
+ }
+
+ /**
+ * Extract this node from its parent, but replace this node with its child (if there is one).
+ *
+ * @see #extractChild(PlanNode)
+ */
+ public void extractFromParent() {
+ this.parent.extractChild(this);
+ }
+
+ /**
+ * Get the keys for the property values that are set on this node.
+ *
+ * @return the property keys; never null but possibly empty
+ */
+ public Set<Property> getPropertyKeys() {
+ return nodeProperties != null ? nodeProperties.keySet() : Collections.<Property>emptySet();
+ }
+
+ /**
+ * Get the node's value for this supplied property.
+ *
+ * @param propertyId the property identifier
+ * @return the value, or null if there is no property on this node
+ */
+ public Object getProperty( Property propertyId ) {
+ return nodeProperties != null ? nodeProperties.get(propertyId) : null;
+ }
+
+ /**
+ * Get the node's value for this supplied property, casting the result to the supplied type.
+ *
+ * @param <ValueType> the type of the value expected
+ * @param propertyId the property identifier
+ * @param type the class denoting the type of value expected; may not be null
+ * @return the value, or null if there is no property on this node
+ */
+ public <ValueType> ValueType getProperty( Property propertyId,
+ Class<ValueType> type ) {
+ return nodeProperties != null ? type.cast(nodeProperties.get(propertyId)) : null;
+ }
+
+ /**
+ * Get the node's value for this supplied property, casting the result to a {@link Collection} of the supplied type.
+ *
+ * @param <ValueType> the type of the value expected
+ * @param propertyId the property identifier
+ * @param type the class denoting the type of value expected; may not be null
+ * @return the value, or null if there is no property on this node
+ */
+ @SuppressWarnings( "unchecked" )
+ public <ValueType> Collection<ValueType> getPropertyAsCollection( Property propertyId,
+ Class<ValueType> type ) {
+ if (nodeProperties == null) return null;
+ return (Collection)nodeProperties.get(propertyId);
+ }
+
+ /**
+ * Get the node's value for this supplied property, casting the result to a {@link List} of the supplied type.
+ *
+ * @param <ValueType> the type of the value expected
+ * @param propertyId the property identifier
+ * @param type the class denoting the type of value expected; may not be null
+ * @return the value, or null if there is no property on this node
+ */
+ @SuppressWarnings( "unchecked" )
+ public <ValueType> List<ValueType> getPropertyAsList( Property propertyId,
+ Class<ValueType> type ) {
+ if (nodeProperties == null) return null;
+ return (List)nodeProperties.get(propertyId);
+ }
+
+ /**
+ * Set the node's value for the supplied property.
+ *
+ * @param propertyId the property identifier
+ * @param value the value, or null if the property is to be removed
+ * @return the previous value that was overwritten by this call, or null if there was prior value
+ */
+ public Object setProperty( Property propertyId,
+ Object value ) {
+ if (value == null) {
+ // Removing this property ...
+ return nodeProperties != null ? nodeProperties.remove(propertyId) : null;
+ }
+ // Otherwise, we're adding the property
+ if (nodeProperties == null) nodeProperties = new HashMap<Property, Object>();
+ return nodeProperties.put(propertyId, value);
+ }
+
+ /**
+ * Remove the node's value for this supplied property.
+ *
+ * @param propertyId the property identifier
+ * @return the value that was removed, or null if there was no property on this node
+ */
+ public Object removeProperty( Object propertyId ) {
+ return nodeProperties != null ? nodeProperties.remove(propertyId) : null;
+ }
+
+ /**
+ * Indicates if there is a non-null value for the property.
+ *
+ * @param propertyId the property identifier
+ * @return true if this node has a non-null value for that property, or false if the value is null or there is no property
+ */
+ public boolean hasProperty( Property propertyId ) {
+ return nodeProperties != null && nodeProperties.containsKey(propertyId);
+ }
+
+ /**
+ * Indicates if there is a non-null and non-empty Collection value for the property.
+ *
+ * @param propertyId the property identifier
+ * @return true if this node has value for the supplied property and that value is a non-empty Collection
+ */
+ public boolean hasCollectionProperty( Property propertyId ) {
+ Object value = getProperty(propertyId);
+ return (value instanceof Collection && !((Collection<?>)value).isEmpty());
+ }
+
+ /**
+ * Indicates if there is a non-null property value that equates to a <code>true</code> boolean value.
+ *
+ * @param propertyId the property identifier
+ * @return true if this node has value for the supplied property and that value is a boolean value of <code>true</code>
+ */
+ public boolean hasBooleanProperty( Property propertyId ) {
+ Object value = getProperty(propertyId);
+ return (value instanceof Boolean && ((Boolean)value).booleanValue());
+ }
+
+ /**
+ * Add a selector to this plan node. This method does nothing if the supplied selector is null.
+ *
+ * @param symbol the symbol of the selector
+ */
+ public void addSelector( SelectorName symbol ) {
+ if (symbol != null) selectors.add(symbol);
+ }
+
+ /**
+ * Add the selectors to this plan node. This method does nothing for any supplied selector that is null.
+ *
+ * @param first the first symbol to be added
+ * @param second the second symbol to be added
+ */
+ public void addSelector( SelectorName first,
+ SelectorName second ) {
+ if (first != null) selectors.add(first);
+ if (second != null) selectors.add(second);
+ }
+
+ /**
+ * Add the selectors to this plan node. This method does nothing for any supplied selector that is null.
+ *
+ * @param names the symbols to be added
+ */
+ public void addSelectors( Iterable<SelectorName> names ) {
+ for (SelectorName name : names) {
+ if (name != null) selectors.add(name);
+ }
+ }
+
+ /**
+ * Get the selectors that are referenced by this plan node.
+ *
+ * @return the names of the selectors; never null but possibly empty
+ */
+ public Set<SelectorName> getSelectors() {
+ return selectors;
+ }
+
+ /**
+ * Get the path from this node (inclusive) to the supplied descendant node (inclusive)
+ *
+ * @param descendant the descendant; may not be null, and must be a descendant of this node
+ * @return the path from this node to the supplied descendant node; never null
+ */
+ public LinkedList<PlanNode> getPathTo( PlanNode descendant ) {
+ assert descendant != null;
+ LinkedList<PlanNode> stack = new LinkedList<PlanNode>();
+ PlanNode node = descendant;
+ while (node != this) {
+ stack.addFirst(node);
+ node = node.getParent();
+ assert node != null : "The supplied node is not a descendant of this node";
+ }
+ stack.addFirst(this);
+ return stack;
+ }
+
+ /**
+ * Determine whether this node has an ancestor with the supplied type.
+ *
+ * @param type the type; may not be null
+ * @return true if there is at least one ancestor of the supplied type, or false otherwise
+ */
+ public boolean hasAncestorOfType( Type type ) {
+ return hasAncestorOfType(EnumSet.of(type));
+ }
+
+ /**
+ * Determine whether this node has an ancestor with any of the supplied types.
+ *
+ * @param firstType the first type; may not be null
+ * @param additionalTypes the additional types; may not be null
+ * @return true if there is at least one ancestor that has any of the supplied types, or false otherwise
+ */
+ public boolean hasAncestorOfType( Type firstType,
+ Type... additionalTypes ) {
+ return hasAncestorOfType(EnumSet.of(firstType, additionalTypes));
+ }
+
+ /**
+ * Determine whether this node has an ancestor with any of the supplied types.
+ *
+ * @param types the types; may not be null
+ * @return true if there is at least one ancestor that has any of the supplied types, or false otherwise
+ */
+ public boolean hasAncestorOfType( Set<Type> types ) {
+ PlanNode node = this.parent;
+ while (node != null) {
+ if (types.contains(node.getType())) return true;
+ node = node.getParent();
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getString(ExecutionContext.DEFAULT_CONTEXT);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public final boolean equals( Object obj ) {
+ // Quite a few methods rely upon instance/reference equality ...
+ return super.equals(obj);
+ }
+
+ /**
+ * Determine whether the supplied plan is equivalent to this plan.
+ *
+ * @param other the other plan to compare with this instance
+ * @return true if the two plans are equivalent, or false otherwise
+ */
+ 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 (!this.getSelectors().equals(other.getSelectors())) return false;
+ return this.getChildren().equals(other.getChildren());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.model.Readable#getString(org.jboss.dna.graph.ExecutionContext)
+ */
+ public String getString( ExecutionContext context ) {
+ StringBuilder sb = new StringBuilder();
+ getRecursiveString(context, sb, 0);
+ return sb.toString();
+ }
+
+ private void getRecursiveString( ExecutionContext context,
+ StringBuilder str,
+ int indentLevel ) {
+ for (int i = 0; i < indentLevel; ++i) {
+ str.append(" ");
+ }
+ getNodeString(context, str).append('\n');
+
+ // Recursively add children at one greater tab level
+ for (PlanNode child : this) {
+ child.getRecursiveString(context, str, indentLevel + 1);
+ }
+ }
+
+ private StringBuilder getNodeString( ExecutionContext context,
+ StringBuilder str ) {
+ str.append(this.type.getSymbol());
+ if (!selectors.isEmpty()) {
+ str.append(" [");
+ boolean first = true;
+ for (SelectorName symbol : selectors) {
+ if (first) first = false;
+ else str.append(',');
+ str.append(symbol.getName());
+ }
+ str.append(']');
+ }
+ if (nodeProperties != null && !nodeProperties.isEmpty()) {
+ str.append(" <");
+ boolean first = true;
+ for (Map.Entry<Property, Object> entry : nodeProperties.entrySet()) {
+ if (first) first = false;
+ else str.append(", ");
+ str.append(entry.getKey()).append('=');
+ Object value = entry.getValue();
+ if (value instanceof Visitable) {
+ str.append(Visitors.readable((Visitable)value, context));
+ } else if (value instanceof Collection) {
+ boolean firstItem = true;
+ str.append('[');
+ for (Object item : (Collection<?>)value) {
+ if (firstItem) firstItem = false;
+ else str.append(", ");
+ if (item instanceof Visitable) {
+ str.append(Visitors.readable((Visitable)item, context));
+ } else {
+ str.append(item);
+ }
+ }
+ str.append(']');
+ } else {
+ str.append(value);
+ }
+ }
+ str.append('>');
+ }
+ return str;
+ }
+
+ /**
+ * Starting at the parent of this node, find the lowest (also closest) ancestor that has the specified type.
+ *
+ * @param typeToFind the type of the node to find; may not be null
+ * @return the node with the specified type, or null if there is no such ancestor
+ */
+ public PlanNode findAncestor( Type typeToFind ) {
+ return findAncestor(EnumSet.of(typeToFind));
+ }
+
+ /**
+ * Starting at the parent of this node, find the lowest (also closest) ancestor that has one of the specified types.
+ *
+ * @param firstTypeToFind the first type to find; may not be null
+ * @param additionalTypesToFind additional types to find; may not be null
+ * @return the node with the specified type, or null if there is no such ancestor
+ */
+ public PlanNode findAncestor( Type firstTypeToFind,
+ Type... additionalTypesToFind ) {
+ return findAncestor(EnumSet.of(firstTypeToFind, additionalTypesToFind));
+ }
+
+ /**
+ * Starting at the parent of this node, find the lowest (also closest) ancestor that has one of the specified types.
+ *
+ * @param typesToFind the set of types to find; may not be null
+ * @return the node with one of the specified types, or null if there is no such ancestor
+ */
+ public PlanNode findAncestor( Set<Type> typesToFind ) {
+ PlanNode node = this;
+ PlanNode parent = null;
+ while ((parent = node.getParent()) != null) {
+ if (typesToFind.contains(parent.getType())) return parent;
+ node = parent;
+ }
+ return null;
+ }
+
+ public static enum Traversal {
+ LEVEL_ORDER,
+ PRE_ORDER;
+ }
+
+ /**
+ * 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
+ * @return the collection of nodes that are at or below this node that all have the supplied type; never null but possibly
+ * empty
+ */
+ public List<PlanNode> findAllAtOrBelow( Type typeToFind ) {
+ return findAllAtOrBelow(EnumSet.of(typeToFind));
+ }
+
+ /**
+ * Find all of the nodes with one of the specified types that are at or below this node.
+ *
+ * @param firstTypeToFind the first type of node to find; may not be null
+ * @param additionalTypesToFind the additional types of node to find; may not be null
+ * @return the collection of nodes that are at or below this node that all have one of the supplied types; never null but
+ * possibly empty
+ */
+ public List<PlanNode> findAllAtOrBelow( Type firstTypeToFind,
+ Type... additionalTypesToFind ) {
+ return findAllAtOrBelow(EnumSet.of(firstTypeToFind, additionalTypesToFind));
+ }
+
+ /**
+ * Find all of the nodes with one of the specified types that are at or below this node.
+ *
+ * @param typesToFind the types of node to find; may not be null
+ * @return the collection of nodes that are at or below this node that all have one of the supplied types; never null but
+ * possibly empty
+ */
+ public List<PlanNode> findAllAtOrBelow( Set<Type> typesToFind ) {
+ return findAllAtOrBelow(Traversal.PRE_ORDER, typesToFind);
+ }
+
+ /**
+ * Find all of the nodes of the specified type that are at or below this node.
+ *
+ * @param order the order to traverse; may not be null
+ * @param typeToFind the type of node to find; may not be null
+ * @return the collection of nodes that are at or below this node that all have the supplied type; never null but possibly
+ * empty
+ */
+ public List<PlanNode> findAllAtOrBelow( Traversal order,
+ Type typeToFind ) {
+ return findAllAtOrBelow(order, EnumSet.of(typeToFind));
+ }
+
+ /**
+ * Find all of the nodes with one of the specified types that are at or below this node.
+ *
+ * @param order the order to traverse; may not be null
+ * @param firstTypeToFind the first type of node to find; may not be null
+ * @param additionalTypesToFind the additional types of node to find; may not be null
+ * @return the collection of nodes that are at or below this node that all have one of the supplied types; never null but
+ * possibly empty
+ */
+ public List<PlanNode> findAllAtOrBelow( Traversal order,
+ Type firstTypeToFind,
+ Type... additionalTypesToFind ) {
+ return findAllAtOrBelow(order, EnumSet.of(firstTypeToFind, additionalTypesToFind));
+ }
+
+ /**
+ * Find all of the nodes with one of the specified types that are at or below this node.
+ *
+ * @param order the order to traverse; may not be null
+ * @param typesToFind the types of node to find; may not be null
+ * @return the collection of nodes that are at or below this node that all have one of the supplied types; never null but
+ * possibly empty
+ */
+ public List<PlanNode> findAllAtOrBelow( Traversal order,
+ Set<Type> typesToFind ) {
+ 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();
+ if (typesToFind.contains(aNode.getType())) {
+ results.add(aNode);
+ }
+ switch (order) {
+ case LEVEL_ORDER:
+ queue.addAll(aNode.getChildren());
+ break;
+ case PRE_ORDER:
+ queue.addAll(0, aNode.getChildren());
+ break;
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Find the first node with the specified type that are at or below this node.
+ *
+ * @param typeToFind the type of node to find; may not be null
+ * @return the first node that is at or below this node that has the supplied type; or null if there is no such node
+ */
+ public PlanNode findAtOrBelow( Type typeToFind ) {
+ return findAtOrBelow(EnumSet.of(typeToFind));
+ }
+
+ /**
+ * Find the first node with one of the specified types that are at or below this node.
+ *
+ * @param firstTypeToFind the first type of node to find; may not be null
+ * @param additionalTypesToFind the additional types of node to find; may not be null
+ * @return the first node that is at or below this node that has one of the supplied types; or null if there is no such node
+ */
+ public PlanNode findAtOrBelow( Type firstTypeToFind,
+ Type... additionalTypesToFind ) {
+ return findAtOrBelow(EnumSet.of(firstTypeToFind, additionalTypesToFind));
+ }
+
+ /**
+ * Find the first node with one of the specified types that are at or below this node.
+ *
+ * @param typesToFind the types of node to find; may not be null
+ * @return the first node that is at or below this node that has one of the supplied types; or null if there is no such node
+ */
+ public PlanNode findAtOrBelow( Set<Type> typesToFind ) {
+ return findAtOrBelow(Traversal.PRE_ORDER, typesToFind);
+ }
+
+ /**
+ * Find the first node with the specified type that are at or below this node.
+ *
+ * @param order the order to traverse; may not be null
+ * @param typeToFind the type of node to find; may not be null
+ * @return the first node that is at or below this node that has the supplied type; or null if there is no such node
+ */
+ public PlanNode findAtOrBelow( Traversal order,
+ Type typeToFind ) {
+ return findAtOrBelow(order, EnumSet.of(typeToFind));
+ }
+
+ /**
+ * Find the first node with one of the specified types that are at or below this node.
+ *
+ * @param order the order to traverse; may not be null
+ * @param firstTypeToFind the first type of node to find; may not be null
+ * @param additionalTypesToFind the additional types of node to find; may not be null
+ * @return the first node that is at or below this node that has one of the supplied types; or null if there is no such node
+ */
+ public PlanNode findAtOrBelow( Traversal order,
+ Type firstTypeToFind,
+ Type... additionalTypesToFind ) {
+ return findAtOrBelow(order, EnumSet.of(firstTypeToFind, additionalTypesToFind));
+ }
+
+ /**
+ * Find the first node with one of the specified types that are at or below this node.
+ *
+ * @param order the order to traverse; may not be null
+ * @param typesToFind the types of node to find; may not be null
+ * @return the first node that is at or below this node that has one of the supplied types; or null if there is no such node
+ */
+ public PlanNode findAtOrBelow( Traversal order,
+ Set<Type> typesToFind ) {
+ LinkedList<PlanNode> queue = new LinkedList<PlanNode>();
+ queue.add(this);
+ while (!queue.isEmpty()) {
+ PlanNode aNode = queue.poll();
+ if (typesToFind.contains(aNode.getType())) {
+ return aNode;
+ }
+ switch (order) {
+ case LEVEL_ORDER:
+ queue.addAll(aNode.getChildren());
+ break;
+ case PRE_ORDER:
+ queue.addAll(0, aNode.getChildren());
+ break;
+ }
+ }
+ return null;
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/Planner.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/Planner.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/Planner.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,44 @@
+/*
+ * 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 org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.QueryCommand;
+
+/**
+ * Interface for a query planner.
+ */
+public interface Planner {
+
+ /**
+ * Create a canonical query plan for the given command.
+ *
+ * @param context the context in which the query is being planned
+ * @param query the query command to be planned
+ * @return the root node of the plan tree representing the canonical plan
+ */
+ public PlanNode createPlan( QueryContext context,
+ QueryCommand query );
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/Planner.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DelegatingComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DelegatingComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DelegatingComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,63 @@
+/*
+ * 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.process;
+
+import org.jboss.dna.graph.query.QueryResults.Columns;
+
+/**
+ */
+public abstract class DelegatingComponent extends ProcessingComponent {
+
+ private final ProcessingComponent delegate;
+
+ protected DelegatingComponent( ProcessingComponent delegate ) {
+ this(delegate, delegate.getColumns());
+ }
+
+ protected DelegatingComponent( ProcessingComponent delegate,
+ Columns overridingColumns ) {
+ super(delegate.getContext(), overridingColumns);
+ this.delegate = delegate;
+ }
+
+ /**
+ * Get the delegate processor.
+ *
+ * @return the delegate processor
+ */
+ protected final ProcessingComponent delegate() {
+ return delegate;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#close()
+ */
+ @Override
+ public void close() {
+ super.close();
+ this.delegate.close();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DelegatingComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,84 @@
+/*
+ * 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.process;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.jboss.dna.graph.Location;
+
+/**
+ * A {@link ProcessingComponent} implementation that removes duplicates. The results from the delegate component do not need to be
+ * sorted; in fact, if the delegate component is a {@link SortValuesComponent}, then use {@link DistinctOfSortedComponent} instead of
+ * this class.
+ */
+public class DistinctComponent extends DelegatingComponent {
+
+ public DistinctComponent( ProcessingComponent delegate ) {
+ super(delegate);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ List<Object[]> tuples = delegate().execute();
+ if (tuples.size() > 1) {
+ // Go through this list and remove any tuples that appear more than once ...
+ Iterator<Object[]> iter = tuples.iterator();
+
+ int locationCount = getColumns().getLocationCount();
+ int firstLocationIndex = getColumns().getColumnCount();
+ if (locationCount == 1) {
+ // We can determine duplicates faster/cheaper using a single Set<Location> ...
+ Set<Location> found = new HashSet<Location>();
+ while (iter.hasNext()) {
+ Location location = (Location)iter.next()[firstLocationIndex];
+ if (!found.add(location)) {
+ // Was already found, so remove this tuple from the results ...
+ iter.remove();
+ }
+ }
+ } else {
+ assert locationCount > 1;
+ // Duplicate tuples are removed using a Set<Location[]> ...
+ Set<Location[]> found = new HashSet<Location[]>();
+ while (iter.hasNext()) {
+ Object[] tuple = iter.next();
+ Location[] locations = new Location[locationCount];
+ System.arraycopy(tuple, firstLocationIndex, locations, 0, locationCount);
+ if (!found.add(locations)) {
+ // Was already found, so remove this tuple from the results ...
+ iter.remove();
+ }
+ }
+ }
+ }
+ return tuples;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctOfSortedComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctOfSortedComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctOfSortedComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,64 @@
+/*
+ * 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.process;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An efficient {@link ProcessingComponent} that removes duplicates from an already-sorted set of results.
+ *
+ * @see DistinctComponent
+ */
+public class DistinctOfSortedComponent extends DelegatingComponent {
+
+ private final Comparator<Object[]> comparator;
+
+ public DistinctOfSortedComponent( SortValuesComponent delegate ) {
+ super(delegate);
+ this.comparator = delegate.getSortingComparator();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ List<Object[]> tuples = delegate().execute();
+ Iterator<Object[]> iter = tuples.iterator();
+ Object[] previous = null;
+ while (iter.hasNext()) {
+ Object[] current = iter.next();
+ if (previous != null && this.comparator.compare(previous, current) == 0) {
+ iter.remove();
+ } else {
+ previous = current;
+ }
+ }
+ return tuples;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/DistinctOfSortedComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ExceptComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ExceptComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ExceptComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,123 @@
+/*
+ * 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.process;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+
+/**
+ */
+public class ExceptComponent extends SetOperationComponent {
+
+ private final Comparator<Object[]> comparator;
+
+ public ExceptComponent( QueryContext context,
+ Columns columns,
+ Iterable<ProcessingComponent> sources,
+ boolean alreadySorted,
+ boolean all ) {
+ super(context, columns, sources, alreadySorted, all);
+ this.comparator = createSortComparator(context, columns);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ Iterator<ProcessingComponent> sources = sources().iterator();
+ if (!sources.hasNext()) return emptyTuples();
+
+ // Execute all of the source components (so we can sort the results from the smallest to the largest) ...
+ // TODO: Parallelize this ???
+ List<List<Object[]>> allTuples = new LinkedList<List<Object[]>>();
+ while (sources.hasNext()) {
+ List<Object[]> tuples = sources.next().execute();
+ if (tuples == null) continue;
+ if (tuples.isEmpty()) return emptyTuples();
+ allTuples.add(tuples);
+ }
+ if (allTuples.isEmpty()) return emptyTuples();
+ if (allTuples.size() == 1) return allTuples.get(0); // just one source
+ // Sort the tuples by size, starting with the smallest ...
+ Collections.sort(allTuples, new Comparator<List<Object[]>>() {
+ public int compare( List<Object[]> tuples1,
+ List<Object[]> tuples2 ) {
+ return tuples1.size() - tuples2.size();
+ }
+ });
+
+ // Do the sources 2 at a time ...
+ Iterator<List<Object[]>> iter = allTuples.iterator();
+ List<Object[]> tuples = iter.next(); // already sorted ...
+ assert iter.hasNext();
+ // Process the next source with the previous ...
+ while (iter.hasNext()) {
+ List<Object[]> next = iter.next(); // already sorted ...
+ // Walk through the first and second results ...
+ Iterator<Object[]> tupleIter = tuples.iterator();
+ Iterator<Object[]> nextIter = next.iterator();
+ // There is at least one tuple in each ...
+ Object[] tuple1 = tupleIter.next();
+ Object[] tuple2 = nextIter.next();
+ while (true) {
+ int comparison = comparator.compare(tuple1, tuple2);
+ if (comparison == 0) {
+ // Both match, so remove the tuple from 'tuples' and go on ...
+ tupleIter.remove();
+ continue;
+ }
+ // No match, so leave tuple1 in 'tuples'
+ if (comparison < 0) {
+ // tuple1 is less than tuple2, so advance tupleIter ...
+ if (!tupleIter.hasNext()) {
+ // The intersection results ('tuples') has no more tuples, so go to the next source ...
+ break;
+ }
+ tuple1 = tupleIter.next();
+ continue;
+ }
+ assert comparison > 0;
+ // tuple1 is greater than tuple2, so advance nextIter ...
+ if (!nextIter.hasNext()) {
+ // The next source has no more tuples, so leave all remaining tuples, and go to the next source ...
+ break;
+ }
+ tuple2 = nextIter.next();
+ continue;
+ }
+ }
+ // Remove duplicates if requested to ...
+ removeDuplicatesIfRequested(tuples);
+ // Return all of the common tuples that were in all sources ...
+ return tuples;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ExceptComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/IntersectComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/IntersectComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/IntersectComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,128 @@
+/*
+ * 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.process;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+
+/**
+ */
+public class IntersectComponent extends SetOperationComponent {
+
+ private final Comparator<Object[]> comparator;
+
+ public IntersectComponent( QueryContext context,
+ Columns columns,
+ Iterable<ProcessingComponent> sources,
+ boolean alreadySorted,
+ boolean all ) {
+ super(context, columns, sources, alreadySorted, all);
+ this.comparator = createSortComparator(context, columns);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ Iterator<ProcessingComponent> sources = sources().iterator();
+ if (!sources.hasNext()) return emptyTuples();
+
+ // Execute all of the source components (so we can sort the results from the smallest to the largest) ...
+ // TODO: Parallelize this ???
+ List<List<Object[]>> allTuples = new LinkedList<List<Object[]>>();
+ while (sources.hasNext()) {
+ List<Object[]> tuples = sources.next().execute();
+ if (tuples == null) continue;
+ if (tuples.isEmpty()) return emptyTuples();
+ allTuples.add(tuples);
+ }
+ if (allTuples.isEmpty()) return emptyTuples();
+ if (allTuples.size() == 1) return allTuples.get(0); // just one source
+ // Sort the tuples by size, starting with the smallest ...
+ Collections.sort(allTuples, new Comparator<List<Object[]>>() {
+ public int compare( List<Object[]> tuples1,
+ List<Object[]> tuples2 ) {
+ return tuples1.size() - tuples2.size();
+ }
+ });
+
+ // Do the sources 2 at a time ...
+ Iterator<List<Object[]>> iter = allTuples.iterator();
+ List<Object[]> tuples = iter.next(); // already sorted ...
+ assert iter.hasNext();
+ // Process the next source with the previous ...
+ while (iter.hasNext()) {
+ List<Object[]> next = iter.next(); // already sorted ...
+ // Walk through the first and second results ...
+ Iterator<Object[]> tupleIter = tuples.iterator();
+ Iterator<Object[]> nextIter = next.iterator();
+ // There is at least one tuple in each ...
+ Object[] tuple1 = tupleIter.next();
+ Object[] tuple2 = nextIter.next();
+ while (true) {
+ int comparison = comparator.compare(tuple1, tuple2);
+ if (comparison == 0) {
+ // Both match, so leave the tuple in 'tuples' and go on ...
+ continue;
+ }
+ // No match, so remove tuple1 from 'tuples'
+ tupleIter.remove();
+ if (comparison < 0) {
+ // tuple1 is less than tuple2, so advance tupleIter ...
+ if (!tupleIter.hasNext()) {
+ // The intersection results ('tuples') has no more tuples, so go to the next source ...
+ break;
+ }
+ tuple1 = tupleIter.next();
+ continue;
+ }
+ assert comparison > 0;
+ // tuple1 is greater than tuple2, so advance nextIter ...
+ if (!nextIter.hasNext()) {
+ // The next source has no more tuples, so remove all remaining tuples ...
+ while (tupleIter.hasNext()) {
+ tupleIter.next();
+ tupleIter.remove();
+ }
+ // then go to the next source ...
+ break;
+ }
+ tuple2 = nextIter.next();
+ continue;
+ }
+ }
+ // Remove duplicates if requested to ...
+ removeDuplicatesIfRequested(tuples);
+ // Return all of the common tuples that were in all sources ...
+ return tuples;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/IntersectComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/JoinComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/JoinComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/JoinComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,477 @@
+/*
+ * 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.process;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import org.jboss.dna.graph.Location;
+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.ValueComparators;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.validate.Schemata;
+
+/**
+ *
+ */
+public abstract class JoinComponent extends ProcessingComponent {
+
+ protected static final Comparator<Location> LOCATION_COMPARATOR = Location.comparator();
+
+ private final ProcessingComponent left;
+ private final ProcessingComponent right;
+ private final JoinCondition condition;
+ private final JoinType joinType;
+
+ protected JoinComponent( QueryContext context,
+ ProcessingComponent left,
+ ProcessingComponent right,
+ JoinCondition condition,
+ JoinType joinType ) {
+ super(context, computeJoinedColumns(left.getColumns(), right.getColumns()));
+ this.left = left;
+ this.right = right;
+ this.joinType = joinType;
+ this.condition = condition;
+ assert this.left != null;
+ assert this.right != null;
+ assert this.joinType != null;
+ }
+
+ /**
+ * Get the type of join this processor represents.
+ *
+ * @return the join type; never null
+ */
+ public final JoinType getJoinType() {
+ return joinType;
+ }
+
+ /**
+ * Get the join condition.
+ *
+ * @return the join condition; never null
+ */
+ public final JoinCondition getJoinCondition() {
+ return condition;
+ }
+
+ /**
+ * Get the processing component that serves as the left side of the join.
+ *
+ * @return the left-side processing component; never null
+ */
+ protected final ProcessingComponent left() {
+ return left;
+ }
+
+ /**
+ * Get the processing component that serves as the right side of the join.
+ *
+ * @return the right-side processing component; never null
+ */
+ protected final ProcessingComponent right() {
+ return right;
+ }
+
+ /**
+ * Get the columns definition for the results from the left side of the join.
+ *
+ * @return the left-side columns that feed this join; never null
+ */
+ protected final Columns leftColunns() {
+ return left.getColumns();
+ }
+
+ /**
+ * Get the columns definition for the results from the right side of the join.
+ *
+ * @return the right-side columns that feed this join; never null
+ */
+ protected final Columns rightColumns() {
+ return right.getColumns();
+ }
+
+ protected static Columns computeJoinedColumns( Columns leftColumns,
+ Columns rightColumns ) {
+ List<Column> columns = new ArrayList<Column>(leftColumns.getColumnCount() + rightColumns.getColumnCount());
+ columns.addAll(leftColumns.getColumns());
+ columns.addAll(rightColumns.getColumns());
+ boolean includeFullTextScores = leftColumns.hasFullTextSearchScores() || rightColumns.hasFullTextSearchScores();
+ return new QueryResultColumns(columns, includeFullTextScores);
+ }
+
+ protected static TupleMerger createMerger( Columns joinColumns,
+ Columns leftColumns,
+ Columns rightColumns ) {
+ final int joinTupleSize = joinColumns.getTupleSize();
+ final int joinColumnCount = joinColumns.getColumnCount();
+ final int joinLocationCount = joinColumns.getLocationCount();
+ final int leftColumnCount = leftColumns.getColumnCount();
+ final int leftLocationCount = leftColumns.getLocationCount();
+ final int leftTupleSize = leftColumns.getTupleSize();
+ final int rightColumnCount = rightColumns.getColumnCount();
+ final int rightLocationCount = rightColumns.getLocationCount();
+ final int rightTupleSize = rightColumns.getTupleSize();
+ final int startLeftLocations = joinColumnCount;
+ final int startRightLocations = startLeftLocations + leftLocationCount;
+
+ // The left and right selectors should NOT overlap ...
+ assert joinLocationCount == leftLocationCount + rightLocationCount;
+
+ // Create different implementations depending upon the options, since this save us from having to make
+ // these decisions while doing the merges...
+ if (joinColumns.hasFullTextSearchScores()) {
+ final int leftScoreCount = leftTupleSize - leftColumnCount - leftLocationCount;
+ final int rightScoreCount = rightTupleSize - rightColumnCount - rightLocationCount;
+ final int startLeftScores = startRightLocations + rightLocationCount;
+ final int startRightScores = startLeftScores + leftScoreCount;
+ final boolean leftScores = leftScoreCount > 0;
+ final boolean rightScores = rightScoreCount > 0;
+
+ return new TupleMerger() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.JoinComponent.TupleMerger#merge(java.lang.Object[],
+ * java.lang.Object[])
+ */
+ public Object[] merge( Object[] leftTuple,
+ Object[] rightTuple ) {
+ Object[] result = new Object[joinTupleSize]; // initialized to null
+ // If the tuple arrays are null, then we don't need to copy because the arrays are
+ // initialized to null values.
+ if (leftTuple != null) {
+ // Copy the left tuple values ...
+ System.arraycopy(result, 0, leftTuple, 0, leftColumnCount);
+ System.arraycopy(result, startLeftLocations, leftTuple, leftColumnCount, leftLocationCount);
+ if (leftScores) {
+ System.arraycopy(result, startLeftScores, leftTuple, leftLocationCount, leftScoreCount);
+ }
+ }
+ if (rightTuple != null) {
+ // Copy the right tuple values ...
+ System.arraycopy(result, leftColumnCount, rightTuple, 0, rightColumnCount);
+ System.arraycopy(result, startRightLocations, rightTuple, rightColumnCount, rightLocationCount);
+ if (rightScores) {
+ System.arraycopy(result, startRightScores, rightTuple, rightLocationCount, rightScoreCount);
+ }
+ }
+ return result;
+ }
+ };
+ }
+ // There are no full-text search scores ...
+ return new TupleMerger() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.JoinComponent.TupleMerger#merge(java.lang.Object[], java.lang.Object[])
+ */
+ public Object[] merge( Object[] leftTuple,
+ Object[] rightTuple ) {
+ Object[] result = new Object[joinTupleSize]; // initialized to null
+ // If the tuple arrays are null, then we don't need to copy because the arrays are
+ // initialized to null values.
+ if (leftTuple != null) {
+ // Copy the left tuple values ...
+ System.arraycopy(result, 0, leftTuple, 0, leftColumnCount);
+ System.arraycopy(result, startLeftLocations, leftTuple, leftColumnCount, leftLocationCount);
+ }
+ if (rightTuple != null) {
+ // Copy the right tuple values ...
+ System.arraycopy(result, leftColumnCount, rightTuple, 0, rightColumnCount);
+ System.arraycopy(result, startRightLocations, rightTuple, rightColumnCount, rightLocationCount);
+ }
+ return result;
+ }
+ };
+ }
+
+ protected static interface TupleMerger {
+ Object[] merge( Object[] leftTuple,
+ Object[] rightTuple );
+ }
+
+ /**
+ * Interface defining the value of a tuple that is used in the join condition.
+ */
+ protected static interface ValueSelector {
+ /**
+ * Obtain the value that is to be used in the join condition.
+ *
+ * @param tuple the tuple
+ * @return the value that should be used
+ */
+ Object evaluate( Object[] tuple );
+ }
+
+ /**
+ * Create a {@link ValueSelector} that obtains the value required to use the supplied join condition.
+ *
+ * @param source the source component; may not be null
+ * @param condition the join condition; may not be null
+ * @return the value selector; never null
+ */
+ protected static ValueSelector valueSelectorFor( ProcessingComponent source,
+ JoinCondition condition ) {
+ if (condition instanceof ChildNodeJoinCondition) {
+ ChildNodeJoinCondition joinCondition = (ChildNodeJoinCondition)condition;
+ String childSelectorName = joinCondition.getChildSelectorName().getName();
+ if (source.getColumns().hasSelector(childSelectorName)) {
+ return selectPath(source, childSelectorName);
+ }
+ String parentSelectorName = joinCondition.getParentSelectorName().getName();
+ return selectPath(source, parentSelectorName);
+ } else if (condition instanceof SameNodeJoinCondition) {
+ SameNodeJoinCondition joinCondition = (SameNodeJoinCondition)condition;
+ String selector1Name = joinCondition.getSelector1Name().getName();
+ if (source.getColumns().hasSelector(selector1Name)) {
+ return selectPath(source, selector1Name);
+ }
+ String selector2Name = joinCondition.getSelector2Name().getName();
+ return selectPath(source, selector2Name);
+ } else if (condition instanceof DescendantNodeJoinCondition) {
+ DescendantNodeJoinCondition joinCondition = (DescendantNodeJoinCondition)condition;
+ String ancestorSelectorName = joinCondition.getAncestorSelectorName().getName();
+ if (source.getColumns().hasSelector(ancestorSelectorName)) {
+ return selectPath(source, ancestorSelectorName);
+ }
+ String descendantSelectorName = joinCondition.getDescendantSelectorName().getName();
+ return selectPath(source, descendantSelectorName);
+ } else if (condition instanceof EquiJoinCondition) {
+ EquiJoinCondition joinCondition = (EquiJoinCondition)condition;
+ SelectorName selector1Name = joinCondition.getSelector1Name();
+ Name propName1 = joinCondition.getProperty1Name();
+ if (source.getColumns().hasSelector(selector1Name.getName())) {
+ return selectValue(source, selector1Name, propName1);
+ }
+ SelectorName selector2Name = joinCondition.getSelector2Name();
+ Name propName2 = joinCondition.getProperty2Name();
+ return selectValue(source, selector2Name, propName2);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private static ValueSelector selectPath( ProcessingComponent component,
+ String selectorName ) {
+ final int index = component.getColumns().getLocationIndex(selectorName);
+ return new ValueSelector() {
+ public Object evaluate( Object[] tuple ) {
+ Location location = (Location)tuple[index];
+ return location != null ? location.getPath() : null;
+ }
+ };
+ }
+
+ private static ValueSelector selectValue( ProcessingComponent component,
+ SelectorName selectorName,
+ Name propertyName ) {
+ final int index = component.getColumns().getColumnIndexForProperty(selectorName.getName(), propertyName);
+ return new ValueSelector() {
+ public Object evaluate( Object[] tuple ) {
+ return tuple[index];
+ }
+ };
+ }
+
+ /**
+ * Interface defining the value of a tuple that is used in the join condition.
+ */
+ protected static interface Joinable {
+ /**
+ * Obtain the value that is to be used in the join condition.
+ *
+ * @param leftValue the value from the left tuple; never null
+ * @param rightValue the value from the right tuple; never null
+ * @return true if the tuples are to be joined
+ */
+ boolean evaluate( Object leftValue,
+ Object rightValue );
+ }
+
+ /**
+ * Create a {@link ValueSelector} that obtains the value required to use the supplied join condition.
+ *
+ * @param left the left source component; may not be null
+ * @param right the left source component; may not be null
+ * @param condition the join condition; may not be null
+ * @return the value selector; never null
+ */
+ protected static Joinable joinableFor( ProcessingComponent left,
+ ProcessingComponent right,
+ JoinCondition condition ) {
+ if (condition instanceof SameNodeJoinCondition) {
+ return new Joinable() {
+ public boolean evaluate( Object locationA,
+ Object locationB ) {
+ Location location1 = (Location)locationA;
+ Location location2 = (Location)locationB;
+ return location1.isSame(location2);
+ }
+ };
+ } else if (condition instanceof EquiJoinCondition) {
+ return new Joinable() {
+ public boolean evaluate( Object leftValue,
+ Object rightValue ) {
+ return leftValue.equals(rightValue);
+ }
+ };
+ } else if (condition instanceof ChildNodeJoinCondition) {
+ ChildNodeJoinCondition joinCondition = (ChildNodeJoinCondition)condition;
+ String childSelectorName = joinCondition.getChildSelectorName().getName();
+ if (left.getColumns().hasSelector(childSelectorName)) {
+ // The child is on the left ...
+ return new Joinable() {
+ public boolean evaluate( Object childLocation,
+ Object parentLocation ) {
+ Path childPath = ((Location)childLocation).getPath();
+ Path parentPath = ((Location)parentLocation).getPath();
+ return childPath.getParent().isSameAs(parentPath);
+ }
+ };
+ }
+ // The child is on the right ...
+ return new Joinable() {
+ public boolean evaluate( Object parentLocation,
+ Object childLocation ) {
+ Path childPath = ((Location)childLocation).getPath();
+ Path parentPath = ((Location)parentLocation).getPath();
+ return childPath.getParent().isSameAs(parentPath);
+ }
+ };
+ } else if (condition instanceof DescendantNodeJoinCondition) {
+ DescendantNodeJoinCondition joinCondition = (DescendantNodeJoinCondition)condition;
+ String ancestorSelectorName = joinCondition.getAncestorSelectorName().getName();
+ if (left.getColumns().hasSelector(ancestorSelectorName)) {
+ // The ancestor is on the left ...
+ return new Joinable() {
+ public boolean evaluate( Object ancestorLocation,
+ Object descendantLocation ) {
+ Path ancestorPath = ((Location)ancestorLocation).getPath();
+ Path descendantPath = ((Location)descendantLocation).getPath();
+ return ancestorPath.isAncestorOf(descendantPath);
+ }
+ };
+ }
+ // The ancestor is on the right ...
+ return new Joinable() {
+ public boolean evaluate( Object descendantLocation,
+ Object ancestorLocation ) {
+ Path ancestorPath = ((Location)ancestorLocation).getPath();
+ Path descendantPath = ((Location)descendantLocation).getPath();
+ return ancestorPath.isAncestorOf(descendantPath);
+ }
+ };
+ }
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Create a {@link Comparable} that can be used to compare the values required to evaluate the supplied join condition.
+ *
+ * @param context the context in which this query is being evaluated; may not be null
+ * @param left the left source component; may not be null
+ * @param right the left source component; may not be null
+ * @param condition the join condition; may not be null
+ * @return the comparator; never null
+ */
+ @SuppressWarnings( "unchecked" )
+ protected static Comparator<Object> comparatorFor( QueryContext context,
+ ProcessingComponent left,
+ ProcessingComponent right,
+ JoinCondition condition ) {
+ final Comparator<Path> pathComparator = ValueComparators.PATH_COMPARATOR;
+ if (condition instanceof SameNodeJoinCondition) {
+ return new Comparator<Object>() {
+ public int compare( Object location1,
+ Object location2 ) {
+ Path path1 = ((Location)location1).getPath();
+ Path path2 = ((Location)location2).getPath();
+ return pathComparator.compare(path1, path2);
+ }
+ };
+ }
+ if (condition instanceof ChildNodeJoinCondition) {
+ ChildNodeJoinCondition joinCondition = (ChildNodeJoinCondition)condition;
+ String childSelectorName = joinCondition.getChildSelectorName().getName();
+ if (left.getColumns().hasSelector(childSelectorName)) {
+ // The child is on the left ...
+ return new Comparator<Object>() {
+ public int compare( Object childLocation,
+ Object parentLocation ) {
+ Path childPath = ((Location)childLocation).getPath();
+ Path parentPath = ((Location)parentLocation).getPath();
+ if (childPath.isRoot()) return parentPath.isRoot() ? 0 : -1;
+ Path parentOfChild = childPath.getParent();
+ return pathComparator.compare(parentPath, parentOfChild);
+ }
+ };
+ }
+ // The child is on the right ...
+ return new Comparator<Object>() {
+ public int compare( Object parentLocation,
+ Object childLocation ) {
+ Path childPath = ((Location)childLocation).getPath();
+ Path parentPath = ((Location)parentLocation).getPath();
+ if (childPath.isRoot()) return parentPath.isRoot() ? 0 : -1;
+ Path parentOfChild = childPath.getParent();
+ return pathComparator.compare(parentPath, parentOfChild);
+ }
+ };
+ }
+ if (condition instanceof EquiJoinCondition) {
+ EquiJoinCondition joinCondition = (EquiJoinCondition)condition;
+ SelectorName leftSelectorName = joinCondition.getSelector1Name();
+ SelectorName rightSelectorName = joinCondition.getSelector2Name();
+ Name leftPropertyName = joinCondition.getProperty1Name();
+ Name rightPropertyName = joinCondition.getProperty2Name();
+
+ ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+ Schemata schemata = context.getSchemata();
+ Schemata.Table leftTable = schemata.getTable(leftSelectorName);
+ Schemata.Column leftColumn = leftTable.getColumn(stringFactory.create(leftPropertyName));
+ PropertyType leftType = leftColumn.getPropertyType();
+
+ Schemata.Table rightTable = schemata.getTable(rightSelectorName);
+ Schemata.Column rightColumn = rightTable.getColumn(stringFactory.create(rightPropertyName));
+ PropertyType rightType = rightColumn.getPropertyType();
+
+ return leftType == rightType ? (Comparator<Object>)leftType.getComparator() : ValueComparators.OBJECT_COMPARATOR;
+ }
+ throw new IllegalArgumentException();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/JoinComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/LimitComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/LimitComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/LimitComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,72 @@
+/*
+ * 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.process;
+
+import java.util.List;
+import org.jboss.dna.graph.query.model.Limit;
+
+/**
+ */
+public class LimitComponent extends DelegatingComponent {
+
+ private final Limit limit;
+
+ public LimitComponent( ProcessingComponent delegate,
+ Limit limit ) {
+ super(delegate);
+ this.limit = limit;
+ assert this.limit != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ List<Object[]> tuples = delegate().execute();
+ if (limit.isOffset()) {
+ if (limit.getOffset() >= tuples.size()) {
+ // There aren't enough results, so return an empty list ...
+ return emptyTuples();
+ }
+ if (limit.isUnlimited()) {
+ // An offset, but no row limit ...
+ tuples = tuples.subList(limit.getOffset(), tuples.size());
+ } else {
+ // Both an offset AND a row limit (which may be more than the number of rows available)...
+ int toIndex = Math.min(limit.getOffset() + limit.getRowLimit(), tuples.size());
+ tuples = tuples.subList(limit.getOffset(), toIndex);
+ }
+ } else {
+ // No offset, but perhaps there's a row limit ...
+ if (!limit.isUnlimited()) {
+ int toIndex = Math.min(limit.getRowLimit(), tuples.size());
+ tuples = tuples.subList(0, toIndex);
+ }
+ }
+ return tuples;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/LimitComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/MergeJoinComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/MergeJoinComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/MergeJoinComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,185 @@
+/*
+ * 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.process;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
+import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
+
+/**
+ * Create a processing component that performs a merge-join algorithm. This algorithm only makes sense for
+ * {@link EquiJoinCondition equi-joins}, {@link ChildNodeJoinCondition child-node joins}, and {@link SameNodeJoinCondition
+ * same-node joins}.
+ * <p>
+ * <b> Note that it is required that the left and right processing components (where this component gets its results) must already
+ * be properly sorted and have had duplicates removed. </b>
+ * </p>
+ */
+@Immutable
+public class MergeJoinComponent extends JoinComponent {
+
+ public MergeJoinComponent( QueryContext context,
+ ProcessingComponent left,
+ ProcessingComponent right,
+ EquiJoinCondition condition,
+ JoinType joinType ) {
+ super(context, left, right, condition, joinType);
+ }
+
+ public MergeJoinComponent( QueryContext context,
+ ProcessingComponent left,
+ ProcessingComponent right,
+ ChildNodeJoinCondition condition,
+ JoinType joinType ) {
+ super(context, left, right, condition, joinType);
+ }
+
+ public MergeJoinComponent( QueryContext context,
+ ProcessingComponent left,
+ ProcessingComponent right,
+ SameNodeJoinCondition condition,
+ JoinType joinType ) {
+ super(context, left, right, condition, joinType);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ // Construct the necessary components ...
+ final Columns leftColumns = left().getColumns();
+ final Columns rightColumns = right().getColumns();
+ final ValueSelector leftSelector = valueSelectorFor(left(), getJoinCondition());
+ final ValueSelector rightSelector = valueSelectorFor(right(), getJoinCondition());
+ final Comparator<Object> comparator = comparatorFor(getContext(), left(), right(), getJoinCondition());
+ final TupleMerger merger = createMerger(getColumns(), leftColumns, rightColumns);
+
+ // Walk through the left and right results ...
+ List<Object[]> leftTuples = left().execute();
+ List<Object[]> rightTuples = right().execute();
+ List<Object[]> tuples = new ArrayList<Object[]>(leftTuples.size() * rightTuples.size());
+ Iterator<Object[]> leftIter = leftTuples.iterator();
+ Iterator<Object[]> rightIter = rightTuples.iterator();
+ Object[] leftTuple = leftIter.next();
+ Object[] rightTuple = rightIter.next();
+ Object[] nextLeftTuple = null;
+ Object[] nextRightTuple = null;
+ while (true) {
+ // Get the value from the left and right side ...
+ Object leftValue = leftSelector.evaluate(leftTuple);
+ Object rightValue = rightSelector.evaluate(rightTuple);
+ // Determine if the tuples should be joined ...
+ int compare = comparator.compare(leftValue, rightValue);
+ while (compare == 0) {
+ Object[] result = merger.merge(leftTuple, rightTuple);
+ tuples.add(result);
+
+ // Peek at the next right tuple, but skip any duplicates ...
+ if (nextRightTuple == null) {
+ nextRightTuple = rightIter.next();
+ while (isSameTuple(rightColumns, nextRightTuple, rightTuple)) {
+ nextRightTuple = rightIter.next();
+ }
+ }
+
+ // Compare the leftTuple with the nextRightTuple ...
+ leftValue = leftSelector.evaluate(leftTuple);
+ rightValue = rightSelector.evaluate(nextRightTuple);
+ compare = comparator.compare(leftValue, rightValue);
+ if (compare == 0) {
+ // This is a good match, update the variables and repeat ...
+ rightTuple = nextRightTuple;
+ nextRightTuple = null;
+ continue;
+ }
+ if (compare > 0) {
+ // The rightTuple is smaller than the left, so we need to increment the right again ...
+ rightTuple = nextRightTuple;
+ nextRightTuple = null;
+ compare = 0; // to prevent iteration below ...
+ break;
+ }
+
+ // Otherwise, the leftTuple didn't work with the nextRightTuple,
+ // so try the nextLeftTuple with the rightTuple ...
+ nextLeftTuple = leftIter.next();
+ while (isSameTuple(leftColumns, nextLeftTuple, leftTuple)) {
+ nextLeftTuple = leftIter.next();
+ }
+ leftValue = leftSelector.evaluate(nextLeftTuple);
+ rightValue = rightSelector.evaluate(rightTuple);
+ compare = comparator.compare(leftValue, rightValue);
+ if (compare == 0) {
+ // This is a good match, so update the variables and repeat ...
+ leftTuple = nextLeftTuple;
+ nextLeftTuple = null;
+ continue;
+ }
+
+ // Otherwise, neither was a good match, so advance both ...
+ leftTuple = nextLeftTuple;
+ rightTuple = nextRightTuple;
+ nextLeftTuple = null;
+ nextRightTuple = null;
+ compare = 0; // to prevent iteration below ...
+ break;
+ }
+ // There wasn't a match ...
+ if (compare < 0) {
+ // The leftValue is smaller than the right, so we need to increment the left ...
+ if (!leftIter.hasNext()) break;
+ leftTuple = leftIter.next();
+ }
+ if (compare > 0) {
+ // The rightValue is smaller than the left, so we need to increment the right ...
+ if (!rightIter.hasNext()) break;
+ rightTuple = rightIter.next();
+ }
+ }
+ return tuples;
+ }
+
+ protected final boolean isSameTuple( Columns columns,
+ Object[] tuple1,
+ Object[] tuple2 ) {
+ for (int i = columns.getColumnCount(); i != columns.getLocationCount(); ++i) {
+ Location location = (Location)tuple1[i];
+ Location location2 = (Location)tuple2[i];
+ if (!location.equals(location2)) return false;
+ }
+ return true;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/MergeJoinComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NestedLoopJoinComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NestedLoopJoinComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NestedLoopJoinComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,76 @@
+/*
+ * 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.process;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.JoinType;
+
+/**
+ *
+ */
+public class NestedLoopJoinComponent extends JoinComponent {
+
+ public NestedLoopJoinComponent( QueryContext context,
+ ProcessingComponent left,
+ ProcessingComponent right,
+ JoinCondition condition,
+ JoinType joinType ) {
+ super(context, left, right, condition, joinType);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ // Construct the necessary components ...
+ final ValueSelector leftSelector = valueSelectorFor(left(), getJoinCondition());
+ final ValueSelector rightSelector = valueSelectorFor(right(), getJoinCondition());
+ final Joinable joinable = joinableFor(left(), right(), getJoinCondition());
+ final TupleMerger merger = createMerger(getColumns(), left().getColumns(), right().getColumns());
+
+ // Walk through the left and right results ...
+ List<Object[]> leftTuples = left().execute();
+ List<Object[]> rightTuples = right().execute();
+ List<Object[]> tuples = new ArrayList<Object[]>(leftTuples.size() * rightTuples.size());
+ for (Object[] leftTuple : leftTuples) {
+ for (Object[] rightTuple : rightTuples) {
+ // Get the value from the left and right side ...
+ Object leftValue = leftSelector.evaluate(leftTuple);
+ Object rightValue = rightSelector.evaluate(rightTuple);
+ // Determine if the tuples should be joined ...
+ if (joinable.evaluate(leftValue, rightValue)) {
+ Object[] result = merger.merge(leftTuple, rightTuple);
+ tuples.add(result);
+ }
+ }
+ }
+ return tuples;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NestedLoopJoinComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NoResultsComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NoResultsComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NoResultsComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,49 @@
+/*
+ * 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.process;
+
+import java.util.List;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+
+/**
+ */
+public class NoResultsComponent extends ProcessingComponent {
+
+ public NoResultsComponent( QueryContext context,
+ Columns columns ) {
+ super(context, columns);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ return emptyTuples();
+
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/NoResultsComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,319 @@
+/*
+ * 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.process;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Binary;
+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.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+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.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+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;
+import org.jboss.dna.graph.query.validate.Schemata.Table;
+
+/**
+ * A component that performs (some) portion of the query processing by {@link #execute() returning the tuples} that result from
+ * this stage of processing. Processing components are designed to be assembled into a processing structure, with a single
+ * component at the top that returns the results of a query.
+ */
+@NotThreadSafe
+public abstract class ProcessingComponent {
+
+ private final QueryContext context;
+ private final Columns columns;
+
+ protected ProcessingComponent( QueryContext context,
+ Columns columns ) {
+ this.context = context;
+ this.columns = columns;
+ assert this.context != null;
+ assert this.columns != null;
+ }
+
+ /**
+ * Get the context in which this query is being executed.
+ *
+ * @return context
+ */
+ public final QueryContext getContext() {
+ return context;
+ }
+
+ /**
+ * Get the column definitions.
+ *
+ * @return the column mappings; never null
+ */
+ public final Columns getColumns() {
+ return columns;
+ }
+
+ /**
+ * Get the container for problems encountered during processing.
+ *
+ * @return the problems container; never null
+ */
+ protected final Problems problems() {
+ return context.getProblems();
+ }
+
+ /**
+ * Execute this stage of processing and return the resulting tuples that each conform to the {@link #getColumns() columns}.
+ *
+ * @return the list of tuples, where each tuple corresonds to the {@link #getColumns() columns}; never null
+ */
+ public abstract List<Object[]> execute();
+
+ /**
+ * Close these results, allowing any resources to be released.
+ */
+ public void close() {
+ }
+
+ /**
+ * Utility method to create a new tuples list that is empty.
+ *
+ * @return the empty tuples list; never null
+ */
+ protected List<Object[]> emptyTuples() {
+ return new ArrayList<Object[]>(0);
+ }
+
+ /**
+ * Interface for evaluating a {@link DynamicOperand} to return the resulting value.
+ */
+ protected static interface DynamicOperation {
+ /**
+ * Get the expected type of the result of this evaluation
+ *
+ * @return the property type; never null
+ */
+ PropertyType getExpectedType();
+
+ /**
+ * Perform the dynamic evaluation to obtain the desired result.
+ *
+ * @param tuple the tuple; never null
+ * @return the value that results from dynamically evaluating the operand against the tuple; may be null
+ */
+ Object evaluate( Object[] tuple );
+ }
+
+ /**
+ * Create a {@link DynamicOperation} instance that is able to evaluate the supplied {@link DynamicOperand}.
+ *
+ * @param context the context in which the query is being evaluated; may not be null
+ * @param columns the definition of the result columns and the tuples; may not be null
+ * @param operand the dynamic operand that is to be evaluated by the returned object; may not be null
+ * @return the dynamic operand operation; never null
+ */
+ protected DynamicOperation createDynamicOperation( QueryContext context,
+ Columns columns,
+ DynamicOperand operand ) {
+ assert operand != null;
+ assert columns != null;
+ assert context != null;
+ final ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+ if (operand instanceof PropertyValue) {
+ PropertyValue propValue = (PropertyValue)operand;
+ Name propertyName = propValue.getPropertyName();
+ String selectorName = propValue.getSelectorName().getName();
+ final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
+ // Find the expected property type of the value ...
+ Table table = context.getSchemata().getTable(propValue.getSelectorName());
+ Column schemaColumn = table.getColumn(stringFactory.create(propertyName));
+ final PropertyType expectedType = schemaColumn.getPropertyType();
+ return new DynamicOperation() {
+ public PropertyType getExpectedType() {
+ return expectedType;
+ }
+
+ public Object evaluate( Object[] tuple ) {
+ return tuple[index];
+ }
+ };
+ }
+ if (operand instanceof Length) {
+ Length length = (Length)operand;
+ PropertyValue value = length.getPropertyValue();
+ Name propertyName = value.getPropertyName();
+ String selectorName = value.getSelectorName().getName();
+ final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
+ return new DynamicOperation() {
+ public PropertyType getExpectedType() {
+ return PropertyType.LONG; // length is always LONG
+ }
+
+ public Object evaluate( Object[] tuple ) {
+ Object value = tuple[index];
+ // Determine the length ...
+ if (value instanceof Binary) {
+ return ((Binary)value).getStream();
+ }
+ String result = stringFactory.create(value);
+ return result != null ? result.length() : null;
+ }
+ };
+ }
+ if (operand instanceof LowerCase) {
+ LowerCase lowerCase = (LowerCase)operand;
+ final DynamicOperation delegate = createDynamicOperation(context, columns, lowerCase.getOperand());
+ return new DynamicOperation() {
+ public PropertyType getExpectedType() {
+ return PropertyType.STRING;
+ }
+
+ public Object evaluate( Object[] tuple ) {
+ String result = stringFactory.create(delegate.evaluate(tuple));
+ return result != null ? result.toLowerCase() : null;
+ }
+ };
+ }
+ if (operand instanceof UpperCase) {
+ UpperCase upperCase = (UpperCase)operand;
+ final DynamicOperation delegate = createDynamicOperation(context, columns, upperCase.getOperand());
+ return new DynamicOperation() {
+ public PropertyType getExpectedType() {
+ return PropertyType.STRING;
+ }
+
+ public Object evaluate( Object[] tuple ) {
+ String result = stringFactory.create(delegate.evaluate(tuple));
+ return result != null ? result.toUpperCase() : null;
+ }
+ };
+ }
+ if (operand instanceof NodeName) {
+ NodeName nodeName = (NodeName)operand;
+ final int locationIndex = columns.getLocationIndex(nodeName.getSelectorName().getName());
+ return new DynamicOperation() {
+ public PropertyType getExpectedType() {
+ return PropertyType.STRING;
+ }
+
+ public Object evaluate( Object[] tuple ) {
+ Location location = (Location)tuple[locationIndex];
+ if (location == null) return null;
+ Path path = location.getPath();
+ assert path != null;
+ return path.isRoot() ? "" : stringFactory.create(location.getPath().getLastSegment().getName());
+ }
+ };
+ }
+ if (operand instanceof NodeLocalName) {
+ NodeLocalName nodeName = (NodeLocalName)operand;
+ final int locationIndex = columns.getLocationIndex(nodeName.getSelectorName().getName());
+ return new DynamicOperation() {
+ public PropertyType getExpectedType() {
+ return PropertyType.STRING;
+ }
+
+ public Object evaluate( Object[] tuple ) {
+ Location location = (Location)tuple[locationIndex];
+ if (location == null) return null;
+ Path path = location.getPath();
+ assert path != null;
+ return path.isRoot() ? "" : location.getPath().getLastSegment().getName().getLocalName();
+ }
+ };
+ }
+ if (operand instanceof FullTextSearchScore) {
+ FullTextSearchScore score = (FullTextSearchScore)operand;
+ String selectorName = score.getSelectorName().getName();
+ final int index = columns.getFullTextSearchScoreIndexFor(selectorName);
+ if (index < 0) {
+ // No full-text search score for this selector, so return 0.0d;
+ return new DynamicOperation() {
+ public PropertyType getExpectedType() {
+ return PropertyType.DOUBLE;
+ }
+
+ public Object evaluate( Object[] tuple ) {
+ return new Double(0.0d);
+ }
+ };
+ }
+ return new DynamicOperation() {
+ public PropertyType getExpectedType() {
+ return PropertyType.DOUBLE;
+ }
+
+ public Object evaluate( Object[] tuple ) {
+ return tuple[index];
+ }
+ };
+ }
+ assert false;
+ return null;
+ }
+
+ protected Comparator<Object[]> createSortComparator( QueryContext context,
+ Columns columns ) {
+ assert context != null;
+ final int numLocations = columns.getLocationCount();
+ assert numLocations > 0;
+ final Comparator<Location> typeComparator = Location.comparator();
+ if (numLocations == 1) {
+ // We can do this a tad faster if we know there is only one Location object ...
+ final int locationIndex = columns.getColumnCount();
+ return new Comparator<Object[]>() {
+ public int compare( Object[] tuple1,
+ Object[] tuple2 ) {
+ Location value1 = (Location)tuple1[locationIndex];
+ Location value2 = (Location)tuple2[locationIndex];
+ return typeComparator.compare(value1, value2);
+ }
+ };
+ }
+ final int firstLocationIndex = columns.getColumnCount();
+ return new Comparator<Object[]>() {
+ public int compare( Object[] tuple1,
+ Object[] tuple2 ) {
+ int result = 0;
+ for (int locationIndex = firstLocationIndex; locationIndex != numLocations; ++locationIndex) {
+ Location value1 = (Location)tuple1[locationIndex];
+ Location value2 = (Location)tuple2[locationIndex];
+ result = typeComparator.compare(value1, value2);
+ if (result != 0) return result;
+ }
+ return result;
+ }
+ };
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProcessingComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/Processor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/Processor.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/Processor.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,50 @@
+/*
+ * 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.process;
+
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.QueryResults.Statistics;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.plan.PlanNode;
+
+/**
+ * Interface for a query processor.
+ */
+public interface Processor {
+
+ /**
+ * Process the supplied query plan for the given command and return the results.
+ *
+ * @param context the context in which the command is being processed
+ * @param command the command being executed
+ * @param statistics the time metrics up until this execution
+ * @param plan the plan to be processed
+ * @return the results of the query
+ */
+ QueryResults execute( QueryContext context,
+ QueryCommand command,
+ Statistics statistics,
+ PlanNode plan );
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/Processor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProjectComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProjectComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProjectComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,50 @@
+/*
+ * 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.process;
+
+import java.util.List;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * A {@link ProcessingComponent} implementation that performs a {@link Type#PROJECT PROJECT} operation to reduce the columns that
+ * appear in the results.
+ */
+public class ProjectComponent extends DelegatingComponent {
+
+ public ProjectComponent( ProcessingComponent delegate,
+ List<Column> columns ) {
+ super(delegate, delegate.getColumns().subSelect(columns));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ return delegate().execute();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ProjectComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,273 @@
+/*
+ * 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.process;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.QueryResults.Statistics;
+import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
+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.JoinType;
+import org.jboss.dna.graph.query.model.Limit;
+import org.jboss.dna.graph.query.model.Ordering;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
+import org.jboss.dna.graph.query.model.SetQuery.Operation;
+import org.jboss.dna.graph.query.plan.JoinAlgorithm;
+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.process.SelectComponent.Analyzer;
+
+/**
+ * An abstract {@link Processor} implementation that builds a tree of {@link ProcessingComponent} objects to perform the different
+ * parts of the query processing logic. Subclasses are required to only implement one method: the
+ * {@link #createAccessComponent(QueryContext, PlanNode, Columns, Analyzer)} should create a ProcessorComponent object that will
+ * perform the (low-level access) query described by the {@link PlanNode plan} given as a parameter.
+ */
+public abstract class QueryProcessor implements Processor {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.Processor#execute(org.jboss.dna.graph.query.QueryContext,
+ * org.jboss.dna.graph.query.model.QueryCommand, org.jboss.dna.graph.query.QueryResults.Statistics,
+ * org.jboss.dna.graph.query.plan.PlanNode)
+ */
+ public QueryResults execute( QueryContext context,
+ QueryCommand command,
+ Statistics statistics,
+ PlanNode plan ) {
+ long nanos = System.nanoTime();
+ Columns columns = null;
+ List<Object[]> tuples = null;
+ try {
+ // Find the topmost PROJECT node and build the Columns ...
+ PlanNode project = plan.findAtOrBelow(Type.PROJECT);
+ assert project != null;
+ List<Column> projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+ assert projectedColumns != null;
+ assert !projectedColumns.isEmpty();
+ columns = new QueryResultColumns(projectedColumns, context.getHints().hasFullTextSearch);
+
+ // Go through the plan and create the corresponding ProcessingComponents ...
+ Analyzer analyzer = createAnalyzer(context);
+ ProcessingComponent component = createComponent(context, plan, columns, analyzer);
+ long nanos2 = System.nanoTime();
+ statistics = statistics.withResultsFormulationTime(nanos2 - nanos);
+
+ // Now execute the component ...
+ nanos = nanos2;
+ tuples = component.execute();
+
+ } finally {
+ statistics = statistics.withExecutionTime(System.nanoTime() - nanos);
+ }
+ if (tuples == null) tuples = Collections.emptyList();
+ return new org.jboss.dna.graph.query.process.QueryResults(context, command, columns, statistics, tuples);
+ }
+
+ /**
+ * Create an {@link Analyzer} implementation that should be used by the non-access {@link ProcessingComponent}s that evaluate
+ * criteria. By default, this method returns null, which means that any criteria evaluation will likely be pushed down under
+ * an {@link Type#ACCESS ACCESS} node (and thus handled by an
+ * {@link #createAccessComponent(QueryContext, PlanNode, Columns, Analyzer) access component}.
+ * <p>
+ * However, for more simple access components that are not capable of handling joins and other non-trivial criteria, simply
+ * return an Analyzer implementation that implements the methods using the source.
+ * </p>
+ *
+ * @param context the context in which query is being evaluated
+ * @return the analyzer, or null if the ProcessingComponent objects that evaluate criteria should use a best-effort approach
+ */
+ protected Analyzer createAnalyzer( QueryContext context ) {
+ return null;
+ }
+
+ protected abstract ProcessingComponent createAccessComponent( QueryContext context,
+ PlanNode accessNode,
+ Columns resultColumns,
+ Analyzer analyzer );
+
+ /**
+ * Method that is called to build up the {@link ProcessingComponent} objects that correspond to the optimized query plan. This
+ * method is called by {@link #execute(QueryContext, QueryCommand, Statistics, PlanNode)} for each of the various
+ * {@link PlanNode} objects in the optimized query plan, and the method is actually recursive (since the optimized query plan
+ * forms a tree). However, whenever this call structure reaches the {@link Type#ACCESS ACCESS} nodes in the query plan (which
+ * each represents a separate atomic low-level query to the underlying system), the
+ * {@link #createAccessComponent(QueryContext, PlanNode, Columns, Analyzer)} method is called. Subclasses should create an
+ * appropriate ProcessingComponent implementation that performs this atomic low-level query.
+ *
+ * @param context the context in which query is being evaluated
+ * @param node the plan node for which the ProcessingComponent is to be created
+ * @param columns the definition of the result columns for this portion of the query
+ * @param analyzer the analyzer (returned from {@link #createAnalyzer(QueryContext)}) that should be used on the components
+ * that evaluate criteria; may be null if a best-effort should be made for the evaluation
+ * @return the processing component for this plan node; never null
+ */
+ protected ProcessingComponent createComponent( QueryContext context,
+ PlanNode node,
+ Columns columns,
+ Analyzer analyzer ) {
+ ProcessingComponent component = null;
+ switch (node.getType()) {
+ case ACCESS:
+ // Create the component under the ACCESS ...
+ assert node.getChildCount() == 1;
+ // 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 ...
+ assert node.getChildCount() == 1;
+ ProcessingComponent distinctDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+ component = new DistinctComponent(distinctDelegate);
+ break;
+ case GROUP:
+ throw new UnsupportedOperationException();
+ case JOIN:
+ // Create the components under the JOIN ...
+ assert node.getChildCount() == 2;
+ ProcessingComponent left = createComponent(context, node.getFirstChild(), columns, analyzer);
+ ProcessingComponent right = createComponent(context, node.getLastChild(), columns, analyzer);
+ // Create the join component ...
+ JoinAlgorithm algorithm = node.getProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.class);
+ JoinType joinType = node.getProperty(Property.JOIN_TYPE, JoinType.class);
+ JoinCondition joinCondition = node.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+ switch (algorithm) {
+ case MERGE:
+ if (joinCondition instanceof SameNodeJoinCondition) {
+ SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
+ component = new MergeJoinComponent(context, left, right, condition, joinType);
+ } else if (joinCondition instanceof ChildNodeJoinCondition) {
+ ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
+ component = new MergeJoinComponent(context, left, right, condition, joinType);
+ } else if (joinCondition instanceof EquiJoinCondition) {
+ EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
+ component = new MergeJoinComponent(context, left, right, condition, joinType);
+ } else {
+ assert false : "Unable to use merge algorithm with descendant node join conditions";
+ throw new UnsupportedOperationException();
+ }
+ break;
+ case NESTED_LOOP:
+ component = new NestedLoopJoinComponent(context, left, right, joinCondition, joinType);
+ break;
+ }
+ // For each Constraint object applied to the JOIN, simply create a SelectComponent on top ...
+ List<Constraint> constraints = node.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
+ for (Constraint constraint : constraints) {
+ component = new SelectComponent(component, constraint, context.getVariables());
+ }
+ break;
+ case LIMIT:
+ // Create the component under the LIMIT ...
+ assert node.getChildCount() == 1;
+ ProcessingComponent limitDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+ // Then create the limit component ...
+ Integer rowLimit = node.getProperty(Property.LIMIT_COUNT, Integer.class);
+ Integer offset = node.getProperty(Property.LIMIT_OFFSET, Integer.class);
+ Limit limit = Limit.NONE;
+ if (rowLimit != null) limit = limit.withRowLimit(rowLimit.intValue());
+ if (offset != null) limit = limit.withOffset(offset.intValue());
+ component = new LimitComponent(limitDelegate, limit);
+ break;
+ case NULL:
+ component = new NoResultsComponent(context, columns);
+ break;
+ case PROJECT:
+ // Create the component under the PROJECT ...
+ assert node.getChildCount() == 1;
+ ProcessingComponent projectDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+ // Then create the project component ...
+ List<Column> projectedColumns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+ component = new ProjectComponent(projectDelegate, projectedColumns);
+ break;
+ case SELECT:
+ // Create the component under the SELECT ...
+ assert node.getChildCount() == 1;
+ ProcessingComponent selectDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+ // Then create the select component ...
+ Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+ component = new SelectComponent(selectDelegate, constraint, context.getVariables(), analyzer);
+ break;
+ case SET_OPERATION:
+ // Create the components under the SET_OPERATION ...
+ List<ProcessingComponent> setDelegates = new LinkedList<ProcessingComponent>();
+ for (PlanNode child : node) {
+ setDelegates.add(createComponent(context, child, columns, analyzer));
+ }
+ // Then create the select component ...
+ Operation operation = node.getProperty(Property.SET_OPERATION, Operation.class);
+ boolean all = node.getProperty(Property.SET_USE_ALL, Boolean.class);
+ boolean alreadySorted = false; // ????
+ switch (operation) {
+ case EXCEPT:
+ component = new ExceptComponent(context, columns, setDelegates, alreadySorted, all);
+ break;
+ case INTERSECT:
+ component = new IntersectComponent(context, columns, setDelegates, alreadySorted, all);
+ break;
+ case UNION:
+ component = new UnionComponent(context, columns, setDelegates, alreadySorted, all);
+ break;
+ }
+ break;
+ case SORT:
+ // Create the component under the SORT ...
+ assert node.getChildCount() == 1;
+ ProcessingComponent sortDelegate = createComponent(context, node.getFirstChild(), columns, analyzer);
+ // Then create the sort component ...
+ List<Object> orderBys = node.getPropertyAsList(Property.SORT_ORDER_BY, Object.class);
+ if (orderBys.isEmpty()) {
+ component = sortDelegate;
+ } else {
+ if (orderBys.get(0) instanceof Ordering) {
+ List<Ordering> orderings = new ArrayList<Ordering>(orderBys.size());
+ for (Object orderBy : orderBys) {
+ orderings.add((Ordering)orderBy);
+ }
+ component = new SortValuesComponent(sortDelegate, orderings);
+ } else {
+ // Order by the location(s) because it's before a merge-join ...
+ component = new SortLocationsComponent(sortDelegate);
+ }
+ }
+ break;
+ case SOURCE:
+ assert false : "Source nodes should always be below ACCESS nodes by the time a plan is executed";
+ throw new UnsupportedOperationException();
+ }
+ assert component != null;
+ return component;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResultColumns.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResultColumns.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResultColumns.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,475 @@
+/*
+ * 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.process;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.Column;
+
+/**
+ * Defines the columns associated with the results of a query. This definition allows the values to be accessed
+ */
+@Immutable
+public final class QueryResultColumns implements Columns {
+
+ private static final QueryResultColumns EMPTY = new QueryResultColumns(Collections.<Column>emptyList(), false);
+
+ /**
+ * Get an empty results column definition.
+ *
+ * @return the empty columns definition; never null
+ */
+ public static QueryResultColumns empty() {
+ return EMPTY;
+ }
+
+ private final int tupleSize;
+ private final List<Column> columns;
+ private final List<String> columnNames;
+ private final List<String> selectorNames;
+ private List<String> tupleValueNames;
+ private final Map<String, Column> columnsByName;
+ private final Map<String, Integer> columnIndexByColumnName;
+ private final Map<String, Integer> locationIndexBySelectorName;
+ private final Map<String, Integer> locationIndexByColumnName;
+ private final Map<Integer, Integer> locationIndexByColumnIndex;
+ private final Map<String, Map<Name, Integer>> columnIndexByPropertyNameBySelectorName;
+ private final Map<String, Integer> fullTextSearchScoreIndexBySelectorName;
+
+ /**
+ * Create a new definition for the query results given the supplied columns.
+ *
+ * @param columns the columns that define the results; should never be modified directly
+ * @param includeFullTextSearchScores true if room should be made in the tuples for the full-text search scores for each
+ * {@link Location}, or false otherwise
+ */
+ public QueryResultColumns( List<Column> columns,
+ boolean includeFullTextSearchScores ) {
+ assert columns != null;
+ this.columns = Collections.unmodifiableList(columns);
+ this.columnsByName = new HashMap<String, Column>();
+ this.columnIndexByColumnName = new HashMap<String, Integer>();
+ Set<String> selectors = new HashSet<String>();
+ final int columnCount = columns.size();
+ Integer selectorIndex = new Integer(columnCount - 1);
+ this.locationIndexBySelectorName = new HashMap<String, Integer>();
+ this.locationIndexByColumnIndex = new HashMap<Integer, Integer>();
+ this.locationIndexByColumnName = new HashMap<String, Integer>();
+ this.columnIndexByPropertyNameBySelectorName = new HashMap<String, Map<Name, Integer>>();
+ List<String> selectorNames = new ArrayList<String>(columnCount);
+ List<String> names = new ArrayList<String>(columnCount);
+ for (int i = 0, max = this.columns.size(); i != max; ++i) {
+ Column column = this.columns.get(i);
+ assert column != null;
+ String columnName = column.getColumnName();
+ assert columnName != null;
+ if (columnsByName.put(columnName, column) != null) {
+ assert false : "Column names must be unique";
+ }
+ names.add(columnName);
+ columnIndexByColumnName.put(columnName, new Integer(i));
+ String selectorName = column.getSelectorName().getName();
+ if (selectors.add(selectorName)) {
+ selectorNames.add(selectorName);
+ selectorIndex = new Integer(selectorIndex.intValue() + 1);
+ locationIndexBySelectorName.put(selectorName, selectorIndex);
+ }
+ locationIndexByColumnIndex.put(new Integer(i), selectorIndex);
+ locationIndexByColumnName.put(columnName, selectorIndex);
+ // Insert the entry by selector name and property name ...
+ Map<Name, Integer> byPropertyName = columnIndexByPropertyNameBySelectorName.get(selectorName);
+ if (byPropertyName == null) {
+ byPropertyName = new HashMap<Name, Integer>();
+ columnIndexByPropertyNameBySelectorName.put(selectorName, byPropertyName);
+ }
+ byPropertyName.put(column.getPropertyName(), new Integer(i));
+ }
+ this.selectorNames = Collections.unmodifiableList(selectorNames);
+ this.columnNames = Collections.unmodifiableList(names);
+ if (includeFullTextSearchScores) {
+ this.fullTextSearchScoreIndexBySelectorName = new HashMap<String, Integer>();
+ int index = columnNames.size() + selectorNames.size();
+ for (String selectorName : selectorNames) {
+ fullTextSearchScoreIndexBySelectorName.put(selectorName, new Integer(index++));
+ }
+ this.tupleSize = columnNames.size() + selectorNames.size() + selectorNames.size();
+ } else {
+ this.fullTextSearchScoreIndexBySelectorName = null;
+ this.tupleSize = columnNames.size() + selectorNames.size();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#subSelect(java.util.List)
+ */
+ public Columns subSelect( List<Column> columns ) {
+ return new QueryResultColumns(columns, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#subSelect(org.jboss.dna.graph.query.model.Column[])
+ */
+ public Columns subSelect( Column... columns ) {
+ return new QueryResultColumns(Arrays.asList(columns), this);
+ }
+
+ private QueryResultColumns( List<Column> columns,
+ QueryResultColumns wrappedAround ) {
+ assert columns != null;
+ this.columns = Collections.unmodifiableList(columns);
+ this.columnsByName = new HashMap<String, Column>();
+ this.columnIndexByColumnName = new HashMap<String, Integer>();
+ this.locationIndexBySelectorName = new HashMap<String, Integer>();
+ this.locationIndexByColumnIndex = new HashMap<Integer, Integer>();
+ this.locationIndexByColumnName = new HashMap<String, Integer>();
+ this.columnIndexByPropertyNameBySelectorName = new HashMap<String, Map<Name, Integer>>();
+ this.selectorNames = new ArrayList<String>(columns.size());
+ List<String> names = new ArrayList<String>(columns.size());
+ for (int i = 0, max = this.columns.size(); i != max; ++i) {
+ Column column = this.columns.get(i);
+ assert column != null;
+ String columnName = column.getColumnName();
+ assert columnName != null;
+ if (columnsByName.put(columnName, column) != null) {
+ assert false : "Column names must be unique";
+ }
+ names.add(columnName);
+ Integer columnIndex = new Integer(wrappedAround.getColumnIndexForName(columnName));
+ columnIndexByColumnName.put(columnName, columnIndex);
+ String selectorName = column.getSelectorName().getName();
+ if (!selectorNames.contains(selectorName)) selectorNames.add(selectorName);
+ Integer selectorIndex = new Integer(wrappedAround.getLocationIndex(selectorName));
+ locationIndexBySelectorName.put(selectorName, selectorIndex);
+ locationIndexByColumnIndex.put(new Integer(0), selectorIndex);
+ locationIndexByColumnName.put(columnName, selectorIndex);
+ // Insert the entry by selector name and property name ...
+ Map<Name, Integer> byPropertyName = columnIndexByPropertyNameBySelectorName.get(selectorName);
+ if (byPropertyName == null) {
+ byPropertyName = new HashMap<Name, Integer>();
+ columnIndexByPropertyNameBySelectorName.put(selectorName, byPropertyName);
+ }
+ byPropertyName.put(column.getPropertyName(), columnIndex);
+ }
+ this.columnNames = Collections.unmodifiableList(names);
+ if (wrappedAround.fullTextSearchScoreIndexBySelectorName != null) {
+ this.fullTextSearchScoreIndexBySelectorName = new HashMap<String, Integer>();
+ int index = columnNames.size() + selectorNames.size();
+ for (String selectorName : selectorNames) {
+ fullTextSearchScoreIndexBySelectorName.put(selectorName, new Integer(index++));
+ }
+ this.tupleSize = columnNames.size() + selectorNames.size() + selectorNames.size();
+ } else {
+ this.fullTextSearchScoreIndexBySelectorName = null;
+ this.tupleSize = columnNames.size() + selectorNames.size();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getColumns()
+ */
+ public List<Column> getColumns() {
+ return columns;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getColumnNames()
+ */
+ public List<String> getColumnNames() {
+ return columnNames;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getColumnCount()
+ */
+ public int getColumnCount() {
+ return columns.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getLocationCount()
+ */
+ public int getLocationCount() {
+ return selectorNames.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getSelectorNames()
+ */
+ public List<String> getSelectorNames() {
+ return selectorNames;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getTupleSize()
+ */
+ public int getTupleSize() {
+ return tupleSize;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getTupleValueNames()
+ */
+ public List<String> getTupleValueNames() {
+ if (this.tupleValueNames == null) {
+ // This is idempotent, so no need to lock ...
+ List<String> results = new ArrayList<String>(getTupleSize());
+ // Add the column names ...
+ results.addAll(columnNames);
+ // Add the location names ...
+ for (String selectorName : selectorNames) {
+ String name = "Location(" + selectorName + ")";
+ results.add(name);
+ }
+ // Add the full-text search score names ...
+ if (fullTextSearchScoreIndexBySelectorName != null) {
+ for (String selectorName : selectorNames) {
+ String name = "Score(" + selectorName + ")";
+ results.add(name);
+ }
+ }
+ this.tupleValueNames = results;
+ }
+ return this.tupleValueNames;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getLocationIndexForColumn(int)
+ */
+ public int getLocationIndexForColumn( int columnIndex ) {
+ Integer result = locationIndexByColumnIndex.get(new Integer(columnIndex));
+ if (result == null) {
+ throw new IndexOutOfBoundsException(GraphI18n.columnDoesNotExistInQuery.text(columnIndex));
+ }
+ return result.intValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getLocationIndexForColumn(java.lang.String)
+ */
+ public int getLocationIndexForColumn( String columnName ) {
+ Integer result = locationIndexByColumnName.get(columnName);
+ if (result == null) {
+ throw new NoSuchElementException(GraphI18n.columnDoesNotExistInQuery.text(columnName));
+ }
+ return result.intValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getLocationIndex(java.lang.String)
+ */
+ public int getLocationIndex( String selectorName ) {
+ Integer result = locationIndexBySelectorName.get(selectorName);
+ if (result == null) {
+ throw new NoSuchElementException(GraphI18n.selectorDoesNotExistInQuery.text(selectorName));
+ }
+ return result.intValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#hasSelector(java.lang.String)
+ */
+ public boolean hasSelector( String selectorName ) {
+ return locationIndexBySelectorName.containsKey(selectorName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getPropertyNameForColumn(int)
+ */
+ public Name getPropertyNameForColumn( int columnIndex ) {
+ return columns.get(columnIndex).getPropertyName();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getPropertyNameForColumn(java.lang.String)
+ */
+ public Name getPropertyNameForColumn( String columnName ) {
+ Column result = columnsByName.get(columnName);
+ if (result == null) {
+ throw new NoSuchElementException(GraphI18n.columnDoesNotExistInQuery.text(columnName));
+ }
+ return result.getPropertyName();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getColumnIndexForName(java.lang.String)
+ */
+ public int getColumnIndexForName( String columnName ) {
+ Integer result = columnIndexByColumnName.get(columnName);
+ if (result == null) {
+ throw new NoSuchElementException(GraphI18n.columnDoesNotExistInQuery.text(columnName));
+ }
+ return result.intValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getColumnIndexForProperty(java.lang.String,
+ * org.jboss.dna.graph.property.Name)
+ */
+ public int getColumnIndexForProperty( String selectorName,
+ Name propertyName ) {
+ Map<Name, Integer> byPropertyName = columnIndexByPropertyNameBySelectorName.get(selectorName);
+ if (byPropertyName == null) {
+ throw new NoSuchElementException(GraphI18n.selectorDoesNotExistInQuery.text(selectorName));
+ }
+ Integer result = byPropertyName.get(propertyName);
+ if (result == null) {
+ throw new NoSuchElementException(GraphI18n.propertyOnSelectorIsNotUsedInQuery.text(propertyName, selectorName));
+ }
+ return result.intValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#getFullTextSearchScoreIndexFor(java.lang.String)
+ */
+ public int getFullTextSearchScoreIndexFor( String selectorName ) {
+ if (fullTextSearchScoreIndexBySelectorName == null) return -1;
+ Integer result = fullTextSearchScoreIndexBySelectorName.get(selectorName);
+ if (result == null) {
+ throw new NoSuchElementException(GraphI18n.selectorDoesNotExistInQuery.text(selectorName));
+ }
+ return result.intValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#hasFullTextSearchScores()
+ */
+ public boolean hasFullTextSearchScores() {
+ return fullTextSearchScoreIndexBySelectorName != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#includes(org.jboss.dna.graph.query.QueryResults.Columns)
+ */
+ public boolean includes( Columns other ) {
+ if (other == this) return true;
+ if (other == null) return false;
+ return this.getColumns().containsAll(other.getColumns());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Columns#isUnionCompatible(org.jboss.dna.graph.query.QueryResults.Columns)
+ */
+ public boolean isUnionCompatible( Columns other ) {
+ if (this == other) return true;
+ if (other == null) return false;
+ if (this.hasFullTextSearchScores() != other.hasFullTextSearchScores()) return false;
+ if (this.getColumnCount() != other.getColumnCount()) return false;
+ return this.getColumns().containsAll(other.getColumns()) && other.getColumns().containsAll(this.getColumns());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof QueryResultColumns) {
+ QueryResultColumns that = (QueryResultColumns)obj;
+ return this.getColumns().equals(that.getColumns());
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" [");
+ boolean first = true;
+ for (Column column : getColumns()) {
+ if (first) first = false;
+ else sb.append(", ");
+ sb.append(column);
+ }
+ sb.append("] => Locations[");
+ first = true;
+ for (int i = 0, count = getColumnCount(); i != count; ++i) {
+ if (first) first = false;
+ else sb.append(", ");
+ sb.append(getLocationIndexForColumn(i));
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResultColumns.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,439 @@
+/*
+ * 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.process;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
+
+/**
+ * The resulting output of a query.
+ */
+@Immutable
+public class QueryResults implements org.jboss.dna.graph.query.QueryResults {
+
+ private final QueryContext context;
+ private final QueryCommand command;
+ private final Columns columns;
+ private final List<Object[]> tuples;
+ private final Statistics statistics;
+
+ /**
+ * Create a results object for the supplied context, command, and result columns and with the supplied tuples.
+ *
+ * @param context the context in which the query was executed
+ * @param command the query command
+ * @param columns the definition of the query result columns
+ * @param statistics the statistics for this query; may not be null
+ * @param tuples the tuples
+ */
+ public QueryResults( QueryContext context,
+ QueryCommand command,
+ Columns columns,
+ Statistics statistics,
+ List<Object[]> tuples ) {
+ assert context != null;
+ assert command != null;
+ assert columns != null;
+ assert statistics != null;
+ this.context = context;
+ this.command = command;
+ this.columns = columns;
+ this.tuples = tuples;
+ this.statistics = statistics;
+ }
+
+ /**
+ * Create an empty {@link QueryResults} object for the supplied context, command, and result columns.
+ *
+ * @param context the context in which the query was executed
+ * @param command the query command
+ * @param columns the definition of the query result columns
+ * @param statistics the statistics for this query; may not be null
+ */
+ public QueryResults( QueryContext context,
+ QueryCommand command,
+ Columns columns,
+ Statistics statistics ) {
+ this(context, command, columns, statistics, Collections.<Object[]>emptyList());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults#getContext()
+ */
+ public ExecutionContext getContext() {
+ return context.getExecutionContext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults#getCommand()
+ */
+ public QueryCommand getCommand() {
+ return command;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults#getColumns()
+ */
+ public Columns getColumns() {
+ return columns;
+ }
+
+ /**
+ * Get a cursor that can be used to walk through the results.
+ *
+ * @return the cursor; never null, though possibly empty (meaning {@link Cursor#hasNext()} may return true)
+ */
+ public Cursor getCursor() {
+ return new TupleCursor(columns, tuples.iterator());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults#getTuples()
+ */
+ public List<Object[]> getTuples() {
+ return tuples;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults#getProblems()
+ */
+ public Problems getProblems() {
+ return context.getProblems();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults#hasErrors()
+ */
+ public boolean hasErrors() {
+ return getProblems().hasErrors();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults#hasWarnings()
+ */
+ public boolean hasWarnings() {
+ return getProblems().hasWarnings();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults#getStatistics()
+ */
+ public Statistics getStatistics() {
+ return statistics;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return toString(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Get a string representation of this result object, with a maximum number of tuples to include.
+ *
+ * @param maxTuples the maximum number of tuples to print, or {@link Integer#MAX_VALUE} if all the tuples are to be printed
+ * @return the string representation; never null
+ */
+ public String toString( int maxTuples ) {
+ StringBuilder sb = new StringBuilder();
+ toString(sb, maxTuples);
+ return sb.toString();
+ }
+
+ /**
+ * Get a string representation of this result object.
+ *
+ * @param sb the string builder to which the results should be written; may not be null
+ */
+ public void toString( StringBuilder sb ) {
+ toString(sb, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Get a string representation of this result object, with a maximum number of tuples to include.
+ *
+ * @param sb the string builder to which the results should be written; may not be null
+ * @param maxTuples the maximum number of tuples to print, or {@link Integer#MAX_VALUE} if all the tuples are to be printed
+ */
+ public void toString( StringBuilder sb,
+ int maxTuples ) {
+ ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+ int[] columnWidths = determineColumnWidths(Integer.MAX_VALUE, true, stringFactory);
+ printDelimiterLine(sb, columnWidths, true);
+ printHeader(sb, columnWidths);
+ printDelimiterLine(sb, columnWidths, true);
+ printLines(sb, columnWidths, stringFactory, maxTuples);
+ printDelimiterLine(sb, columnWidths, false);
+ }
+
+ /**
+ * Determine the width of each column.
+ *
+ * @param maxWidth the maximum width; must be positive
+ * @param useData true if the data should be used to compute the length, or false if just the column names should be used
+ * @param stringFactory the value factory for creating strings; may not be null
+ * @return the array of widths for each column, excluding any decorating characters; never null
+ */
+ protected int[] determineColumnWidths( int maxWidth,
+ boolean useData,
+ ValueFactory<String> stringFactory ) {
+ assert maxWidth > 0;
+ int tupleLength = columns.getTupleSize();
+ int[] columnWidths = new int[tupleLength + 1]; // +1 for the row number column
+ for (int i = 0; i != columnWidths.length; ++i) {
+ columnWidths[i] = 0;
+ }
+ // Determine the width of the first column that shows the row number ...
+ String rowNumber = stringFactory.create(getTuples().size());
+ columnWidths[0] = rowNumber.length();
+
+ // Compute the column names ...
+ List<String> tupleValueNames = columns.getTupleValueNames();
+ for (int i = 0, j = 1, max = tupleValueNames.size(); i != max; ++i, ++j) {
+ String name = tupleValueNames.get(i);
+ columnWidths[j] = Math.max(Math.min(maxWidth, name.length()), columnWidths[j]);
+ }
+ // Look at the data ...
+ if (useData) {
+ for (Object[] tuple : getTuples()) {
+ for (int i = 0, j = 1; i != tupleLength; ++i, ++j) {
+ String valueStr = stringOf(tuple[i], stringFactory);
+ columnWidths[j] = Math.max(Math.min(maxWidth, valueStr.length()), columnWidths[j]);
+ }
+ }
+ }
+ return columnWidths;
+ }
+
+ protected String stringOf( Object value,
+ ValueFactory<String> stringFactory ) {
+ if (value instanceof Binary) {
+ // Just print out the SHA-1 hash in Base64, plus length
+ Binary binary = (Binary)value;
+ return "(Binary,length=" + binary.getSize() + ",SHA1=" + Base64.encode(binary.getHash()) + ")";
+ }
+ return stringFactory.create(value);
+ }
+
+ protected void printHeader( StringBuilder sb,
+ int[] columnWidths ) {
+ // Print the row number column ...
+ sb.append("| ").append(StringUtil.justifyLeft("#", columnWidths[0], ' ')).append(' ');
+ // Print the name line ...
+ sb.append('|');
+ int i = 1;
+ for (String name : columns.getTupleValueNames()) {
+ sb.append(' ');
+ sb.append(StringUtil.justifyLeft(name, columnWidths[i], ' '));
+ sb.append(" |");
+ ++i;
+ }
+ sb.append('\n');
+ }
+
+ protected void printLines( StringBuilder sb,
+ int[] columnWidths,
+ ValueFactory<String> stringFactory,
+ int maxRowsToPrint ) {
+ int rowNumber = 1;
+ int tupleLength = columns.getTupleSize();
+ // Should they all be printed ?
+ if (maxRowsToPrint > tuples.size()) {
+ // Print all tuples ...
+ for (Object[] tuple : getTuples()) {
+ printTuple(sb, columnWidths, stringFactory, rowNumber, tupleLength, tuple);
+ ++rowNumber;
+ }
+ } else {
+ // Print max number of rows ...
+ for (Object[] tuple : getTuples()) {
+ printTuple(sb, columnWidths, stringFactory, rowNumber, tupleLength, tuple);
+ if (rowNumber >= maxRowsToPrint) break;
+ ++rowNumber;
+ }
+ }
+
+ }
+
+ /**
+ * @param sb
+ * @param columnWidths
+ * @param stringFactory
+ * @param rowNumber
+ * @param tupleLength
+ * @param tuple
+ */
+ private final void printTuple( StringBuilder sb,
+ int[] columnWidths,
+ ValueFactory<String> stringFactory,
+ int rowNumber,
+ int tupleLength,
+ Object[] tuple ) {
+ // Print the row number column ...
+ sb.append("| ").append(StringUtil.justifyLeft(Integer.toString(rowNumber), columnWidths[0], ' ')).append(' ');
+ // Print the remaining columns ...
+ for (int i = 0, j = 1; i != tupleLength; ++i, ++j) {
+ String valueStr = stringOf(tuple[i], stringFactory);
+ valueStr = StringUtil.justifyLeft(valueStr, columnWidths[j], ' ');
+ sb.append('|').append(' ').append(valueStr).append(' ');
+ }
+ sb.append('|');
+ sb.append('\n');
+ }
+
+ protected void printDelimiterLine( StringBuilder sb,
+ int[] columnWidths,
+ boolean includeLineFeed ) {
+ sb.append('+');
+ for (int i = 0, max = columnWidths.length; i != max; ++i) {
+ for (int j = 0, width = columnWidths[i] + 2; j != width; ++j) { // +1 for space before, +1 for space after
+ sb.append('-');
+ }
+ sb.append('+');
+ }
+ if (includeLineFeed) sb.append('\n');
+ }
+
+ /**
+ * An interface used to walk through the results.
+ */
+ public final class TupleCursor implements Cursor {
+ private final Columns columns;
+ private final Iterator<Object[]> iterator;
+ private Object[] currentTuple;
+ private int tupleIndex;
+
+ protected TupleCursor( Columns columns,
+ Iterator<Object[]> iterator ) {
+ this.iterator = iterator;
+ this.columns = columns;
+ this.tupleIndex = -1;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Cursor#hasNext()
+ */
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Cursor#next()
+ */
+ public void next() {
+ currentTuple = iterator.next();
+ ++tupleIndex;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Cursor#getLocation(int)
+ */
+ public Location getLocation( int columnNumber ) {
+ return (Location)currentTuple[columns.getLocationIndexForColumn(columnNumber)];
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Cursor#getLocation(java.lang.String)
+ */
+ public Location getLocation( String selectorName ) {
+ return (Location)currentTuple[columns.getLocationIndex(selectorName)];
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Cursor#getRowIndex()
+ */
+ public int getRowIndex() {
+ return tupleIndex;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Cursor#getValue(int)
+ */
+ public Object getValue( int columnNumber ) {
+ if (columnNumber >= columns.getColumnCount()) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (currentTuple == null) {
+ throw new IllegalStateException(GraphI18n.nextMethodMustBeCalledBeforeGettingValue.text());
+ }
+ return currentTuple[columnNumber];
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.QueryResults.Cursor#getValue(java.lang.String)
+ */
+ public Object getValue( String columnName ) {
+ if (currentTuple == null) {
+ throw new IllegalStateException(GraphI18n.nextMethodMustBeCalledBeforeGettingValue.text());
+ }
+ return currentTuple[columns.getColumnIndexForName(columnName)];
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryResults.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SelectComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SelectComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SelectComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,456 @@
+/*
+ * 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.process;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import org.jboss.dna.graph.Location;
+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.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+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.Comparison;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.DescendantNode;
+import org.jboss.dna.graph.query.model.FullTextSearch;
+import org.jboss.dna.graph.query.model.Literal;
+import org.jboss.dna.graph.query.model.Not;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.Or;
+import org.jboss.dna.graph.query.model.PropertyExistence;
+import org.jboss.dna.graph.query.model.SameNode;
+import org.jboss.dna.graph.query.model.StaticOperand;
+
+/**
+ */
+public class SelectComponent extends DelegatingComponent {
+
+ private final Constraint constraint;
+ private final ConstraintChecker checker;
+ private final Map<String, Object> variables;
+
+ /**
+ * Create a SELECT processing component that pass those tuples that satisfy the supplied constraint. Certain constraints
+ * (including {@link FullTextSearch}, {@link SameNode} and {@link PropertyExistence}) are evaluated in a fairly limited
+ * fashion, essentially operating upon the tuple values themselves.
+ * <p>
+ * For example, the {@link SameNode} constraint is satisfied when the selected node has the same path as the constraint's
+ * {@link SameNode#getPath() path}. And the {@link PropertyExistence} constraint is satisfied when the
+ * {@link PropertyExistence#getPropertyName() property} is represented in the tuple with a non-null value. Similarly,
+ * {@link FullTextSearch} always evaluates to true. Obviously these implementations will likely not be sufficient for many
+ * purposes. But in cases where these particular constraints are handled in other ways (and thus not expected to be seen by
+ * this processor), this form may be sufficient.
+ * </p>
+ * <p>
+ * For more control over the behavior, use the constructor that takes an {@link Analyzer} implementation (see
+ * {@link #SelectComponent(ProcessingComponent, Constraint, Map, Analyzer)}).
+ * </p>
+ *
+ * @param delegate the delegate processing component that this component should use to obtain the input tuples; may not be
+ * null
+ * @param constraint the query constraint; may not be null
+ * @param variables the map of variables keyed by their name (as used in {@link BindVariableName} constraints); may be null
+ */
+ public SelectComponent( ProcessingComponent delegate,
+ Constraint constraint,
+ Map<String, Object> variables ) {
+ this(delegate, constraint, variables, null);
+ }
+
+ /**
+ * Create a SELECT processing component that pass those tuples that satisfy the supplied constraint, using the supplied
+ * {@link Analyzer} for the verification of the more complex/arduous constraints.
+ *
+ * @param delegate the delegate processing component that this component should use to obtain the input tuples; may not be
+ * null
+ * @param constraint the query constraint; may not be null
+ * @param variables the map of variables keyed by their name (as used in {@link BindVariableName} constraints); may be null
+ * @param analyzer the analyzer; may be null
+ */
+ public SelectComponent( ProcessingComponent delegate,
+ Constraint constraint,
+ Map<String, Object> variables,
+ Analyzer analyzer ) {
+ super(delegate);
+ this.constraint = constraint;
+ this.variables = variables != null ? variables : Collections.<String, Object>emptyMap();
+ this.checker = createChecker(delegate.getContext(), delegate.getColumns(), this.constraint, this.variables, analyzer);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ List<Object[]> tuples = delegate().execute();
+ if (!tuples.isEmpty()) {
+ // Iterate through the tuples, removing any that do not satisfy the constraint ...
+ Iterator<Object[]> iter = tuples.iterator();
+ while (iter.hasNext()) {
+ if (!checker.satisfiesConstraints(iter.next())) {
+ iter.remove();
+ }
+ }
+ }
+ return tuples;
+ }
+
+ /**
+ * Interface used to determine whether a tuple satisfies all of the constraints applied to the SELECT node.
+ */
+ protected static interface ConstraintChecker {
+ /**
+ * Return true if the tuple satisfies all of the constraints.
+ *
+ * @param tuple the tuple; never null
+ * @return true if the tuple satisifes the constraints, or false otherwise
+ */
+ boolean satisfiesConstraints( Object[] tuple );
+ }
+
+ /**
+ * Inteface for criteria evaluation operations that cannot be defined efficiently, correctly, or completely using only the
+ * tuple values.
+ *
+ * @see SelectComponent#SelectComponent(ProcessingComponent, Constraint, Map, Analyzer)
+ */
+ public static interface Analyzer {
+ /**
+ * Determine whether the node specified by the location is the same node as that supplied by the path. This determines if
+ * the nodes at the supplied location and path are the same node.
+ *
+ * @param location the location of the node; never null
+ * @param accessibleAtPath the path that the node can be accessed via
+ * @return true if the node given by the {@link Location} is also accessible at the supplied path, or false otherwise
+ */
+ boolean isSameNode( Location location,
+ Path accessibleAtPath );
+
+ /**
+ * Determine whether the node at the supplied location has the named property.
+ *
+ * @param location the location of the node; never null
+ * @param propertyName the name of the property
+ * @return true if the node at the supplied {@link Location} does contain the property, or false if it does not
+ */
+ boolean hasProperty( Location location,
+ Name propertyName );
+
+ /**
+ * Determine whether the node at the supplied location satisfies the supplied full-text query.
+ *
+ * @param location the location of the node; never null
+ * @param fullTextQuery the full-text search expression; never null
+ * @return the full-text search score of the node, or 0.0d if the node does not satisfy the full-text query
+ */
+ double hasFullText( Location location,
+ String fullTextQuery );
+
+ /**
+ * Determine whether the named property of the node at the supplied location satisfies the supplied full-text query.
+ *
+ * @param location the location of the node; never null
+ * @param propertyName the name of the property; never null
+ * @param fullTextQuery the full-text search expression; never null
+ * @return the full-text search score of the node, or 0.0d if the node does not satisfy the full-text query
+ */
+ double hasFullText( Location location,
+ Name propertyName,
+ String fullTextQuery );
+ }
+
+ /**
+ * Interface defining the {@link Comparison} functionality for a specific {@link Operator}.
+ */
+ protected static interface CompareOperation {
+ /**
+ * Perform the comparison operation.
+ *
+ * @param tupleValue the value in the tuple
+ * @param criteriaValue the right-hand-side of the comparison
+ * @return true if the comparison criteria is satisfied, or false otherwise
+ */
+ boolean evaluate( Object tupleValue,
+ Object criteriaValue );
+ }
+
+ /**
+ * Create the constraint evaluator that is used by the {@link SelectComponent} to evaluate the supplied {@link Constraint
+ * criteria}. For the most correct behavior, specify an {@link Analyzer} implementation.
+ *
+ * @param context the context in which the query is being evaluated; may not be null
+ * @param columns the definition of the result columns and the tuples; may not be null
+ * @param constraint the criteria that this {@link SelectComponent} is to evaluate
+ * @param variables the variables that are to be substituted for the various {@link BindVariableName} {@link StaticOperand
+ * operands}; may not be null
+ * @param analyzer the analyzer that should be used to evalulate the operations that cannot be defined efficiently, correctly,
+ * or completely using only the tuple values; may be null if the tuple values should be used to perform the evaluation
+ * in perhaps an non-ideal manner
+ * @return the constraint evaluator; never null
+ */
+ @SuppressWarnings( "unchecked" )
+ protected ConstraintChecker createChecker( QueryContext context,
+ Columns columns,
+ Constraint constraint,
+ Map<String, Object> variables,
+ final Analyzer analyzer ) {
+ if (constraint instanceof Or) {
+ Or orConstraint = (Or)constraint;
+ final ConstraintChecker left = createChecker(context, columns, orConstraint.getLeft(), variables, analyzer);
+ final ConstraintChecker right = createChecker(context, columns, orConstraint.getRight(), variables, analyzer);
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ return left.satisfiesConstraints(tuple) || right.satisfiesConstraints(tuple);
+ }
+ };
+ }
+ if (constraint instanceof Not) {
+ Not notConstraint = (Not)constraint;
+ final ConstraintChecker original = createChecker(context, columns, notConstraint.getConstraint(), variables, analyzer);
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ return !original.satisfiesConstraints(tuple);
+ }
+ };
+ }
+ if (constraint instanceof And) {
+ And andConstraint = (And)constraint;
+ final ConstraintChecker left = createChecker(context, columns, andConstraint.getLeft(), variables, analyzer);
+ final ConstraintChecker right = createChecker(context, columns, andConstraint.getRight(), variables, analyzer);
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ return left.satisfiesConstraints(tuple) && right.satisfiesConstraints(tuple);
+ }
+ };
+ }
+ if (constraint instanceof ChildNode) {
+ ChildNode childConstraint = (ChildNode)constraint;
+ final int locationIndex = columns.getLocationIndex(childConstraint.getSelectorName().getName());
+ final Path parentPath = childConstraint.getParentPath();
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ Location location = (Location)tuple[locationIndex];
+ assert location.hasPath();
+ return location.getPath().getParent().equals(parentPath);
+ }
+ };
+ }
+ if (constraint instanceof DescendantNode) {
+ DescendantNode descendantNode = (DescendantNode)constraint;
+ final int locationIndex = columns.getLocationIndex(descendantNode.getSelectorName().getName());
+ final Path ancestorPath = descendantNode.getAncestorPath();
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ Location location = (Location)tuple[locationIndex];
+ assert location.hasPath();
+ return location.getPath().isDecendantOf(ancestorPath);
+ }
+ };
+ }
+ if (constraint instanceof SameNode) {
+ SameNode sameNode = (SameNode)constraint;
+ final int locationIndex = columns.getLocationIndex(sameNode.getSelectorName().getName());
+ final Path path = sameNode.getPath();
+ if (analyzer != null) {
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ Location location = (Location)tuple[locationIndex];
+ return analyzer.isSameNode(location, path);
+ }
+ };
+ }
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ Location location = (Location)tuple[locationIndex];
+ assert location.hasPath();
+ return location.getPath().isSameAs(path);
+ }
+ };
+ }
+ if (constraint instanceof PropertyExistence) {
+ PropertyExistence propertyExistance = (PropertyExistence)constraint;
+ String selectorName = propertyExistance.getSelectorName().getName();
+ final Name propertyName = propertyExistance.getPropertyName();
+ if (analyzer != null) {
+ final int locationIndex = columns.getLocationIndex(selectorName);
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ Location location = (Location)tuple[locationIndex];
+ return analyzer.hasProperty(location, propertyName);
+ }
+ };
+ }
+ final int columnIndex = columns.getColumnIndexForProperty(selectorName, propertyName);
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ return tuple[columnIndex] != null;
+ }
+ };
+ }
+ if (constraint instanceof FullTextSearch) {
+ if (analyzer != null) {
+ FullTextSearch search = (FullTextSearch)constraint;
+ String selectorName = search.getSelectorName().getName();
+ final int locationIndex = columns.getLocationIndex(selectorName);
+ final String expression = search.getFullTextSearchExpression();
+ if (expression == null) {
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ return false;
+ }
+ };
+ }
+ final Name propertyName = search.getPropertyName(); // may be null
+ final int scoreIndex = columns.getFullTextSearchScoreIndexFor(selectorName);
+ assert scoreIndex >= 0 : "Columns do not have room for the search scores";
+ if (propertyName != null) {
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ Location location = (Location)tuple[locationIndex];
+ if (location == null) return false;
+ double score = analyzer.hasFullText(location, propertyName, expression);
+ // put the score on the correct tuple value ...
+ Double existing = (Double)tuple[scoreIndex];
+ if (existing != null) {
+ score = Math.max(existing.doubleValue(), score);
+ }
+ tuple[scoreIndex] = new Double(score);
+ return true;
+ }
+ };
+ }
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ Location location = (Location)tuple[locationIndex];
+ if (location == null) return false;
+ double score = analyzer.hasFullText(location, expression);
+ // put the score on the correct tuple value ...
+ Double existing = (Double)tuple[scoreIndex];
+ if (existing != null) {
+ score = Math.max(existing.doubleValue(), score);
+ }
+ tuple[scoreIndex] = new Double(score);
+ return true;
+ }
+ };
+ }
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuple ) {
+ return true;
+ }
+ };
+ }
+ if (constraint instanceof Comparison) {
+ final ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+ Comparison comparison = (Comparison)constraint;
+
+ // Create the correct dynamic operation ...
+ final DynamicOperation dynamicOperation = createDynamicOperation(context, columns, comparison.getOperand1());
+ final PropertyType expectedType = dynamicOperation.getExpectedType();
+
+ // Determine the literal value ...
+ StaticOperand staticOperand = comparison.getOperand2();
+ Object literalValue = null;
+ if (staticOperand instanceof BindVariableName) {
+ BindVariableName bindVariable = (BindVariableName)staticOperand;
+ String variableName = bindVariable.getVariableName();
+ literalValue = variables.get(variableName); // may be null
+ } else {
+ Literal literal = (Literal)staticOperand;
+ literalValue = literal.getValue();
+ }
+ // Create the correct comparator ...
+ final Comparator<Object> comparator = (Comparator<Object>)expectedType.getComparator();
+ // Create the correct operation ...
+ ValueFactory<?> literalFactory = context.getExecutionContext().getValueFactories().getValueFactory(expectedType);
+ final Object rhs = literalFactory.create(literalValue);
+ switch (comparison.getOperator()) {
+ case EQUAL_TO:
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuples ) {
+ return comparator.compare(dynamicOperation.evaluate(tuples), rhs) == 0;
+ }
+ };
+ case GREATER_THAN:
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuples ) {
+ return comparator.compare(dynamicOperation.evaluate(tuples), rhs) > 0;
+ }
+ };
+ case GREATER_THAN_OR_EQUAL_TO:
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuples ) {
+ return comparator.compare(dynamicOperation.evaluate(tuples), rhs) >= 0;
+ }
+ };
+ case LESS_THAN:
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuples ) {
+ return comparator.compare(dynamicOperation.evaluate(tuples), rhs) < 0;
+ }
+ };
+ case LESS_THAN_OR_EQUAL_TO:
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuples ) {
+ return comparator.compare(dynamicOperation.evaluate(tuples), rhs) <= 0;
+ }
+ };
+ case NOT_EQUAL_TO:
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuples ) {
+ return comparator.compare(dynamicOperation.evaluate(tuples), rhs) != 0;
+ }
+ };
+ case LIKE:
+ // Convert the LIKE expression to a regular expression
+ final Pattern pattern = createRegexFromLikeExpression(stringFactory.create(rhs));
+ return new ConstraintChecker() {
+ public boolean satisfiesConstraints( Object[] tuples ) {
+ Object tupleValue = dynamicOperation.evaluate(tuples);
+ if (tupleValue == null) return false;
+ String value = stringFactory.create(tupleValue);
+ return pattern.matcher(value).matches();
+ }
+ };
+ }
+ }
+ assert false;
+ return null;
+ }
+
+ protected static Pattern createRegexFromLikeExpression( String likeExpression ) {
+ return null;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SelectComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SetOperationComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SetOperationComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SetOperationComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,99 @@
+/*
+ * 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.process;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+
+/**
+ */
+public abstract class SetOperationComponent extends ProcessingComponent {
+
+ private final Iterable<ProcessingComponent> sources;
+ protected final Comparator<Object[]> removeDuplicatesComparator;
+
+ protected SetOperationComponent( QueryContext context,
+ Columns columns,
+ Iterable<ProcessingComponent> sources,
+ boolean alreadySorted,
+ boolean all ) {
+ super(context, columns);
+ assert unionCompatible(columns, sources);
+ this.sources = wrapWithLocationOrdering(sources, alreadySorted);
+ this.removeDuplicatesComparator = all ? null : createSortComparator(context, columns);
+ }
+
+ protected static boolean unionCompatible( Columns columns,
+ Iterable<ProcessingComponent> sources ) {
+ for (ProcessingComponent source : sources) {
+ if (!columns.isUnionCompatible(source.getColumns())) return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return sources
+ */
+ protected Iterable<ProcessingComponent> sources() {
+ return sources;
+ }
+
+ /**
+ * The sources' results must be sorted before the intersection can be computed. Ensure that the sources' results are indeed
+ * sorted, and if not wrap them in a sorting component.
+ *
+ * @param sources the sources being intersected; may not be null
+ * @param alreadySorted true if the sources' results are already sorted, or false if they must be sorted by this component
+ * @return the sources (or their wrappers); never null
+ */
+ protected static Iterable<ProcessingComponent> wrapWithLocationOrdering( Iterable<ProcessingComponent> sources,
+ boolean alreadySorted ) {
+ assert sources != null;
+ if (alreadySorted) return sources;
+ List<ProcessingComponent> wrapped = new LinkedList<ProcessingComponent>();
+ for (ProcessingComponent source : sources) {
+ wrapped.add(new SortLocationsComponent(source));
+ }
+ return wrapped;
+ }
+
+ protected void removeDuplicatesIfRequested( List<Object[]> tuples ) {
+ if (removeDuplicatesComparator != null) {
+ Iterator<Object[]> iter = tuples.iterator();
+ Object[] previous = null;
+ while (iter.hasNext()) {
+ Object[] current = iter.next();
+ if (previous != null && removeDuplicatesComparator.compare(previous, current) == 0) {
+ iter.remove();
+ } else {
+ previous = current;
+ }
+ }
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SetOperationComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortLocationsComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortLocationsComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortLocationsComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,61 @@
+/*
+ * 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.process;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ */
+public class SortLocationsComponent extends DelegatingComponent {
+
+ private Comparator<Object[]> sortingComparator;
+
+ public SortLocationsComponent( ProcessingComponent delegate ) {
+ super(delegate);
+ this.sortingComparator = createSortComparator(delegate.getContext(), delegate.getColumns());
+ }
+
+ public SortLocationsComponent( ProcessingComponent delegate,
+ Comparator<Object[]> sortingComparator ) {
+ super(delegate);
+ this.sortingComparator = sortingComparator;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ List<Object[]> tuples = delegate().execute();
+ if (tuples.size() > 1) {
+ // Sort the tuples ...
+ Collections.sort(tuples, sortingComparator);
+ }
+ return tuples;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortLocationsComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortValuesComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortValuesComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortValuesComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,128 @@
+/*
+ * 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.process;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.Order;
+import org.jboss.dna.graph.query.model.Ordering;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * A {@link ProcessingComponent} implementation that performs a {@link Type#PROJECT PROJECT} operation to reduce the columns that
+ * appear in the results.
+ */
+public class SortValuesComponent extends DelegatingComponent {
+
+ private final Comparator<Object[]> sortingComparator;
+
+ public SortValuesComponent( ProcessingComponent delegate,
+ List<Ordering> orderings ) {
+ super(delegate);
+ this.sortingComparator = createSortComparator(delegate.getContext(), delegate.getColumns(), orderings);
+ }
+
+ /**
+ * @return sortingComparator
+ */
+ public Comparator<Object[]> getSortingComparator() {
+ return sortingComparator;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ List<Object[]> tuples = delegate().execute();
+ if (tuples.size() > 1 && sortingComparator != null) {
+ // Sort the tuples ...
+ Collections.sort(tuples, sortingComparator);
+ }
+ return tuples;
+ }
+
+ protected Comparator<Object[]> createSortComparator( QueryContext context,
+ Columns columns,
+ List<Ordering> orderings ) {
+ assert context != null;
+ assert orderings != null;
+ if (orderings.isEmpty()) {
+ return null;
+ }
+ if (orderings.size() == 1) {
+ return createSortComparator(context, columns, orderings.get(0));
+ }
+ // Create a comparator that uses an ordered list of comparators ...
+ final List<Comparator<Object[]>> comparators = new ArrayList<Comparator<Object[]>>(orderings.size());
+ for (Ordering ordering : orderings) {
+ comparators.add(createSortComparator(context, columns, ordering));
+ }
+ return new Comparator<Object[]>() {
+ public int compare( Object[] tuple1,
+ Object[] tuple2 ) {
+ for (Comparator<Object[]> comparator : comparators) {
+ int result = comparator.compare(tuple1, tuple2);
+ if (result != 0) return result;
+ }
+ return 0;
+ }
+ };
+
+ }
+
+ @SuppressWarnings( "unchecked" )
+ protected Comparator<Object[]> createSortComparator( QueryContext context,
+ Columns columns,
+ Ordering ordering ) {
+ assert context != null;
+ assert ordering != null;
+ final DynamicOperation operation = createDynamicOperation(context, columns, ordering.getOperand());
+ final Comparator<Object> typeComparator = (Comparator<Object>)operation.getExpectedType().getComparator();
+ if (ordering.getOrder() == Order.DESCENDING) {
+ return new Comparator<Object[]>() {
+ public int compare( Object[] tuple1,
+ Object[] tuple2 ) {
+ Object value1 = operation.evaluate(tuple1);
+ Object value2 = operation.evaluate(tuple2);
+ return 0 - typeComparator.compare(value1, value2);
+ }
+ };
+ }
+ return new Comparator<Object[]>() {
+ public int compare( Object[] tuple1,
+ Object[] tuple2 ) {
+ Object value1 = operation.evaluate(tuple1);
+ Object value2 = operation.evaluate(tuple2);
+ return typeComparator.compare(value1, value2);
+ }
+ };
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/SortValuesComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/UnionComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/UnionComponent.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/UnionComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,62 @@
+/*
+ * 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.process;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+
+/**
+ */
+public class UnionComponent extends SetOperationComponent {
+
+ public UnionComponent( QueryContext context,
+ Columns columns,
+ Iterable<ProcessingComponent> sources,
+ boolean alreadySorted,
+ boolean all ) {
+ super(context, columns, sources, alreadySorted, all);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ List<Object[]> tuples = new ArrayList<Object[]>();
+ for (ProcessingComponent source : sources()) {
+ List<Object[]> results = source.execute();
+ tuples.addAll(results);
+ }
+ if (removeDuplicatesComparator != null) {
+ Collections.sort(tuples, this.removeDuplicatesComparator);
+ removeDuplicatesIfRequested(tuples);
+ }
+ return tuples;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/UnionComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,44 @@
+/*
+ * 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.process;
+
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.query.QueryResults;
+
+/**
+ * Interface representing a cache of property values used in a {@link QueryResults}.
+ */
+public interface ValueCache {
+
+ /**
+ * Get the value of the named property on the node at the supplied location.
+ *
+ * @param location the location of the node; may not be null
+ * @param name the property name
+ * @return the value of the property, or null if there is no such value
+ */
+ Object getProperty( Location location,
+ Name name );
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,68 @@
+/*
+ * 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 net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.validate.Schemata.Column;
+
+@Immutable
+class ImmutableColumn implements Column {
+ private final String name;
+ private final PropertyType type;
+
+ protected ImmutableColumn( String name,
+ PropertyType type ) {
+ this.name = name;
+ this.type = type != null ? type : PropertyType.STRING;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Column#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Column#getPropertyType()
+ */
+ public PropertyType getPropertyType() {
+ return type;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return this.name + "(" + type.getName() + ")";
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableColumn.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableKey.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableKey.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableKey.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.validate;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.jboss.dna.graph.query.validate.Schemata.Column;
+import org.jboss.dna.graph.query.validate.Schemata.Key;
+
+/**
+ *
+ */
+public class ImmutableKey implements Key {
+
+ private final Set<Column> columns;
+
+ protected ImmutableKey( Iterable<Column> columns ) {
+ assert columns != null;
+ Set<Column> columnSet = new HashSet<Column>();
+ for (Column column : columns) {
+ if (column != null) columnSet.add(column);
+ }
+ assert !columnSet.isEmpty();
+ this.columns = Collections.unmodifiableSet(columnSet);
+ }
+
+ protected ImmutableKey( Column... columns ) {
+ assert columns != null;
+ assert columns.length > 0;
+ Set<Column> columnSet = new HashSet<Column>();
+ for (Column column : columns) {
+ if (column != null) columnSet.add(column);
+ }
+ assert !columnSet.isEmpty();
+ this.columns = Collections.unmodifiableSet(columnSet);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Key#getColumns()
+ */
+ public Set<Column> getColumns() {
+ return columns;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Key#hasColumns(org.jboss.dna.graph.query.validate.Schemata.Column[])
+ */
+ public boolean hasColumns( Column... columns ) {
+ Set<Column> keyColumns = new HashSet<Column>(this.columns);
+ for (Column expected : columns) {
+ if (!keyColumns.remove(expected)) return false;
+ }
+ return keyColumns.isEmpty();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Key#hasColumns(java.lang.Iterable)
+ */
+ public boolean hasColumns( Iterable<Column> columns ) {
+ Set<Column> keyColumns = new HashSet<Column>(this.columns);
+ for (Column expected : columns) {
+ if (!keyColumns.remove(expected)) return false;
+ }
+ return keyColumns.isEmpty();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ sb.append('[');
+ for (Column column : columns) {
+ if (first) first = false;
+ else sb.append(", ");
+ sb.append(column);
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableKey.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,367 @@
+/*
+ * 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.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 net.jcip.annotations.Immutable;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.model.SelectorName;
+
+/**
+ * An immutable {@link Schemata} implementation.
+ */
+@Immutable
+public class ImmutableSchemata implements Schemata {
+
+ /**
+ * Obtain a new instance for building Schemata objects.
+ *
+ * @return the new builder; never null
+ */
+ public static Builder createBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * A builder of immutable {@link Schemata} objects.
+ */
+ @NotThreadSafe
+ public static class Builder {
+
+ private Map<SelectorName, ImmutableTable> tables = new HashMap<SelectorName, ImmutableTable>();
+
+ /**
+ * 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.
+ *
+ * @param name the name of the new table
+ * @param columnNames the names of the columns.
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the table name is null or empty, any column name is null or empty, or if no column
+ * names are given
+ */
+ public Builder addTable( String name,
+ String... columnNames ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotEmpty(columnNames, "columnNames");
+ List<Column> columns = new ArrayList<Column>();
+ int i = 0;
+ for (String columnName : columnNames) {
+ CheckArg.isNotEmpty(columnName, "columnName[" + (i++) + "]");
+ columns.add(new ImmutableColumn(columnName, PropertyType.STRING));
+ }
+ ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
+ tables.put(table.getName(), table);
+ return this;
+ }
+
+ /**
+ * Add a table with the supplied name and column names and types. The table will also overwrite any existing table
+ * definition with the same name.
+ *
+ * @param name the name of the new table
+ * @param columnNames the names of the columns
+ * @param types the types for the columns
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the table name is null or empty, any column name is null or empty, if no column
+ * names are given, or if the number of types does not match the number of columns
+ */
+ public Builder addTable( String name,
+ String[] columnNames,
+ PropertyType[] types ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotEmpty(columnNames, "columnNames");
+ CheckArg.isNotEmpty(types, "types");
+ CheckArg.isEquals(columnNames.length, "columnNames.length", types.length, "types.length");
+ List<Column> columns = new ArrayList<Column>();
+ assert columnNames.length == types.length;
+ for (int i = 0; i != columnNames.length; ++i) {
+ String columnName = columnNames[i];
+ CheckArg.isNotEmpty(columnName, "columnName[" + i + "]");
+ columns.add(new ImmutableColumn(columnName, types[i]));
+ }
+ ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
+ tables.put(table.getName(), table);
+ return this;
+ }
+
+ /**
+ * Add a table with the supplied name and single column name and type. The table will also overwrite any existing table
+ * definition with the same name.
+ *
+ * @param name the name of the new table
+ * @param column1Name the name of the single column
+ * @param column1Type the type for the single column
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the table name is null or empty, the column name is null or empty, or if the type
+ * is null
+ */
+ public Builder addTable( String name,
+ String column1Name,
+ PropertyType column1Type ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotNull(column1Name, "column1Name");
+ CheckArg.isNotNull(column1Type, "column1Type");
+ List<Column> columns = new ArrayList<Column>();
+ columns.add(new ImmutableColumn(column1Name, column1Type));
+ ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
+ tables.put(table.getName(), table);
+ return this;
+ }
+
+ /**
+ * Add a table with the supplied name and two column names and types. The table will also overwrite any existing table
+ * definition with the same name.
+ *
+ * @param name the name of the new table
+ * @param column1Name the name of the first column
+ * @param column1Type the type for the first column
+ * @param column2Name the name of the second column
+ * @param column2Type the type for the second column
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the table name is null or empty, any column name is null or empty, or any of the
+ * types is null
+ */
+ public Builder addTable( String name,
+ String column1Name,
+ PropertyType column1Type,
+ String column2Name,
+ PropertyType column2Type ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotNull(column1Name, "column1Name");
+ CheckArg.isNotNull(column1Type, "column1Type");
+ CheckArg.isNotNull(column2Name, "column2Name");
+ CheckArg.isNotNull(column2Type, "column2Type");
+ List<Column> columns = new ArrayList<Column>();
+ columns.add(new ImmutableColumn(column1Name, column1Type));
+ columns.add(new ImmutableColumn(column2Name, column2Type));
+ ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
+ tables.put(table.getName(), table);
+ return this;
+ }
+
+ /**
+ * Add a table with the supplied name and three column names and types. The table will also overwrite any existing table
+ * definition with the same name.
+ *
+ * @param name the name of the new table
+ * @param column1Name the name of the first column
+ * @param column1Type the type for the first column
+ * @param column2Name the name of the second column
+ * @param column2Type the type for the second column
+ * @param column3Name the name of the third column
+ * @param column3Type the type for the third column
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the table name is null or empty, any column name is null or empty, or any of the
+ * types is null
+ */
+ public Builder addTable( String name,
+ String column1Name,
+ PropertyType column1Type,
+ String column2Name,
+ PropertyType column2Type,
+ String column3Name,
+ PropertyType column3Type ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotNull(column1Name, "column1Name");
+ CheckArg.isNotNull(column1Type, "column1Type");
+ CheckArg.isNotNull(column2Name, "column2Name");
+ CheckArg.isNotNull(column2Type, "column2Type");
+ CheckArg.isNotNull(column3Name, "column3Name");
+ CheckArg.isNotNull(column3Type, "column3Type");
+ List<Column> columns = new ArrayList<Column>();
+ columns.add(new ImmutableColumn(column1Name, column1Type));
+ columns.add(new ImmutableColumn(column2Name, column2Type));
+ columns.add(new ImmutableColumn(column3Name, column3Type));
+ ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
+ tables.put(table.getName(), table);
+ return this;
+ }
+
+ /**
+ * Add a table with the supplied name and four column names and types. The table will also overwrite any existing table
+ * definition with the same name.
+ *
+ * @param name the name of the new table
+ * @param column1Name the name of the first column
+ * @param column1Type the type for the first column
+ * @param column2Name the name of the second column
+ * @param column2Type the type for the second column
+ * @param column3Name the name of the third column
+ * @param column3Type the type for the third column
+ * @param column4Name the name of the fourth column
+ * @param column4Type the type for the fourth column
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the table name is null or empty, any column name is null or empty, or any of the
+ * types is null
+ */
+ public Builder addTable( String name,
+ String column1Name,
+ PropertyType column1Type,
+ String column2Name,
+ PropertyType column2Type,
+ String column3Name,
+ PropertyType column3Type,
+ String column4Name,
+ PropertyType column4Type ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotNull(column1Name, "column1Name");
+ CheckArg.isNotNull(column1Type, "column1Type");
+ CheckArg.isNotNull(column2Name, "column2Name");
+ CheckArg.isNotNull(column2Type, "column2Type");
+ CheckArg.isNotNull(column3Name, "column3Name");
+ CheckArg.isNotNull(column3Type, "column3Type");
+ CheckArg.isNotNull(column4Name, "column4Name");
+ CheckArg.isNotNull(column4Type, "column4Type");
+ List<Column> columns = new ArrayList<Column>();
+ columns.add(new ImmutableColumn(column1Name, column1Type));
+ columns.add(new ImmutableColumn(column2Name, column2Type));
+ columns.add(new ImmutableColumn(column3Name, column3Type));
+ columns.add(new ImmutableColumn(column4Name, column4Type));
+ ImmutableTable table = new ImmutableTable(new SelectorName(name), columns);
+ tables.put(table.getName(), table);
+ 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.
+ *
+ * @param tableName the name of the new table
+ * @param columnName the names of the column
+ * @param type the type for the column
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the table name is null or empty, any column name is null or empty, if no column
+ * names are given, or if the number of types does not match the number of columns
+ */
+ public Builder addColumn( String tableName,
+ String columnName,
+ PropertyType type ) {
+ CheckArg.isNotEmpty(tableName, "tableName");
+ CheckArg.isNotEmpty(columnName, "columnName");
+ CheckArg.isNotNull(type, "type");
+ SelectorName selector = new SelectorName(tableName);
+ ImmutableTable existing = tables.get(selector);
+ ImmutableTable table = null;
+ if (existing == null) {
+ List<Column> columns = new ArrayList<Column>();
+ columns.add(new ImmutableColumn(columnName, type));
+ table = new ImmutableTable(selector, columns);
+ } else {
+ table = existing.withColumn(columnName, type);
+ }
+ tables.put(table.getName(), table);
+ return this;
+ }
+
+ /**
+ * Add to the specified table a key that references the existing named columns.
+ *
+ * @param tableName the name of the new table
+ * @param columnNames the names of the (existing) columns that make up the key
+ * @return this builder, for convenience in method chaining; never null
+ * @throws IllegalArgumentException if the table name is null or empty, the array of column names is null or empty, or if
+ * the column names do not reference existing columns in the table
+ */
+ public Builder addKey( String tableName,
+ String... columnNames ) {
+ CheckArg.isNotEmpty(tableName, "tableName");
+ CheckArg.isNotEmpty(columnNames, "columnNames");
+ ImmutableTable existing = tables.get(tableName);
+ if (existing == null) {
+ throw new IllegalArgumentException(GraphI18n.tableDoesNotExist.text(tableName));
+ }
+ Set<Column> keyColumns = new HashSet<Column>();
+ for (String columnName : columnNames) {
+ Column existingColumn = existing.getColumnsByName().get(columnName);
+ if (existingColumn == null) {
+ String msg = GraphI18n.schemataKeyReferencesNonExistingColumn.text(tableName, columnName);
+ throw new IllegalArgumentException(msg);
+ }
+ keyColumns.add(existingColumn);
+ }
+ ImmutableTable table = existing.withKey(keyColumns);
+ tables.put(table.getName(), table);
+ return this;
+ }
+
+ /**
+ * Build the {@link Schemata} instance, using the current state of the builder. This method creates a snapshot of the
+ * tables (with their columns) as they exist at the moment this method is called.
+ *
+ * @return the new Schemata; never null
+ */
+ public Schemata build() {
+ return new ImmutableSchemata(new HashMap<SelectorName, Table>(tables));
+ }
+ }
+
+ private final Map<SelectorName, Table> tables;
+
+ protected ImmutableSchemata( Map<SelectorName, Table> tables ) {
+ this.tables = Collections.unmodifiableMap(tables);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata#getTable(org.jboss.dna.graph.query.model.SelectorName)
+ */
+ public Table getTable( SelectorName name ) {
+ return tables.get(name);
+ }
+
+ public ImmutableSchemata with( Table table ) {
+ Map<SelectorName, Table> tables = new HashMap<SelectorName, Table>(this.tables);
+ tables.put(table.getName(), table);
+ return new ImmutableSchemata(tables);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (Table table : tables.values()) {
+ if (first) first = false;
+ else sb.append('\n');
+ sb.append(table);
+ }
+ return sb.toString();
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableTable.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.query.validate;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+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 net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.model.SelectorName;
+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.Table;
+
+@Immutable
+class ImmutableTable implements Table {
+ private final SelectorName name;
+ private final Map<String, Column> columnsByName;
+ private final List<Column> columns;
+ private final Set<Key> keys;
+
+ protected ImmutableTable( SelectorName name,
+ Iterable<Column> columns ) {
+ this(name, columns, (Iterable<Column>[])null);
+ }
+
+ protected ImmutableTable( SelectorName name,
+ Iterable<Column> columns,
+ Iterable<Column>... keyColumns ) {
+ this.name = name;
+ // Define the columns ...
+ List<Column> columnList = new LinkedList<Column>();
+ Map<String, Column> columnMap = new HashMap<String, Column>();
+ for (Column column : columns) {
+ Column old = columnMap.put(column.getName(), column);
+ if (old != null) {
+ columnList.set(columnList.indexOf(old), column);
+ } else {
+ columnList.add(column);
+ }
+ }
+ this.columnsByName = Collections.unmodifiableMap(columnMap);
+ this.columns = Collections.unmodifiableList(columnList);
+ // Define the keys ...
+ if (keyColumns != null) {
+ Set<Key> keys = new HashSet<Key>();
+ for (Iterable<Column> keyColumnSet : keyColumns) {
+ if (keyColumnSet != null) {
+ Key key = new ImmutableKey(keyColumnSet);
+ keys.add(key);
+ }
+ }
+ this.keys = Collections.unmodifiableSet(keys);
+ } else {
+ this.keys = Collections.emptySet();
+ }
+ }
+
+ protected ImmutableTable( SelectorName name,
+ Map<String, Column> columnsByName,
+ List<Column> columns,
+ Set<Key> keys ) {
+ this.name = name;
+ this.columns = columns;
+ this.columnsByName = columnsByName;
+ this.keys = keys;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Table#getName()
+ */
+ public SelectorName getName() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Table#getColumn(java.lang.String)
+ */
+ public Column getColumn( String name ) {
+ return columnsByName.get(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Table#getColumns()
+ */
+ public List<Column> getColumns() {
+ return columns;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Table#getColumnsByName()
+ */
+ public Map<String, Column> getColumnsByName() {
+ return columnsByName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Table#getKeys()
+ */
+ public Collection<Key> getKeys() {
+ return keys;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Table#getKey(org.jboss.dna.graph.query.validate.Schemata.Column[])
+ */
+ public Key getKey( Column... columns ) {
+ for (Key key : keys) {
+ if (key.hasColumns(columns)) return key;
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Table#getKey(java.lang.Iterable)
+ */
+ public Key getKey( Iterable<Column> columns ) {
+ for (Key key : keys) {
+ if (key.hasColumns(columns)) return key;
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Table#hasKey(org.jboss.dna.graph.query.validate.Schemata.Column[])
+ */
+ public boolean hasKey( Column... columns ) {
+ return getKey(columns) != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.validate.Schemata.Table#hasKey(java.lang.Iterable)
+ */
+ public boolean hasKey( Iterable<Column> columns ) {
+ return getKey(columns) != null;
+ }
+
+ public ImmutableTable withColumn( String name,
+ PropertyType type ) {
+ List<Column> newColumns = new LinkedList<Column>(columns);
+ newColumns.add(new ImmutableColumn(name, type));
+ return new ImmutableTable(getName(), newColumns);
+ }
+
+ public ImmutableTable with( SelectorName name ) {
+ return new ImmutableTable(name, columnsByName, columns, keys);
+ }
+
+ public ImmutableTable withKey( Iterable<Column> keyColumns ) {
+ Set<Key> keys = new HashSet<Key>(this.keys);
+ for (Column keyColumn : keyColumns) {
+ assert columns.contains(keyColumn);
+ }
+ if (!keys.add(new ImmutableKey(keyColumns))) return this;
+ return new ImmutableTable(name, columnsByName, columns, keys);
+ }
+
+ public ImmutableTable withKey( Column... keyColumns ) {
+ return withKey(Arrays.asList(keyColumns));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(name.getName());
+ sb.append('(');
+ boolean first = true;
+ for (Column column : columns) {
+ if (first) first = false;
+ else sb.append(", ");
+ sb.append(column);
+ }
+ sb.append(')');
+ if (!keys.isEmpty()) {
+ sb.append(" with keys ");
+ first = true;
+ for (Key key : keys) {
+ 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/ImmutableTable.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,166 @@
+/*
+ * 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.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.model.SelectorName;
+
+/**
+ * The interface used to access the structure being queried and validate a query.
+ */
+public interface Schemata {
+
+ /**
+ * Get the information for the table with the supplied name within this schema.
+ *
+ * @param name the table name; may not be null
+ * @return the table information, or null if there is no such table
+ */
+ Table getTable( SelectorName name );
+
+ /**
+ * Information about a queryable table.
+ */
+ public interface Table {
+ /**
+ * Get the name for this table.
+ *
+ * @return the table name; never null
+ */
+ SelectorName getName();
+
+ /**
+ * Get the information for a column with the supplied name within this table.
+ *
+ * @param name the column name; may not be null
+ * @return the column information, or null if there is no such column
+ */
+ Column getColumn( String name );
+
+ /**
+ * Get the queryable columns in this table.
+ *
+ * @return the map of 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
+ */
+ List<Column> getColumns();
+
+ /**
+ * Get the collection of keys for this table.
+ *
+ * @return the immutable collection of keys; never null, but possibly empty
+ */
+ Collection<Key> getKeys();
+
+ /**
+ * Determine whether this table has a {@link #getKeys() key} that contains exactly those columns listed.
+ *
+ * @param columns the columns for the key
+ * @return true if this table contains a key using exactly the supplied columns, or false otherwise
+ */
+ boolean hasKey( Column... columns );
+
+ /**
+ * Determine whether this table has a {@link #getKeys() key} that contains exactly those columns listed.
+ *
+ * @param columns the columns for the key
+ * @return true if this table contains a key using exactly the supplied columns, or false otherwise
+ */
+ boolean hasKey( Iterable<Column> columns );
+
+ /**
+ * Obtain this table's {@link #getKeys() key} that contains exactly those columns listed.
+ *
+ * @param columns the columns for the key
+ * @return the key that uses exactly the supplied columns, or null if there is no such key
+ */
+ Key getKey( Column... columns );
+
+ /**
+ * Obtain this table's {@link #getKeys() key} that contains exactly those columns listed.
+ *
+ * @param columns the columns for the key
+ * @return the key that uses exactly the supplied columns, or null if there is no such key
+ */
+ Key getKey( Iterable<Column> columns );
+ }
+
+ /**
+ * Information about a queryable column.
+ */
+ public interface Column {
+ /**
+ * Get the name for this column.
+ *
+ * @return the column name; never null
+ */
+ String getName();
+
+ /**
+ * Get the property type for this column.
+ *
+ * @return the property type; never null
+ */
+ PropertyType getPropertyType();
+ }
+
+ /**
+ * Information about a key for a table.
+ */
+ public interface Key {
+ /**
+ * Get the columns that make up this key.
+ *
+ * @return the key's columns; immutable and never null
+ */
+ Set<Column> getColumns();
+
+ /**
+ * Determine whether this key contains exactly those columns listed.
+ *
+ * @param columns the columns for the key
+ * @return true if this key contains exactly the supplied columns, or false otherwise
+ */
+ boolean hasColumns( Column... columns );
+
+ /**
+ * Determine whether this key contains exactly those columns listed.
+ *
+ * @param columns the columns for the key
+ * @return true if this key contains exactly the supplied columns, or false otherwise
+ */
+ boolean hasColumns( Iterable<Column> columns );
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/Schemata.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
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-09-21 18:44:47 UTC (rev 1233)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-09-21 20:03:40 UTC (rev 1234)
@@ -114,3 +114,32 @@
nodeHasAlreadyBeenRemovedFromThisSession = Node "{0}" in workspace "{1} has already been removed from this session
unableToMoveNodeToBeChildOfDecendent = Node "{0}" in workspace "{2}" cannot be moved under a decendant node ("{1}")
childNotFound = Child "{0}" could not be found under "{1}" in workspace "{2}"
+
+# Query
+tableDoesNotExist = Table '{0}' does not exist
+columnDoesNotExistOnTable = Column '{0}' does not exist on the table '{1}'
+columnDoesNotExistInQuery = Column '{0}' does not exist in query
+selectorDoesNotExistInQuery = Selector '{0}' does not exist in query
+propertyOnSelectorIsNotUsedInQuery = Property '{0}' on selector '{1}' is not used in query
+errorResolvingNodesFromLocationsUsingSourceAndWorkspace = Error resolving nodes from locations using '{1}' workspace in '{0}'
+queryHasNoResults = The query has no results
+schemataKeyReferencesNonExistingColumn = Schemata key for table '{0}' references a non-existant column '{1}'
+nextMethodMustBeCalledBeforeGettingValue = The 'next()' method must be called before 'getValue()'
+expectingValidName = Expecting a valid name but found '{0}'
+expectingValidPath = Expecting a valid path but found '{0}'
+columnMustBeScoped = Column '{0}' must be scoped
+expectingValidNameAtLineAndColumn = Expecting a valid name but found '{0}' at line {1}, column {2}
+expectingValidPathAtLineAndColumn = Expecting a valid path but found '{0}' at line {1}, column {2}
+mustBeScopedAtLineAndColumn = '{0}' must be scoped at line {1}, column {2}
+unexpectedToken = Unexpected token '{0}' at line {1}, column {2}
+secondValueInLimitRangeCannotBeLessThanFirst = Second value {0} in LIMIT range cannot be less the first value {1} at line {2}, column {3}
+expectingComparisonOperator = Expecting '=', '<>', '!=', '<', '<=', '>', '>=', or 'LIKE' but found '{0}' at line {1}, column {2}
+expectingConstraintCondition = Expecting a constraint, but found '{0}' at line {1}, column {2}
+functionIsAmbiguous = The {0} function at line {1}, column {2} is ambiguous since there is more than one selector
+bindVariableMustConformToNcName = The name of a variable must conform to a valid NCName, but found '{0}' at line {1}, column {2}
+invalidPropertyType = Expecting 'STRING', 'BINARY', 'DATE', 'LONG', 'DOUBLE', 'DECIMAL', 'BOOLEAN', 'NAME', 'PATH', 'REFERENCE', 'WEAKREFERENCE', or 'URI', but found '{0}' at line {1}, column {2}
+valueCannotBeCastToSpecifiedType = The literal value '{0}' at line {1}, column {2} cannot be cast to a {3} type: {4}
+noMatchingBracketFound = No matching closing bracket for the one at line {0}, column {1}
+expectingLiteralAndUnableToParseAsLong = Expecting literal and unable to parse '{0}' at line {1}, column {2} as a long
+expectingLiteralAndUnableToParseAsDouble = Expecting literal and unable to parse '{0}' at line {1}, column {2} as a double
+expectingLiteralAndUnableToParseAsDate = Expecting literal and unable to parse '{0}' at line {1}, column {2} as a date
Added: 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 (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,72 @@
+/*
+ * 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 static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+
+/**
+ *
+ */
+public abstract class AbstractQueryTest {
+
+ protected SelectorName selector( String selectorName ) {
+ return new SelectorName(selectorName);
+ }
+
+ protected void assertChildren( PlanNode node,
+ PlanNode... children ) {
+ assertThat(node.getChildCount(), is(children.length));
+ for (int i = 0; i != node.getChildCount(); ++i) {
+ assertThat(node.getChild(i), is(sameInstance(children[i])));
+ }
+ }
+
+ protected void assertSelectors( PlanNode node,
+ String... selectors ) {
+ Set<SelectorName> selectorSet = new HashSet<SelectorName>();
+ for (String selectorName : selectors) {
+ selectorSet.add(new SelectorName(selectorName));
+ }
+ assertThat("Selectors don't match", node.getSelectors(), is(selectorSet));
+ }
+
+ protected void assertSortOrderBy( PlanNode sortNode,
+ String... selectors ) {
+ List<SelectorName> expected = new ArrayList<SelectorName>(selectors.length);
+ for (String selectorName : selectors) {
+ expected.add(new SelectorName(selectorName));
+ }
+ List<SelectorName> actualSortedBy = sortNode.getPropertyAsList(Property.SORT_ORDER_BY, SelectorName.class);
+ assertThat("Sort node order-by doesn't match selector name list", actualSortedBy, is(expected));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/AbstractQueryTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,1294 @@
+/*
+ * 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 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.model.Visitors;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class QueryBuilderTest {
+
+ private ExecutionContext context;
+ private QueryBuilder builder;
+ private QueryCommand query;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ builder = new QueryBuilder(context);
+ }
+
+ protected void assertThatSql( QueryCommand query,
+ Matcher<String> expected ) {
+ assertThat(Visitors.readable(query, context), expected);
+ }
+
+ @Test
+ public void shouldBuildSelectStarFromAllNodes() {
+ query = builder.selectStar().fromAllNodes().query();
+ assertThatSql(query, is("SELECT * FROM __ALLNODES__"));
+ }
+
+ @Test
+ public void shouldBuildSelectStarFromAllNodesAs() {
+ query = builder.selectStar().fromAllNodesAs("nodes").query();
+ assertThatSql(query, is("SELECT * FROM __ALLNODES__ AS nodes"));
+ }
+
+ @Test
+ public void shouldBuildSelectStarWithoutOtherClausesAsFromAllNodes() {
+ query = builder.selectStar().query();
+ assertThatSql(query, is("SELECT * FROM __ALLNODES__"));
+ }
+
+ @Test
+ public void shouldBuildSelectColumnsFromAllNodes() {
+ query = builder.select("col1", "col2").fromAllNodes().query();
+ assertThatSql(query, is("SELECT __ALLNODES__.col1,__ALLNODES__.col2 FROM __ALLNODES__"));
+ }
+
+ @Test
+ public void shouldBuildSelectColumnsFromAllNodesAs() {
+ query = builder.select("col1", "col2").fromAllNodesAs("nodes").query();
+ assertThatSql(query, is("SELECT nodes.col1,nodes.col2 FROM __ALLNODES__ AS nodes"));
+ }
+
+ @Test
+ public void shouldBuildSelectColumnsUsingAliasFromAllNodesAs() {
+ query = builder.select("col1", "nodes.col2").fromAllNodesAs("nodes").query();
+ assertThatSql(query, is("SELECT nodes.col1,nodes.col2 FROM __ALLNODES__ AS nodes"));
+ }
+
+ @Test
+ public void shouldBuildSelectStarFromOneTable() {
+ query = builder.selectStar().from("table").query();
+ assertThatSql(query, is("SELECT * FROM table"));
+ }
+
+ @Test
+ public void shouldBuildSelectStarFromOneTableAs() {
+ query = builder.selectStar().from("table AS nodes").query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes"));
+ }
+
+ @Test
+ public void shouldBuildSelectColumnsFromOneTable() {
+ query = builder.select("col1", "col2").from("table").query();
+ assertThatSql(query, is("SELECT table.col1,table.col2 FROM table"));
+ }
+
+ @Test
+ public void shouldBuildSelectColumnsFromOneTableAs() {
+ query = builder.select("col1", "col2").from("table AS nodes").query();
+ assertThatSql(query, is("SELECT nodes.col1,nodes.col2 FROM table AS nodes"));
+ }
+
+ @Test
+ public void shouldBuildSelectColumnsUsingAliasFromOneTableAs() {
+ query = builder.select("col1", "nodes.col2").from("table AS nodes").query();
+ assertThatSql(query, is("SELECT nodes.col1,nodes.col2 FROM table AS nodes"));
+ }
+
+ @Test
+ public void shouldBuildUnionFromTwoSimpleSelects() {
+ query = builder.select("col1", "nodes.col2")
+ .from("table1 AS nodes")
+ .union()
+ .select("col3", "edges.col4")
+ .from("table2 AS edges")
+ .query();
+ assertThatSql(query,
+ is("SELECT nodes.col1,nodes.col2 FROM table1 AS nodes UNION SELECT edges.col3,edges.col4 FROM table2 AS edges"));
+ }
+
+ @Test
+ public void shouldBuildUnionAllFromTwoSimpleSelects() {
+ query = builder.select("col1", "nodes.col2")
+ .from("table1 AS nodes")
+ .unionAll()
+ .select("col3", "edges.col4")
+ .from("table2 AS edges")
+ .query();
+ assertThatSql(query,
+ is("SELECT nodes.col1,nodes.col2 FROM table1 AS nodes UNION ALL SELECT edges.col3,edges.col4 FROM table2 AS edges"));
+ }
+
+ @Test
+ public void shouldBuildUnionAllFromThreeSimpleSelects() {
+ query = builder.select("col1", "nodes.col2")
+ .from("table1 AS nodes")
+ .union()
+ .select("col3", "edges.col4")
+ .from("table2 AS edges")
+ .unionAll()
+ .select("col5", "col6")
+ .from("table3")
+ .query();
+ assertThatSql(query,
+ is("SELECT nodes.col1,nodes.col2 FROM table1 AS nodes UNION SELECT edges.col3,edges.col4 FROM table2 AS edges UNION ALL SELECT table3.col5,table3.col6 FROM table3"));
+ }
+
+ @Test
+ public void shouldBuildIntersectFromTwoSimpleSelects() {
+ query = builder.select("col1", "nodes.col2")
+ .from("table1 AS nodes")
+ .intersect()
+ .select("col3", "edges.col4")
+ .from("table2 AS edges")
+ .query();
+ assertThatSql(query,
+ is("SELECT nodes.col1,nodes.col2 FROM table1 AS nodes INTERSECT SELECT edges.col3,edges.col4 FROM table2 AS edges"));
+ }
+
+ @Test
+ public void shouldBuildIntersectAllFromTwoSimpleSelects() {
+ query = builder.select("col1", "nodes.col2")
+ .from("table1 AS nodes")
+ .intersectAll()
+ .select("col3", "edges.col4")
+ .from("table2 AS edges")
+ .query();
+ assertThatSql(query,
+ is("SELECT nodes.col1,nodes.col2 FROM table1 AS nodes INTERSECT ALL SELECT edges.col3,edges.col4 FROM table2 AS edges"));
+ }
+
+ @Test
+ public void shouldBuildExceptFromTwoSimpleSelects() {
+ query = builder.select("col1", "nodes.col2")
+ .from("table1 AS nodes")
+ .intersect()
+ .select("col3", "edges.col4")
+ .from("table2 AS edges")
+ .query();
+ assertThatSql(query,
+ is("SELECT nodes.col1,nodes.col2 FROM table1 AS nodes INTERSECT SELECT edges.col3,edges.col4 FROM table2 AS edges"));
+ }
+
+ @Test
+ public void shouldBuildExceptAllFromTwoSimpleSelects() {
+ query = builder.select("col1", "nodes.col2")
+ .from("table1 AS nodes")
+ .intersectAll()
+ .select("col3", "edges.col4")
+ .from("table2 AS edges")
+ .query();
+ assertThatSql(query,
+ is("SELECT nodes.col1,nodes.col2 FROM table1 AS nodes INTERSECT ALL SELECT edges.col3,edges.col4 FROM table2 AS edges"));
+ }
+
+ @Test
+ public void shouldBuildEquiJoin() {
+ query = builder.select("t1.c1", "t2.c2").from("table1 AS t1").join("table2 as t2").on(" t1.c0= t2. c0").query();
+ assertThatSql(query, is("SELECT t1.c1,t2.c2 FROM table1 AS t1 INNER JOIN table2 as t2 ON t1.c0 = t2.c0"));
+ }
+
+ @Test
+ public void shouldBuildInnerEquiJoin() {
+ query = builder.select("t1.c1", "t2.c2").from("table1 AS t1").innerJoin("table2 as t2").on(" t1.c0= t2. c0").query();
+ assertThatSql(query, is("SELECT t1.c1,t2.c2 FROM table1 AS t1 INNER JOIN table2 as t2 ON t1.c0 = t2.c0"));
+ }
+
+ @Test
+ public void shouldBuildLeftOuterEquiJoin() {
+ query = builder.select("t1.c1", "t2.c2").from("table1 AS t1").leftOuterJoin("table2 as t2").on(" t1.c0= t2. c0").query();
+ assertThatSql(query, is("SELECT t1.c1,t2.c2 FROM table1 AS t1 LEFT OUTER JOIN table2 as t2 ON t1.c0 = t2.c0"));
+ }
+
+ @Test
+ public void shouldBuildRightOuterEquiJoin() {
+ query = builder.select("t1.c1", "t2.c2")
+ .from("table1 AS t1")
+ .rightOuterJoin("table2 as t2")
+ .on(" t1.c0= t2. c0")
+ .query();
+ assertThatSql(query, is("SELECT t1.c1,t2.c2 FROM table1 AS t1 RIGHT OUTER JOIN table2 as t2 ON t1.c0 = t2.c0"));
+ }
+
+ @Test
+ public void shouldBuildFullOuterEquiJoin() {
+ query = builder.select("t1.c1", "t2.c2").from("table1 AS t1").fullOuterJoin("table2 as t2").on(" t1.c0= t2. c0").query();
+ assertThatSql(query, is("SELECT t1.c1,t2.c2 FROM table1 AS t1 FULL OUTER JOIN table2 as t2 ON t1.c0 = t2.c0"));
+ }
+
+ @Test
+ public void shouldBuildCrossEquiJoin() {
+ query = builder.select("t1.c1", "t2.c2").from("table1 AS t1").crossJoin("table2 as t2").on(" t1.c0= t2. c0").query();
+ assertThatSql(query, is("SELECT t1.c1,t2.c2 FROM table1 AS t1 CROSS JOIN table2 as t2 ON t1.c0 = t2.c0"));
+ }
+
+ @Test
+ public void shouldBuildMultiJoinUsingEquiJoinCriteria() {
+ query = builder.select("t1.c1", "t2.c2")
+ .from("table1 AS t1")
+ .join("table2 as t2")
+ .on(" t1.c0= t2. c0")
+ .join("table3 as t3")
+ .on(" t1.c0= t3. c0")
+ .query();
+ assertThatSql(query, is("SELECT t1.c1,t2.c2 FROM table1 AS t1 " + //
+ "INNER JOIN table2 as t2 ON t1.c0 = t2.c0 " + //
+ "INNER JOIN table3 as t3 ON t1.c0 = t3.c0"));
+ }
+
+ @Test
+ public void shouldBuildMultiJoinAndCrossUsingEquiJoinCriteria() {
+ query = builder.select("t1.c1", "t2.c2")
+ .from("table1 AS t1")
+ .join("table2 as t2")
+ .on(" t1.c0= t2. c0")
+ .crossJoin("table3 as t3")
+ .on(" t1.c0= t3. c0")
+ .query();
+ assertThatSql(query, is("SELECT t1.c1,t2.c2 FROM table1 AS t1 " + //
+ "INNER JOIN table2 as t2 " + //
+ "CROSS JOIN table3 as t3 ON t1.c0 = t3.c0 ON t1.c0 = t2.c0"));
+ }
+
+ @Test
+ public void shouldAddNoConstraintsIfConstraintBuilderIsNotUsedButIsEnded() {
+ query = builder.selectStar().from("table AS nodes").where().end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithOneHasPropertyConstraint() {
+ query = builder.selectStar().from("table AS nodes").where().hasProperty("nodes", "col1").end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes WHERE nodes.col1 IS NOT NULL"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithChildConstraint() {
+ query = builder.selectStar().from("table AS nodes").where().isChild("nodes", "/parent/path").end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes WHERE ISCHILDNODE(nodes,'/parent/path')"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithDescendantConstraint() {
+ query = builder.selectStar().from("table AS nodes").where().isBelowPath("nodes", "/parent/path").end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes WHERE ISDESCENDANTNODE(nodes,'/parent/path')"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithSameNodeConstraint() {
+ query = builder.selectStar().from("table AS nodes").where().isSameNode("nodes", "/other/path").end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes WHERE ISSAMENODE(nodes,'/other/path')"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithFullTextSearchConstraint() {
+ query = builder.selectStar().from("table AS nodes").where().search("nodes", "expression").end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes WHERE CONTAINS(nodes,'expression')"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithPropertyFullTextSearchConstraint() {
+ query = builder.selectStar().from("table AS nodes").where().search("nodes", "property", "expression").end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes WHERE CONTAINS(nodes.property,'expression')"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithTwoHasPropertyConstraint() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .hasProperty("nodes", "col1")
+ .and()
+ .hasProperty("nodes", "col2")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes WHERE (nodes.col1 IS NOT NULL AND nodes.col2 IS NOT NULL)"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithThreeHasPropertyConstraint() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .hasProperty("nodes", "col1")
+ .and()
+ .hasProperty("nodes", "col2")
+ .and()
+ .hasProperty("nodes", "col3")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE ((nodes.col1 IS NOT NULL " + //
+ "AND nodes.col2 IS NOT NULL) " + //
+ "AND nodes.col3 IS NOT NULL)"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCorrectPrecedenceWithAndAndOr() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .hasProperty("nodes", "col1")
+ .or()
+ .hasProperty("nodes", "col2")
+ .and()
+ .hasProperty("nodes", "col3")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE (nodes.col1 IS NOT NULL " + //
+ "OR (nodes.col2 IS NOT NULL " + //
+ "AND nodes.col3 IS NOT NULL))"));
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .hasProperty("nodes", "col1")
+ .and()
+ .hasProperty("nodes", "col2")
+ .or()
+ .hasProperty("nodes", "col3")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE ((nodes.col1 IS NOT NULL " + //
+ "AND nodes.col2 IS NOT NULL) " + //
+ "OR nodes.col3 IS NOT NULL)"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithMixureOfLogicalWithExplicitParenthesesWithHasPropertyConstraint() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .hasProperty("nodes", "col1")
+ .and()
+ .openParen()
+ .hasProperty("nodes", "col2")
+ .and()
+ .hasProperty("nodes", "col3")
+ .closeParen()
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE (nodes.col1 IS NOT NULL " + //
+ "AND (nodes.col2 IS NOT NULL " + //
+ "AND nodes.col3 IS NOT NULL))"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithMixureOfLogicalWithMultipleExplicitParenthesesWithHasPropertyConstraint() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .hasProperty("nodes", "col1")
+ .and()
+ .openParen()
+ .openParen()
+ .hasProperty("nodes", "col2")
+ .and()
+ .hasProperty("nodes", "col3")
+ .closeParen()
+ .and()
+ .search("nodes", "expression")
+ .closeParen()
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE (nodes.col1 IS NOT NULL " + //
+ "AND ((nodes.col2 IS NOT NULL " + //
+ "AND nodes.col3 IS NOT NULL) " + //
+ "AND CONTAINS(nodes,'expression')))"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) = literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) = $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthNotEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isNotEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) != literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthNotEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isNotEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) != $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthLessThan() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isLessThan("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) < literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthLessThanVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isLessThanVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) < $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthLessThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isLessThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) <= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthLessThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isLessThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) <= $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthGreaterThan() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isGreaterThan("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) > literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthGreaterThanVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isGreaterThanVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) > $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthGreaterThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isGreaterThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) >= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthGreaterThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isGreaterThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) >= $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLengthLikeVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .length("nodes", "property")
+ .isLikeVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LENGTH(nodes.property) LIKE $literal"));
+ }
+
+ @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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeNameEqualToVariable() {
+ query = builder.selectStar().from("table AS nodes").where().nodeName("nodes").isEqualToVariable("literal").end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE NAME(nodes) = $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeNameNotEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeName("nodes")
+ .isNotEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE NAME(nodes) != $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeNameLessThanVariable() {
+ query = builder.selectStar().from("table AS nodes").where().nodeName("nodes").isLessThanVariable("literal").end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE NAME(nodes) < $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeNameLessThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeName("nodes")
+ .isLessThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE NAME(nodes) <= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeNameLessThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeName("nodes")
+ .isLessThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE NAME(nodes) <= $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeNameGreaterThanVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeName("nodes")
+ .isGreaterThanVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE NAME(nodes) > $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeNameGreaterThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeName("nodes")
+ .isGreaterThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE NAME(nodes) >= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeNameGreaterThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeName("nodes")
+ .isGreaterThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE NAME(nodes) >= $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeNameLikeVariable() {
+ query = builder.selectStar().from("table AS nodes").where().nodeName("nodes").isLikeVariable("literal").end().query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE NAME(nodes) LIKE $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeLocalNameEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeLocalName("nodes")
+ .isEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOCALNAME(nodes) = $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeLocalNameNotEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeLocalName("nodes")
+ .isNotEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOCALNAME(nodes) != $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeLocalNameLessThanVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeLocalName("nodes")
+ .isLessThanVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOCALNAME(nodes) < $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeLocalNameLessThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeLocalName("nodes")
+ .isLessThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOCALNAME(nodes) <= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeLocalNameLessThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeLocalName("nodes")
+ .isLessThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOCALNAME(nodes) <= $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeLocalNameGreaterThanVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeLocalName("nodes")
+ .isGreaterThanVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOCALNAME(nodes) > $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeLocalNameGreaterThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeLocalName("nodes")
+ .isGreaterThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOCALNAME(nodes) >= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeLocalNameGreaterThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeLocalName("nodes")
+ .isGreaterThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOCALNAME(nodes) >= $literal"));
+ }
+
+ @Test
+ 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"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingNodeLocalNameLikeVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .nodeLocalName("nodes")
+ .isLikeVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOCALNAME(nodes) LIKE $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) = literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) = $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameNotEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isNotEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) != literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameNotEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isNotEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) != $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameLessThan() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isLessThan("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) < literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameLessThanVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isLessThanVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) < $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameLessThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isLessThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) <= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameLessThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isLessThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) <= $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameGreaterThan() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isGreaterThan("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) > literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameGreaterThanVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isGreaterThanVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) > $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameGreaterThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isGreaterThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) >= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameGreaterThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isGreaterThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) >= $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameLike() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isLike("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) LIKE literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingUppercaseOfNodeNameLikeVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isLikeVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE UPPER(NAME(nodes)) LIKE $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) = literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) = $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameNotEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isNotEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) != literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameNotEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isNotEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) != $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameLessThan() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isLessThan("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) < literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameLessThanVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isLessThanVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) < $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameLessThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isLessThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) <= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameLessThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isLessThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) <= $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameGreaterThan() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isGreaterThan("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) > literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameGreaterThanVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isGreaterThanVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) > $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameGreaterThanOrEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isGreaterThanOrEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) >= literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameGreaterThanOrEqualToVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isGreaterThanOrEqualToVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) >= $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameLike() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isLike("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) LIKE literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfNodeNameLikeVariable() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .nodeName("nodes")
+ .isLikeVariable("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(NAME(nodes)) LIKE $literal"));
+ }
+
+ @Test
+ public void shouldBuildQueryWithCriteriaUsingLowercaseOfUppercaseOfNodeNameEqualTo() {
+ query = builder.selectStar()
+ .from("table AS nodes")
+ .where()
+ .lowerCaseOf()
+ .upperCaseOf()
+ .nodeName("nodes")
+ .isEqualTo("literal")
+ .end()
+ .query();
+ assertThatSql(query, is("SELECT * FROM table AS nodes " + //
+ "WHERE LOWER(UPPER(NAME(nodes))) = literal"));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/QueryBuilderTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/AbstractQueryObjectTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/AbstractQueryObjectTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/AbstractQueryObjectTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,54 @@
+/*
+ * 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 org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.junit.Before;
+
+/**
+ *
+ */
+public abstract class AbstractQueryObjectTest {
+
+ protected ExecutionContext context;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ }
+
+ protected Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Path path( String name ) {
+ return context.getValueFactories().getPathFactory().create(name);
+ }
+
+ protected SelectorName selector( String name ) {
+ return new SelectorName(name);
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/AbstractQueryObjectTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/QueryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/QueryTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/QueryTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,202 @@
+/*
+ * 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 static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class QueryTest extends AbstractQueryObjectTest {
+
+ private Query query;
+ private Source source;
+ private Constraint constraint;
+ private List<Ordering> orderings;
+ private List<Column> columns;
+ private Limit limits;
+ private boolean distinct;
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotCreateWithNullSource() {
+ new Query(null);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotCreateWithNullSourceWhenSupplyingOtherParameters() {
+ source = null;
+ constraint = mock(Constraint.class);
+ orderings = Collections.emptyList();
+ columns = Collections.emptyList();
+ limits = null;
+ new Query(source, constraint, orderings, columns, limits, distinct);
+ }
+
+ @Test
+ public void shouldAllowNullConstraint() {
+ source = mock(Source.class);
+ constraint = null;
+ orderings = Collections.emptyList();
+ columns = Collections.emptyList();
+ limits = null;
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(query.getSource(), is(sameInstance(source)));
+ assertThat(query.getConstraint(), is(nullValue()));
+ assertThat(query.getOrderings(), is(sameInstance(orderings)));
+ assertThat(query.getColumns(), is(sameInstance(columns)));
+ }
+
+ @Test
+ public void shouldAllowNullOrderingsList() {
+ source = mock(Source.class);
+ constraint = mock(Constraint.class);
+ orderings = null;
+ columns = Collections.emptyList();
+ limits = null;
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(query.getSource(), is(sameInstance(source)));
+ assertThat(query.getConstraint(), is(sameInstance(constraint)));
+ assertThat(query.getOrderings().isEmpty(), is(true));
+ assertThat(query.getColumns(), is(sameInstance(columns)));
+ }
+
+ @Test
+ public void shouldAllowNullColumnsList() {
+ source = mock(Source.class);
+ constraint = mock(Constraint.class);
+ orderings = Collections.emptyList();
+ columns = null;
+ limits = null;
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(query.getSource(), is(sameInstance(source)));
+ assertThat(query.getConstraint(), is(sameInstance(constraint)));
+ assertThat(query.getOrderings(), is(sameInstance(orderings)));
+ assertThat(query.getColumns().isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldCreateWithNonNullParameters() {
+ source = mock(Source.class);
+ constraint = mock(Constraint.class);
+ orderings = Collections.emptyList();
+ columns = Collections.emptyList();
+ limits = null;
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(query.getSource(), is(sameInstance(source)));
+ assertThat(query.getConstraint(), is(sameInstance(constraint)));
+ assertThat(query.getOrderings(), is(sameInstance(orderings)));
+ assertThat(query.getColumns(), is(sameInstance(columns)));
+ }
+
+ @Test
+ public void shouldConstructReadableString() {
+ source = new NamedSelector(selector("nt:unstructured"));
+ columns = Collections.singletonList(new Column(selector("selector1")));
+ constraint = new PropertyExistence(selector("selector1"), name("jcr:uuid"));
+ orderings = Collections.singletonList(new Ordering(new NodeName(selector("selector1")), Order.ASCENDING));
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(Visitors.readable(query, context),
+ is("SELECT selector1.* FROM nt:unstructured WHERE selector1.jcr:uuid IS NOT NULL ORDER BY NAME(selector1) ASC"));
+ }
+
+ @Test
+ public void shouldConstructReadableStringWithLimits() {
+ source = new NamedSelector(selector("nt:unstructured"));
+ columns = Collections.singletonList(new Column(selector("selector1")));
+ constraint = new PropertyExistence(selector("selector1"), name("jcr:uuid"));
+ orderings = Collections.singletonList(new Ordering(new NodeName(selector("selector1")), Order.ASCENDING));
+ limits = new Limit(10, 100);
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(Visitors.readable(query, context),
+ is("SELECT selector1.* FROM nt:unstructured WHERE selector1.jcr:uuid IS NOT NULL ORDER BY NAME(selector1) ASC LIMIT 10 OFFSET 100"));
+ }
+
+ @Test
+ public void shouldConstructReadableStringWithNoColumns() {
+ source = new NamedSelector(selector("nt:unstructured"));
+ columns = Collections.emptyList();
+ constraint = new PropertyExistence(selector("selector1"), name("jcr:uuid"));
+ orderings = Collections.singletonList(new Ordering(new NodeName(selector("selector1")), Order.ASCENDING));
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(Visitors.readable(query, context),
+ is("SELECT * FROM nt:unstructured WHERE selector1.jcr:uuid IS NOT NULL ORDER BY NAME(selector1) ASC"));
+ }
+
+ @Test
+ public void shouldConstructReadableStringWithNoOrderings() {
+ source = new NamedSelector(selector("nt:unstructured"));
+ columns = Collections.singletonList(new Column(selector("selector1")));
+ constraint = new PropertyExistence(selector("selector1"), name("jcr:uuid"));
+ orderings = Collections.emptyList();
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(Visitors.readable(query, context),
+ is("SELECT selector1.* FROM nt:unstructured WHERE selector1.jcr:uuid IS NOT NULL"));
+ }
+
+ @Test
+ public void shouldConstructReadableStringWithNoConstraint() {
+ source = new NamedSelector(selector("nt:unstructured"));
+ columns = Collections.singletonList(new Column(selector("selector1")));
+ constraint = null;
+ orderings = Collections.singletonList(new Ordering(new NodeName(selector("selector1")), Order.ASCENDING));
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(Visitors.readable(query, context), is("SELECT selector1.* FROM nt:unstructured ORDER BY NAME(selector1) ASC"));
+ }
+
+ @Test
+ public void shouldConstructReadableStringWithDistinctAndNoConstraintOrColumnsOrOrderings() {
+ source = new NamedSelector(selector("nt:unstructured"));
+ columns = Collections.emptyList();
+ constraint = null;
+ orderings = Collections.emptyList();
+ distinct = true;
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(Visitors.readable(query, context), is("SELECT DISTINCT * FROM nt:unstructured"));
+
+ source = new AllNodes();
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(Visitors.readable(query, context), is("SELECT DISTINCT * FROM __ALLNODES__"));
+ }
+
+ @Test
+ public void shouldConstructReadableStringWithNoConstraintOrColumnsOrOrderings() {
+ source = new NamedSelector(selector("nt:unstructured"));
+ columns = Collections.emptyList();
+ constraint = null;
+ orderings = Collections.emptyList();
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(Visitors.readable(query, context), is("SELECT * FROM nt:unstructured"));
+
+ source = new AllNodes();
+ query = new Query(source, constraint, orderings, columns, limits, distinct);
+ assertThat(Visitors.readable(query, context), is("SELECT * FROM __ALLNODES__"));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/QueryTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/SetQueryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/SetQueryTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/SetQueryTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,127 @@
+/*
+ * 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 static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class SetQueryTest extends AbstractQueryObjectTest {
+
+ private SetQuery query;
+ private QueryCommand left;
+ private QueryCommand right;
+ private SetQuery.Operation operation;
+ private boolean all;
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotCreateWithNullSource() {
+ new Query(null);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotCreateWithNullLeftQueryCommand() {
+ left = null;
+ right = mock(QueryCommand.class);
+ operation = SetQuery.Operation.UNION;
+ new SetQuery(left, operation, right, all);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotCreateWithNullRightQueryCommand() {
+ left = mock(QueryCommand.class);
+ right = null;
+ operation = SetQuery.Operation.UNION;
+ new SetQuery(left, operation, right, all);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotCreateWithNullOperation() {
+ left = mock(QueryCommand.class);
+ right = mock(QueryCommand.class);
+ operation = null;
+ new SetQuery(left, operation, right, all);
+ }
+
+ @Test
+ public void shouldCreateWithNonNullQueryCommandsAndUnionOperation() {
+ left = new Query(new NamedSelector(selector("A")));
+ right = new Query(new NamedSelector(selector("B")));
+ operation = SetQuery.Operation.UNION;
+ query = new SetQuery(left, operation, right, all);
+ assertThat(Visitors.readable(query, context), is("SELECT * FROM A UNION SELECT * FROM B"));
+ }
+
+ @Test
+ public void shouldCreateWithNonNullQueryCommandsAndUnionAllOperation() {
+ left = new Query(new NamedSelector(selector("A")));
+ right = new Query(new NamedSelector(selector("B")));
+ operation = SetQuery.Operation.UNION;
+ all = true;
+ query = new SetQuery(left, operation, right, all);
+ assertThat(Visitors.readable(query, context), is("SELECT * FROM A UNION ALL SELECT * FROM B"));
+ }
+
+ @Test
+ public void shouldCreateWithNonNullQueryCommandsAndIntersectOperation() {
+ left = new Query(new NamedSelector(selector("A")));
+ right = new Query(new NamedSelector(selector("B")));
+ operation = SetQuery.Operation.INTERSECT;
+ query = new SetQuery(left, operation, right, all);
+ assertThat(Visitors.readable(query, context), is("SELECT * FROM A INTERSECT SELECT * FROM B"));
+ }
+
+ @Test
+ public void shouldCreateWithNonNullQueryCommandsAndIntersectAllOperation() {
+ left = new Query(new NamedSelector(selector("A")));
+ right = new Query(new NamedSelector(selector("B")));
+ operation = SetQuery.Operation.INTERSECT;
+ all = true;
+ query = new SetQuery(left, operation, right, all);
+ assertThat(Visitors.readable(query, context), is("SELECT * FROM A INTERSECT ALL SELECT * FROM B"));
+ }
+
+ @Test
+ public void shouldCreateWithNonNullQueryCommandsAndExceptOperation() {
+ left = new Query(new NamedSelector(selector("A")));
+ right = new Query(new NamedSelector(selector("B")));
+ operation = SetQuery.Operation.EXCEPT;
+ query = new SetQuery(left, operation, right, all);
+ assertThat(Visitors.readable(query, context), is("SELECT * FROM A EXCEPT SELECT * FROM B"));
+ }
+
+ @Test
+ public void shouldCreateWithNonNullQueryCommandsAndExceptAllOperation() {
+ left = new Query(new NamedSelector(selector("A")));
+ right = new Query(new NamedSelector(selector("B")));
+ operation = SetQuery.Operation.EXCEPT;
+ all = true;
+ query = new SetQuery(left, operation, right, all);
+ assertThat(Visitors.readable(query, context), is("SELECT * FROM A EXCEPT ALL SELECT * FROM B"));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/model/SetQueryTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.optimize;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import java.util.LinkedList;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.AbstractQueryTest;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.optimize.AddAccessNodes;
+import org.jboss.dna.graph.query.optimize.OptimizerRule;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class AddAccessNodesTest extends AbstractQueryTest {
+
+ private AddAccessNodes rule;
+ private QueryContext context;
+
+ @Before
+ public void beforeEach() {
+ context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+ rule = AddAccessNodes.INSTANCE;
+ }
+
+ /**
+ * Before:
+ *
+ * <pre>
+ * ...
+ * |
+ * SOURCE
+ * </pre>
+ *
+ * And after:
+ *
+ * <pre>
+ * ...
+ * |
+ * ACCESS
+ * |
+ * SOURCE
+ * </pre>
+ */
+ @Test
+ public void shouldAddAccessNodeAboveSourceNode() {
+ PlanNode project = new PlanNode(Type.PROJECT, selector("Selector1"));
+ PlanNode source = new PlanNode(Type.SOURCE, project, selector("Selector1"));
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, project, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(project)));
+ PlanNode access = project.getFirstChild();
+ assertThat(access.getType(), is(Type.ACCESS));
+ assertSelectors(access, "Selector1");
+ assertChildren(access, source);
+ assertChildren(source);
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,160 @@
+/*
+ * 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 static org.mockito.Mockito.mock;
+import java.util.LinkedList;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.AbstractQueryTest;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.ChildNodeJoinCondition;
+import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
+import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.optimize.ChooseJoinAlgorithm;
+import org.jboss.dna.graph.query.optimize.OptimizerRule;
+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.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ChooseJoinAlgorithmTest extends AbstractQueryTest {
+
+ private ChooseJoinAlgorithm bestRule;
+ private ChooseJoinAlgorithm nestedRule;
+ private QueryContext context;
+
+ @Before
+ public void beforeEach() {
+ context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+ bestRule = ChooseJoinAlgorithm.USE_BEST_JOIN_ALGORITHM;
+ nestedRule = ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM;
+ }
+
+ /**
+ * The {@link ChooseJoinAlgorithm#USE_ONLY_NESTED_JOIN_ALGORITHM} instance will convert this simple tree:
+ *
+ * <pre>
+ * ...
+ * |
+ * JOIN
+ * / \
+ * ... ...
+ * </pre>
+ *
+ * into this:
+ *
+ * <pre>
+ * ...
+ * |
+ * JOIN ({@link Property#JOIN_ALGORITHM JOIN_ALGORITHM}={@link JoinAlgorithm#NESTED_LOOP NESTED_LOOP})
+ * / \
+ * ... ...
+ * </pre>
+ */
+ @Test
+ public void shouldHaveNestedRuleAlwaysSetJoinAlgorithmToNestedLoop() {
+ PlanNode join = new PlanNode(Type.JOIN, selector("Selector1"), selector("Selector2"));
+ PlanNode s1Source = new PlanNode(Type.SOURCE, join, selector("Selector1"));
+ PlanNode s2Source = new PlanNode(Type.SOURCE, join, selector("Selector2"));
+ // Set the join type ...
+ join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+
+ // Execute the rule ...
+ PlanNode result = nestedRule.execute(context, join, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(join)));
+ assertThat(join.getProperty(Property.JOIN_TYPE, JoinType.class), is(JoinType.INNER));
+ assertThat(join.getProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.class), is(JoinAlgorithm.NESTED_LOOP));
+ assertChildren(join, s1Source, s2Source);
+ }
+
+ @Test
+ public void shouldHaveBestRuleAlwaysSetJoinAlgorithmToNestedLoopIfConditionIsDescendantNode() {
+ PlanNode join = new PlanNode(Type.JOIN, selector("Ancestor"), selector("Descendant"));
+ PlanNode ancestorSource = new PlanNode(Type.SOURCE, join, selector("Ancestor"));
+ PlanNode descendantSource = new PlanNode(Type.SOURCE, join, selector("Descendant"));
+ // Set the join type and condition ...
+ JoinCondition joinCondition = new DescendantNodeJoinCondition(selector("Ancestor"), selector("Descendant"));
+ join.setProperty(Property.JOIN_CONDITION, joinCondition);
+ join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+
+ // Execute the rule ...
+ PlanNode result = bestRule.execute(context, join, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(join)));
+ assertThat(join.getProperty(Property.JOIN_TYPE, JoinType.class), is(JoinType.INNER));
+ assertThat(join.getProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.class), is(JoinAlgorithm.NESTED_LOOP));
+ assertThat(join.getProperty(Property.JOIN_CONDITION, JoinCondition.class), is(sameInstance(joinCondition)));
+ assertChildren(join, ancestorSource, descendantSource);
+ }
+
+ @Test
+ public void shouldHaveBestRuleSetJoinAlgorithmToMergeIfConditionIsNotDescendantNode() {
+ PlanNode join = new PlanNode(Type.JOIN, selector("Parent"), selector("Child"));
+ PlanNode parentSource = new PlanNode(Type.SOURCE, join, selector("Parent"));
+ PlanNode childSource = new PlanNode(Type.SOURCE, join, selector("Child"));
+ // Set the join type and condition ...
+ JoinCondition joinCondition = new ChildNodeJoinCondition(selector("Parent"), selector("Child"));
+ join.setProperty(Property.JOIN_CONDITION, joinCondition);
+ join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+
+ // Execute the rule ...
+ PlanNode result = bestRule.execute(context, join, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(join)));
+ assertThat(join.getProperty(Property.JOIN_TYPE, JoinType.class), is(JoinType.INNER));
+ assertThat(join.getProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.class), is(JoinAlgorithm.MERGE));
+ assertThat(join.getProperty(Property.JOIN_CONDITION, JoinCondition.class), is(sameInstance(joinCondition)));
+
+ PlanNode leftDup = join.getFirstChild();
+ assertThat(leftDup.getType(), is(Type.DUP_REMOVE));
+ assertSelectors(leftDup, "Parent");
+ PlanNode leftSort = leftDup.getFirstChild();
+ assertThat(leftSort.getType(), is(Type.SORT));
+ assertSortOrderBy(leftSort, "Parent");
+ assertSelectors(leftSort, "Parent");
+ assertChildren(leftDup, leftSort);
+ assertChildren(leftSort, parentSource);
+
+ PlanNode rightDup = join.getLastChild();
+ assertThat(rightDup.getType(), is(Type.DUP_REMOVE));
+ assertSelectors(rightDup, "Child");
+ PlanNode rightSort = rightDup.getLastChild();
+ assertThat(rightSort.getType(), is(Type.SORT));
+ assertSortOrderBy(rightSort, "Child");
+ assertSelectors(rightSort, "Child");
+ assertChildren(rightDup, rightSort);
+ assertChildren(rightSort, childSource);
+
+ assertChildren(join, leftDup, rightDup);
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,226 @@
+/*
+ * 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 static org.mockito.Mockito.mock;
+import java.util.LinkedList;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.AbstractQueryTest;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.optimize.OptimizerRule;
+import org.jboss.dna.graph.query.optimize.PushSelectCriteria;
+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.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class PushSelectCriteriaTest extends AbstractQueryTest {
+
+ private PushSelectCriteria rule;
+ private QueryContext context;
+
+ @Before
+ public void beforeEach() {
+ context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+ rule = PushSelectCriteria.INSTANCE;
+ }
+
+ /**
+ * 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
+ * SELECTn
+ * |
+ * ACCESS
+ * |
+ * SOURCE
+ * </pre>
+ *
+ * And after:
+ *
+ * <pre>
+ * ...
+ * |
+ * 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
+ * | together
+ * SELECTn
+ * |
+ * SOURCE
+ * </pre>
+ */
+ @Test
+ public void shouldPushDownAllSelectNodesThatApplyToSelectorBelowAccessNodeButAboveSourceNodeUsingSameSelector() {
+ // 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("Selector1"));
+ PlanNode select1 = new PlanNode(Type.SELECT, project, selector("Selector1"));
+ PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("Selector1"));
+ PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("Selector1"));
+ PlanNode select4 = new PlanNode(Type.SELECT, select3, selector("Selector1"));
+ PlanNode access = new PlanNode(Type.ACCESS, select4, selector("Selector1"));
+ PlanNode source = new PlanNode(Type.SOURCE, access, selector("Selector1"));
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, project, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(project)));
+ assertChildren(project, access);
+ assertChildren(access, select1);
+ assertChildren(select1, select2);
+ assertChildren(select2, select3);
+ assertChildren(select3, select4);
+ assertChildren(select4, source);
+ assertChildren(source);
+ }
+
+ @Test
+ public void shouldNotPushDownSelectNodesThatUseDifferentSelectorNamesThanSourceNode() {
+ // 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("Selector1"));
+ PlanNode select1 = new PlanNode(Type.SELECT, project, selector("Selector2"));
+ PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("Selector1"));
+ PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("Selector2"));
+ PlanNode select4 = new PlanNode(Type.SELECT, select3, selector("Selector1"));
+ PlanNode access = new PlanNode(Type.ACCESS, select4, selector("Selector1"));
+ PlanNode source = new PlanNode(Type.SOURCE, access, selector("Selector1"));
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, project, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(project)));
+ assertChildren(project, select1);
+ assertChildren(select1, select3);
+ assertChildren(select3, access);
+ assertChildren(access, select2);
+ assertChildren(select2, select4);
+ assertChildren(select4, source);
+ assertChildren(source);
+ }
+
+ /**
+ * Before:
+ *
+ * <pre>
+ * ...
+ * |
+ * PROJECT ('s1','s2') with the list of columns being SELECTed (from 's1' and 's2' selectors)
+ * |
+ * SELECT1 ('s1')
+ * | One or more SELECT plan nodes that each have
+ * SELECT2 ('s2') a single non-join constraint that are then all AND-ed
+ * | together, and that each have the selector(s) they apply to
+ * SELECT3 ('s1','s2')
+ * |
+ * SELECT4 ('s1')
+ * |
+ * JOIN ('s1','s2')
+ * / \
+ * / \
+ * ACCESS ACCESS
+ * ('s1') ('s2')
+ * | |
+ * SOURCE SOURCE
+ * ('s1') ('s2')
+ * </pre>
+ *
+ * And after:
+ *
+ * <pre>
+ * ...
+ * |
+ * PROJECT ('s1','s2') with the list of columns being SELECTed (from 's1' and 's2' selectors)
+ * |
+ * SELECT3 ('s1','s2') Any SELECT plan nodes that apply to multiple selectors are left above
+ * | the ACCESS nodes.
+ * JOIN ('s1','s2')
+ * / \
+ * / \
+ * ACCESS ACCESS
+ * ('s1') ('s2')
+ * | |
+ * SELECT1 SELECT2
+ * ('s1') ('s2')
+ * | |
+ * SELECT4 SOURCE
+ * ('s1') ('s2')
+ * |
+ * SOURCE
+ * ('s1')
+ * </pre>
+ */
+ @Test
+ public void shouldPushDownAllSelectNodesThatApplyToOneSelectorToBelowAccessNodeForThatSelector() {
+ // 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("Selector1"), selector("Selector2"));
+ PlanNode select1 = new PlanNode(Type.SELECT, project, selector("Selector1"));
+ PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("Selector2"));
+ PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("Selector1"), selector("Selector2"));
+ PlanNode select4 = new PlanNode(Type.SELECT, select3, selector("Selector1"));
+ PlanNode join = new PlanNode(Type.JOIN, select4, selector("Selector1"), selector("Selector2"));
+ PlanNode s1Access = new PlanNode(Type.ACCESS, join, selector("Selector1"));
+ PlanNode s1Source = new PlanNode(Type.SOURCE, s1Access, selector("Selector1"));
+ PlanNode s2Access = new PlanNode(Type.ACCESS, join, selector("Selector2"));
+ PlanNode s2Source = new PlanNode(Type.SOURCE, s2Access, selector("Selector2"));
+ // Set the join type ...
+ join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, project, new LinkedList<OptimizerRule>());
+
+ System.out.println(result);
+
+ assertThat(result, is(sameInstance(project)));
+ assertChildren(project, select3);
+ assertChildren(select3, join);
+ assertChildren(join, s1Access, s2Access);
+ assertChildren(s1Access, select1);
+ assertChildren(select1, select4);
+ assertChildren(select4, s1Source);
+ assertChildren(s2Access, select2);
+ assertChildren(select2, s2Source);
+ assertChildren(s2Source);
+ assertChildren(s1Source);
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,154 @@
+/*
+ * 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 static org.mockito.Mockito.mock;
+import java.util.LinkedList;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.AbstractQueryTest;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.JoinType;
+import org.jboss.dna.graph.query.optimize.OptimizerRule;
+import org.jboss.dna.graph.query.optimize.RightOuterToLeftOuterJoins;
+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.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class RightOuterToLeftOuterJoinsTest extends AbstractQueryTest {
+
+ private RightOuterToLeftOuterJoins rule;
+ private QueryContext context;
+
+ @Before
+ public void beforeEach() {
+ context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+ rule = RightOuterToLeftOuterJoins.INSTANCE;
+ }
+
+ @Test
+ public void shouldDoNothingWithLeftOuterJoin() {
+ // Create a LEFT_OUTER join ...
+ PlanNode joinNode = new PlanNode(Type.JOIN);
+ joinNode.setProperty(Property.JOIN_TYPE, JoinType.LEFT_OUTER);
+ PlanNode lhs = new PlanNode(Type.SOURCE, joinNode);
+ PlanNode rhs = new PlanNode(Type.SOURCE, joinNode);
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, joinNode, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(joinNode)));
+
+ // Verify nothing has changed ...
+ assertThat(joinNode.getProperty(Property.JOIN_TYPE, JoinType.class), is(JoinType.LEFT_OUTER));
+ assertThat(joinNode.getFirstChild(), is(sameInstance(lhs)));
+ assertThat(joinNode.getLastChild(), is(sameInstance(rhs)));
+ assertThat(joinNode.getChildCount(), is(2));
+ }
+
+ @Test
+ public void shouldDoNothingWithCrossJoin() {
+ // Create a LEFT_OUTER join ...
+ PlanNode joinNode = new PlanNode(Type.JOIN);
+ joinNode.setProperty(Property.JOIN_TYPE, JoinType.CROSS);
+ PlanNode lhs = new PlanNode(Type.SOURCE, joinNode);
+ PlanNode rhs = new PlanNode(Type.SOURCE, joinNode);
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, joinNode, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(joinNode)));
+
+ // Verify nothing has changed ...
+ assertThat(joinNode.getProperty(Property.JOIN_TYPE, JoinType.class), is(JoinType.CROSS));
+ assertThat(joinNode.getFirstChild(), is(sameInstance(lhs)));
+ assertThat(joinNode.getLastChild(), is(sameInstance(rhs)));
+ assertThat(joinNode.getChildCount(), is(2));
+ }
+
+ @Test
+ public void shouldDoNothingWithFullOuterJoin() {
+ // Create a LEFT_OUTER join ...
+ PlanNode joinNode = new PlanNode(Type.JOIN);
+ joinNode.setProperty(Property.JOIN_TYPE, JoinType.FULL_OUTER);
+ PlanNode lhs = new PlanNode(Type.SOURCE, joinNode);
+ PlanNode rhs = new PlanNode(Type.SOURCE, joinNode);
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, joinNode, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(joinNode)));
+
+ // Verify nothing has changed ...
+ assertThat(joinNode.getProperty(Property.JOIN_TYPE, JoinType.class), is(JoinType.FULL_OUTER));
+ assertThat(joinNode.getFirstChild(), is(sameInstance(lhs)));
+ assertThat(joinNode.getLastChild(), is(sameInstance(rhs)));
+ assertThat(joinNode.getChildCount(), is(2));
+ }
+
+ @Test
+ public void shouldDoNothingWithInnerJoin() {
+ // Create a LEFT_OUTER join ...
+ PlanNode joinNode = new PlanNode(Type.JOIN);
+ joinNode.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+ PlanNode lhs = new PlanNode(Type.SOURCE, joinNode);
+ PlanNode rhs = new PlanNode(Type.SOURCE, joinNode);
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, joinNode, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(joinNode)));
+
+ // Verify nothing has changed ...
+ assertThat(joinNode.getProperty(Property.JOIN_TYPE, JoinType.class), is(JoinType.INNER));
+ assertThat(joinNode.getFirstChild(), is(sameInstance(lhs)));
+ assertThat(joinNode.getLastChild(), is(sameInstance(rhs)));
+ assertThat(joinNode.getChildCount(), is(2));
+ }
+
+ @Test
+ public void shouldChangeRightOuterJoinToLeftOuterJoinAndReverseChildNodes() {
+ // Create a RIGHT_OUTER join ...
+ PlanNode joinNode = new PlanNode(Type.JOIN);
+ joinNode.setProperty(Property.JOIN_TYPE, JoinType.RIGHT_OUTER);
+ PlanNode lhs = new PlanNode(Type.SOURCE, joinNode);
+ PlanNode rhs = new PlanNode(Type.SOURCE, joinNode);
+
+ // Execute the rule ...
+ PlanNode result = rule.execute(context, joinNode, new LinkedList<OptimizerRule>());
+ assertThat(result, is(sameInstance(joinNode)));
+
+ // Verify the change ...
+ assertThat(joinNode.getProperty(Property.JOIN_TYPE, JoinType.class), is(JoinType.LEFT_OUTER));
+ assertThat(joinNode.getFirstChild(), is(sameInstance(rhs)));
+ assertThat(joinNode.getLastChild(), is(sameInstance(lhs)));
+ assertThat(joinNode.getChildCount(), is(2));
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,114 @@
+/*
+ * 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.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+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.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class RuleBasedOptimizerTest {
+
+ private RuleBasedOptimizer optimizer;
+ private List<OptimizerRule> rules;
+ private List<Integer> ruleExecutionOrder;
+ private QueryContext context;
+ private PlanNode node;
+
+ @Before
+ public void beforeEach() {
+ context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+ node = new PlanNode(Type.ACCESS);
+
+ ruleExecutionOrder = new ArrayList<Integer>();
+ rules = new ArrayList<OptimizerRule>();
+
+ // Add rules that, when executed, add their number to the 'ruleExecutionOrder' list ...
+ for (int i = 0; i != 5; ++i) {
+ final int ruleNumber = i;
+ this.rules.add(new OptimizerRule() {
+ @SuppressWarnings( "synthetic-access" )
+ public PlanNode execute( QueryContext context,
+ PlanNode plan,
+ LinkedList<OptimizerRule> ruleStack ) {
+ ruleExecutionOrder.add(ruleNumber);
+ return plan;
+ }
+ });
+ }
+
+ // Create a rule-based optimizer that uses a stack of completely artificial mock rules ...
+ this.optimizer = new RuleBasedOptimizer() {
+ @SuppressWarnings( "synthetic-access" )
+ @Override
+ protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
+ PlanHints hints ) {
+ ruleStack.addAll(rules);
+ }
+ };
+ }
+
+ @Test
+ public void shouldExecuteEachRuleInSequence() {
+ optimizer.optimize(context, node);
+ for (int i = 0; i != rules.size(); ++i) {
+ assertThat(ruleExecutionOrder.get(i), is(i));
+ }
+ }
+
+ @Test
+ public void shouldStopExecutingRulesIfThereIsAnErrorInTheProblems() {
+ // Change of the rules to generate an error ...
+ this.rules.set(3, new OptimizerRule() {
+ public PlanNode execute( QueryContext context,
+ PlanNode plan,
+ LinkedList<OptimizerRule> ruleStack ) {
+ context.getProblems().addError(GraphI18n.closedConnectionMayNotBeUsed);
+ return plan;
+ }
+ });
+
+ optimizer.optimize(context, node);
+ assertThat(ruleExecutionOrder.get(0), is(0));
+ assertThat(ruleExecutionOrder.get(1), is(1));
+ assertThat(ruleExecutionOrder.get(2), is(2));
+ assertThat(ruleExecutionOrder.size(), is(3));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/FullTextSearchParserTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/FullTextSearchParserTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/FullTextSearchParserTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,207 @@
+/*
+ * 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.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.graph.query.model.FullTextSearch.CompoundTerm;
+import org.jboss.dna.graph.query.model.FullTextSearch.Conjunction;
+import org.jboss.dna.graph.query.model.FullTextSearch.Disjunction;
+import org.jboss.dna.graph.query.model.FullTextSearch.SimpleTerm;
+import org.jboss.dna.graph.query.model.FullTextSearch.Term;
+import org.junit.Before;
+import org.junit.Test;
+
+public class FullTextSearchParserTest {
+
+ private FullTextSearchParser parser;
+
+ @Before
+ public void beforeEach() {
+ parser = new FullTextSearchParser();
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToParseNullString() {
+ parser.parse((String)null);
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseEmptyString() {
+ parser.parse("");
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseBlankString() {
+ parser.parse(" ");
+ }
+
+ @Test
+ public void shouldParseStringWithOneUnquotedTerm() {
+ Term result = parser.parse("term1");
+ assertSimpleTerm(result, "term1", false, false);
+ }
+
+ @Test
+ public void shouldParseStringWithOneSingleQuotedTermWithOneWord() {
+ Term result = parser.parse("'term1'");
+ assertSimpleTerm(result, "term1", false, false);
+ }
+
+ @Test
+ public void shouldParseStringWithOneSingleQuotedTermWithMultipleWords() {
+ Term result = parser.parse("'term1 has two words'");
+ assertSimpleTerm(result, "term1 has two words", false, true);
+ }
+
+ @Test
+ public void shouldParseStringWithOneDoubleQuotedTermWithOneWord() {
+ Term result = parser.parse("\"term1\"");
+ assertSimpleTerm(result, "term1", false, false);
+ }
+
+ @Test
+ public void shouldParseStringWithOneDoubleQuotedTermWithMultipleWords() {
+ Term result = parser.parse("\"term1 has two words\"");
+ assertSimpleTerm(result, "term1 has two words", false, true);
+ }
+
+ @Test
+ public void shouldParseStringWithMultipleUnquotedTerms() {
+ Term result = parser.parse("term1 term2 term3");
+ assertThat(result, is(notNullValue()));
+ assertThat(result, is(instanceOf(Conjunction.class)));
+ Conjunction conjunction = (Conjunction)result;
+ assertHasSimpleTerms(conjunction, "term1", "term2", "term3");
+ }
+
+ @Test
+ public void shouldParseStringWithMultipleUnquotedTermsWithNegatedTerms() {
+ Term result = parser.parse("term1 term2 -term3");
+ assertThat(result, is(notNullValue()));
+ assertThat(result, is(instanceOf(Conjunction.class)));
+ Conjunction conjunction = (Conjunction)result;
+ assertHasSimpleTerms(conjunction, "term1", "term2", "-term3");
+ }
+
+ @Test
+ public void shouldParseStringWithMultipleQuotedAndUnquotedTermsWithNegatedTerms() {
+ Term result = parser.parse("term1 \"term2 and 2a\" -term3 -'term 4'");
+ assertThat(result, is(notNullValue()));
+ assertThat(result, is(instanceOf(Conjunction.class)));
+ Conjunction conjunction = (Conjunction)result;
+ assertHasSimpleTerms(conjunction, "term1", "term2 and 2a", "-term3", "-term 4");
+ }
+
+ @Test
+ public void shouldParseStringWithMultipleUnquotedORedTerms() {
+ Term result = parser.parse("term1 OR term2 OR term3");
+ assertThat(result, is(notNullValue()));
+ assertThat(result, is(instanceOf(Disjunction.class)));
+ Disjunction disjunction = (Disjunction)result;
+ assertHasSimpleTerms(disjunction, "term1", "term2", "term3");
+ }
+
+ @Test
+ public void shouldParseStringWithMultipleUnquotedORedTermsWithNegatedTerms() {
+ Term result = parser.parse("term1 OR term2 OR -term3");
+ assertThat(result, is(notNullValue()));
+ assertThat(result, is(instanceOf(Disjunction.class)));
+ Disjunction disjunction = (Disjunction)result;
+ assertHasSimpleTerms(disjunction, "term1", "term2", "-term3");
+ }
+
+ @Test
+ public void shouldParseStringWithMultipleUnquotedANDedTermsORedTogether() {
+ Term result = parser.parse("term1 term2 OR -term3 -term4");
+ assertThat(result, is(notNullValue()));
+ assertThat(result, is(instanceOf(Disjunction.class)));
+ Disjunction disjunction = (Disjunction)result;
+ assertThat(disjunction.getTerms().size(), is(2));
+ Conjunction conjunction1 = (Conjunction)disjunction.getTerms().get(0);
+ Conjunction conjunction2 = (Conjunction)disjunction.getTerms().get(1);
+ assertHasSimpleTerms(conjunction1, "term1", "term2");
+ assertHasSimpleTerms(conjunction2, "-term3", "term4");
+ }
+
+ @Test
+ public void shouldParseStringWithTwoANDedUnquotedTermsORedWithMultipleUnquotedTerms() {
+ Term result = parser.parse("term1 term2 OR -term3 OR -term4 OR term5");
+ assertThat(result, is(notNullValue()));
+ assertThat(result, is(instanceOf(Disjunction.class)));
+ Disjunction disjunction = (Disjunction)result;
+ assertThat(disjunction.getTerms().size(), is(4));
+ Conjunction conjunction1 = (Conjunction)disjunction.getTerms().get(0);
+ SimpleTerm term3 = (SimpleTerm)disjunction.getTerms().get(1);
+ SimpleTerm term4 = (SimpleTerm)disjunction.getTerms().get(2);
+ SimpleTerm term5 = (SimpleTerm)disjunction.getTerms().get(3);
+ assertHasSimpleTerms(conjunction1, "term1", "term2");
+ assertSimpleTerm(term3, "term3", true, false);
+ assertSimpleTerm(term4, "term4", true, false);
+ assertSimpleTerm(term5, "term5", false, false);
+ }
+
+ public static void assertHasSimpleTerms( CompoundTerm compoundTerm,
+ String... terms ) {
+ List<Term> expectedTerms = new ArrayList<Term>();
+ for (String term : terms) {
+ SimpleTerm expected = new SimpleTerm(term, false);
+ if (term.startsWith("-")) {
+ term = term.substring(1);
+ expected = new SimpleTerm(term, true);
+ }
+ expectedTerms.add(expected);
+ }
+ assertHasTerms(compoundTerm, expectedTerms.toArray(new Term[expectedTerms.size()]));
+ }
+
+ public static void assertSimpleTerm( Term term,
+ String value,
+ boolean excluded,
+ boolean quotingRequired ) {
+ assertThat(term, is(notNullValue()));
+ assertThat(term, is(instanceOf(SimpleTerm.class)));
+ SimpleTerm simpleTerm = (SimpleTerm)term;
+ assertThat(simpleTerm.getValue(), is(value));
+ assertThat(simpleTerm.isExcluded(), is(excluded));
+ assertThat(simpleTerm.isQuotingRequired(), is(quotingRequired));
+ }
+
+ public static void assertHasTerms( CompoundTerm compoundTerm,
+ Term... terms ) {
+ Iterator<Term> iterator = compoundTerm.iterator();
+ for (int i = 0; i != 0; i++) {
+ Term expected = terms[i];
+ assertThat(iterator.hasNext(), is(true));
+ Term term = iterator.next();
+ assertThat(term, is(expected));
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/FullTextSearchParserTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,1682 @@
+/*
+ * 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.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 static org.mockito.Mockito.mock;
+import java.util.List;
+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.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+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.Constraint;
+import org.jboss.dna.graph.query.model.DescendantNode;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+import org.jboss.dna.graph.query.model.FullTextSearch;
+import org.jboss.dna.graph.query.model.FullTextSearchScore;
+import org.jboss.dna.graph.query.model.Join;
+import org.jboss.dna.graph.query.model.Length;
+import org.jboss.dna.graph.query.model.Limit;
+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.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+import org.jboss.dna.graph.query.model.Not;
+import org.jboss.dna.graph.query.model.Operator;
+import org.jboss.dna.graph.query.model.Or;
+import org.jboss.dna.graph.query.model.Order;
+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.SelectorName;
+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.FullTextSearch.Conjunction;
+import org.jboss.dna.graph.query.model.FullTextSearch.Disjunction;
+import org.jboss.dna.graph.query.model.FullTextSearch.SimpleTerm;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class SqlQueryParserTest {
+
+ private ExecutionContext context;
+ private SqlQueryParser parser;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ parser = new SqlQueryParser();
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseQuery
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseNominalQueries() {
+ parse("SELECT * FROM tableA");
+ parse("SELECT column1 FROM tableA");
+ parse("SELECT tableA.column1 FROM tableA");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA INNER JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA OUTER JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA LEFT OUTER JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA RIGHT OUTER JOIN tableB ON tableA.id = tableB.id");
+ }
+
+ @Test
+ public void shouldParseQueriesWithNonSqlColumnNames() {
+ parse("SELECT * FROM [dna:tableA]");
+ parse("SELECT [jcr:column1] FROM [dna:tableA]");
+ parse("SELECT 'jcr:column1' FROM 'dna:tableA'");
+ parse("SELECT \"jcr:column1\" FROM \"dna:tableA\"");
+ }
+
+ @Test
+ public void shouldParseQueriesSelectingFromAllTables() {
+ parse("SELECT * FROM __AllTables__");
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseQueriesWithNoFromClause() {
+ parse("SELECT 'jcr:column1'");
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseQueriesWithIncompleteFromClause() {
+ parse("SELECT 'jcr:column1' FROM ");
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseQueriesWithUnmatchedSingleQuoteCharacters() {
+ parse("SELECT 'jcr:column1' FROM \"dna:tableA'");
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseQueriesWithUnmatchedDoubleQuoteCharacters() {
+ parse("SELECT \"jcr:column1' FROM \"dna:tableA\"");
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseQueriesWithUnmatchedBracketQuoteCharacters() {
+ parse("SELECT [jcr:column1' FROM [dna:tableA]");
+ }
+
+ @Test
+ public void shouldParseQueriesWithSelectStar() {
+ parse("SELECT * FROM tableA");
+ parse("SELECT tableA.* FROM tableA");
+ parse("SELECT tableA.column1, tableB.* FROM tableA JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.*, tableB.column2 FROM tableA JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.*, tableB.* FROM tableA JOIN tableB ON tableA.id = tableB.id");
+ }
+
+ @Test
+ public void shouldParseQueriesWithAllKindsOfJoins() {
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA INNER JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA OUTER JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA LEFT OUTER JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA RIGHT OUTER JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA FULL OUTER JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA CROSS JOIN tableB ON tableA.id = tableB.id");
+ }
+
+ @Test
+ public void shouldParseQueriesWithMultipleJoins() {
+ parse("SELECT * FROM tableA JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT * FROM tableA JOIN tableB ON tableA.id = tableB.id JOIN tableC ON tableA.id2 = tableC.id2");
+ }
+
+ @Test
+ public void shouldParseQueriesWithEquiJoinCriteria() {
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA JOIN tableB ON tableA.id = tableB.id");
+ parse("SELECT * FROM tableA JOIN tableB ON tableA.id = tableB.id JOIN tableC ON tableA.id2 = tableC.id2");
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseEquiJoinCriteriaMissingPropertyName() {
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA JOIN tableB ON tableA = tableB.id");
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseEquiJoinCriteriaMissingTableName() {
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA JOIN tableB ON column1 = tableB.id");
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseEquiJoinCriteriaMissingEquals() {
+ parse("SELECT tableA.column1, tableB.column2 FROM tableA JOIN tableB ON column1 tableB.id");
+ }
+
+ @Test
+ public void shouldParseQueriesOnMultpleLines() {
+ parse("SELECT * \nFROM tableA");
+ parse("SELECT \ncolumn1 \nFROM tableA");
+ parse("SELECT \ntableA.column1 \nFROM\n tableA");
+ parse("SELECT tableA.\ncolumn1, \ntableB.column2 \nFROM tableA JOIN \ntableB ON tableA.id \n= tableB.\nid");
+ }
+
+ @Test
+ public void shouldParseQueriesThatUseDifferentCaseForKeywords() {
+ parse("select * from tableA");
+ parse("SeLeCt * from tableA");
+ parse("select column1 from tableA");
+ parse("select tableA.column1 from tableA");
+ parse("select tableA.column1, tableB.column2 from tableA join tableB on tableA.id = tableB.id");
+ }
+
+ @Test
+ public void shouldParseUnionQueries() {
+ parse("SELECT * FROM tableA UNION SELECT * FROM tableB");
+ parse("SELECT * FROM tableA UNION ALL SELECT * FROM tableB");
+ parse("SELECT * FROM tableA UNION SELECT * FROM tableB UNION SELECT * FROM tableC");
+ parse("SELECT * FROM tableA UNION ALL SELECT * FROM tableB UNION SELECT * FROM tableC");
+ }
+
+ @Test
+ public void shouldParseIntersectQueries() {
+ parse("SELECT * FROM tableA INTERSECT SELECT * FROM tableB");
+ parse("SELECT * FROM tableA INTERSECT ALL SELECT * FROM tableB");
+ parse("SELECT * FROM tableA INTERSECT SELECT * FROM tableB INTERSECT SELECT * FROM tableC");
+ parse("SELECT * FROM tableA INTERSECT ALL SELECT * FROM tableB INTERSECT ALL SELECT * FROM tableC");
+ }
+
+ @Test
+ public void shouldParseExceptQueries() {
+ parse("SELECT * FROM tableA EXCEPT SELECT * FROM tableB");
+ parse("SELECT * FROM tableA EXCEPT ALL SELECT * FROM tableB");
+ parse("SELECT * FROM tableA EXCEPT SELECT * FROM tableB EXCEPT SELECT * FROM tableC");
+ parse("SELECT * FROM tableA EXCEPT ALL SELECT * FROM tableB EXCEPT ALL SELECT * FROM tableC");
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseSetQuery
+ // ----------------------------------------------------------------------------------------------------------------
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseSelect
+ // ----------------------------------------------------------------------------------------------------------------
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseFrom
+ // ----------------------------------------------------------------------------------------------------------------
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseJoinCondition
+ // ----------------------------------------------------------------------------------------------------------------
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseWhere
+ // ----------------------------------------------------------------------------------------------------------------
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseConstraint
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseConstraintFromStringWithValidExpressions() {
+ assertParseConstraint("ISSAMENODE('/a/b')");
+ assertParseConstraint("ISSAMENODE('/a/b') AND NOT(ISCHILDNODE('/parent'))");
+ assertParseConstraint("ISSAMENODE('/a/b') AND (NOT(ISCHILDNODE('/parent')))");
+ assertParseConstraint("ISSAMENODE('/a/b') AND (NOT(tableA.id < 1234)))");
+ }
+
+ protected void assertParseConstraint( String expression ) {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ parser.parseConstraint(tokens(expression), context, selector);
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseConstraint - parentheses
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseConstraintFromStringWithOuterParentheses() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("( ISSAMENODE('/a/b') )"), context, selector);
+ assertThat(constraint, is(instanceOf(SameNode.class)));
+ SameNode same = (SameNode)constraint;
+ assertThat(same.getSelectorName(), is(selectorName("tableA")));
+ assertThat(same.getPath(), is(path("/a/b")));
+ }
+
+ @Test
+ public void shouldParseConstraintFromStringWithMultipleOuterParentheses() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("((( ISSAMENODE('/a/b') )))"), context, selector);
+ assertThat(constraint, is(instanceOf(SameNode.class)));
+ SameNode same = (SameNode)constraint;
+ assertThat(same.getSelectorName(), is(selectorName("tableA")));
+ assertThat(same.getPath(), is(path("/a/b")));
+ }
+
+ @Test
+ public void shouldParseConstraintFromStringWithParenthesesAndConjunctionAndDisjunctions() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b') OR (ISSAMENODE('/c/d') AND ISSAMENODE('/e/f'))"),
+ context,
+ selector);
+ assertThat(constraint, is(instanceOf(Or.class)));
+ Or or = (Or)constraint;
+
+ assertThat(or.getLeft(), is(instanceOf(SameNode.class)));
+ SameNode first = (SameNode)or.getLeft();
+ assertThat(first.getSelectorName(), is(selectorName("tableA")));
+ assertThat(first.getPath(), is(path("/a/b")));
+
+ assertThat(or.getRight(), is(instanceOf(And.class)));
+ And and = (And)or.getRight();
+
+ assertThat(and.getLeft(), is(instanceOf(SameNode.class)));
+ SameNode second = (SameNode)and.getLeft();
+ assertThat(second.getSelectorName(), is(selectorName("tableA")));
+ assertThat(second.getPath(), is(path("/c/d")));
+
+ assertThat(and.getRight(), is(instanceOf(SameNode.class)));
+ SameNode third = (SameNode)and.getRight();
+ assertThat(third.getSelectorName(), is(selectorName("tableA")));
+ assertThat(third.getPath(), is(path("/e/f")));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseConstraint - AND
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseConstraintFromStringWithAndExpressionWithNoParentheses() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') AND CONTAINS(p1,term1)"), context, selector);
+ assertThat(constraint, is(instanceOf(And.class)));
+ And and = (And)constraint;
+
+ assertThat(and.getLeft(), is(instanceOf(SameNode.class)));
+ SameNode same = (SameNode)and.getLeft();
+ assertThat(same.getSelectorName(), is(selectorName("tableA")));
+ assertThat(same.getPath(), is(path("/a/b/c")));
+
+ assertThat(and.getRight(), is(instanceOf(FullTextSearch.class)));
+ FullTextSearch search = (FullTextSearch)and.getRight();
+ assertThat(search.getSelectorName(), is(selectorName("tableA")));
+ assertThat(search.getPropertyName(), is(name("p1")));
+ assertThat(search.getFullTextSearchExpression(), is("term1"));
+ }
+
+ @Test
+ public void shouldParseConstraintFromStringWithMultipleAndExpressions() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') AND CONTAINS(p1,term1) AND CONTAINS(p2,term2)"),
+ context,
+ selector);
+ assertThat(constraint, is(instanceOf(And.class)));
+ And and = (And)constraint;
+
+ assertThat(and.getLeft(), is(instanceOf(SameNode.class)));
+ SameNode same = (SameNode)and.getLeft();
+ assertThat(same.getSelectorName(), is(selectorName("tableA")));
+ assertThat(same.getPath(), is(path("/a/b/c")));
+
+ assertThat(and.getRight(), is(instanceOf(And.class)));
+ And secondAnd = (And)and.getRight();
+
+ assertThat(secondAnd.getLeft(), is(instanceOf(FullTextSearch.class)));
+ FullTextSearch search1 = (FullTextSearch)secondAnd.getLeft();
+ assertThat(search1.getSelectorName(), is(selectorName("tableA")));
+ assertThat(search1.getPropertyName(), is(name("p1")));
+ assertThat(search1.getFullTextSearchExpression(), is("term1"));
+
+ assertThat(secondAnd.getRight(), is(instanceOf(FullTextSearch.class)));
+ FullTextSearch search2 = (FullTextSearch)secondAnd.getRight();
+ assertThat(search2.getSelectorName(), is(selectorName("tableA")));
+ assertThat(search2.getPropertyName(), is(name("p2")));
+ assertThat(search2.getFullTextSearchExpression(), is("term2"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithAndExpressionWithNoSecondConstraint() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') AND WHAT THE HECK IS THIS"), context, selector);
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseConstraint - OR
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseConstraintFromStringWithOrExpressionWithNoParentheses() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') OR CONTAINS(p1,term1)"), context, selector);
+ assertThat(constraint, is(instanceOf(Or.class)));
+ Or or = (Or)constraint;
+
+ assertThat(or.getLeft(), is(instanceOf(SameNode.class)));
+ SameNode same = (SameNode)or.getLeft();
+ assertThat(same.getSelectorName(), is(selectorName("tableA")));
+ assertThat(same.getPath(), is(path("/a/b/c")));
+
+ assertThat(or.getRight(), is(instanceOf(FullTextSearch.class)));
+ FullTextSearch search = (FullTextSearch)or.getRight();
+ assertThat(search.getSelectorName(), is(selectorName("tableA")));
+ assertThat(search.getPropertyName(), is(name("p1")));
+ assertThat(search.getFullTextSearchExpression(), is("term1"));
+
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithOrExpressionWithNoSecondConstraint() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ parser.parseConstraint(tokens("ISSAMENODE('/a/b/c') OR WHAT THE HECK IS THIS"), context, selector);
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseConstraint - NOT
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseConstraintFromStringWithNotSameNodeExpression() {
+ Constraint constraint = parser.parseConstraint(tokens("NOT(ISSAMENODE(tableA,'/a/b/c'))"), context, mock(Source.class));
+ assertThat(constraint, is(instanceOf(Not.class)));
+ Not not = (Not)constraint;
+ assertThat(not.getConstraint(), is(instanceOf(SameNode.class)));
+ SameNode same = (SameNode)not.getConstraint();
+ assertThat(same.getSelectorName(), is(selectorName("tableA")));
+ assertThat(same.getPath(), is(path("/a/b/c")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithNotConstraintWithOutOpeningParenthesis() {
+ parser.parseConstraint(tokens("NOT CONTAINS(propertyA 'term1 term2 -term3')"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithNotConstraintWithOutClosingParenthesis() {
+ parser.parseConstraint(tokens("NOT( CONTAINS(propertyA 'term1 term2 -term3') BLAH"), context, mock(Source.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseConstraint - CONTAINS
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseConstraintFromStringWithIsContainsExpressionWithPropertyAndNoSelectorNameOnlyIfThereIsOneSelectorSource() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("CONTAINS(propertyA,'term1 term2 -term3')"), context, selector);
+ assertThat(constraint, is(instanceOf(FullTextSearch.class)));
+ FullTextSearch search = (FullTextSearch)constraint;
+ assertThat(search.getSelectorName(), is(selectorName("tableA")));
+ assertThat(search.getPropertyName(), is(name("propertyA")));
+ assertThat(search.getFullTextSearchExpression(), is("term1 term2 -term3"));
+ }
+
+ @Test
+ public void shouldParseConstraintFromStringWithIsContainsExpressionWithSelectorNameAndProperty() {
+ Constraint constraint = parser.parseConstraint(tokens("CONTAINS(tableA.propertyA,'term1 term2 -term3')"),
+ context,
+ mock(Source.class));
+ assertThat(constraint, is(instanceOf(FullTextSearch.class)));
+ FullTextSearch search = (FullTextSearch)constraint;
+ assertThat(search.getSelectorName(), is(selectorName("tableA")));
+ assertThat(search.getPropertyName(), is(name("propertyA")));
+ assertThat(search.getFullTextSearchExpression(), is("term1 term2 -term3"));
+ }
+
+ @Test
+ public void shouldParseConstraintFromStringWithIsContainsExpressionWithSelectorNameAndAnyProperty() {
+ Constraint constraint = parser.parseConstraint(tokens("CONTAINS(tableA.*,'term1 term2 -term3')"),
+ context,
+ mock(Source.class));
+ assertThat(constraint, is(instanceOf(FullTextSearch.class)));
+ FullTextSearch search = (FullTextSearch)constraint;
+ assertThat(search.getSelectorName(), is(selectorName("tableA")));
+ assertThat(search.getPropertyName(), is(nullValue()));
+ assertThat(search.getFullTextSearchExpression(), is("term1 term2 -term3"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithContainsExpressionWithNoCommaAfterSelectorName() {
+ parser.parseConstraint(tokens("CONTAINS(propertyA 'term1 term2 -term3')"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithContainsExpressionWithNoClosingParenthesis() {
+ parser.parseConstraint(tokens("CONTAINS(propertyA,'term1 term2 -term3' OTHER"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithContainsExpressionWithNoOpeningParenthesis() {
+ parser.parseConstraint(tokens("CONTAINS propertyA,'term1 term2 -term3')"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithContainsExpressionWithNoSelectorNameIfSourceIsNotSelector() {
+ parser.parseConstraint(tokens("CONTAINS(propertyA,'term1 term2 -term3')"), context, mock(Join.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseConstraint - ISSAMENODE
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseConstraintFromStringWithIsSameNodeExpressionWithPathOnlyIfThereIsOneSelectorSource() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE('/a/b/c')"), context, selector);
+ assertThat(constraint, is(instanceOf(SameNode.class)));
+ SameNode same = (SameNode)constraint;
+ assertThat(same.getSelectorName(), is(selectorName("tableA")));
+ assertThat(same.getPath(), is(path("/a/b/c")));
+ }
+
+ @Test
+ public void shouldParseConstraintFromStringWithIsSameNodeExpressionWithSelectorNameAndPath() {
+ Constraint constraint = parser.parseConstraint(tokens("ISSAMENODE(tableA,'/a/b/c')"), context, mock(Source.class));
+ assertThat(constraint, is(instanceOf(SameNode.class)));
+ SameNode same = (SameNode)constraint;
+ assertThat(same.getSelectorName(), is(selectorName("tableA")));
+ assertThat(same.getPath(), is(path("/a/b/c")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsSameNodeExpressionWithNoCommaAfterSelectorName() {
+ parser.parseConstraint(tokens("ISSAMENODE(tableA '/a/b/c')"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsSameNodeExpressionWithNoClosingParenthesis() {
+ parser.parseConstraint(tokens("ISSAMENODE(tableA,'/a/b/c' AND"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsSameNodeExpressionWithNoOpeningParenthesis() {
+ parser.parseConstraint(tokens("ISSAMENODE tableA,'/a/b/c')"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsSameNodeExpressionWithNoSelectorNameIfSourceIsNotSelector() {
+ parser.parseConstraint(tokens("ISSAMENODE('/a/b/c')"), context, mock(Join.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseConstraint - ISCHILDNODE
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseConstraintFromStringWithIsChildNodeExpressionWithPathOnlyIfThereIsOneSelectorSource() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("ISCHILDNODE('/a/b/c')"), context, selector);
+ assertThat(constraint, is(instanceOf(ChildNode.class)));
+ ChildNode child = (ChildNode)constraint;
+ assertThat(child.getSelectorName(), is(selectorName("tableA")));
+ assertThat(child.getParentPath(), is(path("/a/b/c")));
+ }
+
+ @Test
+ public void shouldParseConstraintFromStringWithIsChildNodeExpressionWithSelectorNameAndPath() {
+ Constraint constraint = parser.parseConstraint(tokens("ISCHILDNODE(tableA,'/a/b/c')"), context, mock(Source.class));
+ assertThat(constraint, is(instanceOf(ChildNode.class)));
+ ChildNode child = (ChildNode)constraint;
+ assertThat(child.getSelectorName(), is(selectorName("tableA")));
+ assertThat(child.getParentPath(), is(path("/a/b/c")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsChildNodeExpressionWithNoCommaAfterSelectorName() {
+ parser.parseConstraint(tokens("ISCHILDNODE(tableA '/a/b/c')"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsChildNodeExpressionWithNoClosingParenthesis() {
+ parser.parseConstraint(tokens("ISCHILDNODE(tableA,'/a/b/c' AND"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsChildNodeExpressionWithNoOpeningParenthesis() {
+ parser.parseConstraint(tokens("ISCHILDNODE tableA,'/a/b/c')"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsChildNodeExpressionWithNoSelectorNameIfSourceIsNotSelector() {
+ parser.parseConstraint(tokens("ISCHILDNODE('/a/b/c')"), context, mock(Join.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseConstraint - ISDESCENDANTNODE
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseConstraintFromStringWithIsDescendantNodeExpressionWithPathOnlyIfThereIsOneSelectorSource() {
+ NamedSelector selector = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parseConstraint(tokens("ISDESCENDANTNODE('/a/b/c')"), context, selector);
+ assertThat(constraint, is(instanceOf(DescendantNode.class)));
+ DescendantNode descendant = (DescendantNode)constraint;
+ assertThat(descendant.getSelectorName(), is(selectorName("tableA")));
+ assertThat(descendant.getAncestorPath(), is(path("/a/b/c")));
+ }
+
+ @Test
+ public void shouldParseConstraintFromStringWithIsDescendantNodeExpressionWithSelectorNameAndPath() {
+ Constraint constraint = parser.parseConstraint(tokens("ISDESCENDANTNODE(tableA,'/a/b/c')"), context, mock(Source.class));
+ assertThat(constraint, is(instanceOf(DescendantNode.class)));
+ DescendantNode descendant = (DescendantNode)constraint;
+ assertThat(descendant.getSelectorName(), is(selectorName("tableA")));
+ assertThat(descendant.getAncestorPath(), is(path("/a/b/c")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsDescendantNodeExpressionWithNoCommaAfterSelectorName() {
+ parser.parseConstraint(tokens("ISDESCENDANTNODE(tableA '/a/b/c')"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsDescendantNodeExpressionWithNoClosingParenthesis() {
+ parser.parseConstraint(tokens("ISDESCENDANTNODE(tableA,'/a/b/c' AND"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsDescendantNodeExpressionWithNoOpeningParenthesis() {
+ parser.parseConstraint(tokens("ISDESCENDANTNODE tableA,'/a/b/c')"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseConstraintFromStringWithIsDescendantNodeExpressionWithNoSelectorNameIfSourceIsNotSelector() {
+ parser.parseConstraint(tokens("ISDESCENDANTNODE('/a/b/c')"), context, mock(Join.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseFullTextSearchExpression
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseFullTextSearchExpressionFromStringWithValidExpression() {
+ Position pos = new Position(100, 13);
+ FullTextSearch.Term result = parser.parseFullTextSearchExpression("term1 term2 OR -term3 OR -term4 OR term5", pos);
+ assertThat(result, is(notNullValue()));
+ assertThat(result, is(instanceOf(Disjunction.class)));
+ Disjunction disjunction = (Disjunction)result;
+ assertThat(disjunction.getTerms().size(), is(4));
+ Conjunction conjunction1 = (Conjunction)disjunction.getTerms().get(0);
+ SimpleTerm term3 = (SimpleTerm)disjunction.getTerms().get(1);
+ SimpleTerm term4 = (SimpleTerm)disjunction.getTerms().get(2);
+ SimpleTerm term5 = (SimpleTerm)disjunction.getTerms().get(3);
+ FullTextSearchParserTest.assertHasSimpleTerms(conjunction1, "term1", "term2");
+ FullTextSearchParserTest.assertSimpleTerm(term3, "term3", true, false);
+ FullTextSearchParserTest.assertSimpleTerm(term4, "term4", true, false);
+ FullTextSearchParserTest.assertSimpleTerm(term5, "term5", false, false);
+ }
+
+ @Test
+ public void shouldConvertPositionWhenUnableToParseFullTextSearchExpression() {
+ try {
+ parser.parseFullTextSearchExpression("", new Position(100, 13));
+ fail("Should have thrown an exception");
+ } catch (ParsingException e) {
+ assertThat(e.getPosition().getLine(), is(100));
+ assertThat(e.getPosition().getColumn(), is(13));
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseComparisonOperator
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseComparisonOperator() {
+ // Same case
+ for (Operator operator : Operator.values()) {
+ assertThat(parser.parseComparisonOperator(tokens(operator.getSymbol())), is(operator));
+ }
+ // Upper case
+ for (Operator operator : Operator.values()) {
+ assertThat(parser.parseComparisonOperator(tokens(operator.getSymbol().toUpperCase())), is(operator));
+ }
+ // Lower case
+ for (Operator operator : Operator.values()) {
+ assertThat(parser.parseComparisonOperator(tokens(operator.getSymbol().toLowerCase())), is(operator));
+ }
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseComparisonOperatorIfOperatorIsUnknown() {
+ parser.parseComparisonOperator(tokens("FOO"));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseOrderBy
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParserOrderByWithOneOrdering() {
+ List<Ordering> orderBy = parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC"), context, mock(Source.class));
+ assertThat(orderBy.size(), is(1));
+ Ordering first = orderBy.get(0);
+ assertThat(first.getOperand(), is(instanceOf(NodeName.class)));
+ assertThat(first.getOrder(), is(Order.ASCENDING));
+ }
+
+ @Test
+ public void shouldParserOrderByWithTwoOrderings() {
+ List<Ordering> orderBy = parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC, SCORE(tableB) DESC"),
+ context,
+ mock(Source.class));
+ assertThat(orderBy.size(), is(2));
+ Ordering first = orderBy.get(0);
+ assertThat(first.getOperand(), is(instanceOf(NodeName.class)));
+ assertThat(first.getOrder(), is(Order.ASCENDING));
+ Ordering second = orderBy.get(1);
+ assertThat(second.getOperand(), is(instanceOf(FullTextSearchScore.class)));
+ assertThat(second.getOrder(), is(Order.DESCENDING));
+ }
+
+ @Test
+ public void shouldParserOrderByWithMultipleOrderings() {
+ List<Ordering> orderBy = parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC, SCORE(tableB) DESC, LENGTH(tableC.id) ASC"),
+ context,
+ mock(Source.class));
+ assertThat(orderBy.size(), is(3));
+ Ordering first = orderBy.get(0);
+ assertThat(first.getOperand(), is(instanceOf(NodeName.class)));
+ assertThat(first.getOrder(), is(Order.ASCENDING));
+ Ordering second = orderBy.get(1);
+ assertThat(second.getOperand(), is(instanceOf(FullTextSearchScore.class)));
+ assertThat(second.getOrder(), is(Order.DESCENDING));
+ Ordering third = orderBy.get(2);
+ assertThat(third.getOperand(), is(instanceOf(Length.class)));
+ assertThat(third.getOrder(), is(Order.ASCENDING));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseOrderByIfCommaNotFollowedByAnotherOrdering() {
+ parser.parseOrderBy(tokens("ORDER BY NAME(tableA) ASC, NOT A VALID ORDERING"), context, mock(Source.class));
+ }
+
+ @Test
+ public void shouldReturnNullFromParseOrderByWithoutOrderByKeywords() {
+ assertThat(parser.parseOrderBy(tokens("NOT ORDER BY"), context, mock(Source.class)), is(nullValue()));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseOrdering
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseOrderingFromDynamicOperandFollowedByAscendingKeyword() {
+ Ordering ordering = parser.parseOrdering(tokens("NAME(tableA) ASC"), context, mock(Source.class));
+ assertThat(ordering.getOperand(), is(instanceOf(NodeName.class)));
+ assertThat(ordering.getOrder(), is(Order.ASCENDING));
+ }
+
+ @Test
+ public void shouldParseOrderingFromDynamicOperandFollowedByDecendingKeyword() {
+ Ordering ordering = parser.parseOrdering(tokens("NAME(tableA) DESC"), context, mock(Source.class));
+ assertThat(ordering.getOperand(), is(instanceOf(NodeName.class)));
+ assertThat(ordering.getOrder(), is(Order.DESCENDING));
+ }
+
+ @Test
+ public void shouldParseOrderingFromDynamicOperandAndDefaultToAscendingWhenNotFollowedByAscendingOrDescendingKeyword() {
+ Ordering ordering = parser.parseOrdering(tokens("NAME(tableA) OTHER"), context, mock(Source.class));
+ assertThat(ordering.getOperand(), is(instanceOf(NodeName.class)));
+ assertThat(ordering.getOrder(), is(Order.ASCENDING));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parsePropertyExistance
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParsePropertyExistanceFromPropertyNameWithSelectorNameAndPropertyNameFollowedByIsNotNull() {
+ Constraint constraint = parser.parsePropertyExistance(tokens("tableA.property1 IS NOT NULL"), context, mock(Source.class));
+ assertThat(constraint, is(instanceOf(PropertyExistence.class)));
+ PropertyExistence p = (PropertyExistence)constraint;
+ assertThat(p.getPropertyName(), is(name("property1")));
+ assertThat(p.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test
+ public void shouldParsePropertyExistanceFromPropertyNameWithPropertyNameAndNoSelectorNameFollowedByIsNotNull() {
+ NamedSelector source = new NamedSelector(selectorName("tableA"));
+ Constraint constraint = parser.parsePropertyExistance(tokens("property1 IS NOT NULL"), context, source);
+ assertThat(constraint, is(instanceOf(PropertyExistence.class)));
+ PropertyExistence p = (PropertyExistence)constraint;
+ assertThat(p.getPropertyName(), is(name("property1")));
+ assertThat(p.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParsePropertyExistanceFromPropertyNameWithNoSelectorNameIfSourceIsNotSelector() {
+ parser.parsePropertyExistance(tokens("property1 IS NOT NULL"), context, mock(Source.class));
+ }
+
+ @Test
+ public void shouldParseNotPropertyExistanceFromPropertyNameWithSelectorNameAndPropertyNameFollowedByIsNull() {
+ Constraint constraint = parser.parsePropertyExistance(tokens("tableA.property1 IS NULL"), context, mock(Source.class));
+ assertThat(constraint, is(instanceOf(Not.class)));
+ Not not = (Not)constraint;
+ assertThat(not.getConstraint(), is(instanceOf(PropertyExistence.class)));
+ PropertyExistence p = (PropertyExistence)not.getConstraint();
+ assertThat(p.getPropertyName(), is(name("property1")));
+ assertThat(p.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test
+ public void shouldReturnNullFromParsePropertyExistanceIfExpressionDoesNotMatchPattern() {
+ Source s = mock(Source.class);
+ assertThat(parser.parsePropertyExistance(tokens("tableA WILL NOT"), context, s), is(nullValue()));
+ assertThat(parser.parsePropertyExistance(tokens("tableA.property1 NOT NULL"), context, s), is(nullValue()));
+ assertThat(parser.parsePropertyExistance(tokens("tableA.property1 IS NOT SOMETHING"), context, s), is(nullValue()));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseStaticOperand
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseStaticOperandFromStringWithBindVariable() {
+ StaticOperand operand = parser.parseStaticOperand(tokens("$VAR"), context);
+ assertThat(operand, is(instanceOf(BindVariableName.class)));
+ BindVariableName var = (BindVariableName)operand;
+ assertThat(var.getVariableName(), is("VAR"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseStaticOperandFromStringWithBindVariableWithNoVariableName() {
+ parser.parseStaticOperand(tokens("$"), context);
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseStaticOperandFromStringWithBindVariableWithCharactersThatAreNotFromNCName() {
+ parser.parseStaticOperand(tokens("$#2VAR"), context);
+ }
+
+ @Test
+ public void shouldParseStaticOperandFromStringWithLiteralValue() {
+ StaticOperand operand = parser.parseStaticOperand(tokens("CAST(123 AS DOUBLE)"), context);
+ assertThat(operand, is(instanceOf(Literal.class)));
+ Literal literal = (Literal)operand;
+ assertThat((Double)literal.getValue(), is(context.getValueFactories().getDoubleFactory().create("123")));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseLiteral
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseLiteralFromStringWithCastBooleanLiteralToString() {
+ assertThat((String)parser.parseLiteral(tokens("CAST(true AS STRING)"), context).getValue(), is(Boolean.TRUE.toString()));
+ assertThat((String)parser.parseLiteral(tokens("CAST(false AS STRING)"), context).getValue(), is(Boolean.FALSE.toString()));
+ assertThat((String)parser.parseLiteral(tokens("CAST(TRUE AS STRING)"), context).getValue(), is(Boolean.TRUE.toString()));
+ assertThat((String)parser.parseLiteral(tokens("CAST(FALSE AS STRING)"), context).getValue(), is(Boolean.FALSE.toString()));
+ assertThat((String)parser.parseLiteral(tokens("CAST('true' AS stRinG)"), context).getValue(), is(Boolean.TRUE.toString()));
+ assertThat((String)parser.parseLiteral(tokens("CAST(\"false\" AS string)"), context).getValue(),
+ is(Boolean.FALSE.toString()));
+ }
+
+ @Test
+ public void shouldParseLiteralFromStringWithCastBooleanLiteralToBinary() {
+ Binary binaryTrue = context.getValueFactories().getBinaryFactory().create(true);
+ Binary binaryFalse = context.getValueFactories().getBinaryFactory().create(false);
+ assertThat((Binary)parser.parseLiteral(tokens("CAST(true AS BINARY)"), context).getValue(), is(binaryTrue));
+ assertThat((Binary)parser.parseLiteral(tokens("CAST(false AS BINARY)"), context).getValue(), is(binaryFalse));
+ assertThat((Binary)parser.parseLiteral(tokens("CAST(TRUE AS BINARY)"), context).getValue(), is(binaryTrue));
+ assertThat((Binary)parser.parseLiteral(tokens("CAST(FALSE AS BINARY)"), context).getValue(), is(binaryFalse));
+ assertThat((Binary)parser.parseLiteral(tokens("CAST('true' AS biNarY)"), context).getValue(), is(binaryTrue));
+ assertThat((Binary)parser.parseLiteral(tokens("CAST(\"false\" AS binary)"), context).getValue(), is(binaryFalse));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLiteralFromStringWithCastBooleanLiteralToLong() {
+ parser.parseLiteral(tokens("CAST(true AS LONG)"), context);
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLiteralFromStringWithCastBooleanLiteralToDouble() {
+ parser.parseLiteral(tokens("CAST(true AS DOUBLE)"), context);
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLiteralFromStringWithCastBooleanLiteralToDate() {
+ parser.parseLiteral(tokens("CAST(true AS DATE)"), context);
+ }
+
+ @Test
+ public void shouldParseLiteralFromStringWithCastLongLiteralToString() {
+ assertThat((String)parser.parseLiteral(tokens("CAST(123 AS STRING)"), context).getValue(), is("123"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(+123 AS STRING)"), context).getValue(), is("123"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(-123 AS STRING)"), context).getValue(), is("-123"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(0 AS STRING)"), context).getValue(), is("0"));
+ }
+
+ @Test
+ public void shouldParseLiteralFromStringWithCastLongLiteralToLong() {
+ assertThat((Long)parser.parseLiteral(tokens("CAST(123 AS LONG)"), context).getValue(), is(123L));
+ assertThat((Long)parser.parseLiteral(tokens("CAST(+123 AS LONG)"), context).getValue(), is(123L));
+ assertThat((Long)parser.parseLiteral(tokens("CAST(-123 AS LONG)"), context).getValue(), is(-123L));
+ assertThat((Long)parser.parseLiteral(tokens("CAST(0 AS LONG)"), context).getValue(), is(0L));
+ }
+
+ @Test
+ public void shouldParseLiteralFromStringWithCastDoubleLiteralToString() {
+ assertThat((String)parser.parseLiteral(tokens("CAST(1.23 AS STRING)"), context).getValue(), is("1.23"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(+1.23 AS STRING)"), context).getValue(), is("1.23"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(-1.23 AS STRING)"), context).getValue(), is("-1.23"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(1.23e10 AS STRING)"), context).getValue(), is("1.23E10"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(1.23e+10 AS STRING)"), context).getValue(), is("1.23E10"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(1.23e-10 AS STRING)"), context).getValue(), is("1.23E-10"));
+ }
+
+ @Test
+ public void shouldParseLiteralFromStringWithCastDateLiteralToString() {
+ assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.345Z AS STRING)"), context).getValue(),
+ is("2009-03-22T03:22:45.345Z"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.345UTC AS STRING)"), context).getValue(),
+ is("2009-03-22T03:22:45.345Z"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.3-01:00 AS STRING)"), context).getValue(),
+ is("2009-03-22T04:22:45.300Z"));
+ assertThat((String)parser.parseLiteral(tokens("CAST(2009-03-22T03:22:45.345+01:00 AS STRING)"), context).getValue(),
+ is("2009-03-22T02:22:45.345Z"));
+ }
+
+ @Test
+ public void shouldParseLiteralFromStringWithCastStringLiteralToName() {
+ assertThat((Name)parser.parseLiteral(tokens("CAST([dna:name] AS NAME)"), context).getValue(), is(name("dna:name")));
+ assertThat((Name)parser.parseLiteral(tokens("CAST('dna:name' AS NAME)"), context).getValue(), is(name("dna:name")));
+ assertThat((Name)parser.parseLiteral(tokens("CAST(\"dna:name\" AS NAME)"), context).getValue(), is(name("dna:name")));
+ }
+
+ @Test
+ public void shouldParseLiteralFromStringWithCastStringLiteralToPath() {
+ assertThat((Path)parser.parseLiteral(tokens("CAST([/dna:name/a/b] AS PATH)"), context).getValue(),
+ is(path("/dna:name/a/b")));
+ }
+
+ @Test
+ public void shouldParseLiteralFromStringWithUncastLiteralValueAndRepresentValueAsStringRepresentation() {
+ assertThat(parser.parseLiteral(tokens("true"), context).getValue(), is((Object)Boolean.TRUE.toString()));
+ assertThat(parser.parseLiteral(tokens("false"), context).getValue(), is((Object)Boolean.FALSE.toString()));
+ assertThat(parser.parseLiteral(tokens("TRUE"), context).getValue(), is((Object)Boolean.TRUE.toString()));
+ assertThat(parser.parseLiteral(tokens("FALSE"), context).getValue(), is((Object)Boolean.FALSE.toString()));
+ assertThat(parser.parseLiteral(tokens("123"), context).getValue(), is((Object)"123"));
+ assertThat(parser.parseLiteral(tokens("+123"), context).getValue(), is((Object)"123"));
+ assertThat(parser.parseLiteral(tokens("-123"), context).getValue(), is((Object)"-123"));
+ assertThat(parser.parseLiteral(tokens("1.23"), context).getValue(), is((Object)"1.23"));
+ assertThat(parser.parseLiteral(tokens("+1.23"), context).getValue(), is((Object)"1.23"));
+ assertThat(parser.parseLiteral(tokens("-1.23"), context).getValue(), is((Object)"-1.23"));
+ assertThat(parser.parseLiteral(tokens("1.23e10"), context).getValue(), is((Object)"1.23E10"));
+ assertThat(parser.parseLiteral(tokens("1.23e+10"), context).getValue(), is((Object)"1.23E10"));
+ assertThat(parser.parseLiteral(tokens("1.23e-10"), context).getValue(), is((Object)"1.23E-10"));
+ assertThat(parser.parseLiteral(tokens("0"), context).getValue(), is((Object)"0"));
+ assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.345Z"), context).getValue(),
+ is((Object)"2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.345UTC"), context).getValue(),
+ is((Object)"2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.3-01:00"), context).getValue(),
+ is((Object)"2009-03-22T04:22:45.300Z"));
+ assertThat(parser.parseLiteral(tokens("2009-03-22T03:22:45.345+01:00"), context).getValue(),
+ is((Object)"2009-03-22T02:22:45.345Z"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLiteralFromStringWithCastAndNoEndingParenthesis() {
+ parser.parseLiteral(tokens("CAST(123 AS STRING OTHER"), context);
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLiteralFromStringWithCastAndNoOpeningParenthesis() {
+ parser.parseLiteral(tokens("CAST 123 AS STRING) OTHER"), context);
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLiteralFromStringWithCastAndInvalidType() {
+ parser.parseLiteral(tokens("CAST(123 AS FOOD) OTHER"), context);
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLiteralFromStringWithCastAndNoAsKeyword() {
+ parser.parseLiteral(tokens("CAST(123 STRING) OTHER"), context);
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLiteralFromStringWithCastAndNoLiteralValueBeforeAs() {
+ parser.parseLiteral(tokens("CAST(AS STRING) OTHER"), context);
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseLiteralValue - unquoted
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseLiteralValueFromStringWithPositiveAndNegativeIntegerValues() {
+ assertThat(parser.parseLiteralValue(tokens("123"), context), is("123"));
+ assertThat(parser.parseLiteralValue(tokens("-123"), context), is("-123"));
+ assertThat(parser.parseLiteralValue(tokens("- 123"), context), is("-123"));
+ assertThat(parser.parseLiteralValue(tokens("+123"), context), is("123"));
+ assertThat(parser.parseLiteralValue(tokens("+ 123"), context), is("123"));
+ assertThat(parser.parseLiteralValue(tokens("0"), context), is("0"));
+ }
+
+ @Test
+ public void shouldParseLiteralValueFromStringWithPositiveAndNegativeDecimalValues() {
+ assertThat(parser.parseLiteralValue(tokens("1.23"), context), is("1.23"));
+ assertThat(parser.parseLiteralValue(tokens("-1.23"), context), is("-1.23"));
+ assertThat(parser.parseLiteralValue(tokens("+0.123"), context), is("0.123"));
+ }
+
+ @Test
+ public void shouldParseLiteralValueFromStringWithPositiveAndNegativeDecimalValuesInScientificNotation() {
+ assertThat(parser.parseLiteralValue(tokens("1.23"), context), is("1.23"));
+ assertThat(parser.parseLiteralValue(tokens("1.23e10"), context), is("1.23E10"));
+ assertThat(parser.parseLiteralValue(tokens("- 1.23e10"), context), is("-1.23E10"));
+ assertThat(parser.parseLiteralValue(tokens("- 1.23e-10"), context), is("-1.23E-10"));
+ }
+
+ @Test
+ public void shouldParseLiteralValueFromStringWithBooleanValues() {
+ assertThat(parser.parseLiteralValue(tokens("true"), context), is(Boolean.TRUE.toString()));
+ assertThat(parser.parseLiteralValue(tokens("false"), context), is(Boolean.FALSE.toString()));
+ assertThat(parser.parseLiteralValue(tokens("TRUE"), context), is(Boolean.TRUE.toString()));
+ assertThat(parser.parseLiteralValue(tokens("FALSE"), context), is(Boolean.FALSE.toString()));
+ }
+
+ @Test
+ public void shouldParseLiteralValueFromStringWithDateValues() {
+ // sYYYY-MM-DDThh:mm:ss.sssTZD
+ assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.345Z"), context), is("2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.345UTC"), context), is("2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.3-01:00"), context), is("2009-03-22T04:22:45.300Z"));
+ assertThat(parser.parseLiteralValue(tokens("2009-03-22T03:22:45.345+01:00"), context), is("2009-03-22T02:22:45.345Z"));
+
+ assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.345Z"), context), is("-2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.345UTC"), context), is("-2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.3-01:00"), context), is("-2009-03-22T04:22:45.300Z"));
+ assertThat(parser.parseLiteralValue(tokens("-2009-03-22T03:22:45.345+01:00"), context), is("-2009-03-22T02:22:45.345Z"));
+
+ assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.345Z"), context), is("2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.345UTC"), context), is("2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.3-01:00"), context), is("2009-03-22T04:22:45.300Z"));
+ assertThat(parser.parseLiteralValue(tokens("+2009-03-22T03:22:45.345+01:00"), context), is("2009-03-22T02:22:45.345Z"));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseLiteralValue - quoted
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseLiteralValueFromQuotedStringWithPositiveAndNegativeIntegerValues() {
+ assertThat(parser.parseLiteralValue(tokens("'123'"), context), is("123"));
+ assertThat(parser.parseLiteralValue(tokens("'-123'"), context), is("-123"));
+ assertThat(parser.parseLiteralValue(tokens("'- 123'"), context), is("- 123"));
+ assertThat(parser.parseLiteralValue(tokens("'+123'"), context), is("+123"));
+ assertThat(parser.parseLiteralValue(tokens("'+ 123'"), context), is("+ 123"));
+ assertThat(parser.parseLiteralValue(tokens("'0'"), context), is("0"));
+ }
+
+ @Test
+ public void shouldParseLiteralValueFromQuotedStringWithPositiveAndNegativeDecimalValues() {
+ assertThat(parser.parseLiteralValue(tokens("'1.23'"), context), is("1.23"));
+ assertThat(parser.parseLiteralValue(tokens("'-1.23'"), context), is("-1.23"));
+ assertThat(parser.parseLiteralValue(tokens("'+0.123'"), context), is("+0.123"));
+ }
+
+ @Test
+ public void shouldParseLiteralValueFromQuotedStringWithPositiveAndNegativeDecimalValuesInScientificNotation() {
+ assertThat(parser.parseLiteralValue(tokens("'1.23'"), context), is("1.23"));
+ assertThat(parser.parseLiteralValue(tokens("'1.23e10'"), context), is("1.23e10"));
+ assertThat(parser.parseLiteralValue(tokens("'- 1.23e10'"), context), is("- 1.23e10"));
+ assertThat(parser.parseLiteralValue(tokens("'- 1.23e-10'"), context), is("- 1.23e-10"));
+ }
+
+ @Test
+ public void shouldParseLiteralValueFromQuotedStringWithBooleanValues() {
+ assertThat(parser.parseLiteralValue(tokens("'true'"), context), is("true"));
+ assertThat(parser.parseLiteralValue(tokens("'false'"), context), is("false"));
+ assertThat(parser.parseLiteralValue(tokens("'TRUE'"), context), is("TRUE"));
+ assertThat(parser.parseLiteralValue(tokens("'FALSE'"), context), is("FALSE"));
+ }
+
+ @Test
+ public void shouldParseLiteralValueFromQuotedStringWithDateValues() {
+ // sYYYY-MM-DDThh:mm:ss.sssTZD
+ assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.345Z'"), context), is("2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.345UTC'"), context), is("2009-03-22T03:22:45.345UTC"));
+ assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.3-01:00'"), context), is("2009-03-22T03:22:45.3-01:00"));
+ assertThat(parser.parseLiteralValue(tokens("'2009-03-22T03:22:45.345+01:00'"), context),
+ is("2009-03-22T03:22:45.345+01:00"));
+
+ assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.345Z'"), context), is("-2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.345UTC'"), context), is("-2009-03-22T03:22:45.345UTC"));
+ assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.3-01:00'"), context),
+ is("-2009-03-22T03:22:45.3-01:00"));
+ assertThat(parser.parseLiteralValue(tokens("'-2009-03-22T03:22:45.345+01:00'"), context),
+ is("-2009-03-22T03:22:45.345+01:00"));
+
+ assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.345Z'"), context), is("+2009-03-22T03:22:45.345Z"));
+ assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.345UTC'"), context), is("+2009-03-22T03:22:45.345UTC"));
+ assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.3-01:00'"), context),
+ is("+2009-03-22T03:22:45.3-01:00"));
+ assertThat(parser.parseLiteralValue(tokens("'+2009-03-22T03:22:45.345+01:00'"), context),
+ is("+2009-03-22T03:22:45.345+01:00"));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseDynamicOperand - LENGTH
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingLengthOfPropertyValue() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("LENGTH(tableA.property)"), context, mock(Source.class));
+ assertThat(operand, is(instanceOf(Length.class)));
+ Length length = (Length)operand;
+ assertThat(length.getPropertyValue().getPropertyName(), is(name("property")));
+ assertThat(length.getPropertyValue().getSelectorName(), is(selectorName("tableA")));
+ assertThat(length.getSelectorName(), is(selectorName("tableA")));
+
+ Source source = new NamedSelector(selectorName("tableA"));
+ operand = parser.parseDynamicOperand(tokens("LENGTH(property)"), context, source);
+ assertThat(operand, is(instanceOf(Length.class)));
+ length = (Length)operand;
+ assertThat(length.getPropertyValue().getPropertyName(), is(name("property")));
+ assertThat(length.getPropertyValue().getSelectorName(), is(selectorName("tableA")));
+ assertThat(length.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingLengthWithoutClosingParenthesis() {
+ parser.parseDynamicOperand(tokens("LENGTH(tableA.property other"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingLengthWithoutOpeningParenthesis() {
+ parser.parseDynamicOperand(tokens("LENGTH tableA.property other"), context, mock(Source.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseDynamicOperand - LOWER
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingLowerOfAnotherDynamicOperand() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("LOWER(tableA.property)"), context, mock(Source.class));
+ assertThat(operand, is(instanceOf(LowerCase.class)));
+ LowerCase lower = (LowerCase)operand;
+ assertThat(lower.getSelectorName(), is(selectorName("tableA")));
+ assertThat(lower.getOperand(), is(instanceOf(PropertyValue.class)));
+ PropertyValue value = (PropertyValue)lower.getOperand();
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+
+ Source source = new NamedSelector(selectorName("tableA"));
+ operand = parser.parseDynamicOperand(tokens("LOWER(property)"), context, source);
+ assertThat(operand, is(instanceOf(LowerCase.class)));
+ lower = (LowerCase)operand;
+ assertThat(lower.getSelectorName(), is(selectorName("tableA")));
+ assertThat(lower.getOperand(), is(instanceOf(PropertyValue.class)));
+ value = (PropertyValue)lower.getOperand();
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingLowerOfUpperCaseOfAnotherOperand() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("LOWER(UPPER(tableA.property))"), context, mock(Source.class));
+ assertThat(operand, is(instanceOf(LowerCase.class)));
+ LowerCase lower = (LowerCase)operand;
+ assertThat(lower.getSelectorName(), is(selectorName("tableA")));
+ assertThat(lower.getOperand(), is(instanceOf(UpperCase.class)));
+ UpperCase upper = (UpperCase)lower.getOperand();
+ assertThat(upper.getSelectorName(), is(selectorName("tableA")));
+ assertThat(upper.getOperand(), is(instanceOf(PropertyValue.class)));
+ PropertyValue value = (PropertyValue)upper.getOperand();
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingLowerWithoutClosingParenthesis() {
+ parser.parseDynamicOperand(tokens("LOWER(tableA.property other"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingLowerWithoutOpeningParenthesis() {
+ parser.parseDynamicOperand(tokens("LOWER tableA.property other"), context, mock(Source.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseDynamicOperand - UPPER
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingUpperOfAnotherDynamicOperand() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("UPPER(tableA.property)"), context, mock(Source.class));
+ assertThat(operand, is(instanceOf(UpperCase.class)));
+ UpperCase upper = (UpperCase)operand;
+ assertThat(upper.getSelectorName(), is(selectorName("tableA")));
+ assertThat(upper.getOperand(), is(instanceOf(PropertyValue.class)));
+ PropertyValue value = (PropertyValue)upper.getOperand();
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+
+ Source source = new NamedSelector(selectorName("tableA"));
+ operand = parser.parseDynamicOperand(tokens("UPPER(property)"), context, source);
+ assertThat(operand, is(instanceOf(UpperCase.class)));
+ upper = (UpperCase)operand;
+ assertThat(upper.getSelectorName(), is(selectorName("tableA")));
+ assertThat(upper.getOperand(), is(instanceOf(PropertyValue.class)));
+ value = (PropertyValue)upper.getOperand();
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingUpperOfLowerCaseOfAnotherOperand() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("UPPER(LOWER(tableA.property))"), context, mock(Source.class));
+ assertThat(operand, is(instanceOf(UpperCase.class)));
+ UpperCase upper = (UpperCase)operand;
+ assertThat(upper.getSelectorName(), is(selectorName("tableA")));
+ assertThat(upper.getOperand(), is(instanceOf(LowerCase.class)));
+ LowerCase lower = (LowerCase)upper.getOperand();
+ assertThat(lower.getSelectorName(), is(selectorName("tableA")));
+ assertThat(lower.getOperand(), is(instanceOf(PropertyValue.class)));
+ PropertyValue value = (PropertyValue)lower.getOperand();
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingUpperWithoutClosingParenthesis() {
+ parser.parseDynamicOperand(tokens("UPPER(tableA.property other"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingUpperWithoutOpeningParenthesis() {
+ parser.parseDynamicOperand(tokens("Upper tableA.property other"), context, mock(Source.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseDynamicOperand - NAME
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingNameOfSelector() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("NAME(tableA)"), context, mock(Source.class));
+ assertThat(operand, is(instanceOf(NodeName.class)));
+ NodeName name = (NodeName)operand;
+ assertThat(name.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingNameWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
+ Source source = new NamedSelector(selectorName("tableA"));
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("NAME()"), context, source);
+ assertThat(operand, is(instanceOf(NodeName.class)));
+ NodeName name = (NodeName)operand;
+ assertThat(name.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingNameWithNoSelectorIfTheSourceIsNotASelector() {
+ parser.parseDynamicOperand(tokens("NAME()"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingNameWithSelectorNameAndProperty() {
+ parser.parseDynamicOperand(tokens("NAME(tableA.property) other"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingNameWithoutClosingParenthesis() {
+ parser.parseDynamicOperand(tokens("NAME(tableA other"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingNameWithoutOpeningParenthesis() {
+ parser.parseDynamicOperand(tokens("Name tableA other"), context, mock(Source.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseDynamicOperand - LOCALNAME
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingLocalNameOfSelector() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("LOCALNAME(tableA)"), context, mock(Source.class));
+ assertThat(operand, is(instanceOf(NodeLocalName.class)));
+ NodeLocalName name = (NodeLocalName)operand;
+ assertThat(name.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingLocalNameWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
+ Source source = new NamedSelector(selectorName("tableA"));
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("LOCALNAME()"), context, source);
+ assertThat(operand, is(instanceOf(NodeLocalName.class)));
+ NodeLocalName name = (NodeLocalName)operand;
+ assertThat(name.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingLocalNameWithNoSelectorIfTheSourceIsNotASelector() {
+ parser.parseDynamicOperand(tokens("LOCALNAME()"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingLocalNameWithSelectorNameAndProperty() {
+ parser.parseDynamicOperand(tokens("LOCALNAME(tableA.property) other"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingLocalNameWithoutClosingParenthesis() {
+ parser.parseDynamicOperand(tokens("LOCALNAME(tableA other"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingLocalNameWithoutOpeningParenthesis() {
+ parser.parseDynamicOperand(tokens("LocalName tableA other"), context, mock(Source.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseDynamicOperand - SCORE
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingFullTextSearchScoreOfSelector() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("SCORE(tableA)"), context, mock(Source.class));
+ assertThat(operand, is(instanceOf(FullTextSearchScore.class)));
+ FullTextSearchScore score = (FullTextSearchScore)operand;
+ assertThat(score.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test
+ public void shouldParseDynamicOperandFromStringContainingFullTextSearchScoreWithNoSelectorOnlyIfThereIsOneSelectorAsSource() {
+ Source source = new NamedSelector(selectorName("tableA"));
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("SCORE()"), context, source);
+ assertThat(operand, is(instanceOf(FullTextSearchScore.class)));
+ FullTextSearchScore score = (FullTextSearchScore)operand;
+ assertThat(score.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingFullTextSearchScoreWithNoSelectorIfTheSourceIsNotASelector() {
+ parser.parseDynamicOperand(tokens("SCORE()"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingFullTextSearchScoreWithWithSelectorNameAndProperty() {
+ parser.parseDynamicOperand(tokens("SCORE(tableA.property) other"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingFullTextSearchScoreWithoutClosingParenthesis() {
+ parser.parseDynamicOperand(tokens("SCORE(tableA other"), context, mock(Source.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringContainingFullTextSearchScoreWithoutOpeningParenthesis() {
+ parser.parseDynamicOperand(tokens("Score tableA other"), context, mock(Source.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseDynamicOperand - PropertyValue
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseDynamicOperandFromStringWithUnquotedSelectorNameAndUnquotedPropertyName() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("tableA.property"), context, mock(Join.class));
+ assertThat(operand, is(instanceOf(PropertyValue.class)));
+ PropertyValue value = (PropertyValue)operand;
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test
+ public void shouldParseDynamicOperandFromStringWithQuotedSelectorNameAndUnquotedPropertyName() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("[dna:tableA].property"), context, mock(Join.class));
+ assertThat(operand, is(instanceOf(PropertyValue.class)));
+ PropertyValue value = (PropertyValue)operand;
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("dna:tableA")));
+ }
+
+ @Test
+ public void shouldParseDynamicOperandFromStringWithQuotedSelectorNameAndQuotedPropertyName() {
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("[dna:tableA].[dna:property]"), context, mock(Join.class));
+ assertThat(operand, is(instanceOf(PropertyValue.class)));
+ PropertyValue value = (PropertyValue)operand;
+ assertThat(value.getPropertyName(), is(name("dna:property")));
+ assertThat(value.getSelectorName(), is(selectorName("dna:tableA")));
+ }
+
+ @Test
+ public void shouldParseDynamicOperandFromStringWithOnlyPropertyNameIfSourceIsSelector() {
+ Source source = new NamedSelector(selectorName("tableA"));
+ DynamicOperand operand = parser.parseDynamicOperand(tokens("property"), context, source);
+ assertThat(operand, is(instanceOf(PropertyValue.class)));
+ PropertyValue value = (PropertyValue)operand;
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToDynamicOperandValueFromStringWithOnlyPropertyNameIfSourceIsNotSelector() {
+ parser.parsePropertyValue(tokens("property"), context, mock(Join.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseDynamicOperandFromStringWithOnlySelectorNameAndPeriod() {
+ parser.parsePropertyValue(tokens("tableA. "), context, mock(Join.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parsePropertyValue
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParsePropertyValueFromStringWithUnquotedSelectorNameAndUnquotedPropertyName() {
+ PropertyValue value = parser.parsePropertyValue(tokens("tableA.property"), context, mock(Join.class));
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test
+ public void shouldParsePropertyValueFromStringWithQuotedSelectorNameAndUnquotedPropertyName() {
+ PropertyValue value = parser.parsePropertyValue(tokens("[dna:tableA].property"), context, mock(Join.class));
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("dna:tableA")));
+ }
+
+ @Test
+ public void shouldParsePropertyValueFromStringWithQuotedSelectorNameAndQuotedPropertyName() {
+ PropertyValue value = parser.parsePropertyValue(tokens("[dna:tableA].[dna:property]"), context, mock(Join.class));
+ assertThat(value.getPropertyName(), is(name("dna:property")));
+ assertThat(value.getSelectorName(), is(selectorName("dna:tableA")));
+ }
+
+ @Test
+ public void shouldParsePropertyValueFromStringWithOnlyPropertyNameIfSourceIsSelector() {
+ Source source = new NamedSelector(selectorName("tableA"));
+ PropertyValue value = parser.parsePropertyValue(tokens("property"), context, source);
+ assertThat(value.getPropertyName(), is(name("property")));
+ assertThat(value.getSelectorName(), is(selectorName("tableA")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParsePropertyValueFromStringWithOnlyPropertyNameIfSourceIsNotSelector() {
+ parser.parsePropertyValue(tokens("property"), context, mock(Join.class));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParsePropertyValueFromStringWithOnlySelectorNameAndPeriod() {
+ parser.parsePropertyValue(tokens("tableA. "), context, mock(Join.class));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseLimit
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseLimitFromFormWithJustOneNumber() {
+ Limit limit = parser.parseLimit(tokens("LIMIT 10"));
+ assertThat(limit.getRowLimit(), is(10));
+ assertThat(limit.getOffset(), is(0));
+
+ limit = parser.parseLimit(tokens("LIMIT 10 NONOFFSET"));
+ assertThat(limit.getRowLimit(), is(10));
+ assertThat(limit.getOffset(), is(0));
+ }
+
+ @Test
+ public void shouldParseLimitFromFormWithRowLimitAndOffset() {
+ Limit limit = parser.parseLimit(tokens("LIMIT 10 OFFSET 30"));
+ assertThat(limit.getRowLimit(), is(10));
+ assertThat(limit.getOffset(), is(30));
+
+ limit = parser.parseLimit(tokens("LIMIT 10 OFFSET 30 OTHER"));
+ assertThat(limit.getRowLimit(), is(10));
+ assertThat(limit.getOffset(), is(30));
+ }
+
+ @Test
+ public void shouldParseLimitFromFormWithTwoCommaSeparatedNumbers() {
+ Limit limit = parser.parseLimit(tokens("LIMIT 10,30"));
+ assertThat(limit.getRowLimit(), is(20));
+ assertThat(limit.getOffset(), is(10));
+ }
+
+ @Test
+ public void shouldReturnNullFromParseLimitWithNoLimitKeyword() {
+ assertThat(parser.parseLimit(tokens("OTHER")), is(nullValue()));
+ assertThat(parser.parseLimit(tokens(" ")), is(nullValue()));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLimitIfRowLimitNumberTokenIsNotAnInteger() {
+ parser.parseLimit(tokens("LIMIT 10a OFFSET 30"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLimitIfOffsetNumberTokenIsNotAnInteger() {
+ parser.parseLimit(tokens("LIMIT 10 OFFSET 30a"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLimitIfStartingRowNumberTokenIsNotAnInteger() {
+ parser.parseLimit(tokens("LIMIT 10a,20"));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseLimitIfEndingRowNumberTokenIsNotAnInteger() {
+ parser.parseLimit(tokens("LIMIT 10,20a"));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseNamedSelector
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseNamedSelectorFromUnquotedNameWithNoAlias() {
+ NamedSelector selector = parser.parseNamedSelector(tokens("name"));
+ assertThat(selector.getName(), is(selectorName("name")));
+ assertThat(selector.getAlias(), is(nullValue()));
+ assertThat(selector.getAliasOrName(), is(selectorName("name")));
+ }
+
+ @Test
+ public void shouldParseNamedSelectorFromUnquotedNameWithUnquotedAlias() {
+ NamedSelector selector = parser.parseNamedSelector(tokens("name AS alias"));
+ assertThat(selector.getName(), is(selectorName("name")));
+ assertThat(selector.getAlias(), is(selectorName("alias")));
+ assertThat(selector.getAliasOrName(), is(selectorName("alias")));
+ }
+
+ @Test
+ public void shouldParseNamedSelectorFromQuotedNameWithUnquotedAlias() {
+ NamedSelector selector = parser.parseNamedSelector(tokens("'name' AS alias"));
+ assertThat(selector.getName(), is(selectorName("name")));
+ assertThat(selector.getAlias(), is(selectorName("alias")));
+ assertThat(selector.getAliasOrName(), is(selectorName("alias")));
+ }
+
+ @Test
+ public void shouldParseNamedSelectorFromQuotedNameWithQuotedAlias() {
+ NamedSelector selector = parser.parseNamedSelector(tokens("'name' AS [alias]"));
+ assertThat(selector.getName(), is(selectorName("name")));
+ assertThat(selector.getAlias(), is(selectorName("alias")));
+ assertThat(selector.getAliasOrName(), is(selectorName("alias")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailInParseNamedSelectorIfNoMoreTokens() {
+ parser.parseNamedSelector(tokens(" "));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseSelectorName
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseSelectorNameFromUnquotedString() {
+ assertThat(parser.parseSelectorName(tokens("name")), is(selectorName("name")));
+ }
+
+ @Test
+ public void shouldParseSelectorNameFromQuotedString() {
+ assertThat(parser.parseSelectorName(tokens("'name'")), is(selectorName("name")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailInParseSelectorNameIfNoMoreTokens() {
+ parser.parseSelectorName(tokens(" "));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parseName
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParseNameFromSingleQuotedString() {
+ assertThat(parser.parseName(tokens("'jcr:name'"), context), is(name("jcr:name")));
+ }
+
+ @Test
+ public void shouldParseNameFromDoubleQuotedString() {
+ assertThat(parser.parseName(tokens("\"jcr:name\""), context), is(name("jcr:name")));
+ }
+
+ @Test
+ public void shouldParseNameFromBracketedString() {
+ assertThat(parser.parseName(tokens("[jcr:name]"), context), is(name("jcr:name")));
+ }
+
+ @Test
+ public void shouldParseNameFromUnquotedStringWithoutPrefix() {
+ assertThat(parser.parseName(tokens("name"), context), is(name("name")));
+ }
+
+ @Test
+ public void shouldParseNameFromSingleQuotedStringWithoutPrefix() {
+ assertThat(parser.parseName(tokens("'name'"), context), is(name("name")));
+ }
+
+ @Test
+ public void shouldParseNameFromDoubleQuotedStringWithoutPrefix() {
+ assertThat(parser.parseName(tokens("\"name\""), context), is(name("name")));
+ }
+
+ @Test
+ public void shouldParseNameFromBracketedStringWithoutPrefix() {
+ assertThat(parser.parseName(tokens("[name]"), context), is(name("name")));
+ }
+
+ @Test
+ public void shouldParseNameFromBracketedAndQuotedStringWithoutPrefix() {
+ assertThat(parser.parseName(tokens("['name']"), context), is(name("name")));
+ assertThat(parser.parseName(tokens("[\"name\"]"), context), is(name("name")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailToParseNameIfNoMoreTokens() {
+ parser.parseName(tokens(" "), context);
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // parsePath
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldParsePathFromUnquotedStringConsistingOfSql92Identifiers() {
+ String identifier = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+ assertThat(parser.parsePath(tokens(identifier), context), is(path(identifier)));
+ }
+
+ @Test
+ public void shouldParsePathFromSingleQuotedString() {
+ assertThat(parser.parsePath(tokens("'/a/b/c/dna:something/d'"), context), is(path("/a/b/c/dna:something/d")));
+ }
+
+ @Test
+ public void shouldParsePathFromDoubleQuotedString() {
+ assertThat(parser.parsePath(tokens("\"/a/b/c/dna:something/d\""), context), is(path("/a/b/c/dna:something/d")));
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldFailInParsePathIfNoMoreTokens() {
+ parser.parsePath(tokens(" "), context);
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // removeBracketsAndQuotes
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldRemoveBracketsAndQuotes() {
+ assertThat(parser.removeBracketsAndQuotes("string"), is("string"));
+ assertThat(parser.removeBracketsAndQuotes("[string]"), is("string"));
+ assertThat(parser.removeBracketsAndQuotes("'string'"), is("string"));
+ assertThat(parser.removeBracketsAndQuotes("\"string\""), is("string"));
+ assertThat(parser.removeBracketsAndQuotes("word one and two"), is("word one and two"));
+ assertThat(parser.removeBracketsAndQuotes("[word one and two]"), is("word one and two"));
+ assertThat(parser.removeBracketsAndQuotes("'word one and two'"), is("word one and two"));
+ assertThat(parser.removeBracketsAndQuotes("\"word one and two\""), is("word one and two"));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // Utility methods
+ // ----------------------------------------------------------------------------------------------------------------
+
+ protected void parse( String query ) {
+ parser.parseQuery(query, context);
+ }
+
+ protected SelectorName selectorName( String name ) {
+ return new SelectorName(name);
+ }
+
+ protected Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ protected TokenStream tokens( String content ) {
+ return new TokenStream(content, new SqlQueryParser.SqlTokenizer(false), false).start();
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlQueryParserTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlTokenizerTest.java (from rev 1233, trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndTokenizerTest.java)
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlTokenizerTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/parse/SqlTokenizerTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,355 @@
+/*
+ * 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 java.util.LinkedList;
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.common.text.Position;
+import org.jboss.dna.common.text.TokenStream.CharacterArrayStream;
+import org.jboss.dna.common.text.TokenStream.Tokens;
+import org.jboss.dna.graph.query.parse.SqlQueryParser.SqlTokenizer;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SqlTokenizerTest {
+
+ private SqlTokenizer tokenizer;
+ private Tokens tokenFactory;
+ private LinkedList<int[]> tokenValues;
+
+ @Before
+ public void beforeEach() {
+ tokenizer = new SqlTokenizer(true);
+ final LinkedList<int[]> tokenValues = new LinkedList<int[]>();
+ tokenFactory = new Tokens() {
+ public void addToken( Position position,
+ int index ) {
+ int[] token = new int[] {index, index + 1, 0};
+ tokenValues.add(token);
+ }
+
+ public void addToken( Position position,
+ int startIndex,
+ int endIndex ) {
+ int[] token = new int[] {startIndex, endIndex, 0};
+ tokenValues.add(token);
+ }
+
+ public void addToken( Position position,
+ int startIndex,
+ int endIndex,
+ int type ) {
+ int[] token = new int[] {startIndex, endIndex, type};
+ tokenValues.add(token);
+ }
+ };
+ this.tokenValues = tokenValues;
+ }
+
+ protected void tokenize( String input ) {
+ tokenizer.tokenize(new CharacterArrayStream(input.toCharArray()), tokenFactory);
+ }
+
+ protected void assertNextTokenIs( int startIndex,
+ int endIndex,
+ int type ) {
+ int[] token = tokenValues.removeFirst();
+ assertThat(token[0], is(startIndex));
+ assertThat(token[1], is(endIndex));
+ assertThat(token[2], is(type));
+ }
+
+ protected void assertNoMoreTokens() {
+ assertThat(tokenValues.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldCreateNoTokensForEmptyContent() {
+ tokenize("");
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateNoTokensForContentWithOnlyWhitespace() {
+ tokenize(" \t \n \r\n \r ");
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForEachSymbolCharacter() {
+ String content = "(){}*,:+%?$]!<>|=:";
+ int numSymbols = content.length();
+ tokenize(content);
+ for (int i = 0; i != numSymbols; ++i) {
+ assertNextTokenIs(i, i + 1, SqlTokenizer.SYMBOL);
+ }
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldNotConsiderDoubleSlashAsComment() {
+ String content = "++//this\n";
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, 3, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(3, 4, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(4, content.length() - 1, SqlTokenizer.WORD); // -1 because '\n' is not included
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForEndOfLineCommentUsingDoubleDash() {
+ String content = "++--this is a comment\n";
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, content.length() - 1, SqlTokenizer.COMMENT); // -1 because '\n' is not included
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForMultiLineComment() {
+ String content = "==/*this is a comment*/-";
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, content.length() - 1, SqlTokenizer.COMMENT);
+ assertNextTokenIs(content.length() - 1, content.length(), SqlTokenizer.SYMBOL);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForMultiLineCommentAtEndOfContent() {
+ String content = "==/*this is a comment*/";
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, content.length(), SqlTokenizer.COMMENT);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForMultiLineCommentWithoutTerminatingCharacters() {
+ String content = "==/*this is a comment";
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, content.length(), SqlTokenizer.COMMENT);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForMultiLineCommentWithoutAllTerminatingCharacters() {
+ String content = "==/*this is a comment*";
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, content.length(), SqlTokenizer.COMMENT);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForSingleQuotedString() {
+ String content = "=='this is a single-quoted \n string'-";
+ assertThat(content.charAt(2), is('\''));
+ assertThat(content.charAt(35), is('\''));
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, 36, SqlTokenizer.QUOTED_STRING);
+ assertNextTokenIs(36, 37, SqlTokenizer.SYMBOL);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForSingleQuotedStringWithEscapedSingleQuoteCharacters() {
+ String content = "=='this \"is\" a \\'single-quoted\\' \n string'-";
+ assertThat(content.charAt(2), is('\''));
+ assertThat(content.charAt(41), is('\''));
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, 42, SqlTokenizer.QUOTED_STRING);
+ assertNextTokenIs(42, 43, SqlTokenizer.SYMBOL);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForSingleQuotedStringAtEndOfContent() {
+ String content = "=='this is a single-quoted \n string'";
+ assertThat(content.charAt(2), is('\''));
+ assertThat(content.charAt(35), is('\''));
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, 36, SqlTokenizer.QUOTED_STRING);
+ assertNoMoreTokens();
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldCreateTokenForSingleQuotedStringWithoutClosingQuote() {
+ String content = "=='this is a single-quoted \n string";
+ tokenize(content);
+ }
+
+ @Test
+ public void shouldCreateTokenForDoubleQuotedString() {
+ String content = "==\"this is a double-quoted \n string\"-";
+ assertThat(content.charAt(2), is('"'));
+ assertThat(content.charAt(35), is('"'));
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, 36, SqlTokenizer.QUOTED_STRING);
+ assertNextTokenIs(36, 37, SqlTokenizer.SYMBOL);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForDoubleQuotedStringWithEscapedDoubleQuoteCharacters() {
+ String content = "==\"this 'is' a \\\"double-quoted\\\" \n string\"-";
+ assertThat(content.charAt(2), is('"'));
+ assertThat(content.charAt(41), is('"'));
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, 42, SqlTokenizer.QUOTED_STRING);
+ assertNextTokenIs(42, 43, SqlTokenizer.SYMBOL);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokenForDoubleQuotedStringAtEndOfContent() {
+ String content = "==\"this is a double-quoted \n string\"";
+ assertThat(content.charAt(2), is('"'));
+ assertThat(content.charAt(35), is('"'));
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(2, 36, SqlTokenizer.QUOTED_STRING);
+ assertNoMoreTokens();
+ }
+
+ @Test( expected = ParsingException.class )
+ public void shouldCreateTokenForDoubleQuotedStringWithoutClosingQuote() {
+ String content = "==\"this is a double-quoted \n string";
+ tokenize(content);
+ }
+
+ @Test
+ public void shouldCreateTokensForWordsWithAlphabeticCharacters() {
+ String content = "This is a series of words.";
+ tokenize(content);
+ assertNextTokenIs(0, 4, SqlTokenizer.WORD);
+ assertNextTokenIs(5, 7, SqlTokenizer.WORD);
+ assertNextTokenIs(8, 9, SqlTokenizer.WORD);
+ assertNextTokenIs(10, 16, SqlTokenizer.WORD);
+ assertNextTokenIs(17, 19, SqlTokenizer.WORD);
+ assertNextTokenIs(20, 25, SqlTokenizer.WORD);
+ assertNextTokenIs(25, 26, SqlTokenizer.SYMBOL);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokensForWordsWithNumericCharacters() {
+ String content = "1234 4 5353.324";
+ tokenize(content);
+ assertNextTokenIs(0, 4, SqlTokenizer.WORD);
+ assertNextTokenIs(5, 6, SqlTokenizer.WORD);
+ assertNextTokenIs(7, 11, SqlTokenizer.WORD);
+ assertNextTokenIs(11, 12, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(12, 15, SqlTokenizer.WORD);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldCreateTokensForWordsWithAlphaNumericCharacters() {
+ String content = "123a 5353.324e100";
+ tokenize(content);
+ assertNextTokenIs(0, 4, SqlTokenizer.WORD);
+ assertNextTokenIs(5, 9, SqlTokenizer.WORD);
+ assertNextTokenIs(9, 10, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(10, 17, SqlTokenizer.WORD);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldParseAlphaNumericAndUnderscoreCharactersAsOneWordToken() {
+ String content = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
+ tokenize(content);
+ assertNextTokenIs(0, 63, SqlTokenizer.WORD);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldParseUnquotedNameWithPrefixAsSeparateTokens() {
+ String content = "dna:name";
+ tokenize(content);
+ assertNextTokenIs(0, 3, SqlTokenizer.WORD);
+ assertNextTokenIs(3, 4, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(4, 8, SqlTokenizer.WORD);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldParseQuotedNameWithPrefixAsOneToken() {
+ String content = "'dna:name'";
+ tokenize(content);
+ assertNextTokenIs(0, 10, SqlTokenizer.QUOTED_STRING);
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldParseUnquotedPathAsSeparateTokens() {
+ String content = "/a/b/c/dna:name[1]/e/f";
+ tokenize(content);
+ assertNextTokenIs(0, 1, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(1, 2, SqlTokenizer.WORD); // a
+ assertNextTokenIs(2, 3, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(3, 4, SqlTokenizer.WORD); // b
+ assertNextTokenIs(4, 5, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(5, 6, SqlTokenizer.WORD); // c
+ assertNextTokenIs(6, 7, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(7, 10, SqlTokenizer.WORD); // dna
+ assertNextTokenIs(10, 11, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(11, 15, SqlTokenizer.WORD); // name
+ assertNextTokenIs(15, 18, SqlTokenizer.QUOTED_STRING); // [1]
+ assertNextTokenIs(18, 19, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(19, 20, SqlTokenizer.WORD); // e
+ assertNextTokenIs(20, 21, SqlTokenizer.SYMBOL);
+ assertNextTokenIs(21, 22, SqlTokenizer.WORD); // f
+ assertNoMoreTokens();
+ }
+
+ @Test
+ public void shouldParseQuotedPathAsOneToken() {
+ String content = "'/a/b/c/dna:name[1]/e/f'";
+ tokenize(content);
+ assertNextTokenIs(0, 24, SqlTokenizer.QUOTED_STRING);
+ assertNoMoreTokens();
+ }
+}
Added: 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 (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.plan;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.Collection;
+import java.util.List;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.collection.SimpleProblems;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.QueryBuilder;
+import org.jboss.dna.graph.query.QueryContext;
+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;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class CanonicalPlannerTest {
+
+ private CanonicalPlanner planner;
+ private ExecutionContext context;
+ private QueryBuilder builder;
+ private PlanHints hints;
+ private QueryCommand query;
+ private PlanNode plan;
+ private Problems problems;
+ private Schemata schemata;
+ private ImmutableSchemata.Builder schemataBuilder;
+ private QueryContext queryContext;
+
+ @Before
+ public void beforeEach() {
+ planner = new CanonicalPlanner();
+ context = new ExecutionContext();
+ hints = new PlanHints();
+ builder = new QueryBuilder(context);
+ problems = new SimpleProblems();
+ schemataBuilder = ImmutableSchemata.createBuilder();
+ }
+
+ protected SelectorName selector( String name ) {
+ return new SelectorName(name);
+ }
+
+ @SuppressWarnings( "unchecked" )
+ protected void assertProjectNode( PlanNode node,
+ String... columnNames ) {
+ assertThat(node.getType(), is(Type.PROJECT));
+ if (columnNames.length != 0) {
+ assertThat(node.hasCollectionProperty(Property.PROJECT_COLUMNS), is(true));
+ }
+ List<Column> columns = node.getProperty(Property.PROJECT_COLUMNS, List.class);
+ assertThat(columns.size(), is(columnNames.length));
+ for (int i = 0; i != columns.size(); ++i) {
+ Column column = columns.get(i);
+ assertThat(column.getColumnName(), is(columnNames[i]));
+ }
+ }
+
+ @SuppressWarnings( "unchecked" )
+ protected void assertSourceNode( PlanNode node,
+ String sourceName,
+ String sourceAlias,
+ String... availableColumns ) {
+ assertThat(node.getType(), is(Type.SOURCE));
+ assertThat(node.getProperty(Property.SOURCE_NAME, SelectorName.class), is(selector(sourceName)));
+ if (sourceAlias != null) {
+ assertThat(node.getProperty(Property.SOURCE_ALIAS, SelectorName.class), is(selector(sourceAlias)));
+ } else {
+ assertThat(node.hasProperty(Property.SOURCE_ALIAS), is(false));
+ }
+ Collection<Schemata.Column> columns = (Collection)node.getProperty(Property.SOURCE_COLUMNS);
+ assertThat(columns.size(), is(availableColumns.length));
+ int i = 0;
+ for (Schemata.Column column : columns) {
+ String expectedName = availableColumns[i++];
+ assertThat(column.getName(), is(expectedName));
+ }
+ }
+
+ @Test
+ public void shouldProducePlanForSelectStarFromTable() {
+ schemata = schemataBuilder.addTable("__ALLNODES__", "column1", "column2", "column3").build();
+ query = builder.selectStar().fromAllNodes().query();
+ queryContext = new QueryContext(context, hints, schemata, problems);
+ plan = planner.createPlan(queryContext, query);
+ assertThat(problems.isEmpty(), is(true));
+ assertProjectNode(plan, "column1", "column2", "column3");
+ assertThat(plan.getType(), is(PlanNode.Type.PROJECT));
+ assertThat(plan.getChildCount(), is(1));
+ PlanNode source = plan.getFirstChild();
+ assertSourceNode(source, "__ALLNODES__", null, "column1", "column2", "column3");
+ assertThat(source.getChildCount(), is(0));
+ System.out.println(plan);
+ }
+
+ @Test
+ public void shouldProduceErrorWhenSelectingNonExistantTable() {
+ schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
+ query = builder.selectStar().fromAllNodes().query();
+ queryContext = new QueryContext(context, hints, schemata, problems);
+ plan = planner.createPlan(queryContext, query);
+ assertThat(problems.hasErrors(), is(true));
+ }
+
+ @Test
+ public void shouldProduceErrorWhenSelectingNonExistantColumnOnExistingTable() {
+ schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
+ query = builder.select("column1", "column4").from("someTable").query();
+ queryContext = new QueryContext(context, hints, schemata, problems);
+ plan = planner.createPlan(queryContext, query);
+ assertThat(problems.hasErrors(), is(true));
+ }
+
+ @Test
+ public void shouldProducePlanWhenSelectingAllColumnsOnExistingTable() {
+ schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
+ query = builder.selectStar().from("someTable").query();
+ queryContext = new QueryContext(context, hints, schemata, problems);
+ plan = planner.createPlan(queryContext, query);
+ System.out.println(plan);
+ assertThat(problems.hasErrors(), is(false));
+ assertThat(problems.isEmpty(), is(true));
+ assertProjectNode(plan, "column1", "column2", "column3");
+ assertThat(plan.getType(), is(PlanNode.Type.PROJECT));
+ assertThat(plan.getChildCount(), is(1));
+ PlanNode source = plan.getFirstChild();
+ assertSourceNode(source, "someTable", null, "column1", "column2", "column3");
+ assertThat(source.getChildCount(), is(0));
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/PlanHintsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/PlanHintsTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/PlanHintsTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,47 @@
+/*
+ * 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 org.jboss.dna.graph.query.plan.PlanHints;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class PlanHintsTest {
+
+ private PlanHints hints;
+
+ @Before
+ public void beforeEach() {
+ this.hints = new PlanHints();
+ }
+
+ @Test
+ public void shouldProduceToString() {
+ this.hints.toString();
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/PlanHintsTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/PlanNodeTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/PlanNodeTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/PlanNodeTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,558 @@
+/*
+ * 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 static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class PlanNodeTest {
+
+ private PlanNode node;
+ private PlanNode parent;
+
+ @Before
+ public void beforeEach() {
+ node = new PlanNode(Type.GROUP);
+ }
+
+ @After
+ public void afterEach() {
+ this.node = null;
+ this.parent = null;
+ }
+
+ @Test
+ public void shouldFindTypeGivenSymbolWithSameCase() {
+ for (Type type : Type.values()) {
+ assertThat(Type.forSymbol(type.getSymbol()), is(sameInstance(type)));
+ }
+ }
+
+ @Test
+ public void shouldFindTypeGivenSymbolWithLowerCase() {
+ for (Type type : Type.values()) {
+ assertThat(Type.forSymbol(type.getSymbol().toLowerCase()), is(sameInstance(type)));
+ }
+ }
+
+ @Test
+ public void shouldFindTypeGivenSymbolWithUpperCase() {
+ for (Type type : Type.values()) {
+ assertThat(Type.forSymbol(type.getSymbol().toUpperCase()), is(sameInstance(type)));
+ }
+ }
+
+ @Test
+ public void shouldFindTypeGivenSymbolWithLeadingAndTrailingWhitespace() {
+ for (Type type : Type.values()) {
+ assertThat(Type.forSymbol(" \t " + type.getSymbol() + " \t \n"), is(sameInstance(type)));
+ }
+ }
+
+ @Test
+ public void shouldCreatePlanNodeWithTypeAndNoParent() {
+ for (Type type : Type.values()) {
+ node = new PlanNode(type);
+ assertThat(node.getType(), is(type));
+ assertThat(node.getParent(), is(nullValue()));
+ }
+ }
+
+ @Test
+ public void shouldCreatePlanNodeWithTypeAndParent() {
+ for (Type type : Type.values()) {
+ parent = new PlanNode(Type.JOIN);
+ node = new PlanNode(type, parent);
+ assertThat(node.getType(), is(type));
+ assertThat(node.getParent(), is(sameInstance(parent)));
+ assertThat(parent.getFirstChild(), is(sameInstance(node)));
+ assertThat(parent.getChildCount(), is(1));
+ }
+ }
+
+ @Test
+ public void shouldAddNodeToParentWhenConstructingChildNodeWithTypeAndParent() {
+ parent = new PlanNode(Type.JOIN);
+ int counter = 0;
+ for (Type type : Type.values()) {
+ node = new PlanNode(type, parent);
+ ++counter;
+ assertThat(node.getType(), is(type));
+ assertThat(node.getParent(), is(sameInstance(parent)));
+ assertThat(parent.getLastChild(), is(sameInstance(node)));
+ assertThat(parent.getChildCount(), is(counter));
+ }
+ }
+
+ @Test
+ public void shouldSetType() {
+ node = new PlanNode(Type.JOIN);
+ for (Type type : Type.values()) {
+ node.setType(type);
+ assertThat(node.getType(), is(type));
+ }
+ }
+
+ @Test
+ public void shouldGetFirstChildAndLastChildWithOneChild() {
+ parent = new PlanNode(Type.JOIN);
+ node = new PlanNode(Type.ACCESS, parent);
+ assertThat(parent.getFirstChild(), is(sameInstance(node)));
+ assertThat(parent.getLastChild(), is(sameInstance(node)));
+ }
+
+ @Test
+ public void shouldGetFirstChildAndLastChildWithTwoChildren() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.ACCESS, parent);
+ assertThat(parent.getFirstChild(), is(sameInstance(child1)));
+ assertThat(parent.getLastChild(), is(sameInstance(child2)));
+ }
+
+ @Test
+ public void shouldGetFirstChildAndLastChildWithMoreThanTwoChildren() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ new PlanNode(Type.ACCESS, parent);
+ PlanNode child3 = new PlanNode(Type.ACCESS, parent);
+ assertThat(parent.getFirstChild(), is(sameInstance(child1)));
+ assertThat(parent.getLastChild(), is(sameInstance(child3)));
+ }
+
+ @Test
+ public void shouldGetFirstChildAndLastChildWithNoChildren() {
+ parent = new PlanNode(Type.JOIN);
+ assertThat(parent.getFirstChild(), is(nullValue()));
+ assertThat(parent.getLastChild(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldRemoveNodeFromExistingParentWhenSettingParentToNull() {
+ parent = new PlanNode(Type.JOIN);
+ node = new PlanNode(Type.ACCESS, parent);
+ assertThat(parent.getFirstChild(), is(sameInstance(node)));
+ assertThat(parent.getChildCount(), is(1));
+ node.setParent(null);
+ assertThat(parent.getChildCount(), is(0));
+ assertThat(node.getParent(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldInsertNewParentNodeInBetweenExistingParentAndChild() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ assertThat(parent.getFirstChild(), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getLastChild(), is(sameInstance(child3)));
+ assertThat(parent.getChildCount(), is(3));
+ node = new PlanNode(Type.GROUP);
+ child2.insertAsParent(node);
+ assertThat(parent.getChildCount(), is(3));
+ assertThat(parent.getFirstChild(), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(node)));
+ assertThat(parent.getLastChild(), is(sameInstance(child3)));
+ assertThat(node.getParent(), is(sameInstance(parent)));
+ assertThat(child2.getParent(), is(sameInstance(node)));
+ }
+
+ @Test
+ public void shouldInsertNewParentNodeInAboveNodeWithoutParent() {
+ PlanNode child1 = new PlanNode(Type.ACCESS);
+ node = new PlanNode(Type.GROUP);
+ PlanNode nodeChild = new PlanNode(Type.JOIN, node);
+ // Perform the insertAsParent ...
+ child1.insertAsParent(node);
+ assertThat(node.getParent(), is(nullValue()));
+ assertThat(node.getChildCount(), is(2));
+ assertThat(node.getFirstChild(), is(sameInstance(nodeChild)));
+ assertThat(node.getLastChild(), is(sameInstance(child1)));
+ assertThat(child1.getParent(), is(sameInstance(node)));
+ }
+
+ @Test
+ public void shouldRemoveFromParentWhenThereIsAParent() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ PlanNode grandChild21 = new PlanNode(Type.LIMIT, child2);
+ assertThat(parent.getFirstChild(), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getLastChild(), is(sameInstance(child3)));
+ assertThat(parent.getChildCount(), is(3));
+ assertThat(child2.getFirstChild(), is(sameInstance(grandChild21)));
+ // Perform the removeFromParent ...
+ assertThat(child2.removeFromParent(), is(sameInstance(parent)));
+ assertThat(parent.getFirstChild(), is(sameInstance(child1)));
+ assertThat(parent.getLastChild(), is(sameInstance(child3)));
+ assertThat(parent.getChildCount(), is(2));
+ // There should still be the child in the removed node ...
+ assertThat(child2.getFirstChild(), is(sameInstance(grandChild21)));
+ }
+
+ @Test
+ public void shouldRemoveFromParentWhenThereIsNoParent() {
+ node = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, node);
+ assertThat(node.getFirstChild(), is(sameInstance(child1)));
+ assertThat(node.getChildCount(), is(1));
+ // Perform the removeFromParent ...
+ assertThat(node.removeFromParent(), is(nullValue()));
+ assertThat(node.getFirstChild(), is(sameInstance(child1)));
+ assertThat(node.getChildCount(), is(1));
+ }
+
+ @Test
+ public void shouldReturnListOfChildren() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ List<PlanNode> children = parent.getChildren();
+ assertThat(children.get(0), is(sameInstance(child1)));
+ assertThat(children.get(1), is(sameInstance(child2)));
+ assertThat(children.get(2), is(sameInstance(child3)));
+ assertThat(children.size(), is(3));
+ }
+
+ @Test( expected = UnsupportedOperationException.class )
+ public void shouldReturnImmutableListOfChildren() {
+ parent = new PlanNode(Type.JOIN);
+ new PlanNode(Type.ACCESS, parent);
+ new PlanNode(Type.DUP_REMOVE, parent);
+ new PlanNode(Type.GROUP, parent);
+ parent.getChildren().clear();
+ }
+
+ @Test
+ public void shouldReturnIteratorOfChildren() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ Iterator<PlanNode> children = parent.iterator();
+ assertThat(children.next(), is(sameInstance(child1)));
+ assertThat(children.next(), is(sameInstance(child2)));
+ assertThat(children.next(), is(sameInstance(child3)));
+ assertThat(children.hasNext(), is(false));
+ }
+
+ @Test( expected = UnsupportedOperationException.class )
+ public void shouldReturnImmutableIteratorOfChildren() {
+ parent = new PlanNode(Type.JOIN);
+ new PlanNode(Type.ACCESS, parent);
+ new PlanNode(Type.DUP_REMOVE, parent);
+ new PlanNode(Type.GROUP, parent);
+ Iterator<PlanNode> iter = parent.iterator();
+ iter.next();
+ iter.remove();
+ }
+
+ @Test
+ public void shouldRemoveAllChildrenOfParentWithNoChildrenByReturningEmptyList() {
+ parent = new PlanNode(Type.JOIN);
+ // Perform the remove, and verify the list has all the children ...
+ List<PlanNode> children = parent.removeAllChildren();
+ assertThat(children.size(), is(0));
+ assertThat(parent.getChildCount(), is(0));
+ // Add a new child to the parent ...
+ PlanNode child1a = new PlanNode(Type.ACCESS, parent);
+ assertThat(parent.getFirstChild(), is(sameInstance(child1a)));
+ // The returned copy should not be modified ...
+ assertThat(children.size(), is(0));
+ }
+
+ @Test
+ public void shouldRemoveAllChildrenAndReturnCopyOfListOfChildren() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ // Perform the remove, and verify the list has all the children ...
+ List<PlanNode> children = parent.removeAllChildren();
+ assertThat(children.get(0), is(sameInstance(child1)));
+ assertThat(children.get(1), is(sameInstance(child2)));
+ assertThat(children.get(2), is(sameInstance(child3)));
+ assertThat(children.size(), is(3));
+ assertThat(parent.getChildCount(), is(0));
+ // Add a new child to the parent ...
+ PlanNode child1a = new PlanNode(Type.ACCESS, parent);
+ assertThat(parent.getFirstChild(), is(sameInstance(child1a)));
+ // The returned copy should not be modified ...
+ assertThat(children.get(0), is(sameInstance(child1)));
+ assertThat(children.get(1), is(sameInstance(child2)));
+ assertThat(children.get(2), is(sameInstance(child3)));
+ assertThat(children.size(), is(3));
+ }
+
+ @Test
+ public void shouldReturnCorrectChildCount() {
+ parent = new PlanNode(Type.JOIN);
+ assertThat(parent.getChildCount(), is(0));
+ for (int i = 0; i != 10; ++i) {
+ new PlanNode(Type.ACCESS, parent);
+ assertThat(parent.getChildCount(), is(i + 1));
+ }
+ }
+
+ @Test
+ public void shouldAddChildrenAtEnd() {
+ parent = new PlanNode(Type.JOIN);
+ List<PlanNode> children = new ArrayList<PlanNode>();
+ children.add(new PlanNode(Type.ACCESS, parent));
+ children.add(new PlanNode(Type.GROUP, parent));
+ children.add(new PlanNode(Type.NULL, parent));
+ parent.addChildren(children);
+ int index = 0;
+ for (PlanNode child : children) {
+ assertThat(parent.getChild(index++), is(sameInstance(child)));
+ }
+ }
+
+ @Test
+ public void shouldRemoveChild() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ // Perform the remove, and verify children have changed ...
+ assertThat(parent.removeChild(child2), is(true));
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child3)));
+ }
+
+ @Test
+ public void shouldNotRemoveChildIfNotReallyAChild() {
+ node = new PlanNode(Type.PROJECT);
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ // Try to remove the non-child, and verify the children have no changed ...
+ assertThat(parent.removeChild(node), is(false));
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ }
+
+ @Test
+ public void shouldNotRemoveChildIfReferenceIsNull() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ // Try to remove the non-child, and verify the children have no changed ...
+ assertThat(parent.removeChild(null), is(false));
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ }
+
+ @Test
+ public void shouldExtractChildByRemovingIfChildHasNoChildren() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ // Perform the extraction ...
+ parent.extractChild(child2);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child3)));
+ assertThat(parent.getChildCount(), is(2));
+ }
+
+ @Test
+ public void shouldExtractChildByReplacingWithFirstGrandchild() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ PlanNode grandChild1 = new PlanNode(Type.SELECT, child2);
+ PlanNode grandChild2 = new PlanNode(Type.SET_OPERATION, child2);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ // Perform the extraction ...
+ parent.extractChild(child2);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(grandChild1)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ assertThat(parent.getChildCount(), is(3));
+ // The old child should still contain just the remaining child(ren) ...
+ assertThat(child2.getFirstChild(), is(sameInstance(grandChild2)));
+ assertThat(child2.getParent(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldReplaceChild() {
+ PlanNode parentOfReplacement = new PlanNode(Type.SORT);
+ PlanNode replacement = new PlanNode(Type.SELECT, parentOfReplacement);
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ // Perform the replacement ...
+ assertThat(parent.replaceChild(child2, replacement), is(true));
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(replacement)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ assertThat(replacement.getParent(), is(sameInstance(parent)));
+ assertThat(child1.getParent(), is(sameInstance(parent)));
+ assertThat(child2.getParent(), is(nullValue()));
+ assertThat(child3.getParent(), is(sameInstance(parent)));
+ // The replacement should no longer be a child of its former parent ...
+ assertThat(parentOfReplacement.getChildCount(), is(0));
+ }
+
+ @Test
+ public void shouldReplaceChildWithAnotherChildToSwapPositions() {
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ // Perform the replacement ...
+ assertThat(parent.replaceChild(child2, child3), is(true));
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child3)));
+ assertThat(parent.getChild(2), is(sameInstance(child2)));
+ assertThat(child1.getParent(), is(sameInstance(parent)));
+ assertThat(child2.getParent(), is(sameInstance(parent)));
+ assertThat(child3.getParent(), is(sameInstance(parent)));
+ }
+
+ @Test
+ public void shouldNotReplaceChildIfChildNodeIsNotReallyAChild() {
+ PlanNode nonChild = new PlanNode(Type.PROJECT);
+ PlanNode replacement = new PlanNode(Type.SELECT);
+ parent = new PlanNode(Type.JOIN);
+ PlanNode child1 = new PlanNode(Type.ACCESS, parent);
+ PlanNode child2 = new PlanNode(Type.DUP_REMOVE, parent);
+ PlanNode child3 = new PlanNode(Type.GROUP, parent);
+ assertThat(parent.getChild(0), is(sameInstance(child1)));
+ assertThat(parent.getChild(1), is(sameInstance(child2)));
+ assertThat(parent.getChild(2), is(sameInstance(child3)));
+ assertThat(parent.replaceChild(nonChild, replacement), is(false));
+ }
+
+ @Test
+ public void shouldGetPathThatIncludesStartAndEndNodes() {
+ PlanNode root = new PlanNode(Type.JOIN);
+ PlanNode node1 = new PlanNode(Type.ACCESS, root);
+ PlanNode node2 = new PlanNode(Type.DUP_REMOVE, node1);
+ PlanNode node3 = new PlanNode(Type.GROUP, node2);
+ PlanNode node4 = new PlanNode(Type.SELECT, node3);
+ PlanNode node5 = new PlanNode(Type.SET_OPERATION, node4);
+ assertThat(root.getPathTo(root), is(path(root)));
+ assertThat(root.getPathTo(node1), is(path(root, node1)));
+ assertThat(root.getPathTo(node2), is(path(root, node1, node2)));
+ assertThat(root.getPathTo(node3), is(path(root, node1, node2, node3)));
+ assertThat(root.getPathTo(node4), is(path(root, node1, node2, node3, node4)));
+ assertThat(root.getPathTo(node5), is(path(root, node1, node2, node3, node4, node5)));
+
+ assertThat(node1.getPathTo(node1), is(path(node1)));
+ assertThat(node1.getPathTo(node2), is(path(node1, node2)));
+ assertThat(node1.getPathTo(node3), is(path(node1, node2, node3)));
+ assertThat(node1.getPathTo(node4), is(path(node1, node2, node3, node4)));
+ assertThat(node1.getPathTo(node5), is(path(node1, node2, node3, node4, node5)));
+
+ assertThat(node2.getPathTo(node2), is(path(node2)));
+ assertThat(node2.getPathTo(node3), is(path(node2, node3)));
+ assertThat(node2.getPathTo(node4), is(path(node2, node3, node4)));
+ assertThat(node2.getPathTo(node5), is(path(node2, node3, node4, node5)));
+ }
+
+ protected LinkedList<PlanNode> path( PlanNode... expectedNodes ) {
+ LinkedList<PlanNode> result = new LinkedList<PlanNode>();
+ for (PlanNode node : expectedNodes) {
+ result.add(node);
+ }
+ return result;
+ }
+
+ @Test
+ public void shouldNotAddNullSelectorNames() {
+ Collection<SelectorName> names = Collections.singletonList(null);
+ node.addSelectors(names);
+ assertThat(node.getSelectors().isEmpty(), is(true));
+ node.addSelector(null);
+ assertThat(node.getSelectors().isEmpty(), is(true));
+ SelectorName name = new SelectorName("something");
+ node.addSelector(name, null);
+ assertThat(node.getSelectors().size(), is(1));
+ assertThat(node.getSelectors().contains(name), is(true));
+ }
+
+ @Test
+ public void shouldCorrectlyDetermineIfAncestorHasType() {
+ PlanNode root = new PlanNode(Type.JOIN);
+ PlanNode node1 = new PlanNode(Type.ACCESS, root);
+ PlanNode node2 = new PlanNode(Type.DUP_REMOVE, node1);
+ PlanNode node3 = new PlanNode(Type.GROUP, node2);
+ PlanNode node4 = new PlanNode(Type.SELECT, node3);
+ PlanNode node5 = new PlanNode(Type.SET_OPERATION, node4);
+ assertThat(node5.hasAncestorOfType(Type.SET_OPERATION), is(false)); // no ancestor, just self
+ assertThat(node5.hasAncestorOfType(Type.SOURCE), is(false));
+ assertThat(node5.hasAncestorOfType(Type.DUP_REMOVE), is(true));
+ assertThat(node5.hasAncestorOfType(Type.DUP_REMOVE, Type.SELECT), is(true));
+ assertThat(node5.hasAncestorOfType(Type.DUP_REMOVE, Type.SELECT, Type.SOURCE), is(true));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/PlanNodeTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,239 @@
+/*
+ * 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.process;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+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.query.AbstractQueryTest;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+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.NodeLocalName;
+import org.jboss.dna.graph.query.model.NodeName;
+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;
+
+/**
+ *
+ */
+public abstract class AbstractQueryResultsTest extends AbstractQueryTest {
+
+ protected ExecutionContext executionContext = ExecutionContext.DEFAULT_CONTEXT;
+
+ protected Name name( String name ) {
+ return executionContext.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Path path( String name ) {
+ return executionContext.getValueFactories().getPathFactory().create(name);
+ }
+
+ protected Schemata schemataFor( Columns columns ) {
+ return schemataFor(columns, new PropertyType[] {});
+ }
+
+ protected Schemata schemataFor( Columns columns,
+ PropertyType... types ) {
+ ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder();
+ for (String selectorName : columns.getSelectorNames()) {
+ final SelectorName selector = selector(selectorName);
+ int i = 0;
+ for (Column column : columns.getColumns()) {
+ final String name = column.getColumnName();
+ final PropertyType type = types != null && types.length > i && types[i] != null ? types[i] : PropertyType.STRING;
+ if (column.getSelectorName().equals(selector)) {
+ builder.addColumn(selectorName, name, type);
+ ++i;
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ protected Columns resultColumns( String selectorName,
+ String... columnNames ) {
+ // Define the columns ...
+ List<Column> columnObj = new ArrayList<Column>();
+ SelectorName selector = selector(selectorName);
+ for (String columnName : columnNames) {
+ columnObj.add(new Column(selector, name(columnName), columnName));
+ }
+ return new QueryResultColumns(columnObj, false);
+ }
+
+ protected Columns resultColumnsWithSearchResults( String selectorName,
+ String... columnNames ) {
+ // Define the columns ...
+ List<Column> columnObj = new ArrayList<Column>();
+ SelectorName selector = selector(selectorName);
+ for (String columnName : columnNames) {
+ columnObj.add(new Column(selector, name(columnName), columnName));
+ }
+ return new QueryResultColumns(columnObj, true);
+ }
+
+ protected Object[] tuple( Columns columns,
+ String path,
+ Object... values ) {
+ return tuple(columns, path(path), values);
+ }
+
+ protected Object[] tuple( Columns columns,
+ Path path,
+ Object... values ) {
+ return tuple(columns, Location.create(path), values);
+ }
+
+ protected Object[] tuple( Columns columns,
+ String[] paths,
+ Object... values ) {
+ Location[] locations = new Location[paths.length];
+ for (int i = 0; i != paths.length; ++i) {
+ locations[i] = Location.create(path(paths[i]));
+ }
+ return tuple(columns, locations, values);
+ }
+
+ protected Object[] tuple( Columns columns,
+ Path[] paths,
+ Object... values ) {
+ Location[] locations = new Location[paths.length];
+ for (int i = 0; i != paths.length; ++i) {
+ locations[i] = Location.create(paths[i]);
+ }
+ return tuple(columns, locations, values);
+ }
+
+ protected Object[] tuple( Columns columns,
+ Location location,
+ Object... values ) {
+ return tuple(columns, new Location[] {location}, values);
+ }
+
+ protected Object[] tuple( Columns columns,
+ Location[] locations,
+ Object... values ) {
+ Object[] results = new Object[columns.getTupleSize()];
+ // Set the column values ...
+ assertThat(values.length, is(columns.getColumnCount()));
+ for (int i = 0; i != columns.getColumnCount(); ++i) {
+ results[i] = values[i];
+ }
+ // Set the location ...
+ int index = columns.getColumnCount();
+ for (Location location : locations) {
+ results[index++] = location;
+ }
+ // Set the full-text-search results ...
+ if (columns.hasFullTextSearchScores()) {
+ results[index++] = 1.0;
+ }
+ return results;
+ }
+
+ protected Object[] tuple( Columns columns,
+ Location[] locations,
+ double[] scores,
+ Object... values ) {
+ assertThat(values.length, is(columns.getColumnCount()));
+ assertThat(locations.length, is(columns.getLocationCount()));
+ assertThat(scores.length, is(columns.getLocationCount()));
+ Object[] results = new Object[columns.getTupleSize()];
+ // Set the column values ...
+ for (int i = 0; i != columns.getColumnCount(); ++i) {
+ results[i] = values[i];
+ }
+ // Set the location ...
+ int index = columns.getColumnCount();
+ for (Location location : locations) {
+ results[index++] = location;
+ }
+ // Set the full-text-search results ...
+ if (columns.hasFullTextSearchScores()) {
+ for (double score : scores) {
+ results[index++] = score;
+ }
+ }
+ return results;
+ }
+
+ protected Ordering orderByPropertyValue( Column column ) {
+ return orderByPropertyValue(column, Order.ASCENDING);
+ }
+
+ protected Ordering orderByPropertyValue( Column column,
+ Order order ) {
+ return new Ordering(new PropertyValue(column.getSelectorName(), column.getPropertyName()), order);
+ }
+
+ protected Ordering orderByPropertyLength( Column column ) {
+ return orderByPropertyValue(column, Order.ASCENDING);
+ }
+
+ protected Ordering orderByPropertyLength( Column column,
+ Order order ) {
+ return new Ordering(new Length(new PropertyValue(column.getSelectorName(), column.getPropertyName())), order);
+ }
+
+ protected Ordering orderByNodeName( String selectorName ) {
+ return orderByNodeName(selectorName, Order.ASCENDING);
+ }
+
+ protected Ordering orderByNodeName( String selectorName,
+ Order order ) {
+ return new Ordering(new NodeName(selector(selectorName)), order);
+ }
+
+ protected Ordering orderByNodeLocalName( String selectorName ) {
+ return orderByNodeLocalName(selectorName, Order.ASCENDING);
+ }
+
+ protected Ordering orderByNodeLocalName( String selectorName,
+ Order order ) {
+ return new Ordering(new NodeLocalName(selector(selectorName)), order);
+ }
+
+ protected Ordering orderByFullTextSearchScore( String selectorName ) {
+ return orderByFullTextSearchScore(selectorName, Order.ASCENDING);
+ }
+
+ protected Ordering orderByFullTextSearchScore( String selectorName,
+ Order order ) {
+ return new Ordering(new FullTextSearchScore(selector(selectorName)), order);
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/AbstractQueryResultsTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,97 @@
+/*
+ * 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.process;
+
+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.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.process.DistinctComponent;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class DistinctComponentTest extends AbstractQueryResultsTest {
+
+ private DistinctComponent component;
+ private QueryContext context;
+ private Columns columns;
+ private List<Object[]> inputTuples;
+
+ @Before
+ public void beforeEach() {
+ context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+ inputTuples = new ArrayList<Object[]>();
+ // Define the columns for the results ...
+ columns = resultColumns("Selector1", "ColA", "ColB", "ColC");
+ // And define the delegating component ...
+ ProcessingComponent delegate = new ProcessingComponent(context, columns) {
+ @SuppressWarnings( "synthetic-access" )
+ @Override
+ public List<Object[]> execute() {
+ return new ArrayList<Object[]>(inputTuples);
+ }
+ };
+ // Create the component we're testing ...
+ component = new DistinctComponent(delegate);
+ }
+
+ @Test
+ public void shouldReturnAllResultsIfThereAreNoDuplicatesInTuplesContainingOneLocation() {
+ inputTuples.add(tuple(columns, "/a/b/c1", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c2", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v1", "v2", "v3"));
+ assertThat(component.execute(), is(inputTuples));
+ }
+
+ @Test
+ public void shouldReturnNoDuplicatesFromTuplesContainingOneLocation() {
+ inputTuples.add(tuple(columns, "/a/b/c1", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c2", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c0", "v1", "v2", "v3"));
+ List<Object[]> expected = new ArrayList<Object[]>(inputTuples);
+ expected.remove(5);
+ expected.remove(4);
+ assertThat(component.execute(), is(expected));
+ }
+
+ @Test
+ public void shouldReturnEmptyResultsWhenDelegateReturnsEmptyResults() {
+ assertThat(component.execute().isEmpty(), is(true));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,237 @@
+/*
+ * 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.process;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.QueryResults.Cursor;
+import org.jboss.dna.graph.query.QueryResults.Statistics;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.process.QueryResultColumns;
+import org.jboss.dna.graph.query.process.QueryResults;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class QueryResultColumnsTest extends AbstractQueryResultsTest {
+
+ private QueryContext context;
+ private List<Column> columnList;
+ private Columns columns;
+ private QueryResults results;
+ private List<Object[]> tuples;
+ @Mock
+ private Schemata schemata;
+ @Mock
+ private QueryCommand command;
+ @Mock
+ private Statistics statistics;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ context = new QueryContext(executionContext, new PlanHints(), schemata);
+ columnList = new ArrayList<Column>();
+ columnList.add(new Column(selector("table1"), name("colA"), "colA"));
+ columnList.add(new Column(selector("table1"), name("colB"), "colB"));
+ columnList.add(new Column(selector("table1"), name("colC"), "colC"));
+ columnList.add(new Column(selector("table2"), name("colA"), "colA2"));
+ columnList.add(new Column(selector("table2"), name("colB"), "colB2"));
+ columnList.add(new Column(selector("table2"), name("colX"), "colX"));
+ columns = new QueryResultColumns(columnList, false);
+ tuples = new ArrayList<Object[]>();
+ tuples.add(tuple(columns, new String[] {"/a/b/c", "/a/x/y"}, 1, 2, 3, "2a", "2b", "x"));
+ tuples.add(tuple(columns, new String[] {"/a/b/d", "/a/x/y"}, 4, 5, 6, "2a", "2b", "x"));
+ results = new QueryResults(context, command, columns, statistics);
+ }
+
+ @Test
+ public void shouldReturnSameColumnsPassedIntoConstructor() {
+ assertThat(results.getColumns(), is(sameInstance(columns)));
+ }
+
+ @Test
+ public void shouldReturnSameStatisticsPassedIntoConstructor() {
+ assertThat(results.getStatistics(), is(sameInstance(statistics)));
+ }
+
+ @Test
+ public void shouldReturnSameQueryCommandPassedIntoConstructor() {
+ assertThat(results.getCommand(), is(sameInstance(command)));
+ }
+
+ @Test
+ public void shouldReturnSameProblemsObjectAsInQueryContext() {
+ assertThat(results.getProblems(), is(sameInstance(context.getProblems())));
+ }
+
+ @Test
+ public void shouldReturnSameTuplesListPassedIntoConstructor() {
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ assertThat(results.getTuples(), is(sameInstance(tuples)));
+ }
+
+ @Test
+ public void shouldHaveNoTuplesIfConstructedWithNoTuples() {
+ assertThat(results.getTuples().isEmpty(), is(true));
+ assertThat(results.getCursor().hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldHaveNoTuplesIfConstructedWithEmptyTuplesList() {
+ tuples.clear();
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ assertThat(results.getTuples().isEmpty(), is(true));
+ assertThat(results.getCursor().hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldReturnMutableTuplesList() {
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ assertThat(results.getTuples().isEmpty(), is(false));
+ results.getTuples().clear();
+ assertThat(results.getTuples().isEmpty(), is(true));
+ assertThat(tuples.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldReturnCursorThatAccessesTuples() {
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ Cursor cursor = results.getCursor();
+ Iterator<Object[]> expectedIter = tuples.iterator();
+ int rowNumber = 0;
+ while (cursor.hasNext() && expectedIter.hasNext()) {
+ cursor.next();
+ Object[] tuple = expectedIter.next();
+ // Check the column values by column name and index ...
+ for (Column column : results.getColumns().getColumns()) {
+ String columnName = column.getColumnName();
+ int columnIndex = columns.getColumnIndexForName(columnName);
+ assertThat(cursor.getValue(columnName), is(tuple[columnIndex]));
+ assertThat(cursor.getValue(columnIndex), is(tuple[columnIndex]));
+ // Get the location for this column ...
+ int locationIndex = columns.getLocationIndex(column.getSelectorName().getName());
+ Location location = (Location)tuple[locationIndex];
+ assertThat(cursor.getLocation(columnIndex), is(location));
+ }
+ // Check the locations by selector name and index ...
+ for (String selectorName : results.getColumns().getSelectorNames()) {
+ int locationIndex = columns.getLocationIndex(selectorName);
+ Location location = (Location)tuple[locationIndex];
+ assertThat(cursor.getLocation(selectorName), is(location));
+ assertThat(location.hasPath(), is(true));
+ }
+ // Check the row index ...
+ assertThat(cursor.getRowIndex(), is(rowNumber++));
+ }
+ assertThat(cursor.hasNext(), is(false));
+ assertThat(expectedIter.hasNext(), is(false));
+ }
+
+ @Test( expected = IllegalStateException.class )
+ public void shouldRequireNextOnCursorToBeCalledBeforeGettingValueUsingColumnIndex() {
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ Cursor cursor = results.getCursor();
+ assertThat(cursor.hasNext(), is(true));
+ cursor.getValue(0);
+ }
+
+ @Test( expected = IllegalStateException.class )
+ public void shouldRequireNextOnCursorToBeCalledBeforeGettingValueUsingColumnName() {
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ Cursor cursor = results.getCursor();
+ assertThat(cursor.hasNext(), is(true));
+ cursor.getValue("colA");
+ }
+
+ @Test
+ public void shouldPrintToStringAllResults() {
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ List<String> lines = StringUtil.splitLines(results.toString());
+ assertThat(lines.size(), is(tuples.size() + 4)); // = delim + header + delim + (...lines...) + delim
+ }
+
+ @Test
+ public void shouldPrintToStringBuilderAllResults() {
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ StringBuilder sb = new StringBuilder();
+ results.toString(sb);
+ List<String> lines = StringUtil.splitLines(sb.toString());
+ assertThat(lines.size(), is(tuples.size() + 4)); // = delim + header + delim + (...lines...) + delim
+ }
+
+ @Test
+ public void shouldPrintToStringBuilderAllResultsEvenWhenNoTuples() {
+ tuples.clear();
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ StringBuilder sb = new StringBuilder();
+ results.toString(sb);
+ List<String> lines = StringUtil.splitLines(sb.toString());
+ assertThat(lines.size(), is(4)); // = delim + header + delim + (...lines...) + delim
+ }
+
+ @Test
+ public void shouldPrintToStringBuilderOnlyFirstLinesOfResults() {
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ StringBuilder sb = new StringBuilder();
+ results.toString(sb, 1);
+ List<String> lines = StringUtil.splitLines(sb.toString());
+ assertThat(lines.size(), is(1 + 4)); // = delim + header + delim + (...lines...) + delim
+ }
+
+ @Test
+ public void shouldPrintToStringBuilderOnlyFirstLinesOfResultsEvenWhenNoTuples() {
+ tuples.clear();
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ StringBuilder sb = new StringBuilder();
+ results.toString(sb, 3);
+ List<String> lines = StringUtil.splitLines(sb.toString());
+ assertThat(lines.size(), is(4)); // = delim + header + delim + (...lines...) + delim
+ }
+
+ @Test
+ public void shouldPrintToStringBuilderAllResultsWhenMaxRowParameterIsLargerThanNumberOfTuples() {
+ tuples.clear();
+ results = new QueryResults(context, command, columns, statistics, tuples);
+ StringBuilder sb = new StringBuilder();
+ results.toString(sb, 3);
+ List<String> lines = StringUtil.splitLines(sb.toString());
+ assertThat(lines.size(), is(tuples.size() + 4)); // = delim + header + delim + (...lines...) + delim
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultsTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultsTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,363 @@
+/*
+ * 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.process;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.process.QueryResultColumns;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class QueryResultsTest extends AbstractQueryResultsTest {
+
+ private List<Column> columnList;
+ private QueryResultColumns columnsWithoutScores;
+ private QueryResultColumns columnsWithScores;
+
+ @Before
+ public void beforeEach() {
+ columnList = new ArrayList<Column>();
+ columnList.add(new Column(selector("table1"), name("colA"), "colA"));
+ columnList.add(new Column(selector("table1"), name("colB"), "colB"));
+ columnList.add(new Column(selector("table1"), name("colC"), "colC"));
+ columnList.add(new Column(selector("table2"), name("colA"), "colA2"));
+ columnList.add(new Column(selector("table2"), name("colB"), "colB2"));
+ columnList.add(new Column(selector("table2"), name("colX"), "colX"));
+ columnsWithoutScores = new QueryResultColumns(columnList, false);
+ columnsWithScores = new QueryResultColumns(columnList, true);
+ }
+
+ @Test
+ public void shouldHaveCorrectTupleSize() {
+ assertThat(columnsWithScores.getTupleSize(), is(columnList.size() + 2 + 2));
+ assertThat(columnsWithoutScores.getTupleSize(), is(columnList.size() + 2 + 0));
+ }
+
+ @Test
+ public void shouldHaveCorrectTupleNames() {
+ List<String> expected = new ArrayList<String>();
+ expected.add("colA");
+ expected.add("colB");
+ expected.add("colC");
+ expected.add("colA2");
+ expected.add("colB2");
+ expected.add("colX");
+ expected.add("Location(table1)");
+ expected.add("Location(table2)");
+ assertThat(columnsWithoutScores.getTupleValueNames(), is(expected));
+ expected.add("Score(table1)");
+ expected.add("Score(table2)");
+ assertThat(columnsWithScores.getTupleValueNames(), is(expected));
+ }
+
+ @Test
+ public void shouldHaveCorrectSelectorNames() {
+ List<String> expected = new ArrayList<String>();
+ expected.add("table1");
+ expected.add("table2");
+ assertThat(columnsWithoutScores.getSelectorNames(), is(expected));
+ assertThat(columnsWithScores.getSelectorNames(), is(expected));
+ }
+
+ @Test
+ public void shouldHaveCorrectNumberOfColumns() {
+ assertThat(columnsWithScores.getColumnCount(), is(columnList.size()));
+ assertThat(columnsWithoutScores.getColumnCount(), is(columnList.size()));
+ }
+
+ @Test
+ public void shouldReturnColumns() {
+ assertThat(columnsWithScores.getColumns(), is(columnList));
+ assertThat(columnsWithoutScores.getColumns(), is(columnList));
+ }
+
+ @Test( expected = UnsupportedOperationException.class )
+ public void shouldReturnImmutableColumns() {
+ assertThat(columnsWithoutScores.getColumns().isEmpty(), is(false));
+ columnsWithoutScores.getColumns().clear();
+ }
+
+ @Test
+ public void shouldReturnColumnNames() {
+ List<String> names = new ArrayList<String>();
+ for (Column column : columnList) {
+ names.add(column.getColumnName());
+ }
+ assertThat(columnsWithScores.getColumnNames(), is(names));
+ assertThat(columnsWithoutScores.getColumnNames(), is(names));
+ }
+
+ @Test
+ public void shouldReturnCorrectIndexOfColumnGivenColumnName() {
+ for (Column column : columnList) {
+ assertThat(columnsWithoutScores.getColumnIndexForName(column.getColumnName()), is(columnList.indexOf(column)));
+ }
+ }
+
+ @Test
+ public void shouldReturnCorrectIndexOfColumnGivenColumnSelectorAndPropertyName() {
+ for (Column column : columnList) {
+ assertThat(columnsWithoutScores.getColumnIndexForProperty(column.getSelectorName().getName(),
+ column.getPropertyName()), is(columnList.indexOf(column)));
+ }
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfColumnGivenColumnNameWithIncorrectCase() {
+ columnsWithScores.getColumnIndexForName("cola");
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfColumnGivenNonExistantColumnName() {
+ columnsWithScores.getColumnIndexForName("non-existant");
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfColumnGivenNullColumnName() {
+ columnsWithScores.getColumnIndexForName(null);
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfColumnGivenUnusedSelectorName() {
+ columnsWithScores.getColumnIndexForProperty("non-existant", name("colA"));
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfColumnGivenNullSelectorName() {
+ columnsWithScores.getColumnIndexForProperty(null, name("colA"));
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfColumnGivenEmptySelectorName() {
+ columnsWithScores.getColumnIndexForProperty("", name("colA"));
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfColumnGivenUnusedPropertyNameName() {
+ columnsWithScores.getColumnIndexForProperty("table1", name("non-existant"));
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfColumnGivenNullPropertyNameName() {
+ columnsWithScores.getColumnIndexForProperty("table1", null);
+ }
+
+ @Test
+ public void shouldHaveCorrectNumberOfLocations() {
+ assertThat(columnsWithScores.getLocationCount(), is(2));
+ assertThat(columnsWithoutScores.getLocationCount(), is(2));
+ }
+
+ @Test
+ public void shouldReturnCorrectIndexOfLocationGivenSelectorName() {
+ assertThat(columnsWithoutScores.getLocationIndex("table1"), is(columnList.size() + 0));
+ assertThat(columnsWithoutScores.getLocationIndex("table2"), is(columnList.size() + 1));
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfLocationGivenUnusedSelectorName() {
+ columnsWithScores.getLocationIndex("non-existant");
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfLocationGivenNullSelectorName() {
+ columnsWithScores.getLocationIndex(null);
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfLocationGivenEmptySelectorName() {
+ columnsWithScores.getLocationIndex("");
+ }
+
+ @Test
+ public void shouldReturnCorrectIndexOfLocationGivenColumnName() {
+ assertThat(columnsWithoutScores.getLocationIndexForColumn("colA"), is(columnList.size() + 0));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn("colB"), is(columnList.size() + 0));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn("colC"), is(columnList.size() + 0));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn("colA2"), is(columnList.size() + 1));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn("colB2"), is(columnList.size() + 1));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn("colX"), is(columnList.size() + 1));
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfLocationGivenUnusedColumnName() {
+ columnsWithScores.getLocationIndexForColumn("non-existant");
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfLocationGivenNullColumnName() {
+ columnsWithScores.getLocationIndexForColumn(null);
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfLocationGivenEmptyColumnName() {
+ columnsWithScores.getLocationIndexForColumn("");
+ }
+
+ @Test
+ public void shouldReturnCorrectIndexOfLocationGivenColumnIndex() {
+ assertThat(columnsWithoutScores.getLocationIndexForColumn(0), is(columnList.size() + 0));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn(1), is(columnList.size() + 0));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn(2), is(columnList.size() + 0));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn(3), is(columnList.size() + 1));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn(4), is(columnList.size() + 1));
+ assertThat(columnsWithoutScores.getLocationIndexForColumn(5), is(columnList.size() + 1));
+ }
+
+ @Test( expected = IndexOutOfBoundsException.class )
+ public void shouldFailToFindIndexOfLocationGivenColumnIndexEqualToOrLargerThanNumberOfColumns() {
+ columnsWithScores.getLocationIndexForColumn(columnList.size());
+ }
+
+ @Test( expected = IndexOutOfBoundsException.class )
+ public void shouldFailToFindIndexOfLocationGivenColumnIndexLessThanZero() {
+ columnsWithScores.getLocationIndexForColumn(-1);
+ }
+
+ @Test
+ public void shouldCorrectlyReportWhetherScoresAreIncluded() {
+ assertThat(columnsWithScores.hasFullTextSearchScores(), is(true));
+ assertThat(columnsWithoutScores.hasFullTextSearchScores(), is(false));
+ }
+
+ @Test
+ public void shouldReturnCorrectIndexOfFullTextSearchScoreGivenSelectorName() {
+ assertThat(columnsWithScores.getFullTextSearchScoreIndexFor("table1"), is(columnList.size() + 2 + 0));
+ assertThat(columnsWithScores.getFullTextSearchScoreIndexFor("table2"), is(columnList.size() + 2 + 1));
+ }
+
+ @Test
+ public void shouldReturnNegativeOneForIndexOfFullTextSearchScoreGivenValidSelectorNameButWhereNoScoresAreIncluded() {
+ assertThat(columnsWithoutScores.getFullTextSearchScoreIndexFor("table1"), is(-1));
+ assertThat(columnsWithoutScores.getFullTextSearchScoreIndexFor("table2"), is(-1));
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfFullTextSearchScoreGivenUnusedSelectorName() {
+ columnsWithScores.getFullTextSearchScoreIndexFor("non-existant");
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfFullTextSearchScoreGivenNullSelectorName() {
+ columnsWithScores.getFullTextSearchScoreIndexFor(null);
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailToFindIndexOfFullTextSearchScoreGivenEmptySelectorName() {
+ columnsWithScores.getFullTextSearchScoreIndexFor("");
+ }
+
+ @Test
+ public void shouldIncludeSelf() {
+ assertThat(columnsWithScores.includes(columnsWithScores), is(true));
+ assertThat(columnsWithoutScores.includes(columnsWithoutScores), is(true));
+ }
+
+ @Test
+ public void shouldIncludeColumnsObjectWithSubsetOfColumnObjectsAndIndependentOfFullTextSearchScores() {
+ List<Column> subset = new ArrayList<Column>();
+ subset.add(columnList.get(0));
+ subset.add(columnList.get(1));
+ subset.add(columnList.get(4));
+ Columns other = new QueryResultColumns(subset, false);
+ assertThat(columnsWithScores.includes(other), is(true));
+ assertThat(columnsWithoutScores.includes(other), is(true));
+ assertThat(columnsWithoutScores.includes(columnsWithScores), is(true));
+ }
+
+ @Test
+ public void shouldEqualSelf() {
+ assertThat(columnsWithScores.equals(columnsWithScores), is(true));
+ assertThat(columnsWithoutScores.equals(columnsWithoutScores), is(true));
+ }
+
+ @Test
+ public void shouldEqualIndependentOfInclusionOfFullTextSearchScores() {
+ assertThat(columnsWithoutScores.equals(columnsWithScores), is(true));
+ }
+
+ @Test
+ public void shouldNotBeUnionCompatibleUnlessBothHaveFullTextSearchScores() {
+ Columns other = new QueryResultColumns(columnsWithoutScores.getColumns(), !columnsWithoutScores.hasFullTextSearchScores());
+ assertThat(columnsWithoutScores.isUnionCompatible(other), is(false));
+ }
+
+ @Test
+ public void shouldNotBeUnionCompatibleUnlessBothDoNotHaveFullTextSearchScores() {
+ Columns other = new QueryResultColumns(columnsWithScores.getColumns(), !columnsWithScores.hasFullTextSearchScores());
+ assertThat(columnsWithScores.isUnionCompatible(other), is(false));
+ }
+
+ @Test
+ public void shouldBeUnionCompatibleWithEquivalentColumns() {
+ List<Column> columnListCopy = new ArrayList<Column>();
+ for (Column column : columnsWithScores.getColumns()) {
+ columnListCopy.add(new Column(column.getSelectorName(), column.getPropertyName(), column.getColumnName()));
+ }
+ Columns other = new QueryResultColumns(columnListCopy, columnsWithScores.hasFullTextSearchScores());
+ assertThat(columnsWithScores.isUnionCompatible(other), is(true));
+ }
+
+ @Test
+ public void shouldNotBeUnionCompatibleWithSubsetOfColumns() {
+ List<Column> columnListCopy = new ArrayList<Column>();
+ for (Column column : columnsWithScores.getColumns()) {
+ columnListCopy.add(new Column(column.getSelectorName(), column.getPropertyName(), column.getColumnName()));
+ }
+ columnListCopy.remove(3);
+ Columns other = new QueryResultColumns(columnListCopy, columnsWithScores.hasFullTextSearchScores());
+ assertThat(columnsWithScores.isUnionCompatible(other), is(false));
+ }
+
+ @Test
+ public void shouldNotBeUnionCompatibleWithExtraColumns() {
+ List<Column> columnListCopy = new ArrayList<Column>();
+ for (Column column : columnsWithScores.getColumns()) {
+ columnListCopy.add(new Column(column.getSelectorName(), column.getPropertyName(), column.getColumnName()));
+ }
+ columnListCopy.add(new Column(selector("table2"), name("colZ"), "colZ"));
+ Columns other = new QueryResultColumns(columnListCopy, columnsWithScores.hasFullTextSearchScores());
+ assertThat(columnsWithScores.isUnionCompatible(other), is(false));
+ }
+
+ @Test
+ public void shouldBeUnionCompatibleWithSameColumns() {
+ Columns other = new QueryResultColumns(columnsWithScores.getColumns(), columnsWithScores.hasFullTextSearchScores());
+ assertThat(columnsWithScores.isUnionCompatible(other), is(true));
+ }
+
+ @Test
+ public void shouldHaveToString() {
+ columnsWithScores.toString();
+ columnsWithoutScores.toString();
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultsTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.process;
+
+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.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.SortLocationsComponent;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class SortLocationsComponentTest extends AbstractQueryResultsTest {
+
+ private SortLocationsComponent component;
+ private QueryContext context;
+ private Columns columns;
+ private List<Object[]> inputTuples;
+
+ @Before
+ public void beforeEach() {
+ context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+ inputTuples = new ArrayList<Object[]>();
+ // Define the columns for the results ...
+ columns = resultColumns("Selector1", "ColA", "ColB", "ColC");
+ // And define the delegating component ...
+ ProcessingComponent delegate = new ProcessingComponent(context, columns) {
+ @SuppressWarnings( "synthetic-access" )
+ @Override
+ public List<Object[]> execute() {
+ return new ArrayList<Object[]>(inputTuples);
+ }
+ };
+ // Create the component we're testing ...
+ component = new SortLocationsComponent(delegate);
+ }
+
+ @Test
+ public void shouldReturnAllResultsInPathOrderForTuplesContainingOneLocation() {
+ inputTuples.add(tuple(columns, "/a/b/c1", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c2", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v1", "v2", "v3"));
+ 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 shouldReturnAllResultsInPathOrderForTuplesContainingOneLocationAndWithDuplicateTuples() {
+ inputTuples.add(tuple(columns, "/a/b/c1", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c2", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v1", "v2", "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c0", "v1", "v2", "v3"));
+ List<Object[]> expected = new ArrayList<Object[]>();
+ expected.add(inputTuples.get(6));
+ expected.add(inputTuples.get(0));
+ expected.add(inputTuples.get(2));
+ expected.add(inputTuples.get(3));
+ expected.add(inputTuples.get(5));
+ expected.add(inputTuples.get(1));
+ expected.add(inputTuples.get(4));
+ assertThat(component.execute(), is(expected));
+ }
+
+ @Test
+ public void shouldReturnEmptyResultsWhenDelegateReturnsEmptyResults() {
+ assertThat(component.execute().isEmpty(), is(true));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: 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 (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,203 @@
+/*
+ * 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.process;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.query.QueryContext;
+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;
+
+/**
+ *
+ */
+public class SortValuesComponentTest extends AbstractQueryResultsTest {
+
+ private SortValuesComponent component;
+ private ProcessingComponent delegate;
+ private QueryContext context;
+ private Schemata schemata;
+ private Columns columns;
+ private List<Object[]> inputTuples;
+ private List<Ordering> orderings;
+
+ @Before
+ public void beforeEach() {
+ // Define the columns for the results ...
+ columns = resultColumns("Selector1", "ColA", "ColB", "ColC");
+ schemata = schemataFor(columns, PropertyType.STRING, PropertyType.LONG, PropertyType.STRING);
+ // Define the context ...
+ context = new QueryContext(new ExecutionContext(), new PlanHints(), schemata);
+ inputTuples = new ArrayList<Object[]>();
+ // And define the delegating component ...
+ delegate = new ProcessingComponent(context, columns) {
+ @SuppressWarnings( "synthetic-access" )
+ @Override
+ public List<Object[]> execute() {
+ return new ArrayList<Object[]>(inputTuples);
+ }
+ };
+ // Create the component we're testing ...
+ orderings = new ArrayList<Ordering>();
+ component = new SortValuesComponent(delegate, orderings);
+ }
+
+ @Test
+ public void shouldReturnAllResultsOrderedByNodeName() {
+ orderings.add(orderByNodeName("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"));
+ inputTuples.add(tuple(columns, "/a/b3/c2", 100, 100, "v2"));
+ inputTuples.add(tuple(columns, "/a/b4/c3", "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 shouldReturnAllResultsOrderedByNodeNameWhenThereAreDuplicateTuples() {
+ orderings.add(orderByNodeName("Selector1"));
+ component = new SortValuesComponent(delegate, orderings);
+ inputTuples.add(tuple(columns, "/a/b1/c1", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b2/c4", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b3/c2", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b4/c3", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b5/c4", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b6/c3", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b7/c0", "v1", 100, "v3"));
+ List<Object[]> expected = new ArrayList<Object[]>();
+ expected.add(inputTuples.get(6));
+ expected.add(inputTuples.get(0));
+ expected.add(inputTuples.get(2));
+ expected.add(inputTuples.get(3));
+ expected.add(inputTuples.get(5));
+ expected.add(inputTuples.get(1));
+ expected.add(inputTuples.get(4));
+ assertThat(component.execute(), is(expected));
+ }
+
+ @Test
+ public void shouldReturnAllResultsOrderedByNodeLocalName() {
+ orderings.add(orderByNodeName("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"));
+ inputTuples.add(tuple(columns, "/a/b3/c2", 100, 100, "v2"));
+ inputTuples.add(tuple(columns, "/a/b4/c3", "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 shouldReturnAllResultsOrderedByNodeLocalNameWhenThereAreDuplicateTuples() {
+ orderings.add(orderByNodeName("Selector1"));
+ component = new SortValuesComponent(delegate, orderings);
+ inputTuples.add(tuple(columns, "/a/b1/dna:c1", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b2/dna:c4", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b3/dna:c2", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b4/dna:c3", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b5/jcr:c4", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b6/dna:c3", "v1", 100, "v3"));
+ inputTuples.add(tuple(columns, "/a/b7/dna:c0", "v1", 100, "v3"));
+ List<Object[]> expected = new ArrayList<Object[]>();
+ expected.add(inputTuples.get(6));
+ expected.add(inputTuples.get(0));
+ expected.add(inputTuples.get(2));
+ expected.add(inputTuples.get(3));
+ expected.add(inputTuples.get(5));
+ expected.add(inputTuples.get(1));
+ expected.add(inputTuples.get(4));
+ assertThat(component.execute(), is(expected));
+ }
+
+ @Test
+ public void shouldReturnAllResultsOrderedByValueLengthOfLong() {
+ orderings.add(orderByPropertyLength(columns.getColumns().get(1)));
+ component = new SortValuesComponent(delegate, orderings);
+ inputTuples.add(tuple(columns, "/a/b/c1", "v1", 1L, "v4"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1", 1114L, "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c2", "v1", 113L, "v2"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v1", 12L, "v1"));
+ List<Object[]> expected = new ArrayList<Object[]>();
+ expected.add(inputTuples.get(0));
+ expected.add(inputTuples.get(3));
+ expected.add(inputTuples.get(2));
+ expected.add(inputTuples.get(1));
+ assertThat(component.execute(), is(expected));
+ }
+
+ @Test
+ public void shouldReturnAllResultsOrderedByValueLengthOfString() {
+ orderings.add(orderByPropertyLength(columns.getColumns().get(0)));
+ component = new SortValuesComponent(delegate, orderings);
+ inputTuples.add(tuple(columns, "/a/b/c1", "v1", 100L, "v4"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1111", 100L, "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c2", "v111", 100L, "v2"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v11", 100L, "v1"));
+ List<Object[]> expected = new ArrayList<Object[]>();
+ expected.add(inputTuples.get(0));
+ expected.add(inputTuples.get(3));
+ expected.add(inputTuples.get(2));
+ expected.add(inputTuples.get(1));
+ assertThat(component.execute(), is(expected));
+ }
+
+ @Test
+ public void shouldReturnAllResultsInSuppliedOrderWhenThereAreNoOrderings() {
+ orderings.clear();
+ component = new SortValuesComponent(delegate, orderings);
+ inputTuples.add(tuple(columns, "/a/b/c1", "v1", 100L, "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1", 100L, "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c2", "v1", 100L, "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v1", 100L, "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c4", "v1", 100L, "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c3", "v1", 100L, "v3"));
+ inputTuples.add(tuple(columns, "/a/b/c0", "v1", 100L, "v3"));
+ List<Object[]> expected = new ArrayList<Object[]>(inputTuples);
+ assertThat(component.execute(), is(expected));
+ }
+
+ @Test
+ public void shouldReturnEmptyResultsWhenDelegateReturnsEmptyResults() {
+ assertThat(component.execute().isEmpty(), is(true));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/.classpath
===================================================================
--- trunk/dna-search/.classpath (rev 0)
+++ trunk/dna-search/.classpath 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java"/>
+ <classpathentry kind="src" path="src/main/resources"/>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+ <classpathentry kind="src" output="target/test-classes" 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: trunk/dna-search/.classpath
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/.project
===================================================================
--- trunk/dna-search/.project (rev 0)
+++ trunk/dna-search/.project 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>dna-search</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.maven.ide.eclipse.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</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: trunk/dna-search/.project
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/pom.xml
===================================================================
--- trunk/dna-search/pom.xml (rev 0)
+++ trunk/dna-search/pom.xml 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,97 @@
+<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>
+ </parent>
+ <!-- The groupId and version values are inherited from parent -->
+ <artifactId>dna-search</artifactId>
+ <packaging>jar</packaging>
+ <name>JBoss DNA Search</name>
+ <description>JBoss DNA search and query engine library</description>
+ <url>http://labs.jboss.org/dna</url>
+
+ <!--
+ Define the dependencies. Note that all version and scopes default to those
+ defined in the dependencyManagement section of the parent pom.
+ -->
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-common</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>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <!--
+ Lucene
+ -->
+ <dependency>
+ <groupId>org.apache.lucene</groupId>
+ <artifactId>lucene-core</artifactId>
+ <version>2.4.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.lucene</groupId>
+ <artifactId>lucene-analyzers</artifactId>
+ <version>2.4.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.lucene</groupId>
+ <artifactId>lucene-snowball</artifactId>
+ <version>2.4.1</version>
+ </dependency>
+ <!--
+ Testing (note the scope)
+ -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </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>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <!--
+ Java Concurrency in Practice annotations
+ -->
+ <dependency>
+ <groupId>net.jcip</groupId>
+ <artifactId>jcip-annotations</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
Property changes on: trunk/dna-search/pom.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.search;
+
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.store.Directory;
+
+/**
+ * Interface used to obtain the Lucene {@link Directory} instance that should be used for a workspace given the name of the
+ * workspace. There are several implementations (see {@link DirectoryConfigurations}), but custom implementations can always be used.
+ */
+@ThreadSafe
+public interface DirectoryConfiguration {
+ /**
+ * Get the {@link Directory} that should be used for the workspace with the supplied name.
+ *
+ * @param workspaceName the workspace name
+ * @param indexName the name of the index to be created
+ * @return the directory; never null
+ * @throws IllegalArgumentException if the workspace name is null
+ * @throws SearchEngineException if there is a problem creating the directory
+ */
+ Directory getDirectory( String workspaceName,
+ String indexName ) throws SearchEngineException;
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfiguration.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,386 @@
+/*
+ * 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.search;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.store.LockFactory;
+import org.apache.lucene.store.RAMDirectory;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+
+/**
+ * A family of {@link DirectoryConfiguration} implementations.
+ */
+public class DirectoryConfigurations {
+
+ /**
+ * Return a new {@link DirectoryConfiguration} that creates in-memory directories.
+ *
+ * @return the new directory configuration; never null
+ */
+ public static final DirectoryConfiguration inMemory() {
+ return new RamDirectoryFactory();
+ }
+
+ /**
+ * Return a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+ * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
+ * workspace names.
+ *
+ * @param parent the parent folder
+ * @return the new directory configuration; never null
+ * @throws IllegalArgumentException if the parent file is null
+ */
+ public static final DirectoryConfiguration using( File parent ) {
+ return new FileSystemDirectoryFromNameFactory(parent);
+ }
+
+ /**
+ * Return a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+ * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
+ * workspace names.
+ *
+ * @param parent the parent folder
+ * @param lockFactory the lock factory; may be null
+ * @return the new directory configuration; never null
+ * @throws IllegalArgumentException if the parent file is null
+ */
+ public static final DirectoryConfiguration using( File parent,
+ LockFactory lockFactory ) {
+ return new FileSystemDirectoryFromNameFactory(parent, lockFactory);
+ }
+
+ /**
+ * Return a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+ * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
+ * workspace names.
+ *
+ * @param parent the parent folder
+ * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
+ * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
+ * @return the new directory configuration; never null
+ * @throws IllegalArgumentException if the parent file is null
+ */
+ public static final DirectoryConfiguration using( File parent,
+ TextEncoder workspaceNameEncoder,
+ TextEncoder indexNameEncoder ) {
+ return new FileSystemDirectoryFromNameFactory(parent, workspaceNameEncoder, indexNameEncoder);
+ }
+
+ /**
+ * Return a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+ * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the allowable
+ * workspace names.
+ *
+ * @param parent the parent folder
+ * @param lockFactory the lock factory; may be null
+ * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
+ * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
+ * @return the new directory configuration; never null
+ * @throws IllegalArgumentException if the parent file is null
+ */
+ public static final DirectoryConfiguration using( File parent,
+ LockFactory lockFactory,
+ TextEncoder workspaceNameEncoder,
+ TextEncoder indexNameEncoder ) {
+ return new FileSystemDirectoryFromNameFactory(parent, lockFactory, workspaceNameEncoder, indexNameEncoder);
+ }
+
+ /**
+ * A {@link DirectoryConfiguration} implementation that creates {@link Directory} instances of the supplied type for each
+ * workspace and pools the results, ensuring that the same {@link Directory} instance is always returned for the same
+ * workspace name.
+ *
+ * @param <DirectoryType> the concrete type of the directory
+ */
+ @ThreadSafe
+ protected static abstract class PoolingDirectoryFactory<DirectoryType extends Directory> implements DirectoryConfiguration {
+ private final ConcurrentHashMap<IndexId, DirectoryType> directories = new ConcurrentHashMap<IndexId, DirectoryType>();
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.DirectoryConfiguration#getDirectory(java.lang.String, java.lang.String)
+ */
+ public Directory getDirectory( String workspaceName,
+ String indexName ) throws SearchEngineException {
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ IndexId id = new IndexId(workspaceName, indexName);
+ DirectoryType result = directories.get(id);
+ if (result == null) {
+ DirectoryType newDirectory = createDirectory(workspaceName, indexName);
+ result = directories.putIfAbsent(id, newDirectory);
+ if (result == null) result = newDirectory;
+ }
+ return result;
+ }
+
+ /**
+ * Method implemented by subclasses to create a new Directory implementation.
+ *
+ * @param workspaceName the name of the workspace for which the {@link Directory} is to be created; never null
+ * @param indexName the name of the index to be created
+ * @return the new directory; may not be null
+ * @throws SearchEngineException if there is a problem creating the directory
+ */
+ protected abstract DirectoryType createDirectory( String workspaceName,
+ String indexName ) throws SearchEngineException;
+ }
+
+ /**
+ * A {@link DirectoryConfiguration} implementation that creates {@link RAMDirectory} instances for each workspace and index
+ * name. Each factory instance maintains a pool of {@link RAMDirectory} instances, ensuring that the same {@link RAMDirectory}
+ * is always returned for the same workspace name.
+ */
+ @ThreadSafe
+ public static class RamDirectoryFactory extends PoolingDirectoryFactory<RAMDirectory> {
+ protected RamDirectoryFactory() {
+ }
+
+ @Override
+ protected RAMDirectory createDirectory( String workspaceName,
+ String indexName ) {
+ return new RAMDirectory();
+ }
+ }
+
+ /**
+ * A {@link DirectoryConfiguration} implementation that creates {@link FSDirectory} instances for each workspace and index
+ * name. This factory is created with a parent directory under which all workspace and index directories are created.
+ * <p>
+ * This uses the supplied encoders to translate the workspace and index names into valid directory names. By default, no
+ * encoding is performed, meaning that the workspace and index names are used explicitly as directory names. This default
+ * behavior, then, means that not all values of workspace names or index names will work. If you want to be sure that all
+ * workspace names work, supply an encoder for the workspace names. (Index names are currently such that they will always be
+ * valid directory names, but you can always supply an encoder if you'd like.)
+ * </p>
+ */
+ public static class FileSystemDirectoryFromNameFactory extends PoolingDirectoryFactory<FSDirectory> {
+ private final File parentFile;
+ private final LockFactory lockFactory;
+ private final TextEncoder workspaceNameEncoder;
+ private final TextEncoder indexNameEncoder;
+
+ /**
+ * Create a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+ * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
+ * allowable workspace names.
+ *
+ * @param parent the parent folder
+ * @throws IllegalArgumentException if the parent file is null
+ */
+ protected FileSystemDirectoryFromNameFactory( File parent ) {
+ this(parent, null, null, null);
+ }
+
+ /**
+ * Create a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+ * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
+ * allowable workspace names.
+ *
+ * @param parent the parent folder
+ * @param lockFactory the lock factory; may be null
+ * @throws IllegalArgumentException if the parent file is null
+ */
+ protected FileSystemDirectoryFromNameFactory( File parent,
+ LockFactory lockFactory ) {
+ this(parent, lockFactory, null, null);
+ }
+
+ /**
+ * Create a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+ * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
+ * allowable workspace names.
+ *
+ * @param parent the parent folder
+ * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
+ * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
+ * @throws IllegalArgumentException if the parent file is null
+ */
+ protected FileSystemDirectoryFromNameFactory( File parent,
+ TextEncoder workspaceNameEncoder,
+ TextEncoder indexNameEncoder ) {
+ this(parent, null, workspaceNameEncoder, indexNameEncoder);
+ }
+
+ /**
+ * Create a new {@link DirectoryConfiguration} that creates {@link FSDirectory} instances mapped to folders under a parent
+ * folder, where the workspace name is used to create the workspace folder. Note that this has ramifications on the
+ * allowable workspace names.
+ *
+ * @param parent the parent folder
+ * @param lockFactory the lock factory; may be null
+ * @param workspaceNameEncoder the encoder that should be used for encoding the workspace name into a directory name
+ * @param indexNameEncoder the encoder that should be used for encoding the index name into a directory name
+ * @throws IllegalArgumentException if the parent file is null
+ */
+ protected FileSystemDirectoryFromNameFactory( File parent,
+ LockFactory lockFactory,
+ TextEncoder workspaceNameEncoder,
+ TextEncoder indexNameEncoder ) {
+ CheckArg.isNotNull(parent, "parent");
+ this.parentFile = parent;
+ this.lockFactory = lockFactory;
+ this.workspaceNameEncoder = workspaceNameEncoder != null ? workspaceNameEncoder : new NoOpEncoder();
+ this.indexNameEncoder = indexNameEncoder != null ? indexNameEncoder : new NoOpEncoder();
+ }
+
+ @Override
+ protected FSDirectory createDirectory( String workspaceName,
+ String indexName ) {
+ File workspaceFile = new File(parentFile, workspaceNameEncoder.encode(workspaceName));
+ if (!workspaceFile.exists()) {
+ workspaceFile.mkdirs();
+ } else {
+ if (!workspaceFile.isDirectory()) {
+ I18n msg = SearchI18n.locationForIndexesIsNotDirectory;
+ throw new SearchEngineException(msg.text(workspaceFile.getAbsolutePath(), workspaceName));
+ }
+ if (!workspaceFile.canRead()) {
+ I18n msg = SearchI18n.locationForIndexesCannotBeRead;
+ throw new SearchEngineException(msg.text(workspaceFile.getAbsolutePath(), workspaceName));
+ }
+ if (!workspaceFile.canWrite()) {
+ I18n msg = SearchI18n.locationForIndexesCannotBeWritten;
+ throw new SearchEngineException(msg.text(workspaceFile.getAbsolutePath(), workspaceName));
+ }
+ }
+ File directory = workspaceFile;
+ if (indexName != null) {
+ File indexFile = new File(workspaceFile, indexNameEncoder.encode(indexName));
+ if (!indexFile.exists()) {
+ indexFile.mkdirs();
+ } else {
+ if (!indexFile.isDirectory()) {
+ I18n msg = SearchI18n.locationForIndexesIsNotDirectory;
+ throw new SearchEngineException(msg.text(indexFile.getAbsolutePath(), workspaceName));
+ }
+ if (!indexFile.canRead()) {
+ I18n msg = SearchI18n.locationForIndexesCannotBeRead;
+ throw new SearchEngineException(msg.text(indexFile.getAbsolutePath(), workspaceName));
+ }
+ if (!indexFile.canWrite()) {
+ I18n msg = SearchI18n.locationForIndexesCannotBeWritten;
+ throw new SearchEngineException(msg.text(indexFile.getAbsolutePath(), workspaceName));
+ }
+ }
+ directory = indexFile;
+ }
+ try {
+ return create(directory, lockFactory);
+ } catch (IOException e) {
+ throw new SearchEngineException(e);
+ }
+ }
+
+ /**
+ * Override this method to define which subclass of {@link FSDirectory} should be created.
+ *
+ * @param directory the file system directory; never null
+ * @param lockFactory the lock factory; may be null
+ * @return the {@link FSDirectory} instance
+ * @throws IOException if there is a problem creating the FSDirectory instance
+ */
+ protected FSDirectory create( File directory,
+ LockFactory lockFactory ) throws IOException {
+ return FSDirectory.getDirectory(directory, lockFactory);
+ }
+ }
+
+ @Immutable
+ protected static final class IndexId {
+ private final String workspaceName;
+ private final String indexName;
+ private final int hc;
+
+ protected IndexId( String workspaceName,
+ String indexName ) {
+ assert workspaceName != null;
+ this.workspaceName = workspaceName;
+ this.indexName = indexName;
+ this.hc = HashCode.compute(this.workspaceName, this.indexName);
+ }
+
+ /**
+ * @return indexName
+ */
+ public String getIndexName() {
+ return indexName;
+ }
+
+ /**
+ * @return workspaceName
+ */
+ public String getWorkspaceName() {
+ return workspaceName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof IndexId) {
+ IndexId that = (IndexId)obj;
+ if (this.hashCode() != that.hashCode()) return false;
+ if (!this.workspaceName.equals(that.workspaceName)) return false;
+ if (!this.indexName.equals(that.indexName)) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return indexName != null ? workspaceName + "/" + this.indexName : this.workspaceName;
+ }
+ }
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/DirectoryConfigurations.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/EncodingNamespaceRegistry.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/EncodingNamespaceRegistry.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/EncodingNamespaceRegistry.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,233 @@
+/*
+ * 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.search;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import net.jcip.annotations.GuardedBy;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.JcrMixLexicon;
+import org.jboss.dna.graph.JcrNtLexicon;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.basic.BasicNamespace;
+
+/**
+ * A {@link NamespaceRegistry} implementation that uses encoded representations of the namespace URIs for the namespace prefixes.
+ */
+@ThreadSafe
+class EncodingNamespaceRegistry implements NamespaceRegistry {
+
+ public static final Set<String> DEFAULT_FIXED_NAMESPACES = Collections.unmodifiableSet(new HashSet<String>(
+ Arrays.asList(new String[] {
+ "",
+ DnaLexicon.Namespace.URI,
+ JcrLexicon.Namespace.URI,
+ JcrNtLexicon.Namespace.URI,
+ JcrMixLexicon.Namespace.URI})));
+
+ private final NamespaceRegistry registry;
+ private final TextEncoder encoder;
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ @GuardedBy( "lock" )
+ private final Map<String, String> uriToEncodedPrefix = new HashMap<String, String>();
+ @GuardedBy( "lock" )
+ private final Map<String, String> encodedPrefixToUri = new HashMap<String, String>();
+ private final Set<String> fixedNamespaceUris;
+
+ /**
+ * @param registry the original registry
+ * @param encoder the encoder; may be null if no encoding should be used
+ */
+ EncodingNamespaceRegistry( NamespaceRegistry registry,
+ TextEncoder encoder ) {
+ this(registry, encoder, null);
+ }
+
+ /**
+ * @param registry the original registry
+ * @param encoder the encoder; may be null if no encoding should be used
+ * @param fixedUris the set of URIs that is to be fixed and not encoded; or null if the default namespaces are to be fixed
+ */
+ EncodingNamespaceRegistry( NamespaceRegistry registry,
+ TextEncoder encoder,
+ Set<String> fixedUris ) {
+ this.registry = registry;
+ this.encoder = encoder != null ? encoder : new NoOpEncoder();
+ this.fixedNamespaceUris = fixedUris != null ? Collections.unmodifiableSet(new HashSet<String>(fixedUris)) : DEFAULT_FIXED_NAMESPACES;
+ assert this.registry != null;
+ assert this.encoder != null;
+ assert this.fixedNamespaceUris != null;
+ }
+
+ /**
+ * @return fixedNamespaceUris
+ */
+ public Set<String> getFixedNamespaceUris() {
+ return fixedNamespaceUris;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.property.NamespaceRegistry#getDefaultNamespaceUri()
+ */
+ public String getDefaultNamespaceUri() {
+ return this.registry.getDefaultNamespaceUri();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.property.NamespaceRegistry#getNamespaceForPrefix(java.lang.String)
+ */
+ public String getNamespaceForPrefix( String prefix ) {
+ // First look in the map ...
+ String result = null;
+ try {
+ lock.readLock().lock();
+ result = encodedPrefixToUri.get(prefix);
+ if (result != null) return result;
+ } finally {
+ lock.readLock().unlock();
+ }
+
+ // Make sure we have encoded all the namespaces in the registry ...
+ Set<Namespace> namespaces = new HashSet<Namespace>(this.registry.getNamespaces());
+ Set<Namespace> encodedNamespaces = this.getNamespaces();
+ namespaces.removeAll(encodedNamespaces);
+ try {
+ lock.writeLock().lock();
+ for (Namespace namespace : namespaces) {
+ String namespaceUri = namespace.getNamespaceUri();
+ String encoded = fixedNamespaceUris.contains(namespaceUri) ? namespace.getPrefix() : encoder.encode(namespaceUri);
+ uriToEncodedPrefix.put(namespaceUri, encoded);
+ encodedPrefixToUri.put(encoded, namespaceUri);
+ if (result == null && encoded.equals(prefix)) result = namespaceUri;
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ if (result != null) return result;
+
+ // There's nothing, so just delegate to the registry ...
+ return this.registry.getNamespaceForPrefix(prefix);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.property.NamespaceRegistry#getRegisteredNamespaceUris()
+ */
+ public Set<String> getRegisteredNamespaceUris() {
+ return this.registry.getRegisteredNamespaceUris();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.property.NamespaceRegistry#isRegisteredNamespaceUri(java.lang.String)
+ */
+ public boolean isRegisteredNamespaceUri( String namespaceUri ) {
+ return this.registry.isRegisteredNamespaceUri(namespaceUri);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.property.NamespaceRegistry#getPrefixForNamespaceUri(java.lang.String, boolean)
+ */
+ public String getPrefixForNamespaceUri( String namespaceUri,
+ boolean generateIfMissing ) {
+ if (fixedNamespaceUris.contains(namespaceUri)) {
+ return this.registry.getPrefixForNamespaceUri(namespaceUri, generateIfMissing);
+ }
+ String encoded = null;
+ try {
+ lock.readLock().lock();
+ encoded = uriToEncodedPrefix.get(namespaceUri);
+ } finally {
+ lock.readLock().unlock();
+ }
+ if (encoded == null) {
+ encoded = encoder.encode(namespaceUri);
+ try {
+ lock.writeLock().lock();
+ uriToEncodedPrefix.put(namespaceUri, encoded);
+ encodedPrefixToUri.put(encoded, namespaceUri);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+ return encoded;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.property.NamespaceRegistry#getNamespaces()
+ */
+ public Set<Namespace> getNamespaces() {
+ Set<Namespace> results = new HashSet<Namespace>();
+ try {
+ lock.readLock().lock();
+ for (Map.Entry<String, String> entry : uriToEncodedPrefix.entrySet()) {
+ String uri = entry.getKey();
+ String prefix = entry.getValue();
+ results.add(new BasicNamespace(prefix, uri));
+ }
+ } finally {
+ lock.readLock().unlock();
+ }
+ return results;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.property.NamespaceRegistry#register(java.lang.String, java.lang.String)
+ */
+ public String register( String prefix,
+ String namespaceUri ) {
+ return this.registry.register(prefix, namespaceUri);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.property.NamespaceRegistry#unregister(java.lang.String)
+ */
+ public boolean unregister( String namespaceUri ) {
+ return this.registry.unregister(namespaceUri);
+ }
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/EncodingNamespaceRegistry.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,278 @@
+/*
+ * 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.search;
+
+import java.io.IOException;
+import net.jcip.annotations.NotThreadSafe;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriter.MaxFieldLength;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.store.Directory;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.DateTimeFactory;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFactory;
+
+/**
+ * A set of index readers and writers.
+ */
+@NotThreadSafe
+final class IndexContext {
+
+ private final ExecutionContext context;
+ private final Directory pathsIndexDirectory;
+ private final Directory contentIndexDirectory;
+ private final Analyzer analyzer;
+ private final boolean overwrite;
+ private final ValueFactory<String> stringFactory;
+ private final DateTimeFactory dateFactory;
+ private IndexReader pathsReader;
+ private IndexWriter pathsWriter;
+ private IndexSearcher pathsSearcher;
+ private IndexReader contentReader;
+ private IndexWriter contentWriter;
+ private IndexSearcher contentSearcher;
+
+ IndexContext( ExecutionContext context,
+ Directory pathsIndexDirectory,
+ Directory contentIndexDirectory,
+ Analyzer analyzer,
+ boolean overwrite ) {
+ assert context != null;
+ assert pathsIndexDirectory != null;
+ assert contentIndexDirectory != null;
+ this.context = context;
+ this.pathsIndexDirectory = pathsIndexDirectory;
+ this.contentIndexDirectory = contentIndexDirectory;
+ this.analyzer = analyzer;
+ this.overwrite = overwrite;
+ this.stringFactory = context.getValueFactories().getStringFactory();
+ this.dateFactory = context.getValueFactories().getDateFactory();
+ }
+
+ /**
+ * @return context
+ */
+ public ExecutionContext context() {
+ return context;
+ }
+
+ /**
+ * @return stringFactory
+ */
+ public ValueFactory<String> stringFactory() {
+ return stringFactory;
+ }
+
+ public DateTimeFactory dateFactory() {
+ return dateFactory;
+ }
+
+ public PathFactory pathFactory() {
+ return context.getValueFactories().getPathFactory();
+ }
+
+ public IndexReader getPathsReader() throws IOException {
+ if (pathsReader == null) {
+ pathsReader = IndexReader.open(pathsIndexDirectory);
+ }
+ return pathsReader;
+ }
+
+ public IndexReader getContentReader() throws IOException {
+ if (contentReader == null) {
+ contentReader = IndexReader.open(contentIndexDirectory);
+ }
+ return contentReader;
+ }
+
+ public IndexWriter getPathsWriter() throws IOException {
+ if (pathsWriter == null) {
+ pathsWriter = new IndexWriter(pathsIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+ }
+ return pathsWriter;
+ }
+
+ public IndexWriter getContentWriter() throws IOException {
+ if (contentWriter == null) {
+ contentWriter = new IndexWriter(contentIndexDirectory, analyzer, overwrite, MaxFieldLength.UNLIMITED);
+ }
+ return contentWriter;
+ }
+
+ public IndexSearcher getPathsSearcher() throws IOException {
+ if (pathsSearcher == null) {
+ pathsSearcher = new IndexSearcher(getPathsReader());
+ }
+ return pathsSearcher;
+ }
+
+ public IndexSearcher getContentSearcher() throws IOException {
+ if (contentSearcher == null) {
+ contentSearcher = new IndexSearcher(getContentReader());
+ }
+ return contentSearcher;
+ }
+
+ public boolean hasWriters() {
+ return pathsWriter != null || contentWriter != null;
+ }
+
+ public void commit() throws IOException {
+ IOException ioError = null;
+ RuntimeException runtimeError = null;
+ if (pathsReader != null) {
+ try {
+ pathsReader.close();
+ } catch (IOException e) {
+ ioError = e;
+ } catch (RuntimeException e) {
+ runtimeError = e;
+ } finally {
+ pathsReader = null;
+ }
+ }
+ if (contentReader != null) {
+ try {
+ contentReader.close();
+ } catch (IOException e) {
+ if (ioError == null) ioError = e;
+ } catch (RuntimeException e) {
+ if (runtimeError == null) runtimeError = e;
+ } finally {
+ contentReader = null;
+ }
+ }
+ if (pathsWriter != null) {
+ try {
+ pathsWriter.commit();
+ } catch (IOException e) {
+ ioError = e;
+ } catch (RuntimeException e) {
+ runtimeError = e;
+ } finally {
+ try {
+ pathsWriter.close();
+ } catch (IOException e) {
+ ioError = e;
+ } catch (RuntimeException e) {
+ runtimeError = e;
+ } finally {
+ pathsWriter = null;
+ }
+ }
+ }
+ if (contentWriter != null) {
+ try {
+ contentWriter.commit();
+ } catch (IOException e) {
+ if (ioError == null) ioError = e;
+ } catch (RuntimeException e) {
+ if (runtimeError == null) runtimeError = e;
+ } finally {
+ try {
+ contentWriter.close();
+ } catch (IOException e) {
+ ioError = e;
+ } catch (RuntimeException e) {
+ runtimeError = e;
+ } finally {
+ contentWriter = null;
+ }
+ }
+ }
+ if (ioError != null) throw ioError;
+ if (runtimeError != null) throw runtimeError;
+ }
+
+ public void rollback() throws IOException {
+ IOException ioError = null;
+ RuntimeException runtimeError = null;
+ if (pathsReader != null) {
+ try {
+ pathsReader.close();
+ } catch (IOException e) {
+ ioError = e;
+ } catch (RuntimeException e) {
+ runtimeError = e;
+ } finally {
+ pathsReader = null;
+ }
+ }
+ if (contentReader != null) {
+ try {
+ contentReader.close();
+ } catch (IOException e) {
+ if (ioError == null) ioError = e;
+ } catch (RuntimeException e) {
+ if (runtimeError == null) runtimeError = e;
+ } finally {
+ contentReader = null;
+ }
+ }
+ if (pathsWriter != null) {
+ try {
+ pathsWriter.rollback();
+ } catch (IOException e) {
+ ioError = e;
+ } catch (RuntimeException e) {
+ runtimeError = e;
+ } finally {
+ try {
+ pathsWriter.close();
+ } catch (IOException e) {
+ ioError = e;
+ } catch (RuntimeException e) {
+ runtimeError = e;
+ } finally {
+ pathsWriter = null;
+ }
+ }
+ }
+ if (contentWriter != null) {
+ try {
+ contentWriter.rollback();
+ } catch (IOException e) {
+ if (ioError == null) ioError = e;
+ } catch (RuntimeException e) {
+ if (runtimeError == null) runtimeError = e;
+ } finally {
+ try {
+ contentWriter.close();
+ } catch (IOException e) {
+ ioError = e;
+ } catch (RuntimeException e) {
+ runtimeError = e;
+ } finally {
+ contentWriter = null;
+ }
+ }
+ }
+ if (ioError != null) throw ioError;
+ if (runtimeError != null) throw runtimeError;
+ }
+
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,628 @@
+/*
+ * 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.search;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.NotThreadSafe;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * The set of rules that dictate how properties should be indexed.
+ */
+@Immutable
+public class IndexingRules {
+
+ public static final int INDEX = 2 << 0;
+ public static final int ANALYZE = 2 << 1;
+ public static final int STORE = 2 << 2;
+ public static final int STORE_COMPRESSED = 2 << 3;
+ public static final int ANALYZED_WITHOUT_NORMS = 2 << 4;
+ public static final int FULL_TEXT = 2 << 5;
+ public static final int TREAT_AS_DATE = 2 << 6;
+
+ /**
+ * A single rule that dictates how a single property should be indexed.
+ *
+ * @see IndexingRules#getRule(Name)
+ */
+ @Immutable
+ public static interface Rule {
+ boolean isIncluded();
+
+ boolean isSkipped();
+
+ boolean isAnalyzed();
+
+ boolean isAnalyzedWithoutNorms();
+
+ boolean isStored();
+
+ boolean isStoredCompressed();
+
+ boolean isFullText();
+
+ boolean isDate();
+
+ int getMask();
+
+ Field.Store getStoreOption();
+
+ Field.Index getIndexOption();
+ }
+
+ public static final Rule SKIP = new SkipRule();
+
+ @Immutable
+ protected static class SkipRule implements Rule {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#getMask()
+ */
+ public int getMask() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzed()
+ */
+ public boolean isAnalyzed() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzedWithoutNorms()
+ */
+ public boolean isAnalyzedWithoutNorms() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isFullText()
+ */
+ public boolean isFullText() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isIncluded()
+ */
+ public boolean isIncluded() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isSkipped()
+ */
+ public boolean isSkipped() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isStored()
+ */
+ public boolean isStored() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isStoredCompressed()
+ */
+ public boolean isStoredCompressed() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isDate()
+ */
+ public boolean isDate() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#getIndexOption()
+ */
+ public Index getIndexOption() {
+ return Field.Index.NO;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#getStoreOption()
+ */
+ public Store getStoreOption() {
+ return Field.Store.NO;
+ }
+ }
+
+ @Immutable
+ public static final class GeneralRule implements Rule {
+ private final int value;
+ private final Field.Store store;
+ private final Field.Index index;
+
+ protected GeneralRule( int value ) {
+ this.value = value;
+ this.index = isAnalyzed() ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
+ this.store = isStored() ? Field.Store.YES : Field.Store.NO;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#getMask()
+ */
+ public int getMask() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzed()
+ */
+ public boolean isAnalyzed() {
+ return (value & ANALYZE) == ANALYZE;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzedWithoutNorms()
+ */
+ public boolean isAnalyzedWithoutNorms() {
+ return (value & ANALYZED_WITHOUT_NORMS) == ANALYZED_WITHOUT_NORMS;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isFullText()
+ */
+ public boolean isFullText() {
+ return (value & FULL_TEXT) == FULL_TEXT;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isIncluded()
+ */
+ public boolean isIncluded() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isSkipped()
+ */
+ public boolean isSkipped() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isStored()
+ */
+ public boolean isStored() {
+ return (value & STORE) == STORE;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isStoredCompressed()
+ */
+ public boolean isStoredCompressed() {
+ return (value & STORE_COMPRESSED) == STORE_COMPRESSED;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#isDate()
+ */
+ public boolean isDate() {
+ return (value & TREAT_AS_DATE) == TREAT_AS_DATE;
+ }
+
+ protected Rule with( int options ) {
+ return createRule(value | options);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#getIndexOption()
+ */
+ public Index getIndexOption() {
+ return index;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingRules.Rule#getStoreOption()
+ */
+ public Store getStoreOption() {
+ return store;
+ }
+ }
+
+ private static final ConcurrentHashMap<Integer, Rule> CACHE = new ConcurrentHashMap<Integer, Rule>();
+
+ protected static Rule createRule( int value ) {
+ if (value <= 0) {
+ return SKIP;
+ }
+ Integer key = new Integer(value);
+ Rule rule = CACHE.get(key);
+ if (rule == null) {
+ Rule newRule = new GeneralRule(value);
+ rule = CACHE.putIfAbsent(value, newRule);
+ if (rule == null) rule = newRule;
+ }
+ return rule;
+ }
+
+ private final Map<Name, Rule> rulesByName;
+ private final Rule defaultRule;
+
+ protected IndexingRules( Map<Name, Rule> rulesByName,
+ Rule defaultRule ) {
+ this.rulesByName = rulesByName;
+ this.defaultRule = defaultRule != null ? defaultRule : SKIP;
+ assert this.defaultRule != null;
+ }
+
+ /**
+ * Get the rule associated with the given property name.
+ *
+ * @param name the property name, or null if the default rule is to be returned
+ * @return the rule; never null
+ */
+ public Rule getRule( Name name ) {
+ Rule result = rulesByName.get(name);
+ return result != null ? result : this.defaultRule;
+ }
+
+ /**
+ * Return a new builder that can be used to create {@link IndexingRules} objects.
+ *
+ * @return a builder; never null
+ */
+ public static Builder createBuilder() {
+ return new Builder(new HashMap<Name, Rule>());
+ }
+
+ /**
+ * Return a new builder that can be used to create {@link IndexingRules} objects.
+ *
+ * @param initialRules the rules that the builder should start with
+ * @return a builder; never null
+ * @throws IllegalArgumentException if the initial rules reference is null
+ */
+ public static Builder createBuilder( IndexingRules initialRules ) {
+ CheckArg.isNotNull(initialRules, "initialRules");
+ return new Builder(initialRules.rulesByName).defaultTo(initialRules.defaultRule);
+ }
+
+ /**
+ * A builder of immutable {@link IndexingRules} objects.
+ */
+ @NotThreadSafe
+ public static class Builder {
+ private final Map<Name, Rule> rulesByName;
+ private Rule defaultRule;
+
+ Builder( Map<Name, Rule> rulesByName ) {
+ assert rulesByName != null;
+ this.rulesByName = rulesByName;
+ }
+
+ /**
+ * Set the default rules.
+ *
+ * @param rule the default rule to use
+ * @return this builder for convenience and method chaining; never null
+ * @throws IllegalArgumentException if the rule mask is negative
+ */
+ public Builder defaultTo( Rule rule ) {
+ CheckArg.isNotNull(rule, "rule");
+ defaultRule = rule;
+ return this;
+ }
+
+ /**
+ * Set the default rules.
+ *
+ * @param ruleMask the bitmask of rule to use
+ * @return this builder for convenience and method chaining; never null
+ * @throws IllegalArgumentException if the rule mask is negative
+ */
+ public Builder defaultTo( int ruleMask ) {
+ CheckArg.isNonNegative(ruleMask, "options");
+ if (ruleMask == 0) {
+ defaultRule = SKIP;
+ } else {
+ // Make sure the index flag is set ...
+ ruleMask |= INDEX;
+ defaultRule = createRule(ruleMask);
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be skipped from indexing.
+ *
+ * @param namesToIndex the names of the properties that are to be skipped
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder skip( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ rulesByName.put(name, SKIP);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Set the properties with the supplied names to use the supplied rules.
+ *
+ * @param ruleMask the bitmask of rules to use
+ * @param namesToIndex the names of the properties that are to be skipped
+ * @return this builder for convenience and method chaining; never null
+ * @throws IllegalArgumentException if the rule mask is negative
+ */
+ public Builder set( int ruleMask,
+ Name... namesToIndex ) {
+ CheckArg.isNonNegative(ruleMask, "options");
+ if (namesToIndex != null) {
+ if (ruleMask > 0) {
+ skip(namesToIndex);
+ } else {
+ // Make sure the index flag is set ...
+ ruleMask |= INDEX;
+ Rule rule = createRule(ruleMask);
+ for (Name name : namesToIndex) {
+ rulesByName.put(name, rule);
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to use the supplied rule mask. This does not remove any other rules for
+ * these properties.
+ *
+ * @param ruleMask the bitmask of rules to add
+ * @param namesToIndex the names of the properties that are to be skipped
+ * @return this builder for convenience and method chaining; never null
+ * @throws IllegalArgumentException if the rule mask is negative
+ */
+ public Builder add( int ruleMask,
+ Name... namesToIndex ) {
+ CheckArg.isNonNegative(ruleMask, "options");
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, ruleMask);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be indexed. This does not remove any other rules for these properties.
+ *
+ * @param namesToIndex the names of the properties that are to be indexed
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder index( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, INDEX);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be analyzed (and obviously indexed). This does not remove any other
+ * rules for these properties.
+ *
+ * @param namesToIndex the names of the properties that are to be analyzed
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder analyze( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, ANALYZE | INDEX);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be stored (and obviously indexed). This does not remove any other rules
+ * for these properties.
+ *
+ * @param namesToIndex the names of the properties that are to be stored
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder store( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, STORE | INDEX);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be included in full-text searches (and obviously indexed). This does not
+ * remove any other rules for these properties.
+ *
+ * @param namesToIndex the names of the properties that are to be included in full-text searches
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder fullText( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, FULL_TEXT | INDEX);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be treated as dates (and obviously indexed). This does not remove any
+ * other rules for these properties.
+ *
+ * @param namesToIndex the names of the properties that are to be included in full-text searches
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder treatAsDates( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, TREAT_AS_DATE | INDEX);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be indexed, analyzed and stored. This does not remove any other rules
+ * for these properties.
+ *
+ * @param namesToIndex the names of the properties that are to be indexed, analyzed and stored
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder analyzeAndStore( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, INDEX | ANALYZE | STORE);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be indexed, analyzed, stored and included in full-text searches. This
+ * does not remove any other rules for these properties.
+ *
+ * @param namesToIndex the names of the properties that are to be indexed, analyzed, stored and included in full-text
+ * searches
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder analyzeAndStoreAndFullText( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, INDEX | ANALYZE | STORE | FULL_TEXT);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be indexed, analyzed and included in full-text searches. This does not
+ * remove any other rules for these properties.
+ *
+ * @param namesToIndex the names of the properties that are to be indexed, analyzed and included in full-text searches
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder analyzeAndFullText( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, INDEX | ANALYZE | FULL_TEXT);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Mark the properties with the supplied names to be indexed, stored and included in full-text searches. This does not
+ * remove any other rules for these properties.
+ *
+ * @param namesToIndex the names of the properties that are to be indexed, stored and included in full-text searches
+ * @return this builder for convenience and method chaining; never null
+ */
+ public Builder storeAndFullText( Name... namesToIndex ) {
+ if (namesToIndex != null) {
+ for (Name name : namesToIndex) {
+ add(name, INDEX | STORE | FULL_TEXT);
+ }
+ }
+ return this;
+ }
+
+ protected void add( Name name,
+ int option ) {
+ Rule rule = rulesByName.get(name);
+ if (rule != null) {
+ option |= rule.getMask();
+ }
+ rulesByName.put(name, createRule(option));
+ }
+
+ /**
+ * Build the indexing rules.
+ *
+ * @return the immutable indexing rules.
+ */
+ public IndexingRules build() {
+ return new IndexingRules(Collections.unmodifiableMap(new HashMap<Name, Rule>(rulesByName)), defaultRule);
+ }
+ }
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,121 @@
+/*
+ * 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.search;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.queryParser.ParseException;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.request.ChangeRequest;
+
+/**
+ * Interface defining the behaviors associated with indexing graph content.
+ */
+interface IndexingStrategy {
+
+ int getChangeCountForAutomaticOptimization();
+
+ TextEncoder getNamespaceEncoder();
+
+ /**
+ * Index the node given the index writers. Note that implementors should simply just use the writers to add documents to the
+ * index(es), and should never call any of the writer lifecycle methods (e.g., {@link IndexWriter#commit()},
+ * {@link IndexWriter#rollback()}, etc.).
+ *
+ * @param node the node to be indexed; never null
+ * @param indexes the set of index readers and writers; never null
+ * @throws IOException if there is a problem indexing or using the writers
+ */
+ void index( Node node,
+ IndexContext indexes ) throws IOException;
+
+ /**
+ * Update the indexes to reflect the supplied changes to the graph content. Note that implementors should simply just use the
+ * writers to add documents to the index(es), and should never call any of the writer lifecycle methods (e.g.,
+ * {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
+ *
+ * @param changes the set of changes to the content
+ * @param indexes the set of index readers and writers; never null
+ * @return the (approximate) number of nodes that were affected by the changes
+ * @throws IOException if there is a problem indexing or using the writers
+ */
+ int apply( Iterable<ChangeRequest> changes,
+ IndexContext indexes ) throws IOException;
+
+ /**
+ * Remove from the index(es) all of the information pertaining to the nodes at or below the supplied path. Note that
+ * implementors should simply just use the writers to add documents to the index(es), and should never call any of the writer
+ * lifecycle methods (e.g., {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
+ *
+ * @param path the path identifying the graph content that is to be removed; never null
+ * @param indexes the set of index readers and writers; never null
+ * @return the (approximate) number of nodes that were affected by the changes
+ * @throws IOException if there is a problem indexing or using the writers
+ */
+ int deleteBelow( Path path,
+ IndexContext indexes ) throws IOException;
+
+ /**
+ * Create the analyzer that is used for reading and updating the indexes.
+ *
+ * @return the analyzer; may not be null
+ */
+ Analyzer createAnalyzer();
+
+ /**
+ * Perform a full-text search given the supplied query.
+ *
+ * @param fullTextString the full-text query; never null or blank
+ * @param maxResults the maximum number of results that are to be returned; always positive
+ * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+ * @param indexes the set of index readers and writers; never null
+ * @param results the list where the results should be accumulated; never null
+ * @throws IOException if there is a problem indexing or using the writers
+ * @throws ParseException if there is a problem parsing the query
+ */
+ void performQuery( String fullTextString,
+ int maxResults,
+ int offset,
+ IndexContext indexes,
+ List<Location> results ) throws IOException, ParseException;
+
+ /**
+ * Perform a query of the content.
+ *
+ * @param query the query; never null
+ * @param indexes the set of index readers and writers; never null
+ * @return the results of the query
+ * @throws IOException if there is a problem indexing or using the writers
+ * @throws ParseException if there is a problem parsing the query
+ */
+ QueryResults performQuery( QueryCommand query,
+ IndexContext indexes ) throws IOException, ParseException;
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,57 @@
+/*
+ * 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.search;
+
+import java.util.List;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+
+/**
+ * A {@link ProcessingComponent} implementation that is used by the {@link LuceneQueryEngine.LuceneProcessor} to perform atomic
+ * queries against the Lucene indexes.
+ */
+class LuceneQueryComponent extends ProcessingComponent {
+
+ private final PlanNode accessNode;
+
+ LuceneQueryComponent( QueryContext context,
+ Columns columns,
+ PlanNode accessNode ) {
+ super(context, columns);
+ this.accessNode = accessNode;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ if (accessNode != null) return null;
+ return null;
+ }
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,112 @@
+/*
+ * 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.search;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import org.apache.lucene.queryParser.ParseException;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryEngine;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.QueryCommand;
+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.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.QueryProcessor;
+import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
+import org.jboss.dna.graph.query.validate.Schemata;
+
+/**
+ *
+ */
+class LuceneQueryEngine {
+
+ private QueryEngine engine;
+
+ public LuceneQueryEngine( Schemata schemata ) {
+ engine = new QueryEngine(new CanonicalPlanner(), new LuceneOptimizer(), new LuceneProcessor(), schemata);
+ }
+
+ /**
+ * 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
+ * @throws IOException if there is a problem indexing or using the writers
+ * @throws ParseException if there is a problem parsing the query
+ */
+ public QueryResults execute( QueryCommand query,
+ IndexContext indexes ) throws IOException, ParseException {
+ return engine.execute(indexes.context(), query, new PlanHints());
+ }
+
+ /**
+ * An {@link Optimizer} implementation that specializes the {@link RuleBasedOptimizer} by using custom rules.
+ */
+ protected static class LuceneOptimizer extends RuleBasedOptimizer {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
+ * org.jboss.dna.graph.query.plan.PlanHints)
+ */
+ @Override
+ protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
+ PlanHints hints ) {
+ super.populateRuleStack(ruleStack, hints);
+ // Add any custom rules here, either at the front of the stack or at the end
+ }
+ }
+
+ /**
+ * A query processor that operates against Lucene indexes. All functionality is inherited from the {@link QueryProcessor},
+ * except for the creation of the {@link ProcessingComponent} that does the low-level atomic queries (against the Lucene
+ * indexes).
+ */
+ protected static class LuceneProcessor extends QueryProcessor {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.QueryContext,
+ * org.jboss.dna.graph.query.plan.PlanNode, org.jboss.dna.graph.query.QueryResults.Columns,
+ * org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+ */
+ @Override
+ protected ProcessingComponent createAccessComponent( QueryContext context,
+ PlanNode accessNode,
+ Columns resultColumns,
+ Analyzer analyzer ) {
+ return new LuceneQueryComponent(context, resultColumns, accessNode);
+ }
+ }
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,305 @@
+/*
+ * 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.search;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import net.jcip.annotations.GuardedBy;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.store.Directory;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.request.ChangeRequest;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+
+/**
+ * A component that acts as a search engine for the content within a single {@link RepositorySource}. This engine manages a set of
+ * indexes and provides search functionality for each of the workspaces within the source, and provides various methods to
+ * (re)index the content contained with source's workspaces and keep the indexes up-to-date via changes.
+ */
+@ThreadSafe
+public class SearchEngine {
+
+ private final ExecutionContext context;
+ private final String sourceName;
+ private final RepositoryConnectionFactory connectionFactory;
+ private final DirectoryConfiguration directoryFactory;
+ private final IndexingStrategy indexingStrategy;
+ private final PathFactory pathFactory;
+ @GuardedBy( "workspaceEngineLock" )
+ private final Map<String, WorkspaceSearchEngine> workspaceEnginesByName;
+ private final ReadWriteLock workspaceEngineLock = new ReentrantReadWriteLock();
+
+ /**
+ * Create a search engine instance given the supplied {@link ExecutionContext execution context}, name of the
+ * {@link RepositorySource}, the {@link RepositoryConnectionFactory factory for RepositorySource connections}, and the
+ * {@link DirectoryConfiguration directory factory} that defines where each workspace's indexes should be placed.
+ *
+ * @param context the execution context in which all indexing operations should be performed
+ * @param sourceName the name of the {@link RepositorySource}
+ * @param connectionFactory the connection factory
+ * @param directoryFactory the factory for Lucene {@link Directory directories}
+ * @param indexingStrategy the indexing strategy that governs how properties are to be indexed; or null if the default
+ * strategy should be used
+ * @throws IllegalArgumentException if any of the parameters (other than indexing strategy) are null
+ */
+ public SearchEngine( ExecutionContext context,
+ String sourceName,
+ RepositoryConnectionFactory connectionFactory,
+ DirectoryConfiguration directoryFactory,
+ IndexingStrategy indexingStrategy ) {
+ CheckArg.isNotNull(context, "context");
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(connectionFactory, "connectionFactory");
+ CheckArg.isNotNull(directoryFactory, "directoryFactory");
+ this.sourceName = sourceName;
+ this.connectionFactory = connectionFactory;
+ this.directoryFactory = directoryFactory;
+ this.context = context;
+ this.pathFactory = context.getValueFactories().getPathFactory();
+ this.workspaceEnginesByName = new HashMap<String, WorkspaceSearchEngine>();
+ this.indexingStrategy = indexingStrategy != null ? indexingStrategy : new StoreLittleIndexingStrategy();
+ }
+
+ /**
+ * Get the name of the RepositorySource that this engine is to use.
+ *
+ * @return the source name; never null
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * Get the context in which all indexing operations execute.
+ *
+ * @return the execution context; never null
+ */
+ public ExecutionContext getContext() {
+ return context;
+ }
+
+ /**
+ * Utility to create a Graph for the source.
+ *
+ * @return the graph instance; never null
+ */
+ final Graph graph() {
+ return Graph.create(sourceName, connectionFactory, context);
+ }
+
+ /**
+ * Get the search engine for the workspace with the supplied name.
+ *
+ * @param workspaceName the name of the workspace
+ * @return the workspace's search engine
+ * @throws InvalidWorkspaceException if the workspace does not exist
+ */
+ protected WorkspaceSearchEngine getWorkspaceEngine( String workspaceName ) {
+ WorkspaceSearchEngine engine = null;
+ try {
+ workspaceEngineLock.readLock().lock();
+ engine = workspaceEnginesByName.get(workspaceName);
+ } finally {
+ workspaceEngineLock.readLock().unlock();
+ }
+
+ if (engine == null) {
+ // Verify the workspace does exist ...
+ if (!graph().getWorkspaces().contains(workspaceName)) {
+ String msg = GraphI18n.workspaceDoesNotExistInRepository.text(workspaceName, getSourceName());
+ throw new InvalidWorkspaceException(msg);
+ }
+ try {
+ workspaceEngineLock.writeLock().lock();
+ // Check whether another thread got in and created the engine while we waited ...
+ engine = workspaceEnginesByName.get(workspaceName);
+ if (engine == null) {
+ // Create the engine and register it ...
+ engine = new WorkspaceSearchEngine(context, directoryFactory, indexingStrategy, sourceName, workspaceName,
+ connectionFactory);
+ workspaceEnginesByName.put(workspaceName, engine);
+ }
+ } finally {
+ workspaceEngineLock.writeLock().unlock();
+ }
+ }
+ return engine;
+ }
+
+ /**
+ * Index all of the content at or below the supplied path in the named workspace within the {@link #getSourceName() source}.
+ *
+ * @param workspaceName the name of the workspace
+ * @param startingPoint the path that represents the content to be indexed
+ * @param depthPerRead the depth of each subgraph read operation
+ * @throws IllegalArgumentException if the workspace name or path are null
+ * @throws RepositorySourceException if there is a problem accessing the content
+ * @throws SearchEngineException if there is a problem updating the indexes
+ * @throws InvalidWorkspaceException if the workspace does not exist
+ */
+ public void indexContent( String workspaceName,
+ Path startingPoint,
+ int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ CheckArg.isNotNull(startingPoint, "startingPoint");
+ getWorkspaceEngine(workspaceName).indexContent(startingPoint, depthPerRead);
+ }
+
+ /**
+ * Index all of the content in the named workspace within the {@link #getSourceName() source}.
+ *
+ * @param workspaceName the name of the workspace
+ * @param depthPerRead the depth of each subgraph read operation
+ * @throws IllegalArgumentException if the workspace name is null
+ * @throws RepositorySourceException if there is a problem accessing the content
+ * @throws SearchEngineException if there is a problem updating the indexes
+ * @throws InvalidWorkspaceException if the workspace does not exist
+ */
+ public void indexContent( String workspaceName,
+ int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ indexContent(workspaceName, pathFactory.createRootPath(), depthPerRead);
+ }
+
+ /**
+ * Index (or re-index) all of the content in all of the workspaces within the source.
+ *
+ * @param depthPerRead the depth of each subgraph read operation
+ * @throws RepositorySourceException if there is a problem accessing the content
+ * @throws SearchEngineException if there is a problem updating the indexes
+ */
+ public void indexContent( int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+ Path rootPath = pathFactory.createRootPath();
+ for (String workspaceName : graph().getWorkspaces()) {
+ getWorkspaceEngine(workspaceName).indexContent(rootPath, depthPerRead);
+ }
+ }
+
+ /**
+ * Update the indexes with the supplied set of changes to the content.
+ *
+ * @param changes the set of changes to the content
+ * @throws IllegalArgumentException if the path is null
+ * @throws RepositorySourceException if there is a problem accessing the content
+ * @throws SearchEngineException if there is a problem updating the indexes
+ */
+ public void indexChanges( final Iterable<ChangeRequest> changes ) throws SearchEngineException {
+ // First break up all the changes into different collections, one collection per workspace ...
+ Map<String, Collection<ChangeRequest>> changesByWorkspace = new HashMap<String, Collection<ChangeRequest>>();
+ for (ChangeRequest request : changes) {
+ String workspaceName = request.changedWorkspace();
+ Collection<ChangeRequest> changesForWorkspace = changesByWorkspace.get(workspaceName);
+ if (changesForWorkspace == null) {
+ changesForWorkspace = new LinkedList<ChangeRequest>();
+ changesByWorkspace.put(workspaceName, changesForWorkspace);
+ }
+ changesForWorkspace.add(request);
+ }
+ // Now update the indexes for each workspace (serially). This minimizes the time that each workspace
+ // locks its indexes for writing.
+ for (Map.Entry<String, Collection<ChangeRequest>> entry : changesByWorkspace.entrySet()) {
+ String workspaceName = entry.getKey();
+ Collection<ChangeRequest> changesForWorkspace = entry.getValue();
+ getWorkspaceEngine(workspaceName).indexChanges(changesForWorkspace);
+ }
+ }
+
+ /**
+ * Invoke the engine's garbage collection on all indexes used by all workspaces in the source. This method reclaims space and
+ * optimizes the index. This should be done on a periodic basis after changes are made to the engine's indexes.
+ *
+ * @throws SearchEngineException if there is a problem during optimization
+ */
+ public void optimize() throws SearchEngineException {
+ for (String workspaceName : graph().getWorkspaces()) {
+ getWorkspaceEngine(workspaceName).optimize();
+ }
+ }
+
+ /**
+ * Invoke the engine's garbage collection for the indexes associated with the specified workspace. This method reclaims space
+ * and optimizes the index. This should be done on a periodic basis after changes are made to the engine's indexes.
+ *
+ * @param workspaceName the name of the workspace
+ * @throws IllegalArgumentException if the workspace name is null
+ * @throws SearchEngineException if there is a problem during optimization
+ * @throws InvalidWorkspaceException if the workspace does not exist
+ */
+ public void optimize( String workspaceName ) throws SearchEngineException {
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ getWorkspaceEngine(workspaceName).optimize();
+ }
+
+ /**
+ * Perform a full-text search of the content in the named workspace, given the maximum number of results and the offset
+ * defining the first result the caller is interested in.
+ *
+ * @param workspaceName the name of the workspace
+ * @param fullTextSearch the full-text search to be performed; may not be null
+ * @param maxResults the maximum number of results that are to be returned; always positive
+ * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+ * @return the activity that will perform the work
+ * @throws IllegalArgumentException if the workspace name is null
+ * @throws SearchEngineException if there is a problem during optimization
+ * @throws InvalidWorkspaceException if the workspace does not exist
+ */
+ public List<Location> fullTextSearch( String workspaceName,
+ String fullTextSearch,
+ int maxResults,
+ int offset ) {
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ return getWorkspaceEngine(workspaceName).fullTextSearch(fullTextSearch, maxResults, offset);
+ }
+
+ /**
+ * Perform a query of the content in the named workspace, given the Abstract Query Model representation of the query.
+ *
+ * @param workspaceName the name of the workspace
+ * @param query the query that is to be executed, in the form of the Abstract Query Model
+ * @return the query results; never null
+ * @throws IllegalArgumentException if the context or query references are null
+ */
+ public QueryResults execute( String workspaceName,
+ QueryCommand query ) {
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ return getWorkspaceEngine(workspaceName).execute(query);
+ }
+
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,67 @@
+/*
+ * 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.search;
+
+/**
+ * An exception that represents a problem within a search engine.
+ */
+public class SearchEngineException extends RuntimeException {
+
+ /**
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ */
+ public SearchEngineException() {
+ }
+
+ /**
+ * @param message
+ */
+ public SearchEngineException( String message ) {
+ super(message);
+
+ }
+
+ /**
+ * @param cause
+ */
+ public SearchEngineException( Throwable cause ) {
+ super(cause);
+
+ }
+
+ /**
+ * @param message
+ * @param cause
+ */
+ public SearchEngineException( String message,
+ Throwable cause ) {
+ super(message, cause);
+
+ }
+
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngineException.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,68 @@
+/*
+ * 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.search;
+
+import java.util.Locale;
+import java.util.Set;
+import org.jboss.dna.common.CommonI18n;
+import org.jboss.dna.common.i18n.I18n;
+
+/**
+ *
+ */
+public class SearchI18n {
+
+ public static I18n locationForIndexesIsNotDirectory;
+ public static I18n locationForIndexesCannotBeRead;
+ public static I18n locationForIndexesCannotBeWritten;
+ public static I18n errorWhileIndexingContentAtPath;
+ public static I18n errorWhileRemovingContentAtPath;
+ public static I18n errorWhileUpdatingContent;
+ public static I18n errorWhileCommittingIndexChanges;
+ public static I18n errorCreatingIndexWriter;
+ public static I18n errorWhileOptimizingIndexes;
+ public static I18n errorWhilePerformingSearch;
+ public static I18n errorWhilePerformingQuery;
+ public static I18n errorWhileInitializingSearchEngine;
+
+ static {
+ try {
+ I18n.initialize(SearchI18n.class);
+ } catch (final Exception err) {
+ System.err.println(err);
+ }
+ }
+
+ public static Set<Locale> getLocalizationProblemLocales() {
+ return I18n.getLocalizationProblemLocales(CommonI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems() {
+ return I18n.getLocalizationProblems(CommonI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems( Locale locale ) {
+ return I18n.getLocalizationProblems(CommonI18n.class, locale);
+ }
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchI18n.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,375 @@
+/*
+ * 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.search;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocCollector;
+import org.apache.lucene.search.TopDocs;
+import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.DateTimeFactory;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.request.ChangeRequest;
+import org.jboss.dna.search.IndexingRules.Rule;
+
+/**
+ * A simple {@link IndexingStrategy} implementation that relies upon very few fields to be stored in the indexes.
+ */
+@ThreadSafe
+class StoreLittleIndexingStrategy implements IndexingStrategy {
+
+ static class PathIndex {
+ public static final String PATH = "path";
+ public static final String UUID = "uuid";
+ }
+
+ static class ContentIndex {
+ public static final String UUID = PathIndex.UUID;
+ public static final String FULL_TEXT = "fts";
+ }
+
+ public static final int SIZE_OF_DELETE_BATCHES = 100;
+
+ private ThreadLocal<DateFormat> dateFormatter = new ThreadLocal<DateFormat>() {
+ @Override
+ protected DateFormat initialValue() {
+ return new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+ }
+ };
+
+ private static final FieldSelector UUID_FIELD_SELECTOR = new FieldSelector() {
+ private static final long serialVersionUID = 1L;
+
+ public FieldSelectorResult accept( String fieldName ) {
+ return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+ }
+ };
+
+ /**
+ * The default set of {@link IndexingRules} used by {@link StoreLittleIndexingStrategy} instances when no rules are provided.
+ */
+ public static final IndexingRules DEFAULT_RULES;
+
+ static {
+ IndexingRules.Builder builder = IndexingRules.createBuilder();
+ // Configure the default behavior ...
+ builder.defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE);
+ // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
+ builder.index(JcrLexicon.UUID, DnaLexicon.UUID);
+ // Configure the properties that we'll treat as dates ...
+ builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
+ DEFAULT_RULES = builder.build();
+ }
+
+ private final IndexingRules rules;
+ private final Logger logger;
+ private final LuceneQueryEngine queryEngine;
+
+ /**
+ * Create a new indexing strategy instance that does not support queries.
+ */
+ public StoreLittleIndexingStrategy() {
+ this(null, null);
+ }
+
+ /**
+ * Create a new indexing strategy instance.
+ *
+ * @param schemata the schemata that defines the structure that can be queried; may be null if queries are not going to be
+ * used
+ * @param rules the indexing rules that govern how properties are to be index, or null if the {@link #DEFAULT_RULES default
+ * rules} are to be used
+ */
+ public StoreLittleIndexingStrategy( Schemata schemata,
+ IndexingRules rules ) {
+ this.rules = rules != null ? rules : DEFAULT_RULES;
+ this.logger = Logger.getLogger(getClass());
+ this.queryEngine = new LuceneQueryEngine(schemata);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingStrategy#getNamespaceEncoder()
+ */
+ public TextEncoder getNamespaceEncoder() {
+ return new NoOpEncoder();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingStrategy#getChangeCountForAutomaticOptimization()
+ */
+ public int getChangeCountForAutomaticOptimization() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingStrategy#createAnalyzer()
+ */
+ public Analyzer createAnalyzer() {
+ return new StandardAnalyzer();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Because this strategy uses multiple indexes, and since there's no correlation between the documents in those indexes, we
+ * need to perform the delete in multiple steps. First, we need to perform a query to find out which nodes exist below a
+ * certain path. Then, we need to delete those nodes from the paths index. Finally, we need to delete the corresponding
+ * documents in the content index that represent those same nodes.
+ * </p>
+ * <p>
+ * Since we don't know how many documents there will be, we perform these steps in batches, where each batch limits the number
+ * of results to a maximum number. We repeat batches as long as we find more results. This approach has the advantage that
+ * we'll never bring in a large number of results, and it allows us to delete the documents from the content node using a
+ * query.
+ * </p>
+ *
+ * @see org.jboss.dna.search.IndexingStrategy#deleteBelow(Path, IndexContext)
+ */
+ public int deleteBelow( Path path,
+ IndexContext indexes ) throws IOException {
+ // Perform a query using the reader to find those nodes at/below the path ...
+ try {
+ IndexReader pathReader = indexes.getPathsReader();
+ IndexSearcher pathSearcher = new IndexSearcher(pathReader);
+ String pathStr = indexes.stringFactory().create(path) + "/";
+ PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
+ int numberDeleted = 0;
+ while (true) {
+ // Execute the query and get the results ...
+ TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
+ int numResultsInBatch = results.scoreDocs.length;
+ // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
+ IndexReader contentReader = indexes.getContentReader();
+ for (ScoreDoc result : results.scoreDocs) {
+ int docId = result.doc;
+ // Find the UUID of the node ...
+ Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
+ String uuid = doc.get(PathIndex.UUID);
+ // Delete the document from the paths index ...
+ pathReader.deleteDocument(docId);
+ // Delete the corresponding document from the content index ...
+ contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
+ }
+ numberDeleted += numResultsInBatch;
+ if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
+ }
+ indexes.commit();
+ return numberDeleted;
+ } catch (FileNotFoundException e) {
+ // There are no index files yet, so nothing to delete ...
+ return 0;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingStrategy#index(Node, IndexContext)
+ */
+ public void index( Node node,
+ IndexContext indexes ) throws IOException {
+ ValueFactory<String> strings = indexes.stringFactory();
+ Location location = node.getLocation();
+ UUID uuid = location.getUuid();
+ if (uuid == null) uuid = UUID.randomUUID();
+ Path path = location.getPath();
+ String pathStr = path.isRoot() ? "/" : strings.create(location.getPath()) + "/";
+ String uuidStr = uuid.toString();
+
+ if (logger.isTraceEnabled()) {
+ logger.trace("indexing {0}", pathStr);
+ }
+
+ // Create a separate document for the path, which makes it easier to handle moves since the path can
+ // be changed without changing any other content fields ...
+ Document doc = new Document();
+ doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+ doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+ indexes.getPathsWriter().addDocument(doc);
+
+ // Create the document for the content (properties) ...
+ doc = new Document();
+ doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+ String stringValue = null;
+ StringBuilder fullTextSearchValue = null;
+ for (Property property : node.getProperties()) {
+ Name name = property.getName();
+ Rule rule = rules.getRule(name);
+ if (rule.isSkipped()) continue;
+ String nameString = strings.create(name);
+ if (rule.isDate()) {
+ DateTimeFactory dateFactory = indexes.dateFactory();
+ for (Object value : property) {
+ if (value == null) continue;
+ DateTime dateValue = dateFactory.create(value);
+ stringValue = dateFormatter.get().format(dateValue.toDate());
+ // Add a separate field for each property value ...
+ doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
+ // Dates are not added to the full-text search field (since this wouldn't make sense)
+ }
+ continue;
+ }
+ for (Object value : property) {
+ if (value == null) continue;
+ if (value instanceof Binary) {
+ // don't include binary values as individual fields but do include them in the full-text search ...
+ // TODO : add to full-text search ...
+ continue;
+ }
+ stringValue = strings.create(value);
+ // Add a separate field for each property value ...
+ doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
+ // And add to the full-text field ...
+ if (rule.isFullText()) {
+ if (fullTextSearchValue == null) {
+ fullTextSearchValue = new StringBuilder();
+ } else {
+ fullTextSearchValue.append(' ');
+ }
+ fullTextSearchValue.append(stringValue);
+ }
+ }
+ }
+ // Add the full-text-search field ...
+ if (fullTextSearchValue != null) {
+ doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO, Field.Index.ANALYZED));
+ }
+ indexes.getContentWriter().addDocument(doc);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingStrategy#performQuery(String, int, int, IndexContext, List)
+ */
+ public void performQuery( String fullTextString,
+ int maxResults,
+ int offset,
+ IndexContext indexes,
+ List<Location> results ) throws IOException, ParseException {
+ assert fullTextString != null;
+ assert fullTextString.length() > 0;
+ assert offset >= 0;
+ assert maxResults > 0;
+ assert indexes != null;
+ assert results != null;
+
+ // Parse the full-text search and search against the 'fts' field ...
+ QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
+ Query query = parser.parse(fullTextString);
+ TopDocCollector collector = new TopDocCollector(maxResults + offset);
+ indexes.getContentSearcher().search(query, collector);
+
+ // Collect the results ...
+ TopDocs docs = collector.topDocs();
+ IndexReader contentReader = indexes.getContentReader();
+ IndexReader pathReader = indexes.getPathsReader();
+ IndexSearcher pathSearcher = indexes.getPathsSearcher();
+ ScoreDoc[] scoreDocs = docs.scoreDocs;
+ int numberOfResults = scoreDocs.length;
+ if (numberOfResults > offset) {
+ // There are enough results to satisfy the offset ...
+ for (int i = offset, num = scoreDocs.length; i != num; ++i) {
+ ScoreDoc result = scoreDocs[i];
+ int docId = result.doc;
+ // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
+ Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
+ String uuid = doc.get(ContentIndex.UUID);
+ // Find the path for this node (is there a better way to do this than one search per UUID?) ...
+ TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
+ if (pathDocs.scoreDocs.length < 1) {
+ // No path record found ...
+ continue;
+ }
+ Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
+ Path path = indexes.pathFactory().create(pathDoc.get(PathIndex.PATH));
+ // Now add the location ...
+ results.add(Location.create(path, UUID.fromString(uuid)));
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingStrategy#performQuery(QueryCommand, IndexContext)
+ */
+ public QueryResults performQuery( QueryCommand query,
+ IndexContext indexes ) throws IOException, ParseException {
+ return this.queryEngine.execute(query, indexes);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.search.IndexingStrategy#apply(Iterable, IndexContext)
+ */
+ public int apply( Iterable<ChangeRequest> changes,
+ IndexContext indexes ) /*throws IOException*/{
+ for (ChangeRequest change : changes) {
+ if (change != null) continue;
+ }
+ return 0;
+ }
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,548 @@
+/*
+ * 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.search;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriter.MaxFieldLength;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.store.Directory;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.SubgraphNode;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.request.ChangeRequest;
+
+/**
+ * A search engine dedicated to a single workspace.
+ */
+@ThreadSafe
+public class WorkspaceSearchEngine {
+
+ protected static final String PATHS_INDEX_NAME = "paths";
+ protected static final String CONTENT_INDEX_NAME = "content";
+
+ private final Directory pathsDirectory;
+ private final Directory contentDirectory;
+ private final ExecutionContext context;
+ private final ExecutionContext encodedContext;
+ private final String sourceName;
+ private final String workspaceName;
+ private final RepositoryConnectionFactory connectionFactory;
+ private final IndexingStrategy indexingStrategy;
+ protected final AtomicInteger modifiedNodesSinceLastOptimize = new AtomicInteger(0);
+
+ /**
+ * Create a search engine instance given the supplied {@link ExecutionContext execution context}, name of the
+ * {@link RepositorySource}, the {@link RepositoryConnectionFactory factory for RepositorySource connections}, and the
+ * {@link DirectoryConfiguration directory factory} that defines where each workspace's indexes should be placed.
+ *
+ * @param context the execution context in which all indexing operations should be performed
+ * @param directoryFactory the factory from which can be obtained the Lucene directory where the indexes should be persisted
+ * @param indexingStrategy the indexing strategy that governs how properties are to be indexed; may not be null
+ * @param sourceName the name of the {@link RepositorySource}
+ * @param workspaceName the name of the workspace
+ * @param connectionFactory the connection factory
+ * @throws IllegalArgumentException if any of the parameters are null
+ * @throws SearchEngineException if there is a problem initializing this engine
+ */
+ protected WorkspaceSearchEngine( ExecutionContext context,
+ DirectoryConfiguration directoryFactory,
+ IndexingStrategy indexingStrategy,
+ String sourceName,
+ String workspaceName,
+ RepositoryConnectionFactory connectionFactory ) throws SearchEngineException {
+ CheckArg.isNotNull(context, "context");
+ CheckArg.isNotNull(directoryFactory, "directoryFactory");
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ CheckArg.isNotNull(connectionFactory, "connectionFactory");
+ CheckArg.isNotNull(indexingStrategy, "indexingStrategy");
+ this.sourceName = sourceName;
+ this.workspaceName = workspaceName;
+ this.connectionFactory = connectionFactory;
+ this.context = context;
+ this.indexingStrategy = indexingStrategy;
+ this.encodedContext = context.with(new EncodingNamespaceRegistry(context.getNamespaceRegistry(),
+ this.indexingStrategy.getNamespaceEncoder()));
+ this.pathsDirectory = directoryFactory.getDirectory(workspaceName, PATHS_INDEX_NAME);
+ this.contentDirectory = directoryFactory.getDirectory(workspaceName, CONTENT_INDEX_NAME);
+
+ initialize();
+ }
+
+ protected void initialize() throws SearchEngineException {
+ // Always create the index if not there ...
+ try {
+ Analyzer analyzer = this.indexingStrategy.createAnalyzer();
+ ensureIndexesExist(this.pathsDirectory, analyzer);
+ ensureIndexesExist(this.contentDirectory, analyzer);
+ } catch (IOException e) {
+ String msg = SearchI18n.errorWhileInitializingSearchEngine.text(workspaceName, sourceName, e.getMessage());
+ throw new SearchEngineException(msg, e);
+ }
+ }
+
+ private static void ensureIndexesExist( Directory directory,
+ Analyzer analyzer ) throws IOException {
+ IndexWriter writer = null;
+ Throwable error = null;
+ try {
+ writer = new IndexWriter(directory, analyzer, false, MaxFieldLength.UNLIMITED);
+ } catch (FileNotFoundException e) {
+ // The index files don't yet exist, so we need to create them ...
+ try {
+ writer = new IndexWriter(directory, analyzer, true, MaxFieldLength.UNLIMITED);
+ } catch (Throwable t) {
+ error = t;
+ }
+ } catch (Throwable t) {
+ error = t;
+ } finally {
+ if (writer != null) {
+ // Either way, make sure we close the writer that we created ...
+ try {
+ writer.close();
+ } catch (IOException e) {
+ if (error == null) throw e;
+ } catch (RuntimeException e) {
+ if (error == null) throw e;
+ }
+ }
+ }
+ }
+
+ final Graph graph() {
+ Graph graph = Graph.create(sourceName, connectionFactory, context);
+ graph.useWorkspace(workspaceName);
+ return graph;
+ }
+
+ final String workspaceName() {
+ return workspaceName;
+ }
+
+ final String sourceName() {
+ return sourceName;
+ }
+
+ final String readable( Path path ) {
+ return context.getValueFactories().getStringFactory().create(path);
+ }
+
+ final IndexingStrategy strategy() {
+ return indexingStrategy;
+ }
+
+ /**
+ * Index all of the content at or below the supplied path.
+ *
+ * @param startingPoint the path that represents the content to be indexed
+ * @param depthPerBatch the depth of each subgraph read operation
+ * @throws IllegalArgumentException if the path is null or the depth is not positive
+ * @throws RepositorySourceException if there is a problem accessing the content
+ * @throws SearchEngineException if there is a problem updating the indexes
+ */
+ public void indexContent( Path startingPoint,
+ int depthPerBatch ) throws RepositorySourceException, SearchEngineException {
+ CheckArg.isNotNull(startingPoint, "startingPoint");
+ indexContent(Location.create(startingPoint), depthPerBatch);
+ }
+
+ /**
+ * Index all of the content at or below the supplied location.
+ *
+ * @param startingPoint the location that represents the content to be indexed
+ * @param depthPerRead the depth of each subgraph read operation
+ * @throws IllegalArgumentException if the location is null or the depth is not positive
+ * @throws RepositorySourceException if there is a problem accessing the content
+ * @throws SearchEngineException if there is a problem updating the indexes
+ */
+ public void indexContent( Location startingPoint,
+ int depthPerRead ) throws RepositorySourceException, SearchEngineException {
+ CheckArg.isNotNull(startingPoint, "startingPoint");
+ CheckArg.isPositive(depthPerRead, "depthPerBatch");
+ assert startingPoint.hasPath();
+
+ if (startingPoint.getPath().isRoot()) {
+ // More efficient to just start over with a new index ...
+ execute(true, addContent(startingPoint, depthPerRead));
+ } else {
+ // Have to first remove the content below the starting point, then add it again ...
+ execute(false, removeContent(startingPoint), addContent(startingPoint, depthPerRead));
+ }
+ }
+
+ /**
+ * Update the indexes with the supplied set of changes to the content.
+ *
+ * @param changes the set of changes to the content
+ * @throws IllegalArgumentException if the path is null
+ * @throws RepositorySourceException if there is a problem accessing the content
+ * @throws SearchEngineException if there is a problem updating the indexes
+ */
+ public void indexChanges( final Iterable<ChangeRequest> changes ) throws SearchEngineException {
+ CheckArg.isNotNull(changes, "changes");
+ execute(false, updateContent(changes));
+ }
+
+ /**
+ * Invoke the engine's garbage collection on all indexes used by this workspace. This method reclaims space and optimizes the
+ * index. This should be done on a periodic basis after changes are made to the engine's indexes.
+ *
+ * @throws SearchEngineException if there is a problem during optimization
+ */
+ public void optimize() throws SearchEngineException {
+ execute(false, optimizeContent());
+ }
+
+ /**
+ * Create an activity that will perform a full-text search given the supplied query.
+ *
+ * @param fullTextSearch the full-text search to be performed; may not be null
+ * @param maxResults the maximum number of results that are to be returned; always positive
+ * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+ * @return the activity that will perform the work
+ */
+ public List<Location> fullTextSearch( final String fullTextSearch,
+ final int maxResults,
+ final int offset ) {
+ return execute(false, searchContent(fullTextSearch, maxResults, offset)).getResults();
+ }
+
+ /**
+ * Create an activity that will perform a query of the content in this workspace, given the Abstract Query Model
+ * representation of the query.
+ *
+ * @param query the query that is to be executed, in the form of the Abstract Query Model
+ * @return the query results; never null
+ * @throws IllegalArgumentException if the context or query references are null
+ */
+ public QueryResults execute( QueryCommand query ) {
+ return execute(false, queryContent(query)).getResults();
+ }
+
+ /**
+ * Execute the supplied activities against the indexes.
+ *
+ * @param <ActivityType> the type of activity
+ * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
+ * @param activity the activity to execute
+ * @return the same activity that was supplied as a parameter, returned as a convenience
+ * @throws SearchEngineException if there is a problem performing the activities
+ */
+ protected final <ActivityType extends Activity> ActivityType execute( boolean overwrite,
+ ActivityType activity ) throws SearchEngineException {
+ execute(overwrite, new Activity[] {activity});
+ return activity;
+ }
+
+ /**
+ * Execute the supplied activities against the indexes.
+ *
+ * @param overwrite true if the existing indexes should be overwritten, or false if they should be used
+ * @param activities the activities to execute
+ * @throws SearchEngineException if there is a problem performing the activities
+ */
+ protected final void execute( boolean overwrite,
+ Activity... activities ) throws SearchEngineException {
+ Analyzer analyzer = this.indexingStrategy.createAnalyzer();
+ IndexContext indexes = new IndexContext(encodedContext, pathsDirectory, contentDirectory, analyzer, overwrite);
+
+ // Execute the various activities ...
+ Throwable error = null;
+ try {
+ for (Activity activity : activities) {
+ try {
+ activity.execute(indexes);
+ } catch (IOException e) {
+ error = e;
+ throw new SearchEngineException(activity.messageFor(e), e);
+ } catch (ParseException e) {
+ error = e;
+ throw new SearchEngineException(activity.messageFor(e), e);
+ } catch (RuntimeException e) {
+ error = e;
+ throw e;
+ }
+ }
+ if (indexes.hasWriters()) {
+ // Determine if there have been enough changes made to run the optimizer ...
+ int maxChanges = this.indexingStrategy.getChangeCountForAutomaticOptimization();
+ if (maxChanges > 0 && this.modifiedNodesSinceLastOptimize.get() >= maxChanges) {
+ Activity optimizer = optimizeContent();
+ try {
+ optimizer.execute(indexes);
+ } catch (ParseException e) {
+ error = e;
+ throw new SearchEngineException(optimizer.messageFor(e), e);
+ } catch (IOException e) {
+ error = e;
+ throw new SearchEngineException(optimizer.messageFor(e), e);
+ } catch (RuntimeException e) {
+ error = e;
+ throw e;
+ }
+ }
+ }
+ } finally {
+ try {
+ if (error == null) {
+ indexes.commit();
+ } else {
+ indexes.rollback();
+ }
+ } catch (IOException e2) {
+ // We don't want to lose the existing error, if there is one ...
+ if (error == null) {
+ I18n msg = SearchI18n.errorWhileCommittingIndexChanges;
+ throw new SearchEngineException(msg.text(workspaceName(), sourceName(), e2.getMessage()), e2);
+ }
+ }
+ }
+ }
+
+ /**
+ * Interface for activities that will be executed against the set of indexes. These activities don't have to commit or roll
+ * back the writer, nor do they have to translate the exceptions, since this is done by the
+ * {@link WorkspaceSearchEngine#execute(boolean, Activity...)} method.
+ */
+ protected interface Activity {
+
+ /**
+ * Perform the activity by using the index writer.
+ *
+ * @param indexes the set of indexes to use; never null
+ * @throws IOException if there is an error using the writer
+ * @throws ParseException if there is an error due to parsing
+ */
+ void execute( IndexContext indexes ) throws IOException, ParseException;
+
+ /**
+ * Translate an exception obtained during {@link #execute(IndexContext) execution} into a single message.
+ *
+ * @param t the exception
+ * @return the error message
+ */
+ String messageFor( Throwable t );
+ }
+
+ protected interface Search extends Activity {
+ /**
+ * Get the results of the search.
+ *
+ * @return the list of {@link Location} objects for each node satisfying the results; never null
+ */
+ List<Location> getResults();
+ }
+
+ protected interface Query extends Activity {
+ /**
+ * Get the results of the query.
+ *
+ * @return the results of a query; never null
+ */
+ QueryResults getResults();
+ }
+
+ /**
+ * Create an activity that will read from the source the content at the supplied location and add the content to the search
+ * index.
+ *
+ * @param location the location of the content to read; may not be null
+ * @param depthPerRead the depth of each read operation; always positive
+ * @return the activity that will perform the work
+ */
+ protected Activity addContent( final Location location,
+ final int depthPerRead ) {
+ return new Activity() {
+ public void execute( IndexContext indexes ) throws IOException {
+
+ // Create a queue that we'll use to walk the content ...
+ LinkedList<Location> locationsToRead = new LinkedList<Location>();
+ locationsToRead.add(location);
+ int count = 0;
+
+ // Now read and index the content ...
+ Graph graph = graph();
+ while (!locationsToRead.isEmpty()) {
+ Location location = locationsToRead.poll();
+ if (location == null) continue;
+ Subgraph subgraph = graph.getSubgraphOfDepth(depthPerRead).at(location);
+ // Index all of the nodes within this subgraph ...
+ for (SubgraphNode node : subgraph) {
+ // Index the node ...
+ strategy().index(node, indexes);
+ ++count;
+
+ // Process the children ...
+ for (Location child : node.getChildren()) {
+ if (!subgraph.includes(child)) {
+ // Record this location as needing to be read ...
+ locationsToRead.add(child);
+ }
+ }
+ }
+ }
+ modifiedNodesSinceLastOptimize.addAndGet(count);
+ }
+
+ public String messageFor( Throwable error ) {
+ String path = readable(location.getPath());
+ return SearchI18n.errorWhileIndexingContentAtPath.text(path, workspaceName(), sourceName(), error.getMessage());
+ }
+ };
+ }
+
+ /**
+ * Create an activity that will remove from the indexes all documents that represent content at or below the specified
+ * location.
+ *
+ * @param location the location of the content to removed; may not be null
+ * @return the activity that will perform the work
+ */
+ protected Activity removeContent( final Location location ) {
+ return new Activity() {
+
+ public void execute( IndexContext indexes ) throws IOException {
+ // Delete the content at/below the path ...
+ modifiedNodesSinceLastOptimize.addAndGet(strategy().deleteBelow(location.getPath(), indexes));
+ }
+
+ public String messageFor( Throwable error ) {
+ String path = readable(location.getPath());
+ return SearchI18n.errorWhileRemovingContentAtPath.text(path, workspaceName(), sourceName(), error.getMessage());
+ }
+ };
+ }
+
+ /**
+ * Create an activity that will optimize the indexes.
+ *
+ * @return the activity that will perform the work
+ */
+ protected Activity optimizeContent() {
+ return new Activity() {
+ public void execute( IndexContext indexes ) throws IOException {
+ // Don't block ...
+ indexes.getContentWriter().optimize();
+ indexes.getPathsWriter().optimize();
+ }
+
+ public String messageFor( Throwable error ) {
+ return SearchI18n.errorWhileOptimizingIndexes.text(workspaceName(), sourceName(), error.getMessage());
+ }
+ };
+ }
+
+ /**
+ * Create an activity that will update the indexes with changes that were already made to the content.
+ *
+ * @param changes the changes that have been made to the content; may not be null
+ * @return the activity that will perform the work
+ */
+ protected Activity updateContent( final Iterable<ChangeRequest> changes ) {
+ return new Activity() {
+
+ public void execute( IndexContext indexes ) throws IOException {
+ // Iterate over the changes ...
+ modifiedNodesSinceLastOptimize.addAndGet(strategy().apply(changes, indexes));
+ }
+
+ public String messageFor( Throwable error ) {
+ return SearchI18n.errorWhileUpdatingContent.text(workspaceName(), sourceName(), error.getMessage());
+ }
+ };
+ }
+
+ /**
+ * Create an activity that will perform a full-text search given the supplied query.
+ *
+ * @param fullTextSearch the full-text search to be performed; may not be null
+ * @param maxResults the maximum number of results that are to be returned; always positive
+ * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+ * @return the activity that will perform the work
+ */
+ protected Search searchContent( final String fullTextSearch,
+ final int maxResults,
+ final int offset ) {
+ final List<Location> results = new ArrayList<Location>(maxResults);
+ return new Search() {
+ public void execute( IndexContext indexes ) throws IOException, ParseException {
+ strategy().performQuery(fullTextSearch, maxResults, offset, indexes, results);
+ }
+
+ public String messageFor( Throwable error ) {
+ return SearchI18n.errorWhilePerformingSearch.text(fullTextSearch,
+ workspaceName(),
+ sourceName(),
+ error.getMessage());
+ }
+
+ public List<Location> getResults() {
+ return results;
+ }
+ };
+ }
+
+ /**
+ * Create an activity that will perform a query against the index.
+ *
+ * @param query the query to be performed; may not be null
+ * @return the activity that will perform the work
+ */
+ protected Query queryContent( final QueryCommand query ) {
+ return new Query() {
+ private QueryResults results = null;
+
+ public void execute( IndexContext indexes ) throws IOException, ParseException {
+ results = strategy().performQuery(query, indexes);
+ }
+
+ public String messageFor( Throwable error ) {
+ return SearchI18n.errorWhilePerformingQuery.text(query, workspaceName(), sourceName(), error.getMessage());
+ }
+
+ public QueryResults getResults() {
+ return results;
+ }
+ };
+ }
+}
Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties
===================================================================
--- trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties (rev 0)
+++ trunk/dna-search/src/main/resources/org/jboss/dna/search/SearchI18n.properties 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.
+#
+
+locationForIndexesIsNotDirectory = Location "{0}" cannot be used for search indexes for workspace "{1}" because it is a directory
+locationForIndexesCannotBeRead = Location "{0}" cannot be used for search indexes for workspace "{1}" because it cannot be read
+locationForIndexesCannotBeWritten = Location "{0}" cannot be used for search indexes for workspace "{1}" because its contents cannot be written or updated
+
+errorWhileIndexingContentAtPath = Error while indexing the content at "{0}" in the "{1}" workspace of the "{2}" source: {3}
+errorWhileRemovingContentAtPath = Error while removing the content at/below "{0}" in the "{1}" workspace of the "{2}" source: {3}
+errorWhileUpdatingContent = Error while updating content in the "{0}" workspace of the "{1}" source: {2}
+errorWhileCommittingIndexChanges = Error while committing changes to the indexes for the "{0}" workspace of the "{1}" source: {2}
+errorCreatingIndexWriter = Error attempting to create an index writer for the "{0}" index for the "{1}" workspace of the "{2}" source: {3}
+errorWhileOptimizingIndexes = Error while optimizing the indexes for the "{0}" workspace of the "{1}" source: {2}
+errorWhilePerformingSearch = Error while searching for "{0}" in the "{1}" workspace of the "{2}" source: {3}
+errorWhilePerformingQuery = Error while performing the query "{0}" against the content in the "{1}" workspace of the "{2}" source: {3}
+errorWhileInitializingSearchEngine = Error while initializing the search engine for the "{0}" workspace of the "{1}" source: {2}
\ No newline at end of file
Added: trunk/dna-search/src/test/java/org/jboss/dna/search/EncodingNamespaceRegistryTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/EncodingNamespaceRegistryTest.java (rev 0)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/EncodingNamespaceRegistryTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,102 @@
+/*
+ * 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.search;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.Collection;
+import org.jboss.dna.common.text.SecureHashTextEncoder;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.SecureHash.Algorithm;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.NamespaceRegistry.Namespace;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class EncodingNamespaceRegistryTest {
+
+ private ExecutionContext context;
+ private NamespaceRegistry registry;
+ private EncodingNamespaceRegistry encodedRegistry;
+ private TextEncoder encoder;
+ private ExecutionContext encodedContext;
+
+ @Before
+ public void beforeEach() {
+ this.context = new ExecutionContext();
+ this.registry = this.context.getNamespaceRegistry();
+ this.encoder = new SecureHashTextEncoder(Algorithm.SHA_1, 10);
+ this.encodedRegistry = new EncodingNamespaceRegistry(registry, encoder);
+ this.encodedContext = context.with(encodedRegistry);
+ }
+
+ @Test
+ public void shouldHaveEncodedPrefixesForAllRegisteredNamespacesExceptFixedOnes() {
+ Collection<Namespace> namespaces = registry.getNamespaces();
+ assertThat(namespaces.size() > 4, is(true));
+ for (Namespace namespace : namespaces) {
+ String uri = namespace.getNamespaceUri();
+ String actualEncodedPrefix = encodedRegistry.getPrefixForNamespaceUri(uri, false);
+ if (encodedRegistry.getFixedNamespaceUris().contains(uri)) {
+ assertThat(actualEncodedPrefix, is(namespace.getPrefix()));
+ } else {
+ String expectedEncodedPrefix = encoder.encode(uri);
+ assertThat(expectedEncodedPrefix, is(actualEncodedPrefix));
+ }
+ String actualUri = encodedRegistry.getNamespaceForPrefix(actualEncodedPrefix);
+ assertThat(uri, is(actualUri));
+ }
+ }
+
+ @Test
+ public void shouldAllowPathConversionToAndFromString() {
+ String uri1 = "http://acme.com/wabbler";
+ String uri2 = "http://troublemakers.com/contixity";
+ String uri3 = "http://example.com/infinitiy";
+ String ns1 = "wab";
+ String ns2 = "ctx";
+ String ns3 = "inf";
+ registry.register(ns1, uri1);
+ registry.register(ns2, uri2);
+ registry.register(ns3, uri3);
+ String pathStr = "/wab:part1/wab:part2/ctx:part3/inf:part4/dna:part5";
+ Path actualPath = context.getValueFactories().getPathFactory().create(pathStr);
+ String actualPathStr = context.getValueFactories().getStringFactory().create(actualPath);
+ assertThat(pathStr, is(actualPathStr));
+ String encodedPathStr = encodedContext.getValueFactories().getStringFactory().create(actualPath);
+ String encodedPrefix1 = encoder.encode(uri1);
+ String encodedPrefix2 = encoder.encode(uri2);
+ String encodedPrefix3 = encoder.encode(uri3);
+ String expectedPathStr = "/" + encodedPrefix1 + ":part1/" + encodedPrefix1 + ":part2/" + encodedPrefix2 + ":part3/"
+ + encodedPrefix3 + ":part4/dna:part5";
+ assertThat(expectedPathStr, is(encodedPathStr));
+ Path actualPath2 = encodedContext.getValueFactories().getPathFactory().create(encodedPathStr);
+ assertThat(actualPath, is(actualPath2));
+ }
+}
Property changes on: trunk/dna-search/src/test/java/org/jboss/dna/search/EncodingNamespaceRegistryTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java (rev 0)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -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.search;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.search.IndexingRules.Builder;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class IndexingRulesTest {
+
+ private Builder builder;
+ private IndexingRules rules;
+
+ @Before
+ public void beforeEach() {
+ builder = IndexingRules.createBuilder();
+ rules = builder.build();
+ }
+
+ @Test
+ public void shouldBuildValidRulesFromBuilderThatIsNotInvoked() {
+ builder = IndexingRules.createBuilder();
+ rules = builder.build();
+ }
+
+ @Test
+ public void shouldBuildValidRulesFromBuilderAfterJustSettingDefaultRules() {
+ builder.defaultTo(IndexingRules.FULL_TEXT);
+ rules = builder.build();
+ assertThat(rules.getRule(null).isFullText(), is(true));
+ }
+}
Property changes on: trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java (rev 0)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,159 @@
+/*
+ * 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.search;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SearchEngineTest {
+
+ private SearchEngine engine;
+ private ExecutionContext context;
+ private String sourceName;
+ private String workspaceName1;
+ private String workspaceName2;
+ private InMemoryRepositorySource source;
+ private RepositoryConnectionFactory connectionFactory;
+ private DirectoryConfiguration directoryFactory;
+ private IndexingStrategy indexingStrategy;
+ private Schemata schemata;
+ private Graph content;
+
+ @Before
+ public void beforeEach() throws Exception {
+ context = new ExecutionContext();
+ sourceName = "sourceA";
+ workspaceName1 = "workspace1";
+ workspaceName2 = "workspace2";
+
+ // Set up the source and graph instance ...
+ source = new InMemoryRepositorySource();
+ source.setName(sourceName);
+ content = Graph.create(source, context);
+
+ // Create the workspaces ...
+ content.createWorkspace().named(workspaceName1);
+ content.createWorkspace().named(workspaceName2);
+
+ // Load some content ...
+ content.useWorkspace(workspaceName1);
+ content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("cars.xml")).into("/");
+ content.useWorkspace(workspaceName2);
+ content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("aircraft.xml")).into("/");
+
+ // Set up the connection factory ...
+ connectionFactory = new RepositoryConnectionFactory() {
+ @SuppressWarnings( "synthetic-access" )
+ public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+ return source.getConnection();
+ }
+ };
+
+ // Set up the schemata for the queries ...
+ schemata = mock(Schemata.class);
+
+ // Set up the indexing strategy ...
+ IndexingRules rules = IndexingRules.createBuilder(StoreLittleIndexingStrategy.DEFAULT_RULES)
+ .defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE | IndexingRules.FULL_TEXT)
+ .build();
+ indexingStrategy = new StoreLittleIndexingStrategy(schemata, rules);
+
+ // Now set up the search engine ...
+ directoryFactory = DirectoryConfigurations.inMemory();
+ engine = new SearchEngine(context, sourceName, connectionFactory, directoryFactory, indexingStrategy);
+ }
+
+ protected Path path( String string ) {
+ return context.getValueFactories().getPathFactory().create(string);
+ }
+
+ @Test
+ public void shouldHaveLoadedTestContentIntoRepositorySource() {
+ content.useWorkspace(workspaceName1);
+ assertThat(content.getNodeAt("/Cars/Hybrid/Toyota Prius").getProperty("msrp").getFirstValue(), is((Object)"$21,500"));
+ content.useWorkspace(workspaceName2);
+ assertThat(content.getNodeAt("/Aircraft/Commercial/Boeing 787").getProperty("range").getFirstValue(),
+ is((Object)"3050nm"));
+ }
+
+ @Test
+ public void shouldHaveExecutionContext() {
+ assertThat(engine.getContext(), is(sameInstance(context)));
+ }
+
+ @Test
+ public void shouldHaveSourceName() {
+ assertThat(engine.getSourceName(), is(sourceName));
+ }
+
+ @Test
+ public void shouldFindExistingWorkspaces() {
+ assertThat(engine.getWorkspaceEngine(workspaceName1), is(notNullValue()));
+ assertThat(engine.getWorkspaceEngine(workspaceName2), is(notNullValue()));
+ }
+
+ @Test( expected = InvalidWorkspaceException.class )
+ public void shouldNotFindNonExistingWorkspaces() {
+ engine.getWorkspaceEngine("Non-existant workspace");
+ }
+
+ @Test
+ public void shouldIndexAllContentInRepositorySource() {
+ engine.indexContent(3);
+ }
+
+ @Test
+ public void shouldIndexAllContentInWorkspace() {
+ engine.indexContent(workspaceName1, 3);
+ engine.indexContent(workspaceName2, 5);
+ }
+
+ @Test
+ public void shouldIndexAllContentInWorkspaceBelowPath() {
+ engine.indexContent(workspaceName1, path("/Cars/Hybrid"), 3);
+ engine.indexContent(workspaceName2, path("/Aircraft/Commercial"), 5);
+ }
+
+ @Test
+ public void shouldReIndexAllContentInWorkspaceBelowPath() {
+ for (int i = 0; i != 0; i++) {
+ engine.indexContent(workspaceName1, path("/Cars/Hybrid"), 3);
+ engine.indexContent(workspaceName2, path("/Aircraft/Commercial"), 5);
+ }
+ }
+}
Property changes on: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchI18nTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchI18nTest.java (rev 0)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchI18nTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,33 @@
+/*
+ * 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.search;
+
+import org.jboss.dna.common.AbstractI18nTest;
+
+public class SearchI18nTest extends AbstractI18nTest {
+
+ public SearchI18nTest() {
+ super(SearchI18n.class);
+ }
+}
Property changes on: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchI18nTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java (rev 0)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,177 @@
+/*
+ * 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.search;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.junit.Before;
+import org.junit.Test;
+
+public class WorkspaceSearchEngineTest {
+
+ private WorkspaceSearchEngine engine;
+ private ExecutionContext context;
+ private String sourceName;
+ private String workspaceName;
+ private InMemoryRepositorySource source;
+ private RepositoryConnectionFactory connectionFactory;
+ private DirectoryConfiguration directoryFactory;
+ private IndexingStrategy indexingStrategy;
+ private Graph content;
+ private Schemata schemata;
+
+ @Before
+ public void beforeEach() throws Exception {
+ context = new ExecutionContext();
+ sourceName = "sourceA";
+ workspaceName = "workspace1";
+
+ // Set up the source and graph instance ...
+ source = new InMemoryRepositorySource();
+ source.setName(sourceName);
+ source.setDefaultWorkspaceName(workspaceName);
+ content = Graph.create(source, context);
+
+ // Load some content ...
+ content.importXmlFrom(getClass().getClassLoader().getResourceAsStream("cars.xml")).into("/");
+
+ // Set up the connection factory ...
+ connectionFactory = new RepositoryConnectionFactory() {
+ @SuppressWarnings( "synthetic-access" )
+ public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+ return source.getConnection();
+ }
+ };
+
+ // Set up the schemata for the queries ...
+ schemata = mock(Schemata.class);
+
+ // Set up the indexing strategy ...
+ IndexingRules rules = IndexingRules.createBuilder(StoreLittleIndexingStrategy.DEFAULT_RULES)
+ .defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE | IndexingRules.FULL_TEXT)
+ .build();
+ indexingStrategy = new StoreLittleIndexingStrategy(schemata, rules);
+
+ // Now set up the search engine ...
+ directoryFactory = DirectoryConfigurations.inMemory();
+ engine = new WorkspaceSearchEngine(context, directoryFactory, indexingStrategy, sourceName, workspaceName,
+ connectionFactory);
+ }
+
+ protected Path path( String string ) {
+ return context.getValueFactories().getPathFactory().create(string);
+ }
+
+ protected void assertSearchResults( String fullTextSearch,
+ Path... expectedPaths ) {
+ int numExpected = expectedPaths.length;
+ List<Location> results = engine.fullTextSearch(fullTextSearch, numExpected, 0);
+ int numFound = results.size();
+ assertThat("Different number of results were found", numExpected, is(numFound));
+ Path[] actualPaths = new Path[numFound];
+ int i = 0;
+ for (Location actual : results) {
+ actualPaths[i++] = actual.getPath();
+ }
+ assertThat(expectedPaths, is(actualPaths));
+ }
+
+ @Test
+ public void shouldHaveLoadedTestContentIntoRepositorySource() {
+ assertThat(content.getNodeAt("/Cars/Hybrid/Toyota Prius").getProperty("msrp").getFirstValue(), is((Object)"$21,500"));
+ }
+
+ @Test
+ public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfOne() {
+ engine.indexContent(path("/"), 1);
+ }
+
+ @Test
+ public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTwo() {
+ engine.indexContent(path("/"), 2);
+ }
+
+ @Test
+ public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfThree() {
+ engine.indexContent(path("/"), 3);
+ }
+
+ @Test
+ public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfFour() {
+ engine.indexContent(path("/"), 4);
+ }
+
+ @Test
+ public void shouldIndexRepositoryContentStartingAtRootAndUsingDepthOfTen() {
+ engine.indexContent(path("/"), 10);
+ }
+
+ @Test
+ public void shouldIndexRepositoryContentStartingAtNonRootNode() {
+ engine.indexContent(path("/Cars"), 10);
+ }
+
+ @Test
+ public void shouldReIndexRepositoryContentStartingAtNonRootNode() {
+ for (int i = 0; i != 3; ++i) {
+ engine.indexContent(path("/Cars"), 10);
+ }
+ }
+
+ @Test
+ public void shouldFindNodesByFullTextSearch() {
+ engine.indexContent(path("/"), 100);
+ List<Location> results = engine.fullTextSearch("Toyota Prius", 10, 0);
+ assertThat(results, is(notNullValue()));
+ assertThat(results.size(), is(2));
+ assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
+ assertThat(results.get(1).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
+ }
+
+ @Test
+ public void shouldFindNodesByFullTextSearchWithOffset() {
+ engine.indexContent(path("/"), 100);
+ List<Location> results = engine.fullTextSearch("toyota prius", 1, 0);
+ assertThat(results, is(notNullValue()));
+ assertThat(results.size(), is(1));
+ assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Prius")));
+
+ results = engine.fullTextSearch("+Toyota", 1, 1);
+ assertThat(results, is(notNullValue()));
+ assertThat(results.size(), is(1));
+ assertThat(results.get(0).getPath(), is(path("/Cars/Hybrid/Toyota Highlander")));
+ }
+}
Property changes on: trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/test/resources/aircraft.xml
===================================================================
--- trunk/dna-search/src/test/resources/aircraft.xml (rev 0)
+++ trunk/dna-search/src/test/resources/aircraft.xml 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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 distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<Aircraft xmlns:jcr="http://www.jcp.org/jcr/1.0">
+ <Business>
+ <aircraft jcr:name="Gulfstream V" maker="Gulfstream" model="G-V" introduced="1995" range="5800nm" cruiseSpeed="488kt" crew="2" emptyWeight="46200lb" url="http://en.wikipedia.org/wiki/Gulfstream_V"/>
+ <aircraft jcr:name="Learjet 45" maker="Learjet" model="LJ45" introduced="1995" numberBuilt="264+" crew="2" emptyWeight="13695lb" range="2120nm" cruiseSpeed="457kt" url="http://en.wikipedia.org/wiki/Learjet_45"/>
+ </Business>
+ <Commercial>
+ <aircraft jcr:name="Boeing 777" maker="Boeing" model="777-200LR" introduced="1995" numberBuilt="731+" maxRange="7500nm" emptyWeight="326000lb" cruiseSpeed="560mph" url="http://en.wikipedia.org/wiki/Boeing_777"/>
+ <aircraft jcr:name="Boeing 767" maker="Boeing" model="767-200" introduced="1982" numberBuilt="966+" maxRange="3950nm" emptyWeight="176650lb" cruiseSpeed="530mph" url="http://en.wikipedia.org/wiki/Boeing_767"/>
+ <aircraft jcr:name="Boeing 787" maker="Boeing" model="787-3" introduced="2009" range="3050nm" emptyWeight="223000lb" cruiseSpeed="561mph" url="http://en.wikipedia.org/wiki/Boeing_787"/>
+ <aircraft jcr:name="Boeing 757" maker="Boeing" model="757-200" introduced="1983" numberBuilt="1050" range="3900nm" maxWeight="255000lb" cruiseSpeed="530mph" url="http://en.wikipedia.org/wiki/Boeing_757"/>
+ <aircraft jcr:name="Airbus A380" maker="Airbus" model="A380-800" introduced="2007" numberBuilt="18" range="8200nm" maxWeight="1235000lb" cruiseSpeed="647mph" url="http://en.wikipedia.org/wiki/Airbus_a380"/>
+ <aircraft jcr:name="Airbus A340" maker="Airbus" model="A340-200" introduced="1993" numberBuilt="354" range="8000nm" maxWeight="606300lb" cruiseSpeed="557mph" url="http://en.wikipedia.org/wiki/Airbus_A-340"/>
+ <aircraft jcr:name="Airbus A310" maker="Airbus" model="A310-200" introduced="1983" numberBuilt="255" cruiseSpeed="850km/h" emptyWeight="176312lb" range="3670nm" url="http://en.wikipedia.org/wiki/Airbus_A-310"/>
+ <aircraft jcr:name="Embraer RJ-175" maker="Embraer" model="ERJ170-200" introduced="2004" range="3334km" cruiseSpeed="481kt" emptyWeight="21810kg" url="http://en.wikipedia.org/wiki/EMBRAER_170"/>
+ </Commercial>
+ <Vintage>
+ <aircraft jcr:name="Fokker Trimotor" maker="Fokker" model="F.VII" introduced="1925" cruiseSpeed="170km/h" emptyWeight="3050kg" crew="2" url="http://en.wikipedia.org/wiki/Fokker_trimotor"/>
+ <aircraft jcr:name="P-38 Lightning" maker="Lockheed" model="P-38" designedBy="Kelly Johnson" introduced="1941" numberBuilt="10037" rateOfClimb="4750ft/min" range="1300mi" emptyWeight="12780lb" crew="1" url="http://en.wikipedia.org/wiki/P-38_Lightning"/>
+ <aircraft jcr:name="A6M Zero" maker="Mitsubishi" model="A6M" designedBy="Jiro Horikoshi" introduced="1940" numberBuilt="11000" crew="1" emptyWeight="3704lb" serviceCeiling="33000ft" maxSpeed="331mph" range="1929mi" rateOfClimb="3100ft/min" url="http://en.wikipedia.org/wiki/A6M_Zero"/>
+ <aircraft jcr:name="Bf 109" maker="Messerschmitt" model="Bf 109" introduced="1937" url="http://en.wikipedia.org/wiki/BF_109"/>
+ <aircraft jcr:name="Wright Flyer" maker="Wright Brothers" introduced="1903" range="852ft" maxSpeed="30mph" emptyWeight="605lb" crew="1"/>
+ </Vintage>
+ <Homebuilt>
+ <aircraft jcr:name="Long-EZ" maker="Rutan Aircraft Factory" model="61" emptyWeight="760lb" fuelCapacity="200L" maxSpeed="185kt" since="1976" range="1200nm" url="http://en.wikipedia.org/wiki/Rutan_Long-EZ"/>
+ <aircraft jcr:name="Cirrus VK-30" maker="Cirrus Design" model="VK-30" emptyWeight="2400lb" maxLoad="1200lb" maxSpeed="250mph" rateOfClimb="1500ft/min" range="1300mi" url="http://en.wikipedia.org/wiki/Cirrus_VK-30"/>
+ <aircraft jcr:name="Van's RV-4" maker="Van's Aircraft" model="RV-4" introduced="1980" emptyWeight="905lb" maxLoad="500lb" maxSpeed="200mph" rateOfClimb="2450ft/min" range="725mi" url="http://en.wikipedia.org/wiki/Van%27s_Aircraft_RV-4"/>
+ </Homebuilt>
+</Aircraft>
\ No newline at end of file
Property changes on: trunk/dna-search/src/test/resources/aircraft.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/test/resources/cars.xml
===================================================================
--- trunk/dna-search/src/test/resources/cars.xml (rev 0)
+++ trunk/dna-search/src/test/resources/cars.xml 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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 distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<Cars xmlns:jcr="http://www.jcp.org/jcr/1.0">
+ <Hybrid>
+ <car jcr:name="Toyota Prius" maker="Toyota" model="Prius" year="2008" msrp="$21,500" userRating="4.2" valueRating="5" mpgCity="48" mpgHighway="45"/>
+ <car jcr:name="Toyota Highlander" maker="Toyota" model="Highlander" year="2008" msrp="$34,200" userRating="4" valueRating="5" mpgCity="27" mpgHighway="25"/>
+ <car jcr:name="Nissan Altima" maker="Nissan" model="Altima" year="2008" msrp="$18,260" mpgCity="23" mpgHighway="32"/>
+ </Hybrid>
+ <Sports>
+ <car jcr:name="Aston Martin DB9" maker="Aston Martin" model="DB9" year="2008" msrp="$171,600" userRating="5" mpgCity="12" mpgHighway="19" lengthInInches="185.5" wheelbaseInInches="108.0" engine="5,935 cc 5.9 liters V 12"/>
+ <car jcr:name="Infiniti G37" maker="Infiniti" model="G37" year="2008" msrp="$34,900" userRating="3.5" valueRating="4" mpgCity="18" mpgHighway="24" />
+ </Sports>
+ <Luxury>
+ <car jcr:name="Cadillac DTS" maker="Cadillac" model="DTS" year="2008" engine="3.6-liter V6" userRating="0"/>
+ <car jcr:name="Bentley Continental" maker="Bentley" model="Continental" year="2008" msrp="$170,990" mpgCity="10" mpgHighway="17" />
+ <car jcr:name="Lexus IS350" maker="Lexus" model="IS350" year="2008" msrp="$36,305" mpgCity="18" mpgHighway="25" userRating="4" valueRating="5" />
+ </Luxury>
+ <Utility>
+ <car jcr:name="Land Rover LR2" maker="Land Rover" model="LR2" year="2008" msrp="$33,985" userRating="4.5" valueRating="5" mpgCity="16" mpgHighway="23" />
+ <car jcr:name="Land Rover LR3" maker="Land Rover" model="LR3" year="2008" msrp="$48,525" userRating="5" valueRating="2" mpgCity="12" mpgHighway="17" />
+ <car jcr:name="Hummer H3" maker="Hummer" model="H3" year="2008" msrp="$30,595" userRating="3.5" valueRating="4" mpgCity="13" mpgHighway="16" />
+ <car jcr:name="Ford F-150" maker="Ford" model="F-150" year="2008" msrp="$23,910" userRating="4" valueRating="1" mpgCity="14" mpgHighway="20" />
+ </Utility>
+</Cars>
\ No newline at end of file
Property changes on: trunk/dna-search/src/test/resources/cars.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-search/src/test/resources/log4j.properties
===================================================================
--- trunk/dna-search/src/test/resources/log4j.properties (rev 0)
+++ trunk/dna-search/src/test/resources/log4j.properties 2009-09-21 20:03:40 UTC (rev 1234)
@@ -0,0 +1,13 @@
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %m%n
+
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Set up the default logging to be INFO level, then override specific units
+log4j.logger.org.jboss.dna=INFO
+#log4j.logger.org.jboss.dna.search.SimpleIndexingStrategy=TRACE
+
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2009-09-21 18:44:47 UTC (rev 1233)
+++ trunk/pom.xml 2009-09-21 20:03:40 UTC (rev 1234)
@@ -114,6 +114,7 @@
<module>dna-common</module>
<module>dna-graph</module>
<module>dna-repository</module>
+ <module>dna-search</module>
<module>dna-cnd</module>
<module>dna-jcr</module>
<module>extensions/dna-classloader-maven</module>
@@ -825,6 +826,11 @@
</dependency>
<dependency>
<groupId>org.jboss.dna</groupId>
+ <artifactId>dna-search</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
<artifactId>dna-repository</artifactId>
<version>${pom.version}</version>
</dependency>
14 years, 7 months
DNA SVN: r1233 - trunk/dna-jcr/src/test/java/org/jboss/dna/jcr.
by dna-commits@lists.jboss.org
Author: elvisisking
Date: 2009-09-21 14:44:47 -0400 (Mon, 21 Sep 2009)
New Revision: 1233
Modified:
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java
Log:
DNA-524 JcrValueTest Fails Due To Timezone: 2 tests were failing because timezone "US/Central" was not equal to timezone "America/Chicago" but they should be. Changed the test to just check the milliseconds. Reviewed by Randall.
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java 2009-09-21 18:12:14 UTC (rev 1232)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java 2009-09-21 18:44:47 UTC (rev 1233)
@@ -156,7 +156,7 @@
public void shouldProvideDateForDouble() throws Exception {
Calendar expectedValue = Calendar.getInstance();
expectedValue.setTime(new Date(0L));
- assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 0.0).getDate(), is(expectedValue));
+ assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 0.0).getDate().getTimeInMillis(), is(expectedValue.getTimeInMillis()));
}
@Test
@@ -189,7 +189,7 @@
public void shouldProvideDateForLong() throws Exception {
Calendar expectedValue = Calendar.getInstance();
expectedValue.setTime(new Date(0L));
- assertThat(new JcrValue(factories, cache, PropertyType.LONG, 0L).getDate(), is(expectedValue));
+ assertThat(new JcrValue(factories, cache, PropertyType.LONG, 0L).getDate().getTimeInMillis(), is(expectedValue.getTimeInMillis()));
}
@Test
14 years, 7 months
DNA SVN: r1232 - in trunk/extensions: dna-connector-jdbc-metadata and 1 other directory.
by dna-commits@lists.jboss.org
Author: elvisisking
Date: 2009-09-21 14:12:14 -0400 (Mon, 21 Sep 2009)
New Revision: 1232
Modified:
trunk/extensions/dna-common-jdbc/pom.xml
trunk/extensions/dna-connector-jdbc-metadata/pom.xml
Log:
Corrected version.
Modified: trunk/extensions/dna-common-jdbc/pom.xml
===================================================================
--- trunk/extensions/dna-common-jdbc/pom.xml 2009-09-17 20:35:09 UTC (rev 1231)
+++ trunk/extensions/dna-common-jdbc/pom.xml 2009-09-21 18:12:14 UTC (rev 1232)
@@ -3,7 +3,7 @@
<parent>
<artifactId>dna</artifactId>
<groupId>org.jboss.dna</groupId>
- <version>0.6-SNAPSHOT</version>
+ <version>0.7-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<!-- The groupId and version values are inherited from parent -->
Modified: trunk/extensions/dna-connector-jdbc-metadata/pom.xml
===================================================================
--- trunk/extensions/dna-connector-jdbc-metadata/pom.xml 2009-09-17 20:35:09 UTC (rev 1231)
+++ trunk/extensions/dna-connector-jdbc-metadata/pom.xml 2009-09-21 18:12:14 UTC (rev 1232)
@@ -3,7 +3,7 @@
<parent>
<artifactId>dna</artifactId>
<groupId>org.jboss.dna</groupId>
- <version>0.6-SNAPSHOT</version>
+ <version>0.7-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<!-- The groupId and version values are inherited from parent -->
14 years, 7 months
DNA SVN: r1231 - in branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt: actions and 6 other directories.
by dna-commits@lists.jboss.org
Author: elvisisking
Date: 2009-09-17 16:35:09 -0400 (Thu, 17 Sep 2009)
New Revision: 1231
Modified:
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Activator.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/DnaResourceHelper.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/IUiConstants.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Utils.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/BasePublishingAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/DeleteServerAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/EditServerAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/NewServerAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/PublishAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ReconnectToServerAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ShowPublishedLocationsAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/UnpublishAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/dialogs/DeleteServerDialog.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/dialogs/PublishedLocationsDialog.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/PublishJob.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/ReconnectJob.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/EclipseLogger.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/EclipseLoggerFactory.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/StaticLoggerBinder.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/DnaPreferencePage.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFileExtensionEditor.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFoldersEditor.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewItemDialog.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PrefUtils.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PublishingFileFilter.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaContentProvider.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaMessageConsole.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/ServerView.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishPage.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishWizard.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerPage.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerWizard.java
Log:
Removed all @since tags.
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Activator.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Activator.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Activator.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -46,8 +46,6 @@
/**
* The <code>Activator</code> controls the plug-in life cycle, provides utility functions, and keeps track of the
* {@link ServerManager}.
- *
- * @since 0.6
*/
public final class Activator extends AbstractUIPlugin implements IUiConstants {
@@ -57,8 +55,6 @@
/**
* The shared plugin instance.
- *
- * @since 0.6
*/
private static Activator plugin;
@@ -68,7 +64,6 @@
/**
* @return the shared instance or <code>null</code> if not constructed
- * @since 0.6
*/
public static Activator getDefault() {
return plugin;
@@ -80,15 +75,11 @@
/**
* The image used when the requested image cannot be found.
- *
- * @since 0.6
*/
private Image missingImage;
/**
* The manager in charge of the server registry.
- *
- * @since 0.6
*/
private ServerManager serverMgr;
@@ -99,7 +90,6 @@
/**
* @param key the plugin relative path to the image (never <code>null</code>)
* @return the image or an image indicating the requested image could not be found
- * @since 0.6
*/
private ImageDescriptor createImageDescriptor( String key ) {
CheckArg.isNotNull(key, "key"); //$NON-NLS-1$
@@ -115,7 +105,6 @@
/**
* @return the image to use when a requested image cannot be found
- * @since 0.6
*/
private Image getMissingImage() {
if (this.missingImage == null) {
@@ -129,7 +118,6 @@
* @param imageId the shared image identifier (never <code>null</code>)
* @return the image or an image indicating the requested image could not be found
* @see ISharedImages
- * @since 0.6
*/
public Image getSharedImage( String imageId ) {
CheckArg.isNotNull(imageId, "imageId"); //$NON-NLS-1$
@@ -141,7 +129,6 @@
* @param imageId the shared image identifier (never <code>null</code>)
* @return the image descriptor or a descriptor indicating the requested descriptor could not be found
* @see ISharedImages
- * @since 0.6
*/
public ImageDescriptor getSharedImageDescriptor( String imageId ) {
CheckArg.isNotNull(imageId, "imageId"); //$NON-NLS-1$
@@ -157,7 +144,6 @@
/**
* @param object the object whose image is being requested (domain object or plugin-relative path)
* @return the image or <code>null</code> if not found
- * @since 0.6
*/
public Image getImage( Object object ) {
String key = null;
@@ -199,7 +185,6 @@
/**
* @param object the object whose image descriptor is being requested
* @return the image descriptor or <code>null</code> if not found
- * @since 0.6
*/
public ImageDescriptor getImageDescriptor( Object object ) {
String key = null;
@@ -228,7 +213,6 @@
/**
* @return the server manager or <code>null</code> if activator has not been initialized or started
* @see #start(BundleContext)
- * @since 0.6
*/
public ServerManager getServerManager() {
return this.serverMgr;
@@ -236,7 +220,6 @@
/**
* @param status the status being logged (never <code>null</code>)
- * @since 0.6
*/
public void log( Status status ) {
CheckArg.isNotNull(status, "status"); //$NON-NLS-1$
@@ -250,7 +233,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
- * @since 0.6
*/
@Override
public void start( BundleContext context ) throws Exception {
@@ -272,7 +254,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
- * @since 0.6
*/
@Override
public void stop( BundleContext context ) throws Exception {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/DnaResourceHelper.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/DnaResourceHelper.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/DnaResourceHelper.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -41,8 +41,6 @@
/**
* The <code>DnaResourceHelper</code> knows how to get and set the property on a resource that indicates it has been published to
* one or more workspaces.
- *
- * @since 0.6
*/
public final class DnaResourceHelper {
@@ -52,15 +50,11 @@
/**
* Delimiter between a workspace's properties.
- *
- * @since 0.6
*/
private static final String ID_DELIM = "$"; //$NON-NLS-1$
/**
* Delimiter between workspaces.
- *
- * @since 0.6
*/
private static final String DELIM = "|"; //$NON-NLS-1$
@@ -68,8 +62,6 @@
* The name of the persisted file property indicating if the resource has been published. This property will only exist if the
* 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.
- *
- * @since 0.6
*/
private static QualifiedName PUBLISHED_RESOURCE_PROPERTY = new QualifiedName(IUiConstants.PLUGIN_ID, "publishedIn"); //$NON-NLS-1$
@@ -79,8 +71,6 @@
/**
* The server manager used by the helper to obtain workspaces.
- *
- * @since 0.6
*/
private final ServerManager serverManager;
@@ -90,7 +80,6 @@
/**
* @param serverManager the server manager used by this helper (never <code>null</code>)
- * @since 0.6
*/
public DnaResourceHelper( ServerManager serverManager ) {
CheckArg.isNotNull(serverManager, "serverManager"); //$NON-NLS-1$
@@ -105,7 +94,6 @@
* @param file the file that was just published
* @param workspace the workspace where the file was published
* @throws Exception if there is a problem setting the property
- * @since 0.6
*/
public void addPublishedProperty( IFile file,
Workspace workspace ) throws Exception {
@@ -122,7 +110,6 @@
/**
* @param workspaces the workspaces used to create the property value
* @return the property value
- * @since 0.6
*/
private String createPublishedPropertyValue( Set<Workspace> workspaces ) {
StringBuilder value = new StringBuilder();
@@ -137,7 +124,6 @@
/**
* @param workspace the workspace whose identifier is being created
* @return the ID
- * @since 0.6
*/
private String createWorkspaceId( Workspace workspace ) {
StringBuilder result = new StringBuilder();
@@ -153,7 +139,6 @@
* @return the workspaces (never <code>null</code>)
* @throws Exception if there is a problem reading one of the file's persistent properties or a problem with the server
* manager
- * @since 0.6
*/
public Set<Workspace> getPublishedOnWorkspaces( IFile file ) throws Exception {
CheckArg.isNotNull(file, "file"); //$NON-NLS-1$
@@ -213,7 +198,6 @@
/**
* @param file the file whose published status is being requested (never <code>null</code>)
* @return <code>true</code> if the file has been published to any DNA repository
- * @since 0.6
*/
public boolean isPublished( IFile file ) {
CheckArg.isNotNull(file, "file"); //$NON-NLS-1$
@@ -231,7 +215,6 @@
* @param file the file that was just unpublished
* @param workspace the workspace where the file was unpublished
* @throws Exception if there is a problem setting the property
- * @since 0.6
*/
public void removePublishedProperty( IFile file,
Workspace workspace ) throws Exception {
@@ -245,6 +228,11 @@
setPublishedOnPropertyValue(file, workspaces);
}
+ /**
+ * @param file the file whose property is being set
+ * @param workspaces the workspaces the file has been published to or <code>null</code> if the file has not been published
+ * @throws CoreException if there was a problem setting the property
+ */
private void setPublishedOnPropertyValue( IFile file,
Set<Workspace> workspaces ) throws CoreException {
if ((workspaces == null) || workspaces.isEmpty()) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/IUiConstants.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/IUiConstants.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/IUiConstants.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -23,15 +23,10 @@
*/
package org.jboss.dna.web.jcr.rest.client.swt;
-/**
- * @since 0.6
- */
public interface IUiConstants {
/**
* The Plug-in's identifier.
- *
- * @since 0.6
*/
String PLUGIN_ID = "org.jboss.dna.web.jcr.rest.client.swt"; //$NON-NLS-1$
@@ -109,8 +104,6 @@
/**
* The <code>Job</code> framework job family for the DNA publishing and unpublishing operations.
- *
- * @since 0.6
*/
String DNA_PUBLISHING_JOB_FAMILY = "dna.publishing.job.family"; //$NON-NLS-1$
@@ -120,15 +113,11 @@
/**
* A preference for a list of file extensions that will not be part of publishing operations.
- *
- * @since 0.6
*/
String FILTERED_FILE_EXTENSIONS_PREFERENCE = "dna.preference.filteredFileExtensions"; //$NON-NLS-1$
/**
* A preference for a list of folder names whose contents will not be part of publishing operations.
- *
- * @since 0.6
*/
String FILTERED_FOLDER_NAMES_PREFERENCE = "dna.preference.filteredFolderNames"; //$NON-NLS-1$
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -27,8 +27,6 @@
/**
* The <code>RestClientI18n</code> class provides localized messages.
- *
- * @since 0.6
*/
public final class RestClientI18n {
@@ -48,7 +46,7 @@
public static I18n dnaConsoleName;
public static I18n dnaConsoleProblemCreatingHyperlinkMsg;
public static I18n dnaConsoleFilePathNotFoundMsg;
-
+
public static I18n dnaPreferencePageDescription;
public static I18n dnaPreferencePageFilteredFileExtensionsLabel;
public static I18n dnaPreferencePageFilteredFolderNamesLabel;
@@ -61,10 +59,10 @@
public static I18n errorDialogTitle;
public static I18n missingImage;
-
+
public static I18n newFilteredFileExtensionDialogLabel;
public static I18n newFilteredFileExtensionDialogTitle;
-
+
public static I18n newFilteredFolderNameDialogLabel;
public static I18n newFilteredFolderNameDialogTitle;
@@ -84,9 +82,9 @@
public static I18n publishedLocationsDialogTitle;
public static I18n publishedLocationsDialogUserColumnHeader;
public static I18n publishedLocationsDialogWorkspaceColumnHeader;
-
+
public static I18n publishedResourcePropertyErrorMsg;
-
+
public static I18n publishJobCanceled;
public static I18n publishJobDurationMsg;
public static I18n publishJobDurationNoHoursMsg;
@@ -143,7 +141,7 @@
public static I18n publishWizardPublishTitle;
public static I18n publishWizardUnpublishTitle;
public static I18n publishWizardUnpublishErrorMsg;
-
+
public static I18n reconnectJobTaskName;
public static I18n serverManagerGetRepositoriesExceptionMsg;
@@ -178,7 +176,7 @@
public static I18n serverWizardEditServerTitle;
public static I18n serverWizardNewServerErrorMsg;
public static I18n serverWizardNewServerTitle;
-
+
public static I18n showPublishedLocationsErrorMsg;
public static I18n testServerActionText;
@@ -191,5 +189,5 @@
System.err.println(e);
}
}
-
+
}
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Utils.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Utils.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Utils.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -39,9 +39,6 @@
import org.jboss.dna.web.jcr.rest.client.Status;
import org.jboss.dna.web.jcr.rest.client.Status.Severity;
-/**
- * @since 0.6
- */
public final class Utils {
// ===========================================================================================================================
@@ -52,7 +49,6 @@
* @param tokens the tokens being combined into one value
* @param delimiter the character inserted to separate each token
* @return the tokens separated by the delimiter
- * @since 0.6
*/
public static String combineTokens( String[] tokens,
char delimiter ) {
@@ -72,7 +68,6 @@
* @param shell the shell being resized (if necessary) and located
* @param widthPercentage a number between 1 and 100 indicating a percentage of the screen size (defaults to 50 if bad value)
* @param heightPercentage a number between 1 and 100 indicating a percentage of the screen size (defaults to 50 if bad value)
- * @since 0.6
*/
public static void centerAndSizeShellRelativeToDisplay( Shell shell,
int widthPercentage,
@@ -109,7 +104,6 @@
* @param severity the eclipse status severity level
* @return the converted severity level (never <code>null</code>)
* @see IStatus
- * @since 0.6
*/
public static int convertSeverity( Severity severity ) {
if (severity == Severity.OK) return IStatus.OK;
@@ -125,7 +119,6 @@
* @param severity the eclipse status severity level
* @return the converted severity level (never <code>null</code>)
* @see IStatus
- * @since 0.6
*/
public static Severity convertSeverity( int severity ) {
if (severity == IStatus.OK) return Severity.OK;
@@ -138,7 +131,6 @@
/**
* @param status the status being converted (never <code>null</code>)
* @return the Eclipse status object (never <code>null</code>)
- * @since 0.6
*/
public static IStatus convert( Status status ) {
CheckArg.isNotNull(status, "status"); //$NON-NLS-1$
@@ -151,7 +143,6 @@
*
* @param status the status whose image is being requested (never <code>null</code>)
* @return the image or <code>null</code> if no associated image for the status severity
- * @since 0.6
*/
public static Image getImage( Status status ) {
CheckArg.isNotNull(status, "status"); //$NON-NLS-1$
@@ -177,7 +168,6 @@
* @param delimiters the delimiters that separate the tokens (never <code>null</code>)
* @param removeDuplicates a flag indicating if duplicate tokens should be removed
* @return the tokens (never <code>null</code>)
- * @since 0.6
*/
public static String[] getTokens( String string,
String delimiters,
@@ -203,7 +193,6 @@
*
* @param status the status whose image overlay is being requested (never <code>null</code>)
* @return the image descriptor or <code>null</code> if none found for the status severity
- * @since 0.6
*/
public static ImageDescriptor getOverlayImage( Status status ) {
CheckArg.isNotNull(status, "status"); //$NON-NLS-1$
@@ -226,8 +215,6 @@
/**
* Don't allow construction.
- *
- * @since 0.6
*/
public Utils() {
// nothing to do
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/BasePublishingAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/BasePublishingAction.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/BasePublishingAction.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -45,7 +45,7 @@
import org.jboss.dna.web.jcr.rest.client.swt.wizards.PublishWizard;
/**
- * @since 0.6
+ * The <code>BasePublishingAction</code> is a base class for all publishing actions.
*/
public abstract class BasePublishingAction implements IObjectActionDelegate {
@@ -55,22 +55,16 @@
/**
* The current workspace selection.
- *
- * @since 0.6
*/
private IStructuredSelection selection;
/**
* The active part's Shell.
- *
- * @since 0.6
*/
private Shell shell;
/**
* Indicates if this is a publishing or unpublishing action.
- *
- * @since 0.6
*/
private final Type type;
@@ -80,7 +74,6 @@
/**
* @param type indicates the type of action
- * @since 0.6
*/
public BasePublishingAction( Type type ) {
this.type = type;
@@ -94,7 +87,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
- * @since 0.6
*/
@Override
@SuppressWarnings( "unchecked" )
@@ -147,7 +139,6 @@
*
* @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction,
* org.eclipse.jface.viewers.ISelection)
- * @since 0.6
*/
@Override
public void selectionChanged( IAction action,
@@ -163,7 +154,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction, org.eclipse.ui.IWorkbenchPart)
- * @since 0.6
*/
@Override
public void setActivePart( IAction action,
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/DeleteServerAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/DeleteServerAction.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/DeleteServerAction.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -40,9 +40,7 @@
import org.jboss.dna.web.jcr.rest.client.swt.dialogs.DeleteServerDialog;
/**
- * The DeleteServerAction deletes one or more servers from the server registry.
- *
- * @since 0.6
+ * The <code>DeleteServerAction</code> deletes one or more servers from the server registry.
*/
public final class DeleteServerAction extends BaseSelectionListenerAction {
@@ -52,22 +50,16 @@
/**
* The server manager used to delete servers.
- *
- * @since 0.6
*/
private final ServerManager serverManager;
/**
* The servers being deleted (never <code>null</code>).
- *
- * @since 0.6
*/
private final List<Server> serversToDelete;
/**
* The shell used to display the delete confirmation dialog.
- *
- * @since 0.6
*/
private final Shell shell;
@@ -78,7 +70,6 @@
/**
* @param shell the parent shell used to display the confirmation dialog
* @param serverManager the server manager to use when deleting servers
- * @since 0.6
*/
public DeleteServerAction( Shell shell,
ServerManager serverManager ) {
@@ -100,7 +91,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.action.Action#run()
- * @since 0.6
*/
@Override
public void run() {
@@ -133,7 +123,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.actions.BaseSelectionListenerAction#updateSelection(org.eclipse.jface.viewers.IStructuredSelection)
- * @since 0.6
*/
@Override
protected boolean updateSelection( IStructuredSelection selection ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/EditServerAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/EditServerAction.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/EditServerAction.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -37,8 +37,6 @@
/**
* The <code>EditServerAction</code> runs a UI that allows {@link Server server} properties to be changed.
- *
- * @since 0.6
*/
public final class EditServerAction extends BaseSelectionListenerAction {
@@ -48,22 +46,16 @@
/**
* The selected server being edited.
- *
- * @since 0.6
*/
private Server serverBeingEdited;
/**
* The server manager used to create and edit servers.
- *
- * @since 0.6
*/
private final ServerManager serverManager;
/**
* The shell used to display the dialog that edits and creates servers.
- *
- * @since 0.6
*/
private final Shell shell;
@@ -74,7 +66,6 @@
/**
* @param shell the parent shell used to display the dialog
* @param serverManager the server manager to use when creating and editing servers
- * @since 0.6
*/
public EditServerAction( Shell shell,
ServerManager serverManager ) {
@@ -95,7 +86,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.action.Action#run()
- * @since 0.6
*/
@Override
public void run() {
@@ -120,7 +110,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.actions.BaseSelectionListenerAction#updateSelection(org.eclipse.jface.viewers.IStructuredSelection)
- * @since 0.6
*/
@Override
protected boolean updateSelection( IStructuredSelection selection ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/NewServerAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/NewServerAction.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/NewServerAction.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -36,8 +36,6 @@
/**
* The <code>NewServerAction</code> runs a UI that allows the user to create a new {@link Server server}.
- *
- * @since 0.6
*/
public final class NewServerAction extends Action {
@@ -47,15 +45,11 @@
/**
* The server manager used to create and edit servers.
- *
- * @since 0.6
*/
private final ServerManager serverManager;
/**
* The shell used to display the dialog that edits and creates servers.
- *
- * @since 0.6
*/
private final Shell shell;
@@ -66,7 +60,6 @@
/**
* @param shell the parent shell used to display the dialog
* @param serverManager the server manager to use when creating and editing servers
- * @since 0.6
*/
public NewServerAction( Shell shell,
ServerManager serverManager ) {
@@ -86,7 +79,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.action.Action#run()
- * @since 0.6
*/
@Override
public void run() {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/PublishAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/PublishAction.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/PublishAction.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -28,8 +28,6 @@
/**
* The <code>PublishAction</code> controls the publishing of one or more {@link org.eclipse.core.resources.IResource}s to a DNA
* repository.
- *
- * @since 0.6
*/
public final class PublishAction extends BasePublishingAction {
@@ -37,9 +35,6 @@
// Constructors
// ===========================================================================================================================
- /**
- * @since 0.6
- */
public PublishAction() {
super(Type.PUBLISH);
}
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ReconnectToServerAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ReconnectToServerAction.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ReconnectToServerAction.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -38,8 +38,6 @@
/**
* The <code>ReconnectToServerAction</code> tries to reconnect to a selected server.
- *
- * @since 0.6
*/
public final class ReconnectToServerAction extends BaseSelectionListenerAction {
@@ -49,8 +47,6 @@
/**
* The server view tree viewer.
- *
- * @since 0.6
*/
private final TreeViewer viewer;
@@ -60,7 +56,6 @@
/**
* @param viewer the server view tree viewer
- * @since 0.6
*/
public ReconnectToServerAction( TreeViewer viewer ) {
super(RestClientI18n.serverReconnectActionText.text());
@@ -77,25 +72,23 @@
/**
* @return the view's tree viewer
- * @since 0.6
*/
StructuredViewer getViewer() {
return this.viewer;
}
-
+
/**
* @param server the server being connected to
- * @since 0.6
*/
void refresh( final Server server ) {
final Display display = this.viewer.getControl().getDisplay();
-
+
if (!display.isDisposed()) {
// make sure we are in the UI thread
display.asyncExec(new Runnable() {
/**
* {@inheritDoc}
- *
+ *
* @see java.lang.Runnable#run()
*/
@Override
@@ -110,7 +103,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.action.Action#run()
- * @since 0.6
*/
@Override
public void run() {
@@ -142,7 +134,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.actions.BaseSelectionListenerAction#updateSelection(org.eclipse.jface.viewers.IStructuredSelection)
- * @since 0.6
*/
@Override
protected boolean updateSelection( IStructuredSelection selection ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ShowPublishedLocationsAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ShowPublishedLocationsAction.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ShowPublishedLocationsAction.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -46,8 +46,6 @@
/**
* The <code>PublishAction</code> controls the publishing of one or more {@link org.eclipse.core.resources.IResource}s to a DNA
* repository.
- *
- * @since 0.6
*/
public final class ShowPublishedLocationsAction extends Action implements IObjectActionDelegate {
@@ -57,15 +55,11 @@
/**
* The current workspace selection.
- *
- * @since 0.6
*/
private IStructuredSelection selection;
/**
* The active part's Shell.
- *
- * @since 0.6
*/
private Shell shell;
@@ -87,7 +81,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
- * @since 0.6
*/
@Override
public void run( IAction action ) {
@@ -114,7 +107,6 @@
*
* @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction,
* org.eclipse.jface.viewers.ISelection)
- * @since 0.6
*/
@Override
public void selectionChanged( IAction action,
@@ -130,7 +122,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction, org.eclipse.ui.IWorkbenchPart)
- * @since 0.6
*/
@Override
public void setActivePart( IAction action,
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/UnpublishAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/UnpublishAction.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/UnpublishAction.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -28,8 +28,6 @@
/**
* The <code>UnpublishAction</code> controls the unpublishing of one or more {@link org.eclipse.core.resources.IResource}s from a
* DNA repository.
- *
- * @since 0.6
*/
public final class UnpublishAction extends BasePublishingAction {
@@ -37,9 +35,6 @@
// Constructors
// ===========================================================================================================================
- /**
- * @since 0.6
- */
public UnpublishAction() {
super(Type.UNPUBLISH);
}
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/dialogs/DeleteServerDialog.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/dialogs/DeleteServerDialog.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/dialogs/DeleteServerDialog.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -43,8 +43,6 @@
/**
* The <code>DeleteServerDialog</code> class provides a UI for deleting a {@link Server server}.
- *
- * @since 0.6
*/
public final class DeleteServerDialog extends MessageDialog {
@@ -54,8 +52,6 @@
/**
* Collection of servers which will be deleted.
- *
- * @since 0.6
*/
private final Collection<Server> serversBeingDeleted;
@@ -66,7 +62,6 @@
/**
* @param parentShell the dialog parent
* @param serversBeingDeleted the servers being deleted (may not be <code>null</code>)
- * @since 0.6
*/
public DeleteServerDialog( Shell parentShell,
Collection<Server> serversBeingDeleted ) {
@@ -88,7 +83,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.MessageDialog#configureShell(org.eclipse.swt.widgets.Shell)
- * @since 0.6
*/
@Override
protected void configureShell( Shell shell ) {
@@ -111,7 +105,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.MessageDialog#createCustomArea(org.eclipse.swt.widgets.Composite)
- * @since 0.6
*/
@Override
protected Control createCustomArea( Composite parent ) {
@@ -134,7 +127,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
- * @since 0.6
*/
@Override
protected void initializeBounds() {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/dialogs/PublishedLocationsDialog.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/dialogs/PublishedLocationsDialog.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/dialogs/PublishedLocationsDialog.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -71,8 +71,6 @@
/**
* The <code>PublishedLocationsDialog</code> class provides a UI for viewing a list of {@link Server servers} a selected file has
* been published to.
- *
- * @since 0.6
*/
public final class PublishedLocationsDialog extends MessageDialog {
@@ -82,50 +80,36 @@
/**
* The column index of the server URL.
- *
- * @since 0.6
*/
private static final int SERVER_URL_COL = 0;
/**
* The column index of the server user.
- *
- * @since 0.6
*/
private static final int USER_COL = 1;
/**
* The column index of the repository name.
- *
- * @since 0.6
*/
private static final int REPOSITORY_COL = 2;
/**
* The column index of the workspace name.
- *
- * @since 0.6
*/
private static final int WORKSPACE_COL = 3;
/**
* The column index of the URL where the file was published.
- *
- * @since 0.6
*/
private static final int FILE_URL_COL = 4;
/**
* The column indexes of all columns.
- *
- * @since 0.6
*/
private static final int[] COLUMNS = {SERVER_URL_COL, USER_COL, REPOSITORY_COL, WORKSPACE_COL, FILE_URL_COL};
/**
* The column headers.
- *
- * @since 0.6
*/
private static final String[] HEADERS = {RestClientI18n.publishedLocationsDialogServerUrlColumnHeader.text(),
RestClientI18n.publishedLocationsDialogUserColumnHeader.text(),
@@ -139,36 +123,26 @@
/**
* The button that copies the file URL to the clipboard.
- *
- * @since 0.6
*/
private Button btnCopy;
/**
* The file whose workspaces it was published to is being displayed by this dialog.
- *
- * @since 0.6
*/
private final IFile file;
/**
* The server manager who can obtain the URL for the file at each of the workspaces.
- *
- * @since 0.6
*/
private final ServerManager serverManager;
/**
* The viewer of the table holding the published locations.
- *
- * @since 0.6
*/
private TableViewer viewer;
/**
* Collection of workspaces the selected file has been published to.
- *
- * @since 0.6
*/
private final Collection<Workspace> workspaces;
@@ -181,7 +155,6 @@
* @param serverManager the server manager that this dialog will get URLs from (never <code>null</code>)
* @param file the file whose workspaces it has been published on is being requested (never <code>null</code>)
* @param workspaces the workspaces (never <code>null</code>)
- * @since 0.6
*/
public PublishedLocationsDialog( Shell parentShell,
ServerManager serverManager,
@@ -211,7 +184,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.MessageDialog#createCustomArea(org.eclipse.swt.widgets.Composite)
- * @since 0.6
*/
@Override
protected Control createCustomArea( Composite parent ) {
@@ -233,7 +205,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
- * @since 0.6
*/
@Override
public void selectionChanged( SelectionChangedEvent e ) {
@@ -290,7 +261,6 @@
* {@inheritDoc}
*
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
- * @since 0.6
*/
@Override
public void widgetSelected( SelectionEvent e ) {
@@ -303,7 +273,6 @@
/**
* @return the file system file this dialog is showing the published locations of (never <code>null</code>)
- * @since 0.6
*/
private File getFile() {
return this.file.getLocation().toFile();
@@ -313,7 +282,6 @@
* This path does not include the name of the file.
*
* @return the path the file was published (never <code>null</code>)
- * @since 0.6
*/
private String getPath() {
return this.file.getParent().getFullPath().toString();
@@ -322,7 +290,6 @@
/**
* @param workspace the workspace where the file was published
* @return the URL where the file was published
- * @since 0.6
*/
String getPublishedAtUrl( Workspace workspace ) {
try {
@@ -337,7 +304,6 @@
/**
* @return the workspaces the file has been published to (never <code>null</code>)
- * @since 0.6
*/
Object[] getWorkspaces() {
return this.workspaces.toArray();
@@ -345,8 +311,6 @@
/**
* Handler for when the copy URL button is clicked.
- *
- * @since 0.6
*/
void handleCopyUrl() {
Workspace workspace = (Workspace)((IStructuredSelection)this.viewer.getSelection()).getFirstElement();
@@ -357,8 +321,6 @@
/**
* Handler for when a table row is selected.
- *
- * @since 0.6
*/
void handleTableSelection() {
IStructuredSelection selection = (IStructuredSelection)this.viewer.getSelection();
@@ -373,7 +335,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
- * @since 0.6
*/
@Override
protected void initializeBounds() {
@@ -387,15 +348,12 @@
/**
* The <code>LocationsTableProvider</code> provides content, labels, and images for the table.
- *
- * @since 0.6
*/
class LocationsTableProvider implements IStructuredContentProvider, ITableLabelProvider {
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
- * @since 0.6
*/
@Override
public void addListener( ILabelProviderListener listener ) {
@@ -406,7 +364,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
- * @since 0.6
*/
@Override
public void dispose() {
@@ -417,7 +374,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
- * @since 0.6
*/
@Override
public Image getColumnImage( Object element,
@@ -429,7 +385,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
- * @since 0.6
*/
@Override
public String getColumnText( Object element,
@@ -465,7 +420,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
- * @since 0.6
*/
@Override
public Object[] getElements( Object inputElement ) {
@@ -477,7 +431,6 @@
*
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object,
* java.lang.Object)
- * @since 0.6
*/
@Override
public void inputChanged( Viewer viewer,
@@ -490,7 +443,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String)
- * @since 0.6
*/
@Override
public boolean isLabelProperty( Object element,
@@ -502,7 +454,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
- * @since 0.6
*/
@Override
public void removeListener( ILabelProviderListener listener ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/PublishJob.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/PublishJob.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/PublishJob.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -45,8 +45,6 @@
/**
* The <code>PublishJob</code> publishes or unpublishes one or more files using the {@link ServerManager}.
- *
- * @since 0.6
*/
public final class PublishJob extends Job {
@@ -56,29 +54,21 @@
/**
* The job type.
- *
- * @since 0.6
*/
public enum Type {
/**
* Indicates a publish job.
- *
- * @since 0.6
*/
PUBLISH,
/**
* Indicates an unpublish job.
- *
- * @since 0.6
*/
UNPUBLISH
}
/**
* A unique job identifier given to each publishing/unpublishing job.
- *
- * @since 0.6
*/
private static final AtomicInteger JOB_ID = new AtomicInteger(1);
@@ -90,7 +80,6 @@
* @param type the job type (never <code>null</code>)
* @param jobId the job identifier
* @return the job name
- * @since 0.6
*/
private static String getJobName( Type type,
int jobId ) {
@@ -110,29 +99,21 @@
/**
* The files being published or unpublished.
- *
- * @since 0.6
*/
private final List<IFile> files;
/**
* The unique job identifier.
- *
- * @since 0.6
*/
private final int jobId;
/**
* The job type.
- *
- * @since 0.6
*/
private final Type type;
/**
* The workspace to use when publishing or unpublishing.
- *
- * @since 0.6
*/
private final Workspace workspace;
@@ -144,7 +125,6 @@
* @param type the job type (never <code>null</code>)
* @param files the files being published or unpublished (never <code>null</code>)
* @param workspace the workspace to use when publishing or unpublishing (never <code>null</code>)
- * @since 0.6
*/
public PublishJob( Type type,
List<IFile> files,
@@ -169,7 +149,6 @@
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
- * @since 0.6
*/
@Override
public boolean belongsTo( Object family ) {
@@ -178,7 +157,6 @@
/**
* @return the server manager
- * @since 0.6
*/
private ServerManager getServerManager() {
return Activator.getDefault().getServerManager();
@@ -186,7 +164,6 @@
/**
* @return <code>true</code> if a publishing job
- * @since 0.6
*/
private boolean isPublishing() {
return (this.type == Type.PUBLISH);
@@ -196,7 +173,6 @@
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
- * @since 0.6
*/
@Override
protected IStatus run( IProgressMonitor monitor ) {
@@ -341,7 +317,6 @@
* @param file the file involved in the publishing operation
* @param url the destination file URL or <code>null</code>
* @param status the status of the publishing operation
- * @since 0.6
*/
private void writeToConsole( final IFile file,
URL url,
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/ReconnectJob.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/ReconnectJob.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/ReconnectJob.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -38,8 +38,6 @@
/**
* The <code>ReconnectJob</code> attempts to reconnect to the selected {@link Server server(s)}.
- *
- * @since 0.6
*/
public final class ReconnectJob extends Job {
@@ -49,8 +47,6 @@
/**
* The server being reconnected to.
- *
- * @since 0.6
*/
private final Server server;
@@ -60,7 +56,6 @@
/**
* @param server the server being connected to (never <code>null</code>)
- * @since 0.6
*/
public ReconnectJob( Server server ) {
super(reconnectJobTaskName.text(server.getShortDescription()));
@@ -75,7 +70,6 @@
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
- * @since 0.6
*/
@Override
public boolean belongsTo( Object family ) {
@@ -86,7 +80,6 @@
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
- * @since 0.6
*/
@Override
protected IStatus run( IProgressMonitor monitor ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/EclipseLogger.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/EclipseLogger.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/EclipseLogger.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -34,8 +34,6 @@
/**
* The <code>EclipseLogger</code> class provides an <code>org.slf4j.Logger</code> implementation that uses the Eclipse logger.
- *
- * @since 0.6
*/
public final class EclipseLogger implements Logger {
@@ -67,7 +65,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(java.lang.String)
- * @since 0.6
*/
@Override
public void debug( String message ) {
@@ -78,7 +75,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void debug( String pattern,
@@ -90,7 +86,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void debug( String pattern,
@@ -102,7 +97,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void debug( String message,
@@ -114,7 +108,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String)
- * @since 0.6
*/
@Override
public void debug( Marker marker,
@@ -126,7 +119,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void debug( String pattern,
@@ -139,7 +131,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void debug( Marker marker,
@@ -152,7 +143,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void debug( Marker marker,
@@ -165,7 +155,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void debug( Marker marker,
@@ -178,7 +167,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void debug( Marker marker,
@@ -192,7 +180,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(java.lang.String)
- * @since 0.6
*/
@Override
public void error( String message ) {
@@ -203,7 +190,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void error( String pattern,
@@ -216,7 +202,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void error( String pattern,
@@ -229,7 +214,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void error( String message,
@@ -241,7 +225,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String)
- * @since 0.6
*/
@Override
public void error( Marker marker,
@@ -253,7 +236,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void error( String pattern,
@@ -268,7 +250,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void error( Marker marker,
@@ -281,7 +262,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void error( Marker marker,
@@ -294,7 +274,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void error( Marker marker,
@@ -307,7 +286,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void error( Marker marker,
@@ -321,7 +299,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#getName()
- * @since 0.6
*/
@Override
public String getName() {
@@ -332,7 +309,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(java.lang.String)
- * @since 0.6
*/
@Override
public void info( String message ) {
@@ -343,7 +319,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void info( String pattern,
@@ -355,7 +330,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void info( String pattern,
@@ -368,7 +342,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void info( String message,
@@ -380,7 +353,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String)
- * @since 0.6
*/
@Override
public void info( Marker marker,
@@ -392,7 +364,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void info( String pattern,
@@ -406,7 +377,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void info( Marker marker,
@@ -419,7 +389,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void info( Marker marker,
@@ -432,7 +401,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void info( Marker marker,
@@ -445,7 +413,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void info( Marker marker,
@@ -459,7 +426,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isDebugEnabled()
- * @since 0.6
*/
@Override
public boolean isDebugEnabled() {
@@ -470,7 +436,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isDebugEnabled(org.slf4j.Marker)
- * @since 0.6
*/
@Override
public boolean isDebugEnabled( Marker marker ) {
@@ -481,7 +446,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isErrorEnabled()
- * @since 0.6
*/
@Override
public boolean isErrorEnabled() {
@@ -492,7 +456,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isErrorEnabled(org.slf4j.Marker)
- * @since 0.6
*/
@Override
public boolean isErrorEnabled( Marker marker ) {
@@ -503,7 +466,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isInfoEnabled()
- * @since 0.6
*/
@Override
public boolean isInfoEnabled() {
@@ -514,7 +476,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isInfoEnabled(org.slf4j.Marker)
- * @since 0.6
*/
@Override
public boolean isInfoEnabled( Marker marker ) {
@@ -525,7 +486,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isTraceEnabled()
- * @since 0.6
*/
@Override
public boolean isTraceEnabled() {
@@ -536,7 +496,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isTraceEnabled(org.slf4j.Marker)
- * @since 0.6
*/
@Override
public boolean isTraceEnabled( Marker marker ) {
@@ -547,7 +506,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isWarnEnabled()
- * @since 0.6
*/
@Override
public boolean isWarnEnabled() {
@@ -558,7 +516,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#isWarnEnabled(org.slf4j.Marker)
- * @since 0.6
*/
@Override
public boolean isWarnEnabled( Marker marker ) {
@@ -569,7 +526,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(java.lang.String)
- * @since 0.6
*/
@Override
public void trace( String message ) {
@@ -580,7 +536,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void trace( String pattern,
@@ -592,7 +547,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void trace( String pattern,
@@ -604,7 +558,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void trace( String message,
@@ -616,7 +569,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String)
- * @since 0.6
*/
@Override
public void trace( Marker marker,
@@ -628,7 +580,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void trace( String pattern,
@@ -641,7 +592,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void trace( Marker marker,
@@ -654,7 +604,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void trace( Marker marker,
@@ -667,7 +616,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void trace( Marker marker,
@@ -680,7 +628,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void trace( Marker marker,
@@ -694,7 +641,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(java.lang.String)
- * @since 0.6
*/
@Override
public void warn( String message ) {
@@ -705,7 +651,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void warn( String pattern,
@@ -718,7 +663,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void warn( String pattern,
@@ -731,7 +675,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void warn( String message,
@@ -743,7 +686,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String)
- * @since 0.6
*/
@Override
public void warn( Marker marker,
@@ -755,7 +697,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void warn( String pattern,
@@ -770,7 +711,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String, java.lang.Object)
- * @since 0.6
*/
@Override
public void warn( Marker marker,
@@ -783,7 +723,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String, java.lang.Object[])
- * @since 0.6
*/
@Override
public void warn( Marker marker,
@@ -796,7 +735,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
- * @since 0.6
*/
@Override
public void warn( Marker marker,
@@ -809,7 +747,6 @@
* {@inheritDoc}
*
* @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
- * @since 0.6
*/
@Override
public void warn( Marker marker,
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/EclipseLoggerFactory.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/EclipseLoggerFactory.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/EclipseLoggerFactory.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -28,9 +28,6 @@
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
-/**
- * @since 0.6
- */
public final class EclipseLoggerFactory implements ILoggerFactory {
// ===========================================================================================================================
@@ -39,8 +36,6 @@
/**
* The shared instance of the factory.
- *
- * @since 0.6
*/
static final EclipseLoggerFactory INSTANCE = new EclipseLoggerFactory();
@@ -50,8 +45,6 @@
/**
* Map of loggers keyed by logger name.
- *
- * @since 0.6
*/
private Map<String, Logger> loggerMap;
@@ -61,8 +54,6 @@
/**
* Constructs the factory.
- *
- * @since 0.6
*/
public EclipseLoggerFactory() {
this.loggerMap = new HashMap<String, Logger>();
@@ -76,7 +67,6 @@
* {@inheritDoc}
*
* @see org.slf4j.ILoggerFactory#getLogger(java.lang.String)
- * @since 0.6
*/
@Override
public Logger getLogger( String name ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/StaticLoggerBinder.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/StaticLoggerBinder.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/log/StaticLoggerBinder.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -26,9 +26,6 @@
import org.slf4j.ILoggerFactory;
import org.slf4j.spi.LoggerFactoryBinder;
-/**
- * @since 0.6
- */
public final class StaticLoggerBinder implements LoggerFactoryBinder {
// ===========================================================================================================================
@@ -37,15 +34,11 @@
/**
* The class name of the logger factory.
- *
- * @since 0.6
*/
private static final String LOGGER_FACTORY_CLASS_NAME = EclipseLoggerFactory.class.getName();
/**
* The unique instance of this class.
- *
- * @since 0.6
*/
private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
@@ -55,7 +48,6 @@
/**
* @return the static instance of the logger
- * @since 0.6
*/
public static final StaticLoggerBinder getSingleton() {
return SINGLETON;
@@ -67,11 +59,9 @@
/**
* The logger factory used.
- *
- * @since 0.6
*/
private final ILoggerFactory loggerFactory = new EclipseLoggerFactory();
-
+
// ===========================================================================================================================
// Methods
// ===========================================================================================================================
@@ -80,7 +70,6 @@
* {@inheritDoc}
*
* @see org.slf4j.spi.LoggerFactoryBinder#getLoggerFactory()
- * @since 0.6
*/
@Override
public ILoggerFactory getLoggerFactory() {
@@ -91,7 +80,6 @@
* {@inheritDoc}
*
* @see org.slf4j.spi.LoggerFactoryBinder#getLoggerFactoryClassStr()
- * @since 0.6
*/
@Override
public String getLoggerFactoryClassStr() {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/DnaPreferencePage.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/DnaPreferencePage.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/DnaPreferencePage.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -43,8 +43,6 @@
/**
* The <code>DnaPreferencePage</code> is the UI for managing all DNA-related preferences.
- *
- * @since 0.6
*/
public final class DnaPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
@@ -54,15 +52,11 @@
/**
* The editor used to manage the list of filtered file extensions.
- *
- * @since 0.6
*/
private FilteredFileExtensionEditor extensionsEditor;
/**
* The editor used to manage the list of filtered folder names.
- *
- * @since 0.6
*/
private FilteredFoldersEditor foldersEditor;
@@ -74,7 +68,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
- * @since 0.6
*/
@Override
protected Control createContents( Composite parent ) {
@@ -109,7 +102,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.DialogPage#getDescription()
- * @since 0.6
*/
@Override
public String getDescription() {
@@ -120,7 +112,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.DialogPage#getImage()
- * @since 0.6
*/
@Override
public Image getImage() {
@@ -131,7 +122,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.DialogPage#getMessage()
- * @since 0.6
*/
@Override
public String getMessage() {
@@ -142,7 +132,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.PreferencePage#getPreferenceStore()
- * @since 0.6
*/
@Override
public IPreferenceStore getPreferenceStore() {
@@ -153,7 +142,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.DialogPage#getTitle()
- * @since 0.6
*/
@Override
public String getTitle() {
@@ -164,7 +152,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
- * @since 0.6
*/
@Override
public void init( IWorkbench workbench ) {
@@ -175,7 +162,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.PreferencePage#performDefaults()
- * @since 0.6
*/
@Override
protected void performDefaults() {
@@ -188,7 +174,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.PreferencePage#performOk()
- * @since 0.6
*/
@Override
public boolean performOk() {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFileExtensionEditor.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFileExtensionEditor.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFileExtensionEditor.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -18,8 +18,6 @@
/**
* The <code>FilteredFileExtensionEditor</code> is an editor for managing a set of filtered file extensions.
- *
- * @since 0.6
*/
public final class FilteredFileExtensionEditor extends ListEditor implements VerifyListener {
@@ -29,8 +27,6 @@
/**
* The current set of file extensions.
- *
- * @since 0.6
*/
private final Set<String> extensions;
@@ -40,7 +36,6 @@
/**
* @param parent the parent control
- * @since 0.6
*/
public FilteredFileExtensionEditor( Composite parent ) {
super(FILTERED_FILE_EXTENSIONS_PREFERENCE, dnaPreferencePageFilteredFileExtensionsLabel.text(), parent);
@@ -55,7 +50,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.ListEditor#createList(java.lang.String[])
- * @since 0.6
*/
@Override
protected String createList( String[] items ) {
@@ -66,7 +60,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.ListEditor#getNewInputObject()
- * @since 0.6
*/
@Override
protected String getNewInputObject() {
@@ -91,7 +84,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.ListEditor#parseString(java.lang.String)
- * @since 0.6
*/
@Override
protected String[] parseString( String stringList ) {
@@ -107,7 +99,6 @@
* {@inheritDoc}
*
* @see org.eclipse.swt.events.VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
- * @since 0.6
*/
@Override
public void verifyText( VerifyEvent event ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFoldersEditor.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFoldersEditor.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFoldersEditor.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -18,8 +18,6 @@
/**
* The <code>FilteredFoldersEditor</code> is an editor for managing a set of folder names.
- *
- * @since 0.6
*/
public final class FilteredFoldersEditor extends ListEditor implements VerifyListener {
@@ -29,8 +27,6 @@
/**
* The current set of folder names.
- *
- * @since 0.6
*/
private final Set<String> folderNames;
@@ -40,7 +36,6 @@
/**
* @param parent the parent control
- * @since 0.6
*/
public FilteredFoldersEditor( Composite parent ) {
super(FILTERED_FOLDER_NAMES_PREFERENCE, dnaPreferencePageFilteredFolderNamesLabel.text(), parent);
@@ -55,7 +50,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.ListEditor#createList(java.lang.String[])
- * @since 0.6
*/
@Override
protected String createList( String[] items ) {
@@ -66,7 +60,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.ListEditor#getNewInputObject()
- * @since 0.6
*/
@Override
protected String getNewInputObject() {
@@ -91,7 +84,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.preference.ListEditor#parseString(java.lang.String)
- * @since 0.6
*/
@Override
protected String[] parseString( String stringList ) {
@@ -107,7 +99,6 @@
* {@inheritDoc}
*
* @see org.eclipse.swt.events.VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
- * @since 0.6
*/
@Override
public void verifyText( VerifyEvent event ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewItemDialog.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewItemDialog.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewItemDialog.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -19,8 +19,6 @@
/**
* A <code>NewItemDialog</code> allows the user to enter a new file extension or new folder name that they wish to be filtered out
* of all publishing operations.
- *
- * @since 0.6
*/
public final class NewItemDialog extends Dialog implements ModifyListener {
@@ -30,29 +28,21 @@
/**
* The label describing the new item.
- *
- * @since 0.6
*/
private final String label;
/**
* The contents of the new item text field.
- *
- * @since 0.6
*/
private String newItem;
/**
* The dialog title.
- *
- * @since 0.6
*/
private final String title;
/**
* The listener verifying input characters.
- *
- * @since 0.6
*/
private final VerifyListener verifyListener;
@@ -65,7 +55,6 @@
* @param title the localized dialog title (never <code>null</code>)
* @param label the localized label (never <code>null</code>)
* @param verifyListener a listener that validates input characters (may be <code>null</code>)
- * @since 0.6
*/
public NewItemDialog( Shell parentShell,
String title,
@@ -91,7 +80,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
- * @since 0.6
*/
@Override
protected void configureShell( Shell newShell ) {
@@ -103,7 +91,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.Dialog#createButton(org.eclipse.swt.widgets.Composite, int, java.lang.String, boolean)
- * @since 0.6
*/
@Override
protected Button createButton( Composite parent,
@@ -124,7 +111,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
- * @since 0.6
*/
@Override
protected Control createDialogArea( Composite parent ) {
@@ -150,7 +136,6 @@
/**
* @return the new item or <code>null</code> if the dialog was canceled
- * @since 0.6
*/
public String getNewItem() {
if (getReturnCode() == OK) {
@@ -164,7 +149,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
- * @since 0.6
*/
@Override
protected void initializeBounds() {
@@ -180,7 +164,6 @@
* {@inheritDoc}
*
* @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
- * @since 0.6
*/
@Override
public void modifyText( ModifyEvent event ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PrefUtils.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PrefUtils.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PrefUtils.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -39,8 +39,6 @@
/**
* The <code>PrefUtils</code> class provides common utilities relating to preferences. This class assumes the Eclipse runtime
* platform is running.
- *
- * @since 0.6
*/
public final class PrefUtils {
@@ -145,8 +143,8 @@
} else {
// make sure preference has a value
FILE_EXT_INVALID_CHARS = "*?<>|/\\:;."; //$NON-NLS-1$
- Activator.getDefault().log(new Status(Severity.ERROR, prefUtilsPropertyNotFound.text(FILE_EXT_INVALID_CHARS_PREF_NAME),
- null));
+ Activator.getDefault().log(new Status(Severity.ERROR,
+ prefUtilsPropertyNotFound.text(FILE_EXT_INVALID_CHARS_PREF_NAME), null));
}
// folder names delimiter
@@ -180,7 +178,6 @@
/**
* @return the file extensions being filtered out of publishing operations (never null)
- * @since 0.6
*/
public static String[] getFilteredFileExtensions() {
return getListPropertyValue(FILTERED_FILE_EXTENSIONS_PREFERENCE, FILE_EXT_DELIMITER, true);
@@ -188,7 +185,6 @@
/**
* @return the folder names being filtered out of publishing operations (never null)
- * @since 0.6
*/
public static String[] getFilteredFolderNames() {
return getListPropertyValue(FILTERED_FOLDER_NAMES_PREFERENCE, FOLDER_NAME_DELIMITER, true);
@@ -199,7 +195,6 @@
* @param delimiter the character separating the items in the property value
* @param removeDuplicates a flag indicating if duplicate items should be removed
* @return the property value items (never <code>null</code>)
- * @since 0.6
*/
public static String[] getListPropertyValue( String propertyId,
char delimiter,
@@ -210,7 +205,6 @@
/**
* @return the plugin preference store
- * @since 0.6
*/
public static IPreferenceStore getPreferenceStore() {
return Activator.getDefault().getPreferenceStore();
@@ -220,7 +214,6 @@
* @param propertyId the property name being set (never <code>null</code>)
* @param items the items used to create the property value (never <code>null</code>)
* @param delimiter the character to use to separate the items
- * @since 0.6
*/
public static void setListPropertyValue( String propertyId,
String[] items,
@@ -236,8 +229,6 @@
/**
* Don't allow construction.
- *
- * @since 0.6
*/
private PrefUtils() {
// nothing to do
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PublishingFileFilter.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PublishingFileFilter.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PublishingFileFilter.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -30,8 +30,6 @@
/**
* The <code>PublishingFileFilter</code> is a file filter that uses the DNA preferences when filtering files.
- *
- * @since 0.6
*/
public final class PublishingFileFilter {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaContentProvider.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaContentProvider.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaContentProvider.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -50,8 +50,6 @@
/**
* The <code>DnaContentProvider</code> is a content and label provider for DNA repositories.
- *
- * @since 0.6
*/
public final class DnaContentProvider extends ColumnLabelProvider implements ILightweightLabelDecorator, ITreeContentProvider {
@@ -61,8 +59,6 @@
/**
* The decorator ID.
- *
- * @since 0.6
*/
private static final String ID = "org.jboss.dna.web.jcr.rest.client.swt.dnaDecorator"; //$NON-NLS-1$
@@ -72,7 +68,6 @@
/**
* @return the DNA decorator
- * @since 0.6
*/
public static DnaContentProvider getDecorator() {
IDecoratorManager decoratorMgr = Activator.getDefault().getWorkbench().getDecoratorManager();
@@ -90,8 +85,6 @@
/**
* The server manager where the server registry is managed.
- *
- * @since 0.6
*/
private ServerManager serverManager;
@@ -103,7 +96,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
- * @since 0.6
*/
@Override
public void addListener( ILabelProviderListener listener ) {
@@ -114,7 +106,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object, org.eclipse.jface.viewers.IDecoration)
- * @since 0.6
*/
@Override
public void decorate( final Object element,
@@ -145,7 +136,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IContentProvider#dispose()
- * @since 0.6
*/
@Override
public void dispose() {
@@ -156,7 +146,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
- * @since 0.6
*/
@Override
public Object[] getChildren( Object parentElement ) {
@@ -189,7 +178,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
- * @since 0.6
*/
@Override
public Object[] getElements( Object inputElement ) {
@@ -200,7 +188,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
- * @since 0.6
*/
@Override
public Image getImage( Object element ) {
@@ -211,7 +198,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
- * @since 0.6
*/
@Override
public Object getParent( Object element ) {
@@ -231,7 +217,6 @@
/**
* @return the server manager or <code>null</code>
- * @since 0.6
*/
private ServerManager getServerManager() {
if (this.serverManager == null) {
@@ -245,7 +230,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
- * @since 0.6
*/
@Override
public String getText( Object element ) {
@@ -257,7 +241,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipImage(java.lang.Object)
- * @since 0.6
*/
@Override
public Image getToolTipImage( Object object ) {
@@ -268,7 +251,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object)
- * @since 0.6
*/
@Override
public String getToolTipText( Object element ) {
@@ -283,7 +265,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipTimeDisplayed(java.lang.Object)
- * @since 0.6
*/
@Override
public int getToolTipTimeDisplayed( Object object ) {
@@ -294,7 +275,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
- * @since 0.6
*/
@Override
public boolean hasChildren( Object element ) {
@@ -306,7 +286,6 @@
*
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object,
* java.lang.Object)
- * @since 0.6
*/
@Override
public void inputChanged( Viewer viewer,
@@ -319,7 +298,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String)
- * @since 0.6
*/
@Override
public boolean isLabelProperty( Object element,
@@ -352,7 +330,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
- * @since 0.6
*/
@Override
public void removeListener( ILabelProviderListener listener ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaMessageConsole.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaMessageConsole.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaMessageConsole.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -49,8 +49,6 @@
/**
* The <code>DnaMessageConsole</code> is a message console view where status of publishing operations are logged. This class
* ensures all writes to the console are done in the UI thread.
- *
- * @since 0.6
*/
public final class DnaMessageConsole extends MessageConsole {
@@ -70,15 +68,11 @@
/**
* The identifier and type of the DNA Message Console.
- *
- * @since 0.6
*/
private static final String ID = "org.jboss.dna.web.jcr.rest.client.swt.views.DnaMessageConsole"; //$NON-NLS-1$
/**
* The message console name.
- *
- * @since 0.6
*/
private static final String NAME = RestClientI18n.dnaConsoleName.text();
@@ -90,7 +84,6 @@
* Note: The <code>DnaMessageConsole</code> should <strong>NOT</strong> be cached as the user can open/close/create instances.
*
* @return the DNA Message Console if available or a new one (never <code>null</code>)
- * @since 0.6
*/
private static DnaMessageConsole getDnaConsole() {
DnaMessageConsole console = null;
@@ -118,7 +111,6 @@
* Adds a line feed to the console after the message is printed.
*
* @param message the message being written to the console (never <code>null</code>)
- * @since 0.6
*/
public static void writeln( String message ) {
CheckArg.isNotNull(message, "message"); //$NON-NLS-1$
@@ -131,7 +123,6 @@
* @param message the message being written to the console (never <code>null</code>)
* @param file the file whose full path, which is contained in the message, will be made into a hyperlink (may be
* <code>null</code>)
- * @since 0.6
*/
public static void writeln( String message,
IFile file ) {
@@ -146,7 +137,7 @@
// =======================================================================================================================
/**
- * @since 0.6
+ * Prevent construction.
*/
private DnaMessageConsole() {
super(NAME, Activator.getDefault().getImageDescriptor(DNA_IMAGE_16x));
@@ -159,7 +150,6 @@
/**
* @param message the message being searched for
* @param file the file whose full path appears in the message and will become a hyperlink
- * @since 0.6
*/
void addDocumentListener( String message,
IFile file ) {
@@ -170,7 +160,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.console.AbstractConsole#getType()
- * @since 0.6
*/
@Override
public String getType() {
@@ -181,7 +170,6 @@
* @param message the message being written to the console (never <code>null</code>)
* @param doLineFeedAtEnd a flag indicating if a line feed should be done after writing the message
* @param file the file to create a hyperlink for (may be <code>null</code>)
- * @since 0.6
*/
private void print( final String message,
final boolean doLineFeedAtEnd,
@@ -195,7 +183,6 @@
* {@inheritDoc}
*
* @see java.lang.Runnable#run()
- * @since 0.6
*/
@Override
public void run() {
@@ -234,6 +221,7 @@
endIndex = ((startTagIndex < 0) ? msgLength : startTagIndex);
}
+ // print to console
stream.print(message.substring(beginIndex, endIndex));
// need to construct a new stream as changes to font style seem to only work one time
@@ -263,8 +251,6 @@
/**
* The <code>HyperlinkCreator</code> creates a hyperlink in a DNA Message Console for the first occurrence of the full path of
* a specified file.
- *
- * @since 0.6
*/
class HyperlinkCreator implements IDocumentListener {
@@ -274,22 +260,16 @@
/**
* The console where the message is printed to and the hyperlink will be created.
- *
- * @since 0.6
*/
private final DnaMessageConsole console;
/**
* The file whose full path will become a hyperlink.
- *
- * @since 0.6
*/
private final IFile file;
/**
* The message where the file path is located in.
- *
- * @since 0.6
*/
private final String message;
@@ -301,7 +281,6 @@
* @param message the message that contains the full path of the file
* @param console the console where the message appears
* @param file the file whose full path appears in the message and will become a hyperlink
- * @since 0.6
*/
public HyperlinkCreator( String message,
DnaMessageConsole console,
@@ -319,7 +298,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
- * @since 0.6
*/
@Override
public void documentAboutToBeChanged( DocumentEvent arg0 ) {
@@ -330,7 +308,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
- * @since 0.6
*/
@Override
public void documentChanged( DocumentEvent event ) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/ServerView.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/ServerView.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/ServerView.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -58,8 +58,6 @@
/**
* The <code>ServerView</code> shows all defined servers and their DNA repositories.
- *
- * @since 0.6
*/
public final class ServerView extends ViewPart implements IServerRegistryListener {
@@ -69,58 +67,40 @@
/**
* Collapses all tree nodes.
- *
- * @since 0.6
*/
private IAction collapseAllAction;
/**
* Deletes a server.
- *
- * @since 0.6
*/
private BaseSelectionListenerAction deleteAction;
/**
* Edits a server's properties.
- *
- * @since 0.6
*/
private BaseSelectionListenerAction editAction;
/**
* Creates a new server.
- *
- * @since 0.6
*/
private Action newAction;
/**
* The viewer's content and label provider.
- *
- * @since 0.6
*/
private DnaContentProvider provider;
/**
* Refreshes the server connections.
- *
- * @since 0.6
*/
private ReconnectToServerAction reconnectAction;
- /**
- * @since 0.6
- */
private TreeViewer viewer;
// ===========================================================================================================================
// Methods
// ===========================================================================================================================
- /**
- * @since 0.6
- */
private void constructActions() {
// the collapse all action is always enabled
this.collapseAllAction = new Action() {
@@ -157,9 +137,6 @@
this.newAction = new NewServerAction(shell, getServerManager());
}
- /**
- * @since 0.6
- */
private void constructContextMenu() {
MenuManager menuMgr = new MenuManager();
menuMgr.add(this.newAction);
@@ -172,9 +149,6 @@
getSite().registerContextMenu(menuMgr, this.viewer);
}
- /**
- * @since 0.6
- */
private void constructToolBar() {
IToolBarManager toolBar = getViewSite().getActionBars().getToolBarManager();
toolBar.add(this.newAction);
@@ -186,7 +160,6 @@
/**
* @param parent the viewer's parent
- * @since 0.6
*/
private void constructTreeViewer( Composite parent ) {
this.provider = new DnaContentProvider();
@@ -228,7 +201,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
- * @since 0.6
*/
@Override
public void createPartControl( Composite parent ) {
@@ -251,7 +223,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.part.WorkbenchPart#dispose()
- * @since 0.6
*/
@Override
public void dispose() {
@@ -261,7 +232,6 @@
/**
* @return the server manager being used by this view
- * @since 0.6
*/
private ServerManager getServerManager() {
return Activator.getDefault().getServerManager();
@@ -269,7 +239,6 @@
/**
* @return the tree viewer
- * @since 0.6
*/
TreeViewer getViewer() {
return this.viewer;
@@ -277,8 +246,6 @@
/**
* Opens a dialog to edit server properties.
- *
- * @since 0.6
*/
void handleDoubleClick() {
this.editAction.run();
@@ -286,7 +253,6 @@
/**
* @param event the event being processed
- * @since 0.6
*/
void handleSelectionChanged( SelectionChangedEvent event ) {
updateStatusLine((IStructuredSelection)event.getSelection());
@@ -296,7 +262,6 @@
* {@inheritDoc}
*
* @see org.jboss.dna.web.jcr.rest.client.IServerRegistryListener#serverRegistryChanged(org.jboss.dna.web.jcr.rest.client.ServerRegistryEvent)
- * @since 0.6
*/
@Override
public Exception[] serverRegistryChanged( ServerRegistryEvent event ) {
@@ -313,7 +278,6 @@
* {@inheritDoc}
*
* @see org.eclipse.ui.part.WorkbenchPart#setFocus()
- * @since 0.6
*/
@Override
public void setFocus() {
@@ -324,7 +288,6 @@
/**
* @param selection the current viewer selection (never <code>null</code>)
- * @since 0.6
*/
private void updateStatusLine( IStructuredSelection selection ) {
assert (selection.size() < 2);
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishPage.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishPage.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishPage.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -70,8 +70,6 @@
/**
* The <code>PublishPage</code> is a UI for publishing or unpublishing one or more files to a DNA repository.
- *
- * @since 0.6
*/
public final class PublishPage extends WizardPage implements IServerRegistryListener, ModifyListener {
@@ -81,8 +79,6 @@
/**
* The key in the wizard <code>IDialogSettings</code> for the recurse flag.
- *
- * @since 0.6
*/
private static final String RECURSE_KEY = "recurse"; //$NON-NLS-1$
@@ -92,8 +88,6 @@
/**
* Indicates if the file filter should be used.
- *
- * @since 0.6
*/
private static boolean filterFiles = true;
@@ -107,7 +101,6 @@
* @param filter the file filter or <code>null</code> if not used
* @return the list of files contained in the specified container (never <code>null</code>)
* @throws CoreException if there is a problem finding the files
- * @since 0.6
*/
private static List<IFile> findFiles( IContainer container,
boolean recurse,
@@ -141,7 +134,6 @@
* @param filter the file filter or <code>null</code> if not used
* @return the files being published or unpublished (never <code>null</code>)
* @throws CoreException if there is a problem processing the resources
- * @since 0.6
*/
private static List<IFile> processResources( List<IResource> resources,
boolean recurse,
@@ -171,7 +163,7 @@
IProject project = null;
List<IFile> files = null;
Map<IContainer, List<IFile>> parentMap = null;
-
+
// see if resource is filtered
if ((filter != null) && !filter.accept(resource)) {
continue;
@@ -277,30 +269,22 @@
// ===========================================================================================================================
/**
- * The repository control.
- *
- * @since 0.6
+ * The repository chooser control.
*/
private Combo cbxRepository;
/**
- * The server control.
- *
- * @since 0.6
+ * The server chooser control.
*/
private Combo cbxServer;
/**
- * The workspace control.
- *
- * @since 0.6
+ * The workspace chooser control.
*/
private Combo cbxWorkspace;
/**
* The files being published or unpublished (never <code>null</code>).
- *
- * @since 0.6
*/
private List<IFile> files;
@@ -311,78 +295,56 @@
/**
* The control containing all the files being published or unpublished.
- *
- * @since 0.6
*/
private org.eclipse.swt.widgets.List lstResources;
/**
* The flag indicating if child containers should be traversed.
- *
- * @since 0.6
*/
private boolean recurse;
/**
* A collection of repositories for the selected server (never <code>null</code>).
- *
- * @since 0.6
*/
private List<Repository> repositories;
/**
* The repository where the workspace is located.
- *
- * @since 0.6
*/
private Repository repository;
/**
* The collection of resources selected by the user to be published or unpublished.
- *
- * @since 0.6
*/
private final List<IResource> resources;
/**
* The server where the repository is located.
- *
- * @since 0.6
*/
private Server server;
/**
* A collection of servers from the server registry (never <code>null</code>).
- *
- * @since 0.6
*/
private List<Server> servers;
/**
* The current validation status.
- *
- * @since 0.6
*/
private Status status;
/**
* Indicates if publishing or unpublishing is being done.
- *
- * @since 0.6
*/
private final Type type;
/**
* The workspace where the resources are being published/unpublished (may be <code>null</code>).
- *
- * @since 0.6
*/
private Workspace workspace;
/**
* A collection of workspaces for the selected server repository (never <code>null</code>).
- *
- * @since 0.6
*/
private List<Workspace> workspaces;
@@ -394,7 +356,6 @@
* @param type indicates if publishing or unpublishing is being done
* @param resources the resources being published or unpublished (never <code>null</code>)
* @throws CoreException if there is a problem processing the input resources
- * @since 0.6
*/
public PublishPage( Type type,
List<IResource> resources ) throws CoreException {
@@ -612,7 +573,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.DialogPage#dispose()
- * @since 0.6
*/
@Override
public void dispose() {
@@ -622,7 +582,6 @@
/**
* @return the files to publish or unpublish (never <code>null</code>)
- * @since 0.6
*/
public List<IFile> getFiles() {
return this.files;
@@ -630,8 +589,6 @@
/**
* Updates the initial page message.
- *
- * @since 0.6
*/
void updateInitialMessage() {
String msg = ((this.type == Type.PUBLISH) ? RestClientI18n.publishPagePublishOkStatusMsg.text()
@@ -644,7 +601,6 @@
/**
* @return the server manager obtained from the wizard
- * @since 0.6
*/
private ServerManager getServerManager() {
return ((PublishWizard)getWizard()).getServerManager();
@@ -652,7 +608,6 @@
/**
* @return thw workspace to use when publishing or unpublishing (page must be complete)
- * @since 0.6
*/
public Workspace getWorkspace() {
assert isPageComplete();
@@ -663,7 +618,6 @@
* Saves the recurse setting and reloads the files to be published or unpublished.
*
* @param selected the flag indicating the new recurse setting
- * @since 0.6
*/
void handleRecurseChanged( boolean selected ) {
this.recurse = selected;
@@ -685,8 +639,6 @@
/**
* Handler for when the repository control value is modified
- *
- * @since 0.6
*/
void handleRepositoryModified() {
int index = this.cbxRepository.getSelectionIndex();
@@ -705,8 +657,6 @@
/**
* Handler for when the server control value is modified
- *
- * @since 0.6
*/
void handleServerModified() {
int index = this.cbxServer.getSelectionIndex();
@@ -725,8 +675,6 @@
/**
* Handler for when the workspace control value is modified
- *
- * @since 0.6
*/
void handleWorkspaceModified() {
int index = this.cbxWorkspace.getSelectionIndex();
@@ -742,7 +690,6 @@
/**
* @return the flag indicating if resources found recursively under projects and folders should also be published or
* unpublished
- * @since 0.6
*/
protected boolean isRecursing() {
boolean recurse = true;
@@ -757,8 +704,6 @@
/**
* Populates the list of files to be published based on the recurse flag and the list of workspace selected resources.
* Pre-condition is that {@link #processResources(List, boolean, PublishingFileFilter)} has been called.
- *
- * @since 0.6
*/
private void loadFiles() {
this.lstResources.removeAll();
@@ -789,8 +734,6 @@
/**
* Refreshes the repository-related fields and controls based on the server registry. This in turn causes the workspaces to
* also to be refreshed.
- *
- * @since 0.6
*/
private void refreshRepositories() {
this.repository = null;
@@ -848,8 +791,6 @@
/**
* Refreshes the server-related fields and controls based on the server registry. This in turn causes the repositories and
* workspaces to also to be refreshed.
- *
- * @since 0.6
*/
void refreshServers() {
this.server = null;
@@ -890,8 +831,6 @@
/**
* Refreshes the workspace-related fields and controls based on the server registry.
- *
- * @since 0.6
*/
private void refreshWorkspaces() {
this.workspace = null;
@@ -945,8 +884,6 @@
/**
* Saves the current recurse value to the wizard <code>IDialogSettings</code>.
- *
- * @since 0.6
*/
protected void saveRecurseSetting() {
getDialogSettings().put(RECURSE_KEY, this.recurse);
@@ -956,7 +893,6 @@
* {@inheritDoc}
*
* @see org.jboss.dna.web.jcr.rest.client.IServerRegistryListener#serverRegistryChanged(org.jboss.dna.web.jcr.rest.client.ServerRegistryEvent)
- * @since 0.6
*/
@Override
public Exception[] serverRegistryChanged( ServerRegistryEvent event ) {
@@ -973,7 +909,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean)
- * @since 0.6
*/
@Override
public void setVisible( boolean visible ) {
@@ -1001,7 +936,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.wizard.WizardPage#setWizard(org.eclipse.jface.wizard.IWizard)
- * @since 0.6
*/
@Override
public void setWizard( IWizard newWizard ) {
@@ -1019,8 +953,6 @@
/**
* Updates message, message icon, and OK button enablement based on validation results
- *
- * @since 0.6
*/
void updateState() {
// get the current state
@@ -1045,8 +977,6 @@
/**
* Validates all inputs and sets the validation status.
- *
- * @since 0.6
*/
private void validate() {
String msg = null;
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishWizard.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishWizard.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishWizard.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -39,7 +39,7 @@
import org.jboss.dna.web.jcr.rest.client.swt.jobs.PublishJob.Type;
/**
- * @since 0.6
+ * The <code>PublishWizard</code> is the wizard that published and unpublishes resources.
*/
public final class PublishWizard extends Wizard {
@@ -49,22 +49,16 @@
/**
* The wizard page containing all the controls that allow publishing/unpublishing of resources.
- *
- * @since 0.6
*/
private final PublishPage page;
/**
* The manager in charge of the server registry.
- *
- * @since 0.6
*/
private final ServerManager serverManager;
/**
* Indicates if the wizard will perform a publishing or unpublishing operation.
- *
- * @since 0.6
*/
private final Type type;
@@ -77,7 +71,6 @@
* @param resources the resources being published or unpublished (never <code>null</code>)
* @param serverManager the server manager in charge of the server registry (never <code>null</code>)
* @throws CoreException if there is a problem processing the resources
- * @since 0.6
*/
public PublishWizard( Type type,
List<IResource> resources,
@@ -90,7 +83,8 @@
this.page = new PublishPage(type, resources);
this.serverManager = serverManager;
- setWindowTitle((type == Type.PUBLISH) ? RestClientI18n.publishWizardPublishTitle.text() : RestClientI18n.publishWizardUnpublishTitle.text());
+ setWindowTitle((type == Type.PUBLISH) ? RestClientI18n.publishWizardPublishTitle.text()
+ : RestClientI18n.publishWizardUnpublishTitle.text());
setDefaultPageImageDescriptor(Activator.getDefault().getImageDescriptor(DNA_WIZARD_BANNER_IMAGE));
}
@@ -102,7 +96,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.wizard.Wizard#addPages()
- * @since 0.6
*/
@Override
public void addPages() {
@@ -113,7 +106,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.wizard.Wizard#getDialogSettings()
- * @since 0.6
*/
@Override
public IDialogSettings getDialogSettings() {
@@ -135,7 +127,6 @@
/**
* @return the server manager (never <code>null</code>)
- * @since 0.6
*/
protected ServerManager getServerManager() {
return this.serverManager;
@@ -145,7 +136,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.wizard.Wizard#performFinish()
- * @since 0.6
*/
@Override
public boolean performFinish() {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerPage.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerPage.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerPage.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -53,7 +53,7 @@
import org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n;
/**
- * @since 0.6
+ * The <code>ServerPage</code> is used to create or modify a server.
*/
public final class ServerPage extends WizardPage {
@@ -63,50 +63,36 @@
/**
* The button used to test the connection to the server. Should only be enabled when server properties are valid.
- *
- * @since 0.6
*/
private Button btnTestConnection;
/**
* The user password needed to login to the server.
- *
- * @since 0.6
*/
private String password;
/**
* Indicates if the password should be persisted.
- *
- * @since 0.6
*/
private boolean savePassword;
/**
* The server being editor or <code>null</code> if creating a new server.
- *
- * @since 0.6
*/
private Server server;
/**
* The current validation status.
- *
- * @since 0.6
*/
private Status status;
/**
* The server URL.
- *
- * @since 0.6
*/
private String url;
/**
* The user needed to login to the server.
- *
- * @since 0.6
*/
private String user;
@@ -116,8 +102,6 @@
/**
* Constructs a wizard page that will create a new server.
- *
- * @since 0.6
*/
public ServerPage() {
super(ServerPage.class.getSimpleName());
@@ -129,7 +113,6 @@
* Constructs a wizard page that edits the specified server's properties.
*
* @param server the server being edited
- * @since 0.6
*/
public ServerPage( Server server ) {
super(ServerPage.class.getSimpleName());
@@ -354,7 +337,6 @@
* @return the server represented by the dialog inputs
* @throws RuntimeException if called when all inputs are not valid
* @see #isPageComplete()
- * @since 0.6
*/
public Server getServer() {
if (!this.status.isError()) {
@@ -367,7 +349,6 @@
/**
* @return the server manager obtained from the wizard
- * @since 0.6
*/
ServerManager getServerManager() {
return ((ServerWizard)getWizard()).getServerManager();
@@ -377,7 +358,6 @@
* Handler for when the password control value is modified
*
* @param newPassword the new password value
- * @since 0.6
*/
void handlePasswordModified( String newPassword ) {
this.password = newPassword;
@@ -386,7 +366,6 @@
/**
* @param savePassword <code>true</code> if the password should be persisted on the local file system
- * @since 0.6
*/
void handleSavePasswordChanged( boolean savePassword ) {
this.savePassword = savePassword;
@@ -395,8 +374,6 @@
/**
* Tests the connection of the server specified by the properties entered on this page. Precondition is that server properties
* are valid.
- *
- * @since 0.6
*/
void handleTestConnection() {
final Server server = getServer();
@@ -429,7 +406,6 @@
* Handler for when the URL control value is modified
*
* @param newUrl the new URL value
- * @since 0.6
*/
void handleUrlModified( String newUrl ) {
this.url = newUrl;
@@ -440,7 +416,6 @@
* Handler for when the user control value is modified
*
* @param newUser the new user value
- * @since 0.6
*/
void handleUserModified( String newUser ) {
this.user = newUser;
@@ -449,8 +424,6 @@
/**
* If the initial message is being displayed do a validation.
- *
- * @since 0.6
*/
void updateInitialMessage() {
if (RestClientI18n.serverPageOkStatusMsg.text().equals(getMessage())) {
@@ -462,7 +435,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean)
- * @since 0.6
*/
@Override
public void setVisible( boolean visible ) {
@@ -479,8 +451,6 @@
/**
* Updates message, message icon, and OK button enablement based on validation results
- *
- * @since 0.6
*/
private void updateState() {
// get the current status
@@ -506,8 +476,6 @@
/**
* Validates all inputs and sets the validation status.
- *
- * @since 0.6
*/
private void validate() {
if (this.server == null) {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerWizard.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerWizard.java 2009-09-17 19:46:22 UTC (rev 1230)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerWizard.java 2009-09-17 20:35:09 UTC (rev 1231)
@@ -33,7 +33,7 @@
import org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n;
/**
- * @since 0.6
+ * The <code>ServerWizard</code> is the wizard used to create and edit servers.
*/
public final class ServerWizard extends Wizard {
@@ -43,22 +43,16 @@
/**
* Non-<code>null</code> if the wizard is editing an existing server.
- *
- * @since 0.6
*/
private Server existingServer;
/**
* The wizard page containing all the controls that allow editing of server properties.
- *
- * @since 0.6
*/
private final ServerPage page;
/**
* The manager in charge of the server registry.
- *
- * @since 0.6
*/
private final ServerManager serverManager;
@@ -70,7 +64,6 @@
* Constructs a wizard that creates a new server.
*
* @param serverManager the server manager in charge of the server registry (never <code>null</code>)
- * @since 0.6
*/
public ServerWizard( ServerManager serverManager ) {
this.page = new ServerPage();
@@ -85,7 +78,6 @@
*
* @param serverManager the server manager in charge of the server registry (never <code>null</code>)
* @param server the server whose properties are being edited (never <code>null</code>)
- * @since 0.6
*/
public ServerWizard( ServerManager serverManager,
Server server ) {
@@ -103,7 +95,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.wizard.Wizard#addPages()
- * @since 0.6
*/
@Override
public void addPages() {
@@ -112,7 +103,6 @@
/**
* @return the server manager (never <code>null</code>)
- * @since 0.6
*/
protected ServerManager getServerManager() {
return this.serverManager;
@@ -122,7 +112,6 @@
* {@inheritDoc}
*
* @see org.eclipse.jface.wizard.Wizard#performFinish()
- * @since 0.6
*/
@Override
public boolean performFinish() {
14 years, 7 months
DNA SVN: r1230 - in branches/eclipse/org.jboss.dna.publish.ui.swt: docs and 7 other directories.
by dna-commits@lists.jboss.org
Author: elvisisking
Date: 2009-09-17 15:46:22 -0400 (Thu, 17 Sep 2009)
New Revision: 1230
Added:
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaPreferencesDialog.html
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/PublishedLocationsDialog.html
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/DnaPreferencesDialog.png
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/NewFilteredFileExtension.png
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/NewFilteredFolderName.png
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/OperationDialog.png
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/PublishedLocationsDialog.png
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/ServerViewContextMenu.png
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/PublishJob.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/ReconnectJob.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFileExtensionEditor.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFoldersEditor.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewItemDialog.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PublishingFileFilter.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/dnaPrefs.properties
Removed:
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/ExtensionEditor.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewFileExtensionDialog.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishJob.java
Modified:
branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.6-SNAPSHOT-jar-with-dependencies.jar
branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.6-SNAPSHOT-sources.jar
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaConsole.html
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaServerView.html
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaToc.html
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/PublishDialog.html
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/ResourceContextMenu.html
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/ServerDialog.html
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/helpContexts.xml
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/DnaConsole.png
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/ResourceDnaContextMenu.png
branches/eclipse/org.jboss.dna.publish.ui.swt/docs/toc.xml
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/IUiConstants.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.properties
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Utils.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/BasePublishingAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/EditServerAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/NewServerAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/PublishAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ReconnectToServerAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/UnpublishAction.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/DnaPreferencePage.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PrefUtils.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaContentProvider.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaMessageConsole.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/ServerView.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishPage.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishWizard.java
branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerPage.java
Log:
Updated help documents. Added a way to add emphasis (bold) messages or message segments that appear in the DNA Message Console. Added a preference that filters out files that are contained in folders that match a list of folder names created by the user. Added a UI to the preference page for this. Create a "jobs" package and refactored PublishJob and created a ReconnectJob. The reconnect action was rewritten to use this new job. Added javadocs for anonymous inner classes.
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.6-SNAPSHOT-jar-with-dependencies.jar
===================================================================
(Binary files differ)
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/dna-web-jcr-rest-client-0.6-SNAPSHOT-sources.jar
===================================================================
(Binary files differ)
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaConsole.html
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaConsole.html 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaConsole.html 2009-09-17 19:46:22 UTC (rev 1230)
@@ -1,33 +1,50 @@
<h2>DNA Console</h2>
-<p>The DNA Console is shown in the Workbench Console view. Entries in this view show the output of the publishing or unpublishing operations. For each operation there is an entry at the start of the operation and another entry at the end of the operation. Inbetween those entries will be an entry for each file involved in the operation.</p>
+<p>The <i>DNA Console</i> is shown in the <i>Workbench Console</i> view. Entries show the status and outcome of the publishing or unpublishing operations.</p>
-<p>The following symbols indicate the type of outcome for each file:</p>
+<p>Here is what the <i>DNA Console</i> looks like:</p>
+<img src="images/DnaConsole.png" border="0" alt="">
+
+<p>There are view toolbar buttons to clear the console, scroll lock, pin the console, display a different console, and to open another <i>DNA Console</i>.</p>
+
+<h3>Console Message Entries</h3>
+
+<p>For each operation there is an entry at the start of the operation that identifies the job number, operation type, number of files involved in the operation, server, repository, and workspace. There is another entry at the end of the operation that identifies the job number, operation type, and how long the operation took. Inbetween these entries are entries for each file involved in the operation. The file entry takes on the form of a job number, message prefix, and message. The message consists of an operation type, eclipse workspace path, and the URL of the DNA repository for that file. The URL is a hyperlink that opens that file in the default or last used editor for that file.</p>
+
+<p>Here are the file entry message prefixes:</p>
+
<table border="1">
<tr>
- <td align="center"><strong>+</strong> </td>
+ <td align="center"><strong>++</strong> </td>
<td>if the file was successfully published (uploaded)</td>
</tr>
<tr>
- <td align="center"><strong>-</strong> </td>
+ <td align="center"><strong>--</strong> </td>
<td>if the file was successfully unpublished (removed)</td>
</tr>
<tr>
- <td align="center"><strong>*</strong> </td>
+ <td align="center"><strong>ERROR</strong> </td>
<td>if there was an error publishing or unpublishing the file</td>
</tr>
<tr>
- <td align="center"><strong>w</strong> </td>
+ <td align="center"><strong>INFO</strong> </td>
+ <td>if there was an informational message publishing or unpublishing the file</td>
+</tr>
+<tr>
+ <td align="center"><strong>WARN</strong> </td>
<td>if there is a warning when publishing or unpublishing a file (like trying to unpublish a file that was never unpublished)</td>
</tr>
</table>
-<p>Here is what the DNA Console in the Console view looks like:</p>
+<h3>Operations</h3>
-<img src="images/DnaConsole.png" width="610" height="168" border="0" alt="">
+<p>Each publishing or unpublishing operation can be run "in the background" so you don't have to wait around until an operation is finished. Simply click "Run in Background" on the operation dialog. This dialog identifies the operation type, the job number, and the operations progress. You can also cancel the operation using this dialog.</p>
-<p>There are view toolbar buttons to clear the console, scroll lock, pin the console, display a different console, and to open another DNA Console.</p>
+<p>Here is what the <i>Operation Dialog</i> looks like:</p>
+
+<img src="images/OperationDialog.png" border="0" alt="">
+
<h3>Related Topics</h3>
<ul>
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaPreferencesDialog.html
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaPreferencesDialog.html (rev 0)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaPreferencesDialog.html 2009-09-17 19:46:22 UTC (rev 1230)
@@ -0,0 +1,26 @@
+<h2>DNA Preferences Page</h2>
+
+<p>The <i>DNA Preferences Page</i> provides settings that control what files are included in publishing and unpublishing operations. A publishing operation occurs when the selected files in your local workspace are uploaded to a specific DNA repository workspace. While an unpublishing operation occurs when the selected files in your local workspace are removed from a specific DNA repository workspace. The <i>Filtered File Extensions</i> preference and the <i>Filtered Folder Names</i> preference each provide a way to filter out certain files from these operations.</p>
+
+<p>Here is what the <i>DNA Preferences Page</i> looks like:</p>
+
+<img src="images/DnaPreferencesDialog.png" border="0" alt="">
+
+<h3>Filtered File Extensions Preference</h3>
+
+The <i>Filtered File Extensions</i> preference provides a way to construct a list of file extensions. If a file has an extension that matches one of these file extensions it will not be involved in either a publishing or unpublishing operation. To add a file extension simply click on the <code>New...</code> button to display the following dialog:
+
+<p><img src="images/NewFilteredFileExtension.png" border="0" alt=""></p>
+
+<h3>Filtered Folder Names Preference</h3>
+
+The <i>Filtered File Names</i> preference provides a way to construct a list of folder names. If a file is contained in a folder, or has an ancestor folder, with a name that matches one of these folder names it will not be involved in either a publishing or unpublishing operation. To add a folder name simply click on the <code>New...</code> button to display the following dialog:
+
+<p><img src="images/NewFilteredFolderName.png" border="0" alt=""></p>
+
+<h3>Related Topics</h3>
+
+<ul>
+<li><a href="PublishDialog.html">Publish/Unpublish Dialog</a></li>
+<li><a href="ResourceContextMenu.html">Resource DNA Context Menu</a></li>
+</ul>
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaPreferencesDialog.html
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaServerView.html
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaServerView.html 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaServerView.html 2009-09-17 19:46:22 UTC (rev 1230)
@@ -1,13 +1,15 @@
<h2>Server View</h2>
-<p>The DNA Server View displays the registered servers hosting DNA repositories. For each server, the repositories and repository workspaces are also displayed. There are view toolbar buttons to create a new server (<img src="images/new_server.gif" width="16" height="16" border="0" alt="">), edit a selected server's properties (<img src="images/edit_server.gif" width="16" height="16" border="0" alt="">), delete one or more selected servers (<img src="images/delete_server.gif" width="16" height="16" border="0" alt="">), refresh, which attempts to connect to all the selected servers (<img src="images/refresh.gif" width="16" height="16" border="0" alt="">), and collapse all servers so that their repositories and workspaces can't be seen (<img src="images/collapse_all.gif" width="16" height="16" border="0" alt="">).</p>
+<p>The <i>DNA Server View</i> displays the registered servers hosting DNA repositories. For each server, the repositories and repository workspaces are also displayed. There are view toolbar buttons to create a new server (<img src="images/new_server.gif" border="0" alt="">), edit a selected server's properties (<img src="images/edit_server.gif" border="0" alt="">), discard one or more selected servers (<img src="images/delete_server.gif" border="0" alt="">), reconnect to a selected server (<img src="images/refresh.gif" border="0" alt="">), and collapse all servers so that their repositories and workspaces can't be seen (<img src="images/collapse_all.gif" border="0" alt="">). If a connection to a server cannot be established that server icon will be decorated with an error overlay image.</p>
-<p>Here is what the DNA Server View looks like:</p>
+<p>Here is what the <i>DNA Server View</i> looks like:</p>
-<img src="images/ServerView.png" width="532" height="166" border="0" alt="">
+<img src="images/ServerView.png" border="0" alt="">
-<p>If a connection to a server cannot be established that server icon will be decorated with an error overlay image. There is also a context menu with actions to create a new server (always enabled), edit server properties (enabled only when one server is selected), and delete one or more selected servers.</p>
+<p>The view's context menu includes the new server, edit server properties, discard server, and reconnect to server actions. Here is what the DNA Server View context menu looks like:</p>
+<img src="images/ServerViewContextMenu.png" border="0" alt="">
+
<h3>Related Topics</h3>
<ul>
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaToc.html
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaToc.html 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/DnaToc.html 2009-09-17 19:46:22 UTC (rev 1230)
@@ -1,9 +1,11 @@
<h2>DNA Publishing</h2>
<ul>
-<li><a href="ResourceContextMenu.html">Resource Context Menu</a></li>
<li><a href="PublishDialog.html">Publish/Unpublish Dialog</a></li>
<li><a href="ServerDialog.html">Server Dialog</a></li>
<li><a href="DnaServerView.html">Server View</a></li>
<li><a href="DnaConsole.html">DNA Message Console</a></li>
+<li><a href="PublishedLocationsDialog.html">Published Locations Dialog</a></li>
+<li><a href="ResourceContextMenu.html">Resource Context Menu</a></li>
+<li><a href="DnaPreferencesDialog.html">DNA Preferences Page</a></li>
</ul>
\ No newline at end of file
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/PublishDialog.html
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/PublishDialog.html 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/PublishDialog.html 2009-09-17 19:46:22 UTC (rev 1230)
@@ -1,7 +1,11 @@
<h2>Publish/Unpublish Dialog</h2>
-<p>The DNA Publish/Unpublish Dialog is used to publish or unpublish workspace selected files and folders to/from DNA repository workspaces. When publishing, the selected files will be uploaded to the selected DNA repository workspace. When unpublishing, the selected files will be removed from the selected DNA repository workspace (if they exist there).</p>
+<p>The <i>DNA Publish/Unpublish Dialog</i> is used to publish or unpublish workspace selected files and folders to/from DNA repository workspaces. When publishing, the selected files will be uploaded to the selected DNA repository workspace. When unpublishing, the selected files will be removed from the selected DNA repository workspace (if they exist there). The <a href="DnaPreferencesDialog.html">DNA Preferences Page</a> allows you to filter out files contained within the selection based on their file extensions or by the name of the folder, or ancestor folder, they are contained within.</p>
+<p>Here is what the <i>DNA Publish/Unpublish Dialog</i> looks like:</p>
+
+<img src="images/PublishDialog.png" border="0" alt="">
+
<p>The dialog consists of the following controls:</p>
<table border="1">
@@ -31,10 +35,6 @@
</tr>
</table>
-<p>Here is what the DNA Publish/Unpublish Dialog looks like:</p>
-
-<img src="images/PublishDialog.png" width="525" height="405" border="0" alt="">
-
<p>A server must be selected before a repository can be selected. Likewise, a repository must be selected before a workspace can be selected. The message area at the top of the dialog will indicate the type of operation (publish or unpublish) and if there is any missing information. Once the workspace has been selected the 'Finish' button will be enabled and the operation can be executed. These operations can be run in the background so that multiple operations can be run at the same time. The DNA Message Console displays output from the operations.</p>
<h3>Related Topics</h3>
@@ -42,4 +42,5 @@
<ul>
<li><a href="ServerDialog.html">Server Dialog</a></li>
<li><a href="DnaConsole.html">DNA Message Console</a></li>
+<li><a href="DnaPreferencesDialog.html">DNA Preferences Page</a></li>
</ul>
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/PublishedLocationsDialog.html
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/PublishedLocationsDialog.html (rev 0)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/PublishedLocationsDialog.html 2009-09-17 19:46:22 UTC (rev 1230)
@@ -0,0 +1,15 @@
+<h2>Published Locations Dialog</h2>
+
+<p>The <i>Published Locations Dialog</i> identifies all the DNA workspaces the selected file has been published to from this Eclipse workspace.</p>
+
+<p>Here is what the <i>Published Locations Dialog</i> looks like:</p>
+
+<img src="images/PublishedLocationsDialog.png" border="0" alt="">
+
+<p>The table data identifies, for each workspace, the server URL, user, repository name, workspace name, and published URL. A button to copy the published URL of the selected row to the system clipboard is also available.</p>
+
+<h3>Related Topics</h3>
+
+<ul>
+<li><a href="PublishDialog.html">Publish/Unpublish Dialog</a></li>
+</ul>
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/PublishedLocationsDialog.html
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/ResourceContextMenu.html
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/ResourceContextMenu.html 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/ResourceContextMenu.html 2009-09-17 19:46:22 UTC (rev 1230)
@@ -1,13 +1,16 @@
<h2>Resource Context Menu</h2>
-<p>The Resource Context Menu consists of two actions: publish and unpublish. Each action will be enabled when one or more resources (i.e, project, folder, or file) in the workspace are selected. When publishing, the selected resources will be uploaded to a DNA repository workspace. When unpublishing, the selected resources will be removed from a selected DNA repository workspace (if they exist there).</p>
+<p>The <i>Resource Context Menu</i> consists of these actions: publish, unpublish, and show published locations. The publish and unpublish actions will be enabled when one or more resources (i.e, project, folder, or file) in the workspace are selected. When publishing, the selected resources will be uploaded to a DNA repository workspace. When unpublishing, the selected resources will be removed from a selected DNA repository workspace (if they exist there). If a resource has already been published using the current Eclipse workspace, the show published locations action will be enabled.</p>
-<p>Here is what the Resource Context Menu looks like:</p>
+<p>Here is what the <i>Resource Context Menu</i> looks like:</p>
-<img src="images/ResourceDnaContextMenu.png" width="358" height="118" border="0" alt="">
+<img src="images/ResourceDnaContextMenu.png" border="0" alt="">
+<p>Files can be filtered out of publishing and unpublishing operations even though they have been selected. Use the <a href="DnaPreferencesDialog.html">DNA Preferences Page</a> to filter out files contained within the selection based on their file extensions or by the name of the folder, or ancestor folder, they are contained within. </p>
+
<h3>Related Topics</h3>
<ul>
<li><a href="PublishDialog.html">Publishing and Unpublishing Dialog</a></li>
+<li><a href="DnaPreferenceDialog.html">DNA Preferences Dialog</a></li>
</ul>
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/ServerDialog.html
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/ServerDialog.html 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/ServerDialog.html 2009-09-17 19:46:22 UTC (rev 1230)
@@ -1,7 +1,11 @@
<h2>Server Dialog</h2>
-<p>The DNA Server Dialog is used to create and register new servers that host DNA repositories. This dialog is also used to edit an existing server's properties.</p>
+<p>The <i>DNA Server Dialog</i> is used to create and register new servers that host DNA repositories. This dialog is also used to edit an existing server's properties.</p>
+<p>Here is what the <i>DNA Server Dialog</i> looks like:</p>
+
+<img src="images/ServerDialog.png" border="0" alt="">
+
<p>The dialog consists of the following controls:</p>
<table border="1">
@@ -19,7 +23,7 @@
</tr>
<tr>
<td align="right"><strong>Save Password Checkbox</strong> </td>
- <td>when checked the password will be saved in clear-text on the local machined</td>
+ <td>when checked, all the server properties (including the password) will be saved to a file on the local machine</td>
</tr>
<tr>
<td align="right"><strong>Test Server Connection Button</strong> </td>
@@ -27,10 +31,6 @@
</tr>
</table>
-<p>Here is what the DNA Server Dialog looks like:</p>
-
-<img src="images/ServerDialog.png" width="525" height="515" border="0" alt="">
-
<p>Once all server properties have been entered the 'Finish' button will be enabled and the new server can be created or the existing server's properties can be modified. You can display the Server Dialog from the Server View and from the Publish/Unpublish Dialog.</p>
<h3>Related Topics</h3>
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/helpContexts.xml
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/helpContexts.xml 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/helpContexts.xml 2009-09-17 19:46:22 UTC (rev 1230)
@@ -14,12 +14,19 @@
<context id="serverDialogHelpContext" title="Server Dialog">
<description>The DNA Server Dialog allows you to edit or create a server that hosts one or more DNA repositories.</description>
<topic href="docs/ServerDialog.html" label="Server Dialog"/>
+ <topic href="docs/DnaServerView.html" label="DNA Server View"/>
</context>
<context id="publishDialogHelpContext" title="DNA Publish and Unpublish Dialog Help">
<description>The DNA Publish/Unpublish Dialog uploads or removes selected resources from a selected DNA server.</description>
<topic href="docs/PublishDialog.html" label="Publish/Unpublish Dialog"/>
<topic href="docs/ServerDialog.html" label="Create a new server"/>
+ <topic href="docs/DnaPreferencesDialog.html" label="DNA Preferences"/>
</context>
+ <context id="preferencesHelpContext" title="DNA Preferences Dialog Help">
+ <description>The DNA Preferences Dialog allows you to identify file extensions and folder names that should not be involved in publishing operations.</description>
+ <topic href="docs/DnaPreferencesDialog.html" label="DNA Preferences"/>
+ <topic href="docs/PublishDialog.html" label="Publish/Unpublish Dialog"/>
+ </context>
<context id="dnaConsoleHelpContext" title="DNA Message Console">
<description>The DNA Message Console contains the output from the publishing and unpublishing operations.</description>
<topic href="docs/DnaConsole.html" label="DNA Message Console"/>
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/DnaConsole.png
===================================================================
(Binary files differ)
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/DnaPreferencesDialog.png
===================================================================
(Binary files differ)
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/DnaPreferencesDialog.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/NewFilteredFileExtension.png
===================================================================
(Binary files differ)
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/NewFilteredFileExtension.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/NewFilteredFolderName.png
===================================================================
(Binary files differ)
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/NewFilteredFolderName.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/OperationDialog.png
===================================================================
(Binary files differ)
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/OperationDialog.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/PublishedLocationsDialog.png
===================================================================
(Binary files differ)
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/PublishedLocationsDialog.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/ResourceDnaContextMenu.png
===================================================================
(Binary files differ)
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/ServerViewContextMenu.png
===================================================================
(Binary files differ)
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/images/ServerViewContextMenu.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/docs/toc.xml
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/docs/toc.xml 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/docs/toc.xml 2009-09-17 19:46:22 UTC (rev 1230)
@@ -3,5 +3,7 @@
<topic label="Server View" href="docs/DnaServerView.html"/>
<topic label="Server Dialog" href="docs/ServerDialog.html"/>
<topic label="DNA Console" href="docs/DnaConsole.html"/>
+ <topic label="Published Locations Dialog" href="docs/PublishedLocationsDialog.html"/>
<topic label="Resource Context Menu" href="docs/ResourceContextMenu.html"/>
+ <topic label="DNA Preferences Dialog" href="docs/DnaPreferencesDialog.html"/>
</toc>
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/IUiConstants.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/IUiConstants.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/IUiConstants.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -94,6 +94,8 @@
String HELP_CONTEXT_PREFIX = PLUGIN_ID + '.';
String DNA_CONSOLE_HELP_CONTEXT = HELP_CONTEXT_PREFIX + "dnaConsoleHelpContext"; //$NON-NLS-1$
+
+ String PREFERENCE_PAGE_HELP_CONTEXT = HELP_CONTEXT_PREFIX + "preferencesHelpContext"; //$NON-NLS-1$
String PUBLISH_DIALOG_HELP_CONTEXT = HELP_CONTEXT_PREFIX + "publishDialogHelpContext"; //$NON-NLS-1$
@@ -123,4 +125,11 @@
*/
String FILTERED_FILE_EXTENSIONS_PREFERENCE = "dna.preference.filteredFileExtensions"; //$NON-NLS-1$
+ /**
+ * A preference for a list of folder names whose contents will not be part of publishing operations.
+ *
+ * @since 0.6
+ */
+ String FILTERED_FOLDER_NAMES_PREFERENCE = "dna.preference.filteredFolderNames"; //$NON-NLS-1$
+
}
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -49,10 +49,11 @@
public static I18n dnaConsoleProblemCreatingHyperlinkMsg;
public static I18n dnaConsoleFilePathNotFoundMsg;
- public static I18n dnaPreferencePageFilteredFileExtensionsDescription;
+ public static I18n dnaPreferencePageDescription;
public static I18n dnaPreferencePageFilteredFileExtensionsLabel;
- public static I18n dnaPreferencePageFilteredFileExtensionsMessage;
- public static I18n dnaPreferencePageFilteredFileExtensionsTitle;
+ public static I18n dnaPreferencePageFilteredFolderNamesLabel;
+ public static I18n dnaPreferencePageMessage;
+ public static I18n dnaPreferencePageTitle;
public static I18n editServerActionText;
public static I18n editServerActionToolTip;
@@ -61,12 +62,18 @@
public static I18n missingImage;
- public static I18n newFileExtensionDialogLabel;
- public static I18n newFileExtensionDialogTitle;
+ public static I18n newFilteredFileExtensionDialogLabel;
+ public static I18n newFilteredFileExtensionDialogTitle;
+
+ public static I18n newFilteredFolderNameDialogLabel;
+ public static I18n newFilteredFolderNameDialogTitle;
public static I18n newServerActionText;
public static I18n newServerActionToolTip;
+ public static I18n prefUtilsPropertyFileNotFound;
+ public static I18n prefUtilsPropertyNotFound;
+
public static I18n publishedLocationsDialogCopyUrlButton;
public static I18n publishedLocationsDialogCopyUrlButtonToolTip;
public static I18n publishedLocationsDialogErrorObtainingUrlMsg;
@@ -81,12 +88,13 @@
public static I18n publishedResourcePropertyErrorMsg;
public static I18n publishJobCanceled;
- public static I18n publishJobLongDurationMsg;
- public static I18n publishJobShortDurationMsg;
+ public static I18n publishJobDurationMsg;
+ public static I18n publishJobDurationNoHoursMsg;
+ public static I18n publishJobDurationNoHoursNoMinutesMsg;
+ public static I18n publishJobDurationShortMsg;
public static I18n publishJobPublish;
public static I18n publishJobPublishCanceledMsg;
public static I18n publishJobPublishFile;
- public static I18n publishJobPublishFile2;
public static I18n publishJobPublishFileFailed;
public static I18n publishJobPublishFileInfo;
public static I18n publishJobPublishFileWarning;
@@ -135,6 +143,8 @@
public static I18n publishWizardPublishTitle;
public static I18n publishWizardUnpublishTitle;
public static I18n publishWizardUnpublishErrorMsg;
+
+ public static I18n reconnectJobTaskName;
public static I18n serverManagerGetRepositoriesExceptionMsg;
public static I18n serverManagerGetWorkspacesExceptionMsg;
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.properties
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.properties 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/RestClientI18n.properties 2009-09-17 19:46:22 UTC (rev 1230)
@@ -41,21 +41,28 @@
dnaConsoleProblemCreatingHyperlinkMsg = Unexpected problem creating hyperlink in DNA Console view
dnaConsoleFilePathNotFoundMsg = A hyperlink could not be created in the DNA Message Console because the file path of "{0}" could not be found.
-dnaPreferencePageFilteredFileExtensionsDescription = Add, remove, or edit the filtered file extensions. Files with matching extensions will NOT be published to DNA repositories.
+dnaPreferencePageDescription = Add or remove filtered file extensions and folder names. Files with a matching file extension or files that are contained in a folder with a matching name will NOT be published to or unpublished from DNA repositories.
dnaPreferencePageFilteredFileExtensionsLabel = Filtered File Extensions:
-dnaPreferencePageFilteredFileExtensionsMessage = DNA
-dnaPreferencePageFilteredFileExtensionsTitle = DNA Filtered File Extensions
+dnaPreferencePageFilteredFolderNamesLabel = Filtered Folder Names:
+dnaPreferencePageMessage = DNA
+dnaPreferencePageTitle = DNA Preferences
errorDialogTitle = Error
missingImage = The following image cannot be found "{0}"
-newFileExtensionDialogLabel = File Extension:
-newFileExtensionDialogTitle = New Filtered File Extension
+newFilteredFileExtensionDialogLabel = File extension:
+newFilteredFileExtensionDialogTitle = New Filtered File Extension
+
+newFilteredFolderNameDialogLabel = Folder name:
+newFilteredFolderNameDialogTitle = New Filtered Folder Name
newServerActionText = New Server
newServerActionToolTip = Create a new server
+prefUtilsPropertyFileNotFound = The "{0}" property preference file is missing or could not be loaded. All property default values will be used.
+prefUtilsPropertyNotFound = The "{0}" property is missing. Default value of "{1}" will be used.
+
publishedLocationsDialogCopyUrlButton = Copy URL
publishedLocationsDialogCopyUrlButtonToolTip = Copy to the clipboard the selected workspace URL where the file is published
publishedLocationsDialogErrorObtainingUrlMsg = Error obtaining URL
@@ -70,25 +77,26 @@
publishedResourcePropertyErrorMsg = Error obtaining the "published" property from the resource "{0}"
publishJobCanceled = Job {0} was canceled. See log for details.
-publishJobLongDurationMsg = {0} hour(s), {1} minute(s), {2} second(s)
-publishJobShortDurationMsg = less than 1 second
+publishJobDurationMsg = {0} hour(s), {1} minute(s), {2} second(s)
+publishJobDurationNoHoursMsg = {0} minute(s), {1} second(s)
+publishJobDurationNoHoursNoMinutesMsg = {0} second(s)
+publishJobDurationShortMsg = less than 1 second
publishJobPublish = [{0}] Publishing {4} file(s) to server "{1}" (repository "{2}", workspace "{3}") ...
-publishJobPublishCanceledMsg = [{0}] !! Publishing to server "{1}" (repository "{2}", workspace "{3}") was canceled after {4} file(s) out of {5} were processed.\n
-publishJobPublishFile = \t[{0}] ++ Published file "{1}" to URL\
-publishJobPublishFile2 = "{0}"
-publishJobPublishFileFailed = \t[{0}] ERROR Publishing file "{1}" generated an ERROR message. See log.
-publishJobPublishFileInfo = \t[{0}] INFO Publishing file "{1}" generated an INFO message. See log.
-publishJobPublishFileWarning = \t[{0}] WARN Publishing file "{1}" generated a WARNING message. See log.
-publishJobPublishFinishedMsg = [{0}] Done publishing. Elapsed time: {1}. \n
+publishJobPublishCanceledMsg = [{0}] Publishing was canceled after {1} file(s) out of {2} were processed. Elapsed time: {3}.\n
+publishJobPublishFile = [{0}] + Published file "{1}" to URL "<em>{2}</em>"
+publishJobPublishFileFailed = [{0}] -- ERROR -- Publishing file "{1}" generated an ERROR message. See log.
+publishJobPublishFileInfo = [{0}] -- INFO -- Publishing file "{1}" generated an INFO message. See log.
+publishJobPublishFileWarning = [{0}] -- WARNING -- Publishing file "{1}" generated a WARNING message. See log.
+publishJobPublishFinishedMsg = [{0}] Done publishing. Elapsed time: {1}.\n
publishJobPublishName = DNA Publish [{0}]
publishJobPublishTaskName = Publishing resources [{0}]
publishJobUnexpectedErrorMsg = Unexpected error occurred. See log for more details.
publishJobUnpublish = [{0}] Unpublishing {4} file(s) from server "{1}" (repository "{2}", workspace "{3}") ...
-publishJobUnpublishCanceledMsg = [{0}] !! Unpublishing from server "{1}" (repository "{2}", workspace "{3}") was canceled after {4} file(s) out of {5} were processed.\n
-publishJobUnpublishFile = \t[{0}] -- Unpublished file "{1}"
-publishJobUnpublishFileFailed = \t[{0}] ERROR Unpublishing file "{0}" generated an ERROR message. See log.
-publishJobUnpublishFileInfo = \t[{0}] INFO Unpublishing file "{1}" generated an INFO message. See log.
-publishJobUnpublishFileWarning = \t[{0}] WARN Unpublishing file "{1}" generated a WARNING message. See log.
+publishJobUnpublishCanceledMsg = [{0}] Unublishing was canceled after {1} file(s) out of {2} were processed. Elapsed time: {3}.\n
+publishJobUnpublishFile = [{0}] - Unpublished file "{1}"
+publishJobUnpublishFileFailed = [{0}] -- ERROR -- Unpublishing file "{0}" generated an ERROR message. See log.
+publishJobUnpublishFileInfo = [{0}] -- INFO -- Unpublishing file "{1}" generated an INFO message. See log.
+publishJobUnpublishFileWarning = [{0}] -- WARN -- Unpublishing file "{1}" generated a WARNING message. See log.
publishJobUnpublishFinishedMsg = [{0}] Done unpublishing. Elapsed time: {1}. \n
publishJobUnpublishName = DNA Unpublish [{0}]
publishJobUnpublishTaskName = Unpublishing resources [{0}]
@@ -125,8 +133,10 @@
publishWizardUnpublishErrorMsg = Error Unpublishing
publishWizardUnpublishTitle = Unpublish
+reconnectJobTaskName = Connecting to server "{0}"
+
serverReconnectActionText = Reconnect
-serverReconnectActionToolTip = Reconnect to selected servers or all servers if no servers selected
+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}."
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Utils.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Utils.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/Utils.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -23,16 +23,11 @@
*/
package org.jboss.dna.web.jcr.rest.client.swt;
+import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.PLUGIN_ID;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
import java.util.StringTokenizer;
import java.util.TreeSet;
-import org.eclipse.core.resources.IContainer;
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Image;
@@ -141,30 +136,14 @@
}
/**
- * @param container the project or folder whose files are being requested
- * @param recurse the flag indicating if child containers should be traversed
- * @return the list of files contained in the specified container (never <code>null</code>)
- * @throws CoreException if there is a problem finding the files
+ * @param status the status being converted (never <code>null</code>)
+ * @return the Eclipse status object (never <code>null</code>)
* @since 0.6
*/
- public static List<IFile> findFiles( IContainer container,
- boolean recurse ) throws CoreException {
- List<IFile> result = new ArrayList<IFile>();
-
- for (IResource member : container.members()) {
- if (recurse && (member instanceof IContainer)) {
- // don't select closed projects
- if ((member instanceof IProject) && !((IProject)member).isOpen()) {
- continue;
- }
-
- result.addAll(findFiles((IContainer)member, recurse));
- } else if ((member instanceof IFile) && ((IFile)member).getLocation().toFile().exists()) {
- result.add((IFile)member);
- }
- }
-
- return result;
+ public static IStatus convert( Status status ) {
+ CheckArg.isNotNull(status, "status"); //$NON-NLS-1$
+ return new org.eclipse.core.runtime.Status(convertSeverity(status.getSeverity()), PLUGIN_ID, status.getMessage(),
+ status.getException());
}
/**
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/BasePublishingAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/BasePublishingAction.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/BasePublishingAction.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -41,8 +41,8 @@
import org.jboss.dna.web.jcr.rest.client.swt.Activator;
import org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n;
import org.jboss.dna.web.jcr.rest.client.swt.Utils;
+import org.jboss.dna.web.jcr.rest.client.swt.jobs.PublishJob.Type;
import org.jboss.dna.web.jcr.rest.client.swt.wizards.PublishWizard;
-import org.jboss.dna.web.jcr.rest.client.swt.wizards.PublishJob.Type;
/**
* @since 0.6
@@ -114,6 +114,11 @@
try {
WizardDialog dialog = new WizardDialog(shell, new PublishWizard(this.type, resources,
Activator.getDefault().getServerManager())) {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
+ */
@Override
protected void initializeBounds() {
super.initializeBounds();
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/EditServerAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/EditServerAction.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/EditServerAction.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -101,6 +101,11 @@
public void run() {
ServerWizard wizard = new ServerWizard(this.serverManager, this.serverBeingEdited);
WizardDialog dialog = new WizardDialog(this.shell, wizard) {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.wizard.WizardDialog#configureShell(org.eclipse.swt.widgets.Shell)
+ */
@Override
protected void configureShell( Shell newShell ) {
super.configureShell(newShell);
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/NewServerAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/NewServerAction.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/NewServerAction.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -92,6 +92,11 @@
public void run() {
ServerWizard wizard = new ServerWizard(this.serverManager);
WizardDialog dialog = new WizardDialog(this.shell, wizard) {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.wizard.WizardDialog#configureShell(org.eclipse.swt.widgets.Shell)
+ */
@Override
protected void configureShell( Shell newShell ) {
super.configureShell(newShell);
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/PublishAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/PublishAction.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/PublishAction.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -23,7 +23,7 @@
*/
package org.jboss.dna.web.jcr.rest.client.swt.actions;
-import org.jboss.dna.web.jcr.rest.client.swt.wizards.PublishJob.Type;
+import org.jboss.dna.web.jcr.rest.client.swt.jobs.PublishJob.Type;
/**
* The <code>PublishAction</code> controls the publishing of one or more {@link org.eclipse.core.resources.IResource}s to a DNA
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ReconnectToServerAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ReconnectToServerAction.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/ReconnectToServerAction.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -24,21 +24,24 @@
package org.jboss.dna.web.jcr.rest.client.swt.actions;
import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.REFRESH_IMAGE;
-import org.eclipse.jface.action.Action;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
-import org.jboss.dna.web.jcr.rest.client.IServerRegistryListener;
-import org.jboss.dna.web.jcr.rest.client.ServerRegistryEvent;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.actions.BaseSelectionListenerAction;
+import org.jboss.dna.web.jcr.rest.client.domain.Server;
import org.jboss.dna.web.jcr.rest.client.swt.Activator;
import org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n;
+import org.jboss.dna.web.jcr.rest.client.swt.jobs.ReconnectJob;
/**
- * The <code>ReconnectToServerAction</code> tries to reconnect to one or more selected servers or to all servers if none are
- * selected. This action must be registered to received server registry events.
+ * The <code>ReconnectToServerAction</code> tries to reconnect to a selected server.
*
* @since 0.6
*/
-public final class ReconnectToServerAction extends Action implements IServerRegistryListener {
+public final class ReconnectToServerAction extends BaseSelectionListenerAction {
// ===========================================================================================================================
// Fields
@@ -73,6 +76,37 @@
// ===========================================================================================================================
/**
+ * @return the view's tree viewer
+ * @since 0.6
+ */
+ StructuredViewer getViewer() {
+ return this.viewer;
+ }
+
+ /**
+ * @param server the server being connected to
+ * @since 0.6
+ */
+ void refresh( final Server server ) {
+ final Display display = this.viewer.getControl().getDisplay();
+
+ if (!display.isDisposed()) {
+ // make sure we are in the UI thread
+ display.asyncExec(new Runnable() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Runnable#run()
+ */
+ @Override
+ public void run() {
+ getViewer().refresh(server);
+ }
+ });
+ }
+ }
+
+ /**
* {@inheritDoc}
*
* @see org.eclipse.jface.action.Action#run()
@@ -80,27 +114,39 @@
*/
@Override
public void run() {
- IStructuredSelection selection = (IStructuredSelection)this.viewer.getSelection();
+ final Server server = (Server)getStructuredSelection().getFirstElement();
+ final ReconnectJob job = new ReconnectJob(server);
- if (selection.size() == 0) {
- this.viewer.refresh();
- } else {
- for (Object server : selection.toArray()) {
- this.viewer.refresh(server);
+ // add listener so we can refresh tree
+ job.addJobChangeListener(new JobChangeAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
+ */
+ @Override
+ public void done( IJobChangeEvent event ) {
+ refresh(server);
+ job.removeJobChangeListener(this);
}
- }
+ });
+
+ // run job in own thread not in the UI thread
+ Thread t = new Thread();
+ t.run();
+ job.setThread(t);
+ job.schedule();
}
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.web.jcr.rest.client.IServerRegistryListener#serverRegistryChanged(org.jboss.dna.web.jcr.rest.client.ServerRegistryEvent)
+ * @see org.eclipse.ui.actions.BaseSelectionListenerAction#updateSelection(org.eclipse.jface.viewers.IStructuredSelection)
* @since 0.6
*/
@Override
- public Exception[] serverRegistryChanged( ServerRegistryEvent event ) {
- setEnabled(!event.getServerManager().getServers().isEmpty());
- return null;
+ protected boolean updateSelection( IStructuredSelection selection ) {
+ return ((selection.size() == 1) && (selection.getFirstElement() instanceof Server));
}
}
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/UnpublishAction.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/UnpublishAction.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/actions/UnpublishAction.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -23,7 +23,7 @@
*/
package org.jboss.dna.web.jcr.rest.client.swt.actions;
-import org.jboss.dna.web.jcr.rest.client.swt.wizards.PublishJob.Type;
+import org.jboss.dna.web.jcr.rest.client.swt.jobs.PublishJob.Type;
/**
* The <code>UnpublishAction</code> controls the unpublishing of one or more {@link org.eclipse.core.resources.IResource}s from a
Copied: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/PublishJob.java (from rev 1182, branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishJob.java)
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/PublishJob.java (rev 0)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/PublishJob.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -0,0 +1,390 @@
+/*
+ * 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.swt.jobs;
+
+import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.DNA_PUBLISHING_JOB_FAMILY;
+import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.PLUGIN_ID;
+import java.io.File;
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.web.jcr.rest.client.ServerManager;
+import org.jboss.dna.web.jcr.rest.client.Status;
+import org.jboss.dna.web.jcr.rest.client.domain.Workspace;
+import org.jboss.dna.web.jcr.rest.client.swt.Activator;
+import org.jboss.dna.web.jcr.rest.client.swt.DnaResourceHelper;
+import org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n;
+import org.jboss.dna.web.jcr.rest.client.swt.views.DnaContentProvider;
+import org.jboss.dna.web.jcr.rest.client.swt.views.DnaMessageConsole;
+
+/**
+ * The <code>PublishJob</code> publishes or unpublishes one or more files using the {@link ServerManager}.
+ *
+ * @since 0.6
+ */
+public final class PublishJob extends Job {
+
+ // ===========================================================================================================================
+ // Constants
+ // ===========================================================================================================================
+
+ /**
+ * The job type.
+ *
+ * @since 0.6
+ */
+ public enum Type {
+ /**
+ * Indicates a publish job.
+ *
+ * @since 0.6
+ */
+ PUBLISH,
+
+ /**
+ * Indicates an unpublish job.
+ *
+ * @since 0.6
+ */
+ UNPUBLISH
+ }
+
+ /**
+ * A unique job identifier given to each publishing/unpublishing job.
+ *
+ * @since 0.6
+ */
+ private static final AtomicInteger JOB_ID = new AtomicInteger(1);
+
+ // ===========================================================================================================================
+ // Class Methods
+ // ===========================================================================================================================
+
+ /**
+ * @param type the job type (never <code>null</code>)
+ * @param jobId the job identifier
+ * @return the job name
+ * @since 0.6
+ */
+ private static String getJobName( Type type,
+ int jobId ) {
+ CheckArg.isNotNull(type, "type"); //$NON-NLS-1$
+
+ if (Type.PUBLISH == type) {
+ return RestClientI18n.publishJobPublishName.text(jobId);
+ }
+
+ // unpublish
+ return RestClientI18n.publishJobUnpublishName.text(jobId);
+ }
+
+ // ===========================================================================================================================
+ // Fields
+ // ===========================================================================================================================
+
+ /**
+ * The files being published or unpublished.
+ *
+ * @since 0.6
+ */
+ private final List<IFile> files;
+
+ /**
+ * The unique job identifier.
+ *
+ * @since 0.6
+ */
+ private final int jobId;
+
+ /**
+ * The job type.
+ *
+ * @since 0.6
+ */
+ private final Type type;
+
+ /**
+ * The workspace to use when publishing or unpublishing.
+ *
+ * @since 0.6
+ */
+ private final Workspace workspace;
+
+ // ===========================================================================================================================
+ // Constructors
+ // ===========================================================================================================================
+
+ /**
+ * @param type the job type (never <code>null</code>)
+ * @param files the files being published or unpublished (never <code>null</code>)
+ * @param workspace the workspace to use when publishing or unpublishing (never <code>null</code>)
+ * @since 0.6
+ */
+ public PublishJob( Type type,
+ List<IFile> files,
+ Workspace workspace ) {
+ super(getJobName(type, JOB_ID.incrementAndGet()));
+
+ CheckArg.isNotNull(files, "files"); //$NON-NLS-1$
+
+ this.type = type;
+ this.files = files;
+ this.workspace = workspace;
+ this.jobId = JOB_ID.get();
+
+ setUser(true); // allow user to run in background
+ }
+
+ // ===========================================================================================================================
+ // Methods
+ // ===========================================================================================================================
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
+ * @since 0.6
+ */
+ @Override
+ public boolean belongsTo( Object family ) {
+ return DNA_PUBLISHING_JOB_FAMILY.equals(family);
+ }
+
+ /**
+ * @return the server manager
+ * @since 0.6
+ */
+ private ServerManager getServerManager() {
+ return Activator.getDefault().getServerManager();
+ }
+
+ /**
+ * @return <code>true</code> if a publishing job
+ * @since 0.6
+ */
+ private boolean isPublishing() {
+ return (this.type == Type.PUBLISH);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+ * @since 0.6
+ */
+ @Override
+ protected IStatus run( IProgressMonitor monitor ) {
+ assert (this.workspace != null);
+ long startTime = System.currentTimeMillis();
+ boolean canceled = false;
+ int numProcessed = 0;
+
+ try {
+ int fileCount = this.files.size();
+ String name = (isPublishing() ? RestClientI18n.publishJobPublishTaskName.text(this.jobId)
+ : RestClientI18n.publishJobUnpublishTaskName.text(this.jobId));
+ monitor.beginTask(name, fileCount);
+ monitor.setTaskName(name);
+
+ String serverUrl = this.workspace.getServer().getUrl();
+ String repositoryName = this.workspace.getRepository().getName();
+ String workspaceName = this.workspace.getName();
+
+ // write initial message to console
+ if (isPublishing()) {
+ DnaMessageConsole.writeln(RestClientI18n.publishJobPublish.text(this.jobId,
+ serverUrl,
+ repositoryName,
+ workspaceName,
+ fileCount));
+ } else {
+ DnaMessageConsole.writeln(RestClientI18n.publishJobUnpublish.text(this.jobId,
+ serverUrl,
+ repositoryName,
+ workspaceName,
+ fileCount));
+ }
+
+ DnaResourceHelper resourceHelper = new DnaResourceHelper(getServerManager());
+
+ // process the files
+ for (IFile eclipseFile : this.files) {
+ if (monitor.isCanceled()) {
+ canceled = true;
+ throw new InterruptedException(RestClientI18n.publishJobCanceled.text(jobId));
+ }
+
+ File file = eclipseFile.getLocation().toFile();
+ String path = eclipseFile.getParent().getFullPath().toString();
+ Status status = null;
+
+ if (isPublishing()) {
+ status = getServerManager().publish(this.workspace, path, file);
+
+ // 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);
+
+ // 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);
+
+ // write outcome message to console
+ if (isPublishing() && status.isOk()) {
+ URL url = getServerManager().getUrl(file, path, this.workspace);
+ writeToConsole(eclipseFile, url, status);
+ } else {
+ writeToConsole(eclipseFile, null, status);
+ }
+ }
+
+ return org.eclipse.core.runtime.Status.OK_STATUS;
+ } catch (Exception e) {
+ String msg = null;
+
+ if (e instanceof InterruptedException) {
+ msg = e.getLocalizedMessage();
+ } else {
+ msg = RestClientI18n.publishJobUnexpectedErrorMsg.text();
+ }
+
+ return new org.eclipse.core.runtime.Status(IStatus.INFO, PLUGIN_ID, msg, e);
+ } finally {
+ monitor.done();
+
+ // add operation completed message
+ String duration;
+ long milliseconds = (System.currentTimeMillis() - startTime);
+ long hours = milliseconds / (1000 * 60 * 60);
+ long minutes = (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
+ long seconds = ((milliseconds % (1000 * 60 * 60)) % (1000 * 60)) / 1000;
+
+ if (hours > 0) {
+ duration = RestClientI18n.publishJobDurationMsg.text(hours, minutes, seconds);
+ } else if (minutes > 0) {
+ duration = RestClientI18n.publishJobDurationNoHoursMsg.text(minutes, seconds);
+ } else if (seconds > 0) {
+ duration = RestClientI18n.publishJobDurationNoHoursNoMinutesMsg.text(seconds);
+ } else {
+ duration = RestClientI18n.publishJobDurationShortMsg.text();
+ }
+
+ if (canceled) {
+ if (isPublishing()) {
+ DnaMessageConsole.writeln(RestClientI18n.publishJobPublishCanceledMsg.text(this.jobId,
+ numProcessed,
+ this.files.size(),
+ duration));
+ } else {
+ DnaMessageConsole.writeln(RestClientI18n.publishJobUnpublishCanceledMsg.text(this.jobId,
+ numProcessed,
+ this.files.size(),
+ duration));
+ }
+ } else {
+ if (isPublishing()) {
+ DnaMessageConsole.writeln(RestClientI18n.publishJobPublishFinishedMsg.text(this.jobId, duration));
+ } else {
+ DnaMessageConsole.writeln(RestClientI18n.publishJobUnpublishFinishedMsg.text(this.jobId, duration));
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a hyperlink in console.
+ *
+ * @param file the file involved in the publishing operation
+ * @param url the destination file URL or <code>null</code>
+ * @param status the status of the publishing operation
+ * @since 0.6
+ */
+ private void writeToConsole( final IFile file,
+ URL url,
+ Status status ) {
+ String message = null;
+
+ if (status.isOk()) {
+ if (isPublishing()) {
+ message = RestClientI18n.publishJobPublishFile.text(this.jobId, file.getFullPath(), url.toString());
+ } else {
+ message = RestClientI18n.publishJobUnpublishFile.text(this.jobId, file.getFullPath());
+ }
+ } else if (status.isError()) {
+ if (isPublishing()) {
+ message = RestClientI18n.publishJobPublishFileFailed.text(this.jobId, file.getFullPath());
+ } else {
+ message = RestClientI18n.publishJobUnpublishFileFailed.text(this.jobId, file.getFullPath());
+ }
+
+ // log
+ Activator.getDefault().log(status);
+ } else if (status.isWarning()) {
+ if (isPublishing()) {
+ message = RestClientI18n.publishJobPublishFileWarning.text(this.jobId, file.getFullPath());
+ } else {
+ message = RestClientI18n.publishJobUnpublishFileWarning.text(this.jobId, file.getFullPath());
+ }
+
+ // log
+ Activator.getDefault().log(status);
+ } else {
+ if (isPublishing()) {
+ message = RestClientI18n.publishJobPublishFileInfo.text(this.jobId, file.getFullPath());
+ } else {
+ message = RestClientI18n.publishJobUnpublishFileInfo.text(this.jobId, file.getFullPath());
+ }
+
+ // log
+ Activator.getDefault().log(status);
+ }
+
+ // write to console creating a hyperlink
+ DnaMessageConsole.writeln(message, file);
+ }
+
+}
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/PublishJob.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/ReconnectJob.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/ReconnectJob.java (rev 0)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/ReconnectJob.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -0,0 +1,120 @@
+/*
+ * 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.swt.jobs;
+
+import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.DNA_PUBLISHING_JOB_FAMILY;
+import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.PLUGIN_ID;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.reconnectJobTaskName;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.jboss.dna.web.jcr.rest.client.ServerManager;
+import org.jboss.dna.web.jcr.rest.client.Status;
+import org.jboss.dna.web.jcr.rest.client.domain.Server;
+import org.jboss.dna.web.jcr.rest.client.swt.Activator;
+import org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n;
+import org.jboss.dna.web.jcr.rest.client.swt.Utils;
+
+/**
+ * The <code>ReconnectJob</code> attempts to reconnect to the selected {@link Server server(s)}.
+ *
+ * @since 0.6
+ */
+public final class ReconnectJob extends Job {
+
+ // ===========================================================================================================================
+ // Fields
+ // ===========================================================================================================================
+
+ /**
+ * The server being reconnected to.
+ *
+ * @since 0.6
+ */
+ private final Server server;
+
+ // ===========================================================================================================================
+ // Constructors
+ // ===========================================================================================================================
+
+ /**
+ * @param server the server being connected to (never <code>null</code>)
+ * @since 0.6
+ */
+ public ReconnectJob( Server server ) {
+ super(reconnectJobTaskName.text(server.getShortDescription()));
+ this.server = server;
+ }
+
+ // ===========================================================================================================================
+ // Methods
+ // ===========================================================================================================================
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
+ * @since 0.6
+ */
+ @Override
+ public boolean belongsTo( Object family ) {
+ return DNA_PUBLISHING_JOB_FAMILY.equals(family);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+ * @since 0.6
+ */
+ @Override
+ protected IStatus run( IProgressMonitor monitor ) {
+ IStatus result = null;
+ ServerManager serverManager = Activator.getDefault().getServerManager();
+
+ try {
+ String taskName = reconnectJobTaskName.text(this.server.getShortDescription());
+ monitor.beginTask(taskName, 1);
+ monitor.setTaskName(taskName);
+ Status status = serverManager.ping(this.server);
+ result = Utils.convert(status);
+ } catch (Exception e) {
+ String msg = null;
+
+ if (e instanceof InterruptedException) {
+ msg = e.getLocalizedMessage();
+ } else {
+ msg = RestClientI18n.publishJobUnexpectedErrorMsg.text();
+ }
+
+ result = new org.eclipse.core.runtime.Status(IStatus.ERROR, PLUGIN_ID, msg, e);
+ } finally {
+ monitor.done();
+ done(result);
+ }
+
+ return result;
+ }
+
+}
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/jobs/ReconnectJob.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/DnaPreferencePage.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/DnaPreferencePage.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/DnaPreferencePage.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -24,9 +24,10 @@
package org.jboss.dna.web.jcr.rest.client.swt.preferences;
import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.DNA_IMAGE_16x;
-import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.dnaPreferencePageFilteredFileExtensionsDescription;
-import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.dnaPreferencePageFilteredFileExtensionsMessage;
-import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.dnaPreferencePageFilteredFileExtensionsTitle;
+import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.PREFERENCE_PAGE_HELP_CONTEXT;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.dnaPreferencePageDescription;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.dnaPreferencePageMessage;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.dnaPreferencePageTitle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.swt.SWT;
@@ -37,6 +38,7 @@
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.help.IWorkbenchHelpSystem;
import org.jboss.dna.web.jcr.rest.client.swt.Activator;
/**
@@ -51,12 +53,19 @@
// ===========================================================================================================================
/**
- * The editor used to manage the list of file extensions.
+ * The editor used to manage the list of filtered file extensions.
*
* @since 0.6
*/
- private ExtensionEditor editor;
+ private FilteredFileExtensionEditor extensionsEditor;
+ /**
+ * The editor used to manage the list of filtered folder names.
+ *
+ * @since 0.6
+ */
+ private FilteredFoldersEditor foldersEditor;
+
// ===========================================================================================================================
// Methods
// ===========================================================================================================================
@@ -73,14 +82,26 @@
panel.setLayout(new GridLayout(2, false));
panel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- // create the editor
- this.editor = new ExtensionEditor(panel);
- this.editor.setPreferenceStore(getPreferenceStore());
- this.editor.getListControl(panel).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ // create the filtered extensions editor
+ this.extensionsEditor = new FilteredFileExtensionEditor(panel);
+ this.extensionsEditor.setPreferenceStore(getPreferenceStore());
+ this.extensionsEditor.getListControl(panel).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- // populate the editor
- this.editor.load();
+ // populate the extensions editor
+ this.extensionsEditor.load();
+ // create the filtered folders editor
+ this.foldersEditor = new FilteredFoldersEditor(panel);
+ this.foldersEditor.setPreferenceStore(getPreferenceStore());
+ this.foldersEditor.getListControl(panel).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ // populate the folders editor
+ this.foldersEditor.load();
+
+ // register with the help system
+ IWorkbenchHelpSystem helpSystem = Activator.getDefault().getWorkbench().getHelpSystem();
+ helpSystem.setHelp(panel, PREFERENCE_PAGE_HELP_CONTEXT);
+
return panel;
}
@@ -92,7 +113,7 @@
*/
@Override
public String getDescription() {
- return dnaPreferencePageFilteredFileExtensionsDescription.text();
+ return dnaPreferencePageDescription.text();
}
/**
@@ -114,7 +135,7 @@
*/
@Override
public String getMessage() {
- return dnaPreferencePageFilteredFileExtensionsMessage.text();
+ return dnaPreferencePageMessage.text();
}
/**
@@ -136,7 +157,7 @@
*/
@Override
public String getTitle() {
- return dnaPreferencePageFilteredFileExtensionsTitle.text();
+ return dnaPreferencePageTitle.text();
}
/**
@@ -158,7 +179,8 @@
*/
@Override
protected void performDefaults() {
- this.editor.loadDefault();
+ this.extensionsEditor.loadDefault();
+ this.foldersEditor.loadDefault();
super.performDefaults();
}
@@ -170,7 +192,8 @@
*/
@Override
public boolean performOk() {
- this.editor.store();
+ this.extensionsEditor.store();
+ this.foldersEditor.store();
return super.performOk();
}
Deleted: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/ExtensionEditor.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/ExtensionEditor.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/ExtensionEditor.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -1,110 +0,0 @@
-package org.jboss.dna.web.jcr.rest.client.swt.preferences;
-
-import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.FILTERED_FILE_EXTENSIONS_PREFERENCE;
-import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.dnaPreferencePageFilteredFileExtensionsLabel;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.TreeSet;
-import org.eclipse.jface.preference.ListEditor;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.widgets.Composite;
-import org.jboss.dna.web.jcr.rest.client.swt.Utils;
-
-/**
- * The <code>ExtensionEditor</code> is an editor for managing a set of file extensions.
- *
- * @since 0.6
- */
-public final class ExtensionEditor extends ListEditor {
-
- // =======================================================================================================================
- // Constants
- // =======================================================================================================================
-
- /**
- * The delimiter used to separate file extensions when saved to the preference store.
- *
- * @since 0.6
- */
- private static final char DELIM = ',';
-
- // =======================================================================================================================
- // Fields
- // =======================================================================================================================
-
- /**
- * The current set of file extensions.
- *
- * @since 0.6
- */
- private final Set<String> extensions;
-
- // =======================================================================================================================
- // Constructors
- // =======================================================================================================================
-
- /**
- * @param parent the parent control
- * @since 0.6
- */
- public ExtensionEditor( Composite parent ) {
- super(FILTERED_FILE_EXTENSIONS_PREFERENCE, dnaPreferencePageFilteredFileExtensionsLabel.text(), parent);
- this.extensions = new TreeSet<String>();
- }
-
- // =======================================================================================================================
- // Methods
- // =======================================================================================================================
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.jface.preference.ListEditor#createList(java.lang.String[])
- * @since 0.6
- */
- @Override
- protected String createList( String[] items ) {
- return Utils.combineTokens(items, DELIM);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.jface.preference.ListEditor#getNewInputObject()
- * @since 0.6
- */
- @Override
- protected String getNewInputObject() {
- NewFileExtensionDialog dialog = new NewFileExtensionDialog(getShell());
-
- if (dialog.open() == Window.OK) {
- String extension = dialog.getFileExtension();
-
- // add new extension
- if (extension != null) {
- this.extensions.add(extension);
- return extension;
- }
- }
-
- // user canceled dialog
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.jface.preference.ListEditor#parseString(java.lang.String)
- * @since 0.6
- */
- @Override
- protected String[] parseString( String stringList ) {
- String[] values = Utils.getTokens(stringList, Character.toString(DELIM), true);
-
- this.extensions.clear();
- this.extensions.addAll(Arrays.asList(values));
-
- return values;
- }
-
-}
Copied: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFileExtensionEditor.java (from rev 1194, branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/ExtensionEditor.java)
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFileExtensionEditor.java (rev 0)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFileExtensionEditor.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -0,0 +1,122 @@
+package org.jboss.dna.web.jcr.rest.client.swt.preferences;
+
+import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.FILTERED_FILE_EXTENSIONS_PREFERENCE;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.dnaPreferencePageFilteredFileExtensionsLabel;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.newFilteredFileExtensionDialogLabel;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.newFilteredFileExtensionDialogTitle;
+import static org.jboss.dna.web.jcr.rest.client.swt.preferences.PrefUtils.FILE_EXT_DELIMITER;
+import static org.jboss.dna.web.jcr.rest.client.swt.preferences.PrefUtils.FILE_EXT_INVALID_CHARS;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.TreeSet;
+import org.eclipse.jface.preference.ListEditor;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.widgets.Composite;
+import org.jboss.dna.web.jcr.rest.client.swt.Utils;
+
+/**
+ * The <code>FilteredFileExtensionEditor</code> is an editor for managing a set of filtered file extensions.
+ *
+ * @since 0.6
+ */
+public final class FilteredFileExtensionEditor extends ListEditor implements VerifyListener {
+
+ // =======================================================================================================================
+ // Fields
+ // =======================================================================================================================
+
+ /**
+ * The current set of file extensions.
+ *
+ * @since 0.6
+ */
+ private final Set<String> extensions;
+
+ // =======================================================================================================================
+ // Constructors
+ // =======================================================================================================================
+
+ /**
+ * @param parent the parent control
+ * @since 0.6
+ */
+ public FilteredFileExtensionEditor( Composite parent ) {
+ super(FILTERED_FILE_EXTENSIONS_PREFERENCE, dnaPreferencePageFilteredFileExtensionsLabel.text(), parent);
+ this.extensions = new TreeSet<String>();
+ }
+
+ // =======================================================================================================================
+ // Methods
+ // =======================================================================================================================
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.preference.ListEditor#createList(java.lang.String[])
+ * @since 0.6
+ */
+ @Override
+ protected String createList( String[] items ) {
+ return Utils.combineTokens(items, FILE_EXT_DELIMITER);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.preference.ListEditor#getNewInputObject()
+ * @since 0.6
+ */
+ @Override
+ protected String getNewInputObject() {
+ NewItemDialog dialog = new NewItemDialog(getShell(), newFilteredFileExtensionDialogTitle.text(),
+ newFilteredFileExtensionDialogLabel.text(), this);
+
+ if (dialog.open() == Window.OK) {
+ String extension = dialog.getNewItem();
+
+ // add new extension
+ if (extension != null) {
+ this.extensions.add(extension);
+ return extension;
+ }
+ }
+
+ // user canceled dialog
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.preference.ListEditor#parseString(java.lang.String)
+ * @since 0.6
+ */
+ @Override
+ protected String[] parseString( String stringList ) {
+ String[] values = Utils.getTokens(stringList, Character.toString(FILE_EXT_DELIMITER), true);
+
+ this.extensions.clear();
+ this.extensions.addAll(Arrays.asList(values));
+
+ return values;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
+ * @since 0.6
+ */
+ @Override
+ public void verifyText( VerifyEvent event ) {
+ for (char c : FILE_EXT_INVALID_CHARS.toCharArray()) {
+ if (c == event.character) {
+ event.doit = false;
+ break;
+ }
+ }
+ }
+
+}
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFileExtensionEditor.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFoldersEditor.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFoldersEditor.java (rev 0)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFoldersEditor.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -0,0 +1,122 @@
+package org.jboss.dna.web.jcr.rest.client.swt.preferences;
+
+import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.FILTERED_FOLDER_NAMES_PREFERENCE;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.dnaPreferencePageFilteredFolderNamesLabel;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.newFilteredFolderNameDialogLabel;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.newFilteredFolderNameDialogTitle;
+import static org.jboss.dna.web.jcr.rest.client.swt.preferences.PrefUtils.FOLDER_NAME_DELIMITER;
+import static org.jboss.dna.web.jcr.rest.client.swt.preferences.PrefUtils.FOLDER_NAME_INVALID_CHARS;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.TreeSet;
+import org.eclipse.jface.preference.ListEditor;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.widgets.Composite;
+import org.jboss.dna.web.jcr.rest.client.swt.Utils;
+
+/**
+ * The <code>FilteredFoldersEditor</code> is an editor for managing a set of folder names.
+ *
+ * @since 0.6
+ */
+public final class FilteredFoldersEditor extends ListEditor implements VerifyListener {
+
+ // =======================================================================================================================
+ // Fields
+ // =======================================================================================================================
+
+ /**
+ * The current set of folder names.
+ *
+ * @since 0.6
+ */
+ private final Set<String> folderNames;
+
+ // =======================================================================================================================
+ // Constructors
+ // =======================================================================================================================
+
+ /**
+ * @param parent the parent control
+ * @since 0.6
+ */
+ public FilteredFoldersEditor( Composite parent ) {
+ super(FILTERED_FOLDER_NAMES_PREFERENCE, dnaPreferencePageFilteredFolderNamesLabel.text(), parent);
+ this.folderNames = new TreeSet<String>();
+ }
+
+ // =======================================================================================================================
+ // Methods
+ // =======================================================================================================================
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.preference.ListEditor#createList(java.lang.String[])
+ * @since 0.6
+ */
+ @Override
+ protected String createList( String[] items ) {
+ return Utils.combineTokens(items, FOLDER_NAME_DELIMITER);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.preference.ListEditor#getNewInputObject()
+ * @since 0.6
+ */
+ @Override
+ protected String getNewInputObject() {
+ NewItemDialog dialog = new NewItemDialog(getShell(), newFilteredFolderNameDialogTitle.text(),
+ newFilteredFolderNameDialogLabel.text(), this);
+
+ if (dialog.open() == Window.OK) {
+ String folderName = dialog.getNewItem();
+
+ // add new folder name
+ if (folderName != null) {
+ this.folderNames.add(folderName);
+ return folderName;
+ }
+ }
+
+ // user canceled dialog
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.preference.ListEditor#parseString(java.lang.String)
+ * @since 0.6
+ */
+ @Override
+ protected String[] parseString( String stringList ) {
+ String[] values = Utils.getTokens(stringList, Character.toString(FOLDER_NAME_DELIMITER), true);
+
+ this.folderNames.clear();
+ this.folderNames.addAll(Arrays.asList(values));
+
+ return values;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
+ * @since 0.6
+ */
+ @Override
+ public void verifyText( VerifyEvent event ) {
+ for (char c : FOLDER_NAME_INVALID_CHARS.toCharArray()) {
+ if (c == event.character) {
+ event.doit = false;
+ break;
+ }
+ }
+ }
+
+}
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/FilteredFoldersEditor.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Deleted: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewFileExtensionDialog.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewFileExtensionDialog.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewFileExtensionDialog.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -1,156 +0,0 @@
-package org.jboss.dna.web.jcr.rest.client.swt.preferences;
-
-import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.newFileExtensionDialogLabel;
-import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.newFileExtensionDialogTitle;
-import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.VerifyEvent;
-import org.eclipse.swt.events.VerifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * A <code>NewFileExtensionDialog</code> allows the user to enter a file extension that they wish to be filtered out of all
- * publishing operations.
- *
- * @since 0.6
- */
-public final class NewFileExtensionDialog extends Dialog implements ModifyListener, VerifyListener {
-
- // =======================================================================================================================
- // Fields
- // =======================================================================================================================
-
- /**
- * The contents of the extension text field.
- *
- * @since 0.6
- */
- private String extension;
-
- // =======================================================================================================================
- // Constructors
- // =======================================================================================================================
-
- /**
- * @param parentShell the parent shell
- * @since 0.6
- */
- protected NewFileExtensionDialog( Shell parentShell ) {
- super(parentShell);
- setShellStyle(getShellStyle() | SWT.RESIZE);
- }
-
- // =======================================================================================================================
- // Methods
- // =======================================================================================================================
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
- * @since 0.6
- */
- @Override
- protected void configureShell( Shell newShell ) {
- newShell.setText(newFileExtensionDialogTitle.text());
- super.configureShell(newShell);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.jface.dialogs.Dialog#createButton(org.eclipse.swt.widgets.Composite, int, java.lang.String, boolean)
- * @since 0.6
- */
- @Override
- protected Button createButton( Composite parent,
- int id,
- String label,
- boolean defaultButton ) {
- Button button = super.createButton(parent, id, label, defaultButton);
-
- // disable OK button initially
- if (id == OK) {
- button.setEnabled(false);
- }
-
- return button;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
- * @since 0.6
- */
- @Override
- protected Control createDialogArea( Composite parent ) {
- Composite panel = (Composite)super.createDialogArea(parent);
- Composite pnlEditor = new Composite(panel, SWT.NONE);
- pnlEditor.setLayout(new GridLayout(2, false));
- pnlEditor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
- Label label = new Label(pnlEditor, SWT.NONE);
- label.setText(newFileExtensionDialogLabel.text());
-
- Text textField = new Text(pnlEditor, SWT.BORDER);
- textField.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
- textField.addVerifyListener(this);
- textField.addModifyListener(this);
-
- return panel;
- }
-
- /**
- * @return the file extension or <code>null</code> if dialog was canceled
- * @since 0.6
- */
- public String getFileExtension() {
- if (getReturnCode() == OK) {
- return this.extension;
- }
-
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
- * @since 0.6
- */
- @Override
- public void modifyText( ModifyEvent event ) {
- // disable OK button if text field does not have any characters
- this.extension = ((Text)event.widget).getText();
- boolean enable = (this.extension.length() != 0);
-
- if (getButton(OK).getEnabled() != enable) {
- getButton(OK).setEnabled(enable);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.swt.events.VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
- * @since 0.6
- */
- @Override
- public void verifyText( VerifyEvent event ) {
- // only allow letters, numbers, backspace, and delete
- if (!Character.isLetterOrDigit(event.character) && (event.character != '\b') && (event.character != '\u007F')) {
- event.doit = false;
- }
- }
-
-}
Copied: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewItemDialog.java (from rev 1194, branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewFileExtensionDialog.java)
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewItemDialog.java (rev 0)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewItemDialog.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -0,0 +1,196 @@
+package org.jboss.dna.web.jcr.rest.client.swt.preferences;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A <code>NewItemDialog</code> allows the user to enter a new file extension or new folder name that they wish to be filtered out
+ * of all publishing operations.
+ *
+ * @since 0.6
+ */
+public final class NewItemDialog extends Dialog implements ModifyListener {
+
+ // =======================================================================================================================
+ // Fields
+ // =======================================================================================================================
+
+ /**
+ * The label describing the new item.
+ *
+ * @since 0.6
+ */
+ private final String label;
+
+ /**
+ * The contents of the new item text field.
+ *
+ * @since 0.6
+ */
+ private String newItem;
+
+ /**
+ * The dialog title.
+ *
+ * @since 0.6
+ */
+ private final String title;
+
+ /**
+ * The listener verifying input characters.
+ *
+ * @since 0.6
+ */
+ private final VerifyListener verifyListener;
+
+ // =======================================================================================================================
+ // Constructors
+ // =======================================================================================================================
+
+ /**
+ * @param parentShell the parent shell (may be <code>null</code>)
+ * @param title the localized dialog title (never <code>null</code>)
+ * @param label the localized label (never <code>null</code>)
+ * @param verifyListener a listener that validates input characters (may be <code>null</code>)
+ * @since 0.6
+ */
+ public NewItemDialog( Shell parentShell,
+ String title,
+ String label,
+ VerifyListener verifyListener ) {
+ super(parentShell);
+
+ CheckArg.isNotNull(title, "title"); //$NON-NLS-1$
+ CheckArg.isNotNull(label, "label"); //$NON-NLS-1$
+
+ this.title = title;
+ this.label = label;
+ this.verifyListener = verifyListener;
+
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ }
+
+ // =======================================================================================================================
+ // Methods
+ // =======================================================================================================================
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+ * @since 0.6
+ */
+ @Override
+ protected void configureShell( Shell newShell ) {
+ newShell.setText(this.title);
+ super.configureShell(newShell);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.dialogs.Dialog#createButton(org.eclipse.swt.widgets.Composite, int, java.lang.String, boolean)
+ * @since 0.6
+ */
+ @Override
+ protected Button createButton( Composite parent,
+ int id,
+ String label,
+ boolean defaultButton ) {
+ Button button = super.createButton(parent, id, label, defaultButton);
+
+ // disable OK button initially
+ if (id == OK) {
+ button.setEnabled(false);
+ }
+
+ return button;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ * @since 0.6
+ */
+ @Override
+ protected Control createDialogArea( Composite parent ) {
+ Composite panel = (Composite)super.createDialogArea(parent);
+ Composite pnlEditor = new Composite(panel, SWT.NONE);
+ pnlEditor.setLayout(new GridLayout(2, false));
+ pnlEditor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ Label label = new Label(pnlEditor, SWT.NONE);
+ label.setText(this.label);
+
+ Text textField = new Text(pnlEditor, SWT.BORDER);
+ textField.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+ textField.addModifyListener(this);
+
+ // add listener if necessary
+ if (this.verifyListener != null) {
+ textField.addVerifyListener(this.verifyListener);
+ }
+
+ return panel;
+ }
+
+ /**
+ * @return the new item or <code>null</code> if the dialog was canceled
+ * @since 0.6
+ */
+ public String getNewItem() {
+ if (getReturnCode() == OK) {
+ return this.newItem;
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
+ * @since 0.6
+ */
+ @Override
+ protected void initializeBounds() {
+ super.initializeBounds();
+
+ // resize shell to be twice the width needed for the title (without this the title maybe cropped)
+ int width = (2 * convertWidthInCharsToPixels(this.title.length()));
+ Rectangle rectangle = getShell().getBounds();
+ getShell().setBounds(rectangle.x, rectangle.y, width, rectangle.height);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
+ * @since 0.6
+ */
+ @Override
+ public void modifyText( ModifyEvent event ) {
+ // disable OK button if text field does not have any characters
+ this.newItem = ((Text)event.widget).getText();
+ boolean enable = (this.newItem.length() != 0);
+
+ if (getButton(OK).getEnabled() != enable) {
+ getButton(OK).setEnabled(enable);
+ }
+ }
+
+}
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/NewItemDialog.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PrefUtils.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PrefUtils.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PrefUtils.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -24,8 +24,15 @@
package org.jboss.dna.web.jcr.rest.client.swt.preferences;
import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.FILTERED_FILE_EXTENSIONS_PREFERENCE;
+import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.FILTERED_FOLDER_NAMES_PREFERENCE;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.prefUtilsPropertyFileNotFound;
+import static org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n.prefUtilsPropertyNotFound;
+import java.io.InputStream;
+import java.util.Properties;
import org.eclipse.jface.preference.IPreferenceStore;
import org.jboss.dna.common.util.CheckArg;
+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.swt.Activator;
import org.jboss.dna.web.jcr.rest.client.swt.Utils;
@@ -38,6 +45,136 @@
public final class PrefUtils {
// =======================================================================================================================
+ // Constants
+ // =======================================================================================================================
+
+ /**
+ * The name of the properties file located in this package.
+ */
+ private static final String PREFERENCES_FILE = "dnaPrefs.properties"; //$NON-NLS-1$
+
+ /**
+ * The preference name for the delimiter that separates filtered file extensions when the preference value is stored.
+ */
+ private static final String FILE_EXT_DELIMITER_PREF_NAME = "fileExtension.delimiter"; //$NON-NLS-1$
+
+ /**
+ * The delimiter that separates filtered file extensions when the preference value is stored.
+ */
+ public static final char FILE_EXT_DELIMITER;
+
+ /**
+ * The preference name for characters that are <strong>NOT</strong> allowed to appear in a file extension.
+ */
+ private static final String FILE_EXT_INVALID_CHARS_PREF_NAME = "fileExtension.invalidChars"; //$NON-NLS-1$
+
+ /**
+ * The characters that are <strong>NOT</strong> allowed to appear in a file extension.
+ */
+ public static final String FILE_EXT_INVALID_CHARS;
+
+ /**
+ * The preference name for the delimiter that separates filtered folder names when the preference value is stored.
+ */
+ private static final String FOLDER_NAME_DELIMITER_PREF_NAME = "folderName.delimiter"; //$NON-NLS-1$
+
+ /**
+ * The delimiter that separates filtered folder names when the preference value is stored.
+ */
+ public static final char FOLDER_NAME_DELIMITER;
+
+ /**
+ * The preference name for characters that are <strong>NOT</strong> allowed to appear in a folder name.
+ */
+ private static final String FOLDER_NAME_INVALID_CHARS_PREF_NAME = "folderName.invalidChars"; //$NON-NLS-1$
+
+ /**
+ * The characters that are <strong>NOT</strong> allowed to appear in a folder name.
+ */
+ public static final String FOLDER_NAME_INVALID_CHARS;
+
+ // =======================================================================================================================
+ // Class Initializer
+ // =======================================================================================================================
+
+ static {
+ InputStream is = null;
+ Properties preferenceProperties = new Properties();
+
+ // get the properties file
+ try {
+ is = PrefUtils.class.getResourceAsStream(PREFERENCES_FILE);
+ } catch (Exception e) {
+ Activator.getDefault().log(new Status(Severity.ERROR, e.getMessage(), e));
+ }
+
+ if (is == null) {
+ Activator.getDefault().log(new Status(Severity.ERROR, prefUtilsPropertyFileNotFound.text(), null));
+ } else {
+ // load the properties file
+ try {
+ preferenceProperties.load(is);
+ } catch (Exception e) {
+ Activator.getDefault().log(new Status(Severity.ERROR, e.getMessage(), e));
+ } finally {
+ try {
+ is.close();
+ } catch (Exception e) {
+ Activator.getDefault().log(new Status(Severity.ERROR, e.getMessage(), e));
+ }
+ }
+ }
+
+ // file extensions delimiter
+ String temp = preferenceProperties.getProperty(FILE_EXT_DELIMITER_PREF_NAME);
+
+ if ((temp != null) && (temp.length() > 0)) {
+ FILE_EXT_DELIMITER = temp.charAt(0);
+ } else {
+ // make sure preference has a value
+ FILE_EXT_DELIMITER = ',';
+ Activator.getDefault().log(new Status(Severity.ERROR, prefUtilsPropertyNotFound.text(FILE_EXT_DELIMITER_PREF_NAME),
+ null));
+ }
+
+ // file extension invalid characters
+ temp = preferenceProperties.getProperty(FILE_EXT_INVALID_CHARS_PREF_NAME);
+
+ if ((temp != null) && (temp.length() > 0)) {
+ FILE_EXT_INVALID_CHARS = temp;
+ } else {
+ // make sure preference has a value
+ FILE_EXT_INVALID_CHARS = "*?<>|/\\:;."; //$NON-NLS-1$
+ Activator.getDefault().log(new Status(Severity.ERROR, prefUtilsPropertyNotFound.text(FILE_EXT_INVALID_CHARS_PREF_NAME),
+ null));
+ }
+
+ // folder names delimiter
+ temp = preferenceProperties.getProperty(FOLDER_NAME_DELIMITER_PREF_NAME);
+
+ if ((temp != null) && (temp.length() > 0)) {
+ FOLDER_NAME_DELIMITER = temp.charAt(0);
+ } else {
+ // make sure preference has a value
+ FOLDER_NAME_DELIMITER = ',';
+ Activator.getDefault().log(new Status(Severity.ERROR,
+ prefUtilsPropertyNotFound.text(FOLDER_NAME_DELIMITER_PREF_NAME), null));
+ }
+
+ // folder name invalid characters
+ temp = preferenceProperties.getProperty(FOLDER_NAME_INVALID_CHARS_PREF_NAME);
+
+ if ((temp != null) && (temp.length() > 0)) {
+ FOLDER_NAME_INVALID_CHARS = temp;
+ } else {
+ // make sure preference has a value
+ FOLDER_NAME_INVALID_CHARS = "*?<>|/\\:;"; //$NON-NLS-1$
+ Activator.getDefault().log(new Status(Severity.ERROR,
+ prefUtilsPropertyNotFound.text(FOLDER_NAME_INVALID_CHARS_PREF_NAME), null));
+ }
+ }
+
+ // =======================================================================================================================
// Class Methods
// =======================================================================================================================
@@ -46,10 +183,18 @@
* @since 0.6
*/
public static String[] getFilteredFileExtensions() {
- return getListPropertyValue(FILTERED_FILE_EXTENSIONS_PREFERENCE, ',', true);
+ return getListPropertyValue(FILTERED_FILE_EXTENSIONS_PREFERENCE, FILE_EXT_DELIMITER, true);
}
/**
+ * @return the folder names being filtered out of publishing operations (never null)
+ * @since 0.6
+ */
+ public static String[] getFilteredFolderNames() {
+ return getListPropertyValue(FILTERED_FOLDER_NAMES_PREFERENCE, FOLDER_NAME_DELIMITER, true);
+ }
+
+ /**
* @param propertyId the property name whose list values are being requested
* @param delimiter the character separating the items in the property value
* @param removeDuplicates a flag indicating if duplicate items should be removed
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PublishingFileFilter.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PublishingFileFilter.java (rev 0)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PublishingFileFilter.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -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.web.jcr.rest.client.swt.preferences;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * The <code>PublishingFileFilter</code> is a file filter that uses the DNA preferences when filtering files.
+ *
+ * @since 0.6
+ */
+public final class PublishingFileFilter {
+
+ // =======================================================================================================================
+ // Fields
+ // =======================================================================================================================
+
+ /**
+ * The file extensions that should not be involved in publishing operations.
+ */
+ private final String[] filteredFileExtensions;
+
+ /**
+ * The folder names that should not be involved in publishing operations.
+ */
+ private final String[] filteredFolderNames;
+
+ // =======================================================================================================================
+ // Constructors
+ // =======================================================================================================================
+
+ /**
+ * Construct a filter using the current DNA preferences.
+ */
+ public PublishingFileFilter() {
+ this.filteredFileExtensions = PrefUtils.getFilteredFileExtensions();
+ this.filteredFolderNames = PrefUtils.getFilteredFolderNames();
+ }
+
+ // =======================================================================================================================
+ // Methods
+ // =======================================================================================================================
+
+ /**
+ * @param resource the resource being tested (never <code>null</code>)
+ * @return <code>true</code> if the resource should be included (i.e., it is not filtered out)
+ */
+ public boolean accept( IResource resource ) {
+ CheckArg.isNotNull(resource, "resource"); //$NON-NLS-1$
+
+ if (resource instanceof IFolder) {
+ String name = resource.getName();
+
+ // see if folder name has been filtered
+ for (String filteredName : this.filteredFolderNames) {
+ if (filteredName.equals(name)) {
+ return false;
+ }
+ }
+
+ // check parent
+ if (resource.getParent() != null) {
+ return accept(resource.getParent());
+ }
+ } else if (resource instanceof IFile) {
+ // see if file extension has been filtered
+ for (String extension : this.filteredFileExtensions) {
+ if (resource.getFullPath().toString().endsWith('.' + extension)) {
+ return false;
+ }
+ }
+
+ // check parent
+ if (resource.getParent() != null) {
+ return accept(resource.getParent());
+ }
+ }
+
+ // must be project
+ return true;
+ }
+
+}
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/PublishingFileFilter.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/dnaPrefs.properties
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/dnaPrefs.properties (rev 0)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/dnaPrefs.properties 2009-09-17 19:46:22 UTC (rev 1230)
@@ -0,0 +1,32 @@
+#
+# 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 properties file is read in by PrefUtils. The delimiter values are used when the preference values are stored and the
+# valid character values are used by the editors when they validate user input.
+
+fileExtension.delimiter = ,
+fileExtension.invalidChars = *?<>|/\:;.
+
+folderName.delimiter = ,
+folderName.invalidChars = *?<>|/\:;
\ No newline at end of file
Property changes on: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/preferences/dnaPrefs.properties
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaContentProvider.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaContentProvider.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaContentProvider.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -335,6 +335,11 @@
}
display.asyncExec(new Runnable() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Runnable#run()
+ */
@SuppressWarnings( "synthetic-access" )
@Override
public void run() {
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaMessageConsole.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaMessageConsole.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/DnaMessageConsole.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -34,6 +34,7 @@
import org.eclipse.jface.text.IRegion;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.console.AbstractConsole;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleManager;
@@ -58,6 +59,16 @@
// =======================================================================================================================
/**
+ * Start tag for adding emphasis to a message. Tag will appear in a properties file.
+ */
+ private static final String EMPHASIS_START_TAG = "<em>"; //$NON-NLS-1$
+
+ /**
+ * End tag for adding emphasis to a message. Tag will appear in a properties file.
+ */
+ private static final String EMPHASIS_END_TAG = "</em>"; //$NON-NLS-1$
+
+ /**
* The identifier and type of the DNA Message Console.
*
* @since 0.6
@@ -104,32 +115,6 @@
}
/**
- * @param message the message being written to the console (never <code>null</code>)
- * @param emphasize indicates if the message should be emphasized in the console
- * @since 0.6
- */
- public static void write( String message,
- boolean emphasize ) {
- CheckArg.isNotNull(message, "message"); //$NON-NLS-1$
- DnaMessageConsole console = getDnaConsole();
- console.print(message, emphasize, false, null);
- }
-
- /**
- * @param message the message being written to the console
- * @param file the file whose full path, which is contained in the message, will be made into a hyperlink (may be
- * <code>null</code>)
- * @since 0.6
- */
- public static void write( String message,
- IFile file ) {
- CheckArg.isNotNull(message, "message"); //$NON-NLS-1$
-
- DnaMessageConsole console = getDnaConsole();
- console.print(message, false, false, file);
- }
-
- /**
* Adds a line feed to the console after the message is printed.
*
* @param message the message being written to the console (never <code>null</code>)
@@ -153,7 +138,7 @@
CheckArg.isNotNull(message, "message"); //$NON-NLS-1$
DnaMessageConsole console = getDnaConsole();
- console.print(message, false, true, file);
+ console.print(message, true, file);
}
// =======================================================================================================================
@@ -194,16 +179,15 @@
/**
* @param message the message being written to the console (never <code>null</code>)
- * @param emphasize a flag indicating if the message should be emphasized
* @param doLineFeedAtEnd a flag indicating if a line feed should be done after writing the message
* @param file the file to create a hyperlink for (may be <code>null</code>)
* @since 0.6
*/
private void print( final String message,
- final boolean emphasize,
final boolean doLineFeedAtEnd,
final IFile file ) {
final Display display = Display.getDefault();
+ final AbstractConsole console = this;
if (!display.isDisposed()) {
display.asyncExec(new Runnable() {
@@ -216,23 +200,55 @@
@Override
public void run() {
if (!display.isDisposed()) {
- MessageConsoleStream stream = setup();
+ // bring focus to this view
+ console.activate();
- // configure stream
- if (emphasize) {
- stream.setColor(display.getSystemColor(SWT.COLOR_BLUE));
- }
-
// register document listener before writing to console
if (file != null) {
addDocumentListener(message, file);
}
- // write message
+ MessageConsoleStream stream = newMessageStream();
+
+ for (int beginIndex = 0, endIndex = 0, msgLength = message.length(); endIndex < msgLength;) {
+ int startTagIndex = message.indexOf(EMPHASIS_START_TAG, beginIndex);
+ int endTagIndex = ((startTagIndex < 0) ? -1 : message.indexOf(EMPHASIS_END_TAG,
+ startTagIndex
+ + EMPHASIS_START_TAG.length()));
+
+ // ignore tags if both tags are not found
+ if ((endTagIndex < 0) && (startTagIndex >= 0)) {
+ startTagIndex = -1;
+ }
+
+ // determine if in emphasize mode
+ boolean emphasize = (beginIndex == startTagIndex);
+
+ // skip over start tag and set stream to bold font style
+ if (emphasize) {
+ beginIndex += EMPHASIS_START_TAG.length();
+ stream.setFontStyle(SWT.BOLD);
+ endIndex = endTagIndex;
+ } else {
+ stream.setFontStyle(SWT.NORMAL);
+ endIndex = ((startTagIndex < 0) ? msgLength : startTagIndex);
+ }
+
+ stream.print(message.substring(beginIndex, endIndex));
+
+ // need to construct a new stream as changes to font style seem to only work one time
+ stream = newMessageStream();
+
+ // skip over end tag
+ if (emphasize) {
+ endIndex += EMPHASIS_END_TAG.length();
+ }
+
+ beginIndex = endIndex;
+ }
+
if (doLineFeedAtEnd) {
- stream.println(message);
- } else {
- stream.print(message);
+ stream.println();
}
}
}
@@ -240,17 +256,6 @@
}
}
- /**
- * Performs any setup required before writing to the console.
- *
- * @return the stream that can be written to
- * @since 0.6
- */
- MessageConsoleStream setup() {
- activate(); // show and focus console view
- return newMessageStream();
- }
-
// ===========================================================================================================================
// Inner Class
// ===========================================================================================================================
@@ -301,7 +306,7 @@
public HyperlinkCreator( String message,
DnaMessageConsole console,
IFile file ) {
- this.message = message;
+ this.message = message.replaceAll("<em>", "").replaceAll("\\Q</em>\\E", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
this.console = console;
this.file = file;
}
@@ -343,7 +348,7 @@
throw new BadLocationException(RestClientI18n.dnaConsoleFilePathNotFoundMsg.text(target));
}
- this.console.addHyperlink(new FileLink(file, null, -1, -1, -1), region.getOffset() + 1, target.length());
+ this.console.addHyperlink(new FileLink(file, null, -1, -1, -1), (region.getOffset() + index), target.length());
// created hyperlink so no need to listen any longer
document.removeDocumentListener(this);
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/ServerView.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/ServerView.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/views/ServerView.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -93,7 +93,7 @@
*
* @since 0.6
*/
- private BaseSelectionListenerAction newAction;
+ private Action newAction;
/**
* The viewer's content and label provider.
@@ -124,6 +124,11 @@
private void constructActions() {
// the collapse all action is always enabled
this.collapseAllAction = new Action() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.action.Action#run()
+ */
@Override
public void run() {
getViewer().collapseAll();
@@ -133,9 +138,9 @@
this.collapseAllAction.setToolTipText(RestClientI18n.collapseActionToolTip.text());
this.collapseAllAction.setImageDescriptor(Activator.getDefault().getImageDescriptor(COLLAPSE_ALL_IMAGE));
- // the refresh action is always enabled
+ // the reconnect action tries to ping a selected server
this.reconnectAction = new ReconnectToServerAction(this.viewer);
- getServerManager().addRegistryListener(this.reconnectAction);
+ this.viewer.addSelectionChangedListener(this.reconnectAction);
// the shell used for dialogs that the actions display
Shell shell = this.getSite().getShell();
@@ -150,7 +155,6 @@
// the new server action is always enabled
this.newAction = new NewServerAction(shell, getServerManager());
- this.viewer.addSelectionChangedListener(this.newAction);
}
/**
@@ -194,12 +198,22 @@
ColumnViewerToolTipSupport.enableFor(this.viewer);
this.viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
+ */
@Override
public void selectionChanged( SelectionChangedEvent event ) {
handleSelectionChanged(event);
}
});
this.viewer.addDoubleClickListener(new IDoubleClickListener() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
+ */
@Override
public void doubleClick( DoubleClickEvent arg0 ) {
handleDoubleClick();
@@ -286,7 +300,12 @@
*/
@Override
public Exception[] serverRegistryChanged( ServerRegistryEvent event ) {
- this.viewer.refresh();
+ if (event.isNew() || event.isUpdate()) {
+ this.viewer.refresh();
+ } else {
+ this.viewer.remove(event.getServer());
+ }
+
return null;
}
Deleted: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishJob.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishJob.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishJob.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -1,388 +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.swt.wizards;
-
-import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.DNA_PUBLISHING_JOB_FAMILY;
-import static org.jboss.dna.web.jcr.rest.client.swt.IUiConstants.PLUGIN_ID;
-import java.io.File;
-import java.net.URL;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.jobs.Job;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.web.jcr.rest.client.ServerManager;
-import org.jboss.dna.web.jcr.rest.client.Status;
-import org.jboss.dna.web.jcr.rest.client.domain.Workspace;
-import org.jboss.dna.web.jcr.rest.client.swt.Activator;
-import org.jboss.dna.web.jcr.rest.client.swt.DnaResourceHelper;
-import org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n;
-import org.jboss.dna.web.jcr.rest.client.swt.views.DnaContentProvider;
-import org.jboss.dna.web.jcr.rest.client.swt.views.DnaMessageConsole;
-
-/**
- * The <code>PublishJob</code> publishes or unpublishes one or more files using the {@link ServerManager}.
- *
- * @since 0.6
- */
-public final class PublishJob extends Job {
-
- // ===========================================================================================================================
- // Constants
- // ===========================================================================================================================
-
- /**
- * The job type.
- *
- * @since 0.6
- */
- public enum Type {
- /**
- * Indicates a publish job.
- *
- * @since 0.6
- */
- PUBLISH,
-
- /**
- * Indicates an unpublish job.
- *
- * @since 0.6
- */
- UNPUBLISH
- }
-
- /**
- * A unique job identifier given to each publishing/unpublishing job.
- *
- * @since 0.6
- */
- private static final AtomicInteger JOB_ID = new AtomicInteger(1);
-
- // ===========================================================================================================================
- // Class Methods
- // ===========================================================================================================================
-
- /**
- * @param type the job type (never <code>null</code>)
- * @param jobId the job identifier
- * @return the job name
- * @since 0.6
- */
- private static String getJobName( Type type,
- int jobId ) {
- CheckArg.isNotNull(type, "type"); //$NON-NLS-1$
-
- if (Type.PUBLISH == type) {
- return RestClientI18n.publishJobPublishName.text(jobId);
- }
-
- // unpublish
- return RestClientI18n.publishJobUnpublishName.text(jobId);
- }
-
- // ===========================================================================================================================
- // Fields
- // ===========================================================================================================================
-
- /**
- * The files being published or unpublished.
- *
- * @since 0.6
- */
- private final List<IFile> files;
-
- /**
- * The unique job identifier.
- *
- * @since 0.6
- */
- private final int jobId;
-
- /**
- * The job type.
- *
- * @since 0.6
- */
- private final Type type;
-
- /**
- * The workspace to use when publishing or unpublishing.
- *
- * @since 0.6
- */
- private final Workspace workspace;
-
- // ===========================================================================================================================
- // Constructors
- // ===========================================================================================================================
-
- /**
- * @param type the job type (never <code>null</code>)
- * @param files the files being published or unpublished (never <code>null</code>)
- * @param workspace the workspace to use when publishing or unpublishing (never <code>null</code>)
- * @since 0.6
- */
- public PublishJob( Type type,
- List<IFile> files,
- Workspace workspace ) {
- super(getJobName(type, JOB_ID.incrementAndGet()));
-
- CheckArg.isNotNull(files, "files"); //$NON-NLS-1$
-
- this.type = type;
- this.files = files;
- this.workspace = workspace;
- this.jobId = JOB_ID.get();
-
- setUser(true); // allow user to run in background
- }
-
- // ===========================================================================================================================
- // Methods
- // ===========================================================================================================================
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
- * @since 0.6
- */
- @Override
- public boolean belongsTo( Object family ) {
- return DNA_PUBLISHING_JOB_FAMILY.equals(family);
- }
-
- /**
- * @return the server manager
- * @since 0.6
- */
- private ServerManager getServerManager() {
- return Activator.getDefault().getServerManager();
- }
-
- /**
- * @return <code>true</code> if a publishing job
- * @since 0.6
- */
- private boolean isPublishing() {
- return (this.type == Type.PUBLISH);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
- * @since 0.6
- */
- @Override
- protected IStatus run( IProgressMonitor monitor ) {
- assert (this.workspace != null);
- long startTime = System.currentTimeMillis();
-
- try {
- int fileCount = this.files.size();
- String name = (isPublishing() ? RestClientI18n.publishJobPublishTaskName.text(this.jobId)
- : RestClientI18n.publishJobUnpublishTaskName.text(this.jobId));
- monitor.beginTask(name, fileCount);
- monitor.setTaskName(name);
-
- String serverUrl = this.workspace.getServer().getUrl();
- String repositoryName = this.workspace.getRepository().getName();
- String workspaceName = this.workspace.getName();
-
- // write initial message to console
- if (isPublishing()) {
- DnaMessageConsole.writeln(RestClientI18n.publishJobPublish.text(this.jobId,
- serverUrl,
- repositoryName,
- workspaceName,
- fileCount));
- } else {
- DnaMessageConsole.writeln(RestClientI18n.publishJobUnpublish.text(this.jobId,
- serverUrl,
- repositoryName,
- workspaceName,
- fileCount));
- }
-
- DnaResourceHelper resourceHelper = new DnaResourceHelper(getServerManager());
- int numProcessed = 0;
-
- // process the files
- for (IFile eclipseFile : this.files) {
- if (monitor.isCanceled()) {
- if (isPublishing()) {
- DnaMessageConsole.writeln(RestClientI18n.publishJobPublishCanceledMsg.text(this.jobId,
- serverUrl,
- repositoryName,
- workspaceName,
- numProcessed,
- fileCount));
- } else {
- DnaMessageConsole.writeln(RestClientI18n.publishJobUnpublishCanceledMsg.text(this.jobId,
- serverUrl,
- repositoryName,
- workspaceName,
- numProcessed,
- fileCount));
- }
-
- throw new InterruptedException(RestClientI18n.publishJobCanceled.text(jobId));
- }
-
- File file = eclipseFile.getLocation().toFile();
- String path = eclipseFile.getParent().getFullPath().toString();
- Status status = null;
-
- if (isPublishing()) {
- status = getServerManager().publish(this.workspace, path, file);
-
- // 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);
-
- // 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);
-
- // write outcome message to console
- if (isPublishing() && status.isOk()) {
- URL url = getServerManager().getUrl(file, path, this.workspace);
- writeToConsole(eclipseFile, url, status);
- } else {
- writeToConsole(eclipseFile, null, status);
- }
- }
-
- return org.eclipse.core.runtime.Status.OK_STATUS;
- } catch (Exception e) {
- String msg = null;
-
- if (e instanceof InterruptedException) {
- msg = e.getLocalizedMessage();
- } else {
- msg = RestClientI18n.publishJobUnexpectedErrorMsg.text();
- }
-
- return new org.eclipse.core.runtime.Status(IStatus.ERROR, PLUGIN_ID, msg, e);
- } finally {
- monitor.done();
-
- // add operation completed message
- String duration;
- long milliseconds = (System.currentTimeMillis() - startTime);
- long hours = milliseconds / (1000 * 60 * 60);
- long minutes = (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
- long seconds = ((milliseconds % (1000 * 60 * 60)) % (1000 * 60)) / 1000;
-
- if ((seconds > 0) || (minutes > 0) || (hours > 0)) {
- duration = RestClientI18n.publishJobLongDurationMsg.text(hours, minutes, seconds);
- } else {
- duration = RestClientI18n.publishJobShortDurationMsg.text();
- }
-
- if (isPublishing()) {
- DnaMessageConsole.writeln(RestClientI18n.publishJobPublishFinishedMsg.text(this.jobId, duration));
- } else {
- DnaMessageConsole.writeln(RestClientI18n.publishJobUnpublishFinishedMsg.text(this.jobId, duration));
- }
- }
- }
-
- /**
- * Create a hyperlink in console.
- *
- * @param file the file involved in the publishing operation
- * @param url the destination file URL or <code>null</code>
- * @param status the status of the publishing operation
- * @since 0.6
- */
- private void writeToConsole( final IFile file,
- URL url,
- Status status ) {
- String message = null;
-
- if (status.isOk()) {
- if (isPublishing()) {
- DnaMessageConsole.write(RestClientI18n.publishJobPublishFile.text(this.jobId, file.getFullPath()), false);
- DnaMessageConsole.write(RestClientI18n.publishJobPublishFile2.text(url.toString()), true);
- message = ""; //$NON-NLS-1$
- } else {
- message = RestClientI18n.publishJobUnpublishFile.text(this.jobId, file.getFullPath());
- }
- } else if (status.isError()) {
- if (isPublishing()) {
- message = RestClientI18n.publishJobPublishFileFailed.text(this.jobId, file.getFullPath());
- } else {
- message = RestClientI18n.publishJobUnpublishFileFailed.text(this.jobId, file.getFullPath());
- }
-
- // log
- Activator.getDefault().log(status);
- } else if (status.isWarning()) {
- if (isPublishing()) {
- message = RestClientI18n.publishJobPublishFileWarning.text(this.jobId, file.getFullPath());
- } else {
- message = RestClientI18n.publishJobUnpublishFileWarning.text(this.jobId, file.getFullPath());
- }
-
- // log
- Activator.getDefault().log(status);
- } else {
- if (isPublishing()) {
- message = RestClientI18n.publishJobPublishFileInfo.text(this.jobId, file.getFullPath());
- } else {
- message = RestClientI18n.publishJobUnpublishFileInfo.text(this.jobId, file.getFullPath());
- }
-
- // log
- Activator.getDefault().log(status);
- }
-
- // write to console creating a hyperlink
- DnaMessageConsole.writeln(message, file);
- }
-
-}
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishPage.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishPage.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishPage.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -64,10 +64,9 @@
import org.jboss.dna.web.jcr.rest.client.domain.Workspace;
import org.jboss.dna.web.jcr.rest.client.swt.Activator;
import org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n;
-import org.jboss.dna.web.jcr.rest.client.swt.Utils;
import org.jboss.dna.web.jcr.rest.client.swt.actions.NewServerAction;
-import org.jboss.dna.web.jcr.rest.client.swt.preferences.PrefUtils;
-import org.jboss.dna.web.jcr.rest.client.swt.wizards.PublishJob.Type;
+import org.jboss.dna.web.jcr.rest.client.swt.jobs.PublishJob.Type;
+import org.jboss.dna.web.jcr.rest.client.swt.preferences.PublishingFileFilter;
/**
* The <code>PublishPage</code> is a UI for publishing or unpublishing one or more files to a DNA repository.
@@ -88,21 +87,65 @@
private static final String RECURSE_KEY = "recurse"; //$NON-NLS-1$
// ===========================================================================================================================
+ // Class Fields
+ // ===========================================================================================================================
+
+ /**
+ * Indicates if the file filter should be used.
+ *
+ * @since 0.6
+ */
+ private static boolean filterFiles = true;
+
+ // ===========================================================================================================================
// Class Methods
// ===========================================================================================================================
/**
+ * @param container the project or folder whose files are being requested
+ * @param recurse the flag indicating if child containers should be traversed
+ * @param filter the file filter or <code>null</code> if not used
+ * @return the list of files contained in the specified container (never <code>null</code>)
+ * @throws CoreException if there is a problem finding the files
+ * @since 0.6
+ */
+ private static List<IFile> findFiles( IContainer container,
+ boolean recurse,
+ PublishingFileFilter filter ) throws CoreException {
+ List<IFile> result = new ArrayList<IFile>();
+
+ for (IResource member : container.members()) {
+ if (recurse && (member instanceof IContainer)) {
+ // don't select closed projects
+ if ((member instanceof IProject) && !((IProject)member).isOpen()) {
+ continue;
+ }
+
+ result.addAll(findFiles((IContainer)member, recurse, filter));
+ } else if ((member instanceof IFile) && ((IFile)member).getLocation().toFile().exists()) {
+ if ((filter == null) || filter.accept(member)) {
+ result.add((IFile)member);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
* Processes the specified list of files and for (1) each file found adds it to the result and (2) for each project or folder
* adds all contained files. For projects and folders processing will be recursive based on saved wizard settings.
*
* @param resources the resources being processed (never <code>null</code>)
* @param recurse the flag indicating if child containers should be traversed
+ * @param filter the file filter or <code>null</code> if not used
* @return the files being published or unpublished (never <code>null</code>)
* @throws CoreException if there is a problem processing the resources
* @since 0.6
*/
private static List<IFile> processResources( List<IResource> resources,
- boolean recurse ) throws CoreException {
+ boolean recurse,
+ PublishingFileFilter filter ) throws CoreException {
List<IFile> result = new ArrayList<IFile>();
// Project Map - the outer map. Its keys are IProjects and its values are a Parent Map
@@ -128,6 +171,11 @@
IProject project = null;
List<IFile> files = null;
Map<IContainer, List<IFile>> parentMap = null;
+
+ // see if resource is filtered
+ if ((filter != null) && !filter.accept(resource)) {
+ continue;
+ }
if (resource instanceof IFile) {
IContainer parent = null; // project or folder
@@ -195,14 +243,14 @@
Map<IContainer, List<IFile>> parentMap = projectMap.get(project);
if (parentMap == null) {
- result.addAll(Utils.findFiles(project, recurse));
+ result.addAll(findFiles(project, recurse, filter));
} else {
// process folders with no folder entries
for (IContainer folder : parentMap.keySet()) {
List<IFile> files = parentMap.get(folder);
if (files == null) {
- result.addAll(Utils.findFiles(folder, recurse));
+ result.addAll(findFiles(folder, recurse, filter));
}
}
}
@@ -257,11 +305,9 @@
private List<IFile> files;
/**
- * Indicates if the selected files should be filtered based on their file extension.
- *
- * @since 0.6
+ * The filter used to determine if a file should be included in publishing operations (may be <code>null</code>).
*/
- private boolean filterFiles = true;
+ private PublishingFileFilter filter;
/**
* The control containing all the files being published or unpublished.
@@ -359,6 +405,9 @@
this.type = type;
this.resources = resources;
+
+ // filter should not be cached as preferences may change
+ this.filter = (filterFiles ? new PublishingFileFilter() : null);
}
// ===========================================================================================================================
@@ -399,6 +448,11 @@
btnNewServer.setText(RestClientI18n.publishPageNewServerButton.text());
btnNewServer.setToolTipText(action.getToolTipText());
btnNewServer.addSelectionListener(new SelectionAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
@Override
public void widgetSelected( SelectionEvent e ) {
action.run();
@@ -407,6 +461,11 @@
// update page message first time selected to get rid of initial message by forcing validation
btnNewServer.addSelectionListener(new SelectionAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
@Override
public void widgetSelected( SelectionEvent e ) {
updateInitialMessage();
@@ -471,6 +530,11 @@
// update page message first time selected to get rid of initial message by forcing validation
this.lstResources.addSelectionListener(new SelectionAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
@Override
public void widgetSelected( SelectionEvent e ) {
// do the very first time to get rid of initial message then remove listener
@@ -490,6 +554,11 @@
chkRecurse.setToolTipText(RestClientI18n.publishPageRecurseCheckBoxToolTip.text());
chkRecurse.setSelection(this.recurse);
chkRecurse.addSelectionListener(new SelectionAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
@Override
public void widgetSelected( SelectionEvent e ) {
handleRecurseChanged(chkRecurse.getSelection());
@@ -498,6 +567,11 @@
// update page message first time selected to get rid of initial message by forcing validation
chkRecurse.addSelectionListener(new SelectionAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
@Override
public void widgetSelected( SelectionEvent e ) {
updateInitialMessage();
@@ -547,7 +621,7 @@
}
/**
- * @return the file to publish or unpublish (never <code>null</code>)
+ * @return the files to publish or unpublish (never <code>null</code>)
* @since 0.6
*/
public List<IFile> getFiles() {
@@ -596,7 +670,7 @@
saveRecurseSetting();
try {
- this.files = processResources(this.resources, isRecursing());
+ this.files = processResources(this.resources, isRecursing(), filter);
loadFiles();
} catch (CoreException e) {
Activator.getDefault().log(new Status(Severity.ERROR, RestClientI18n.publishPageRecurseProcessingErrorMsg.text(), e));
@@ -682,39 +756,16 @@
/**
* Populates the list of files to be published based on the recurse flag and the list of workspace selected resources.
- * Pre-condition is that {@link #processResources(List, boolean)} has been called.
+ * Pre-condition is that {@link #processResources(List, boolean, PublishingFileFilter)} has been called.
*
- * @return the number of files filtered out
* @since 0.6
*/
- private int loadFiles() {
+ private void loadFiles() {
this.lstResources.removeAll();
- String[] filteredExtensions = null;
- int numFiltered = 0;
-
- if (this.filterFiles) {
- filteredExtensions = PrefUtils.getFilteredFileExtensions();
- }
-
- LOADING:
for (IResource resource : this.files) {
- String filePath = resource.getFullPath().toString();
-
- // filter if necessary
- if (filteredExtensions != null) {
- for (String ext : filteredExtensions) {
- if (filePath.endsWith('.' + ext)) {
- ++numFiltered;
- continue LOADING; // don't add file
- }
- }
- }
-
- this.lstResources.add(filePath);
+ this.lstResources.add(resource.getFullPath().toString());
}
-
- return numFiltered;
}
/**
@@ -960,7 +1011,7 @@
this.recurse = isRecursing();
try {
- this.files = processResources(this.resources, this.recurse);
+ this.files = processResources(this.resources, this.recurse, this.filter);
} catch (CoreException e) {
Activator.getDefault().log(new Status(Severity.ERROR, RestClientI18n.publishPageRecurseProcessingErrorMsg.text(), e));
}
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishWizard.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishWizard.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/PublishWizard.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -35,7 +35,8 @@
import org.jboss.dna.web.jcr.rest.client.domain.Workspace;
import org.jboss.dna.web.jcr.rest.client.swt.Activator;
import org.jboss.dna.web.jcr.rest.client.swt.RestClientI18n;
-import org.jboss.dna.web.jcr.rest.client.swt.wizards.PublishJob.Type;
+import org.jboss.dna.web.jcr.rest.client.swt.jobs.PublishJob;
+import org.jboss.dna.web.jcr.rest.client.swt.jobs.PublishJob.Type;
/**
* @since 0.6
Modified: branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerPage.java
===================================================================
--- branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerPage.java 2009-09-17 14:01:59 UTC (rev 1229)
+++ branches/eclipse/org.jboss.dna.publish.ui.swt/src/org/jboss/dna/web/jcr/rest/client/swt/wizards/ServerPage.java 2009-09-17 19:46:22 UTC (rev 1230)
@@ -167,6 +167,11 @@
}
txtUser.addModifyListener(new ModifyListener() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
+ */
@Override
public void modifyText( ModifyEvent e ) {
handleUserModified(((Text)e.widget).getText());
@@ -191,6 +196,11 @@
// listener for when value changes
txtPassword.addModifyListener(new ModifyListener() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
+ */
@Override
public void modifyText( ModifyEvent e ) {
handlePasswordModified(((Text)e.widget).getText());
@@ -212,6 +222,11 @@
// listener for when value changes
btn.addSelectionListener(new SelectionAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
@Override
public void widgetSelected( SelectionEvent e ) {
handleSavePasswordChanged(((Button)e.widget).getSelection());
@@ -220,6 +235,11 @@
// update page message first time selected to get rid of initial message by forcing validation
btn.addSelectionListener(new SelectionAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
@Override
public void widgetSelected( SelectionEvent e ) {
updateInitialMessage();
@@ -267,6 +287,11 @@
this.btnTestConnection.setLayoutData(gd);
this.btnTestConnection.addSelectionListener(new SelectionAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
@Override
public void widgetSelected( SelectionEvent e ) {
handleTestConnection();
@@ -293,6 +318,11 @@
}
txtUrl.addModifyListener(new ModifyListener() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
+ */
@Override
public void modifyText( ModifyEvent e ) {
handleUrlModified(((Text)e.widget).getText());
@@ -373,6 +403,11 @@
final boolean[] success = new boolean[1];
BusyIndicator.showWhile(null, new Runnable() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Runnable#run()
+ */
@Override
public void run() {
success[0] = getServerManager().ping(server).isOk();
14 years, 7 months
DNA SVN: r1229 - in trunk: dna-cnd/src/test/java/org/jboss/dna/cnd and 2 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-09-17 10:01:59 -0400 (Thu, 17 Sep 2009)
New Revision: 1229
Added:
trunk/dna-common/src/main/java/org/jboss/dna/common/text/ParsingException.java
trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java
Modified:
trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndImporter.java
trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java
trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java
trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndTokenizerTest.java
trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java
trunk/dna-common/src/test/java/org/jboss/dna/common/text/SampleSqlParser.java
trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamBasicTokenizerTest.java
trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java
Log:
Moved the ParsingException and Position classes out of the TokenStream class and into top-level classes, making them reusable outside of the TokenStream framework. (They actually could be reused already, but making them top-level no longer denotes the use of TokenStream.)
Modified: trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndImporter.java
===================================================================
--- trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndImporter.java 2009-09-16 23:13:07 UTC (rev 1228)
+++ trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndImporter.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -35,9 +35,9 @@
import java.util.Set;
import net.jcip.annotations.NotThreadSafe;
import org.jboss.dna.common.collection.Problems;
+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.ParsingException;
-import org.jboss.dna.common.text.TokenStream.Position;
import org.jboss.dna.common.text.TokenStream.Tokenizer;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.IoUtil;
Modified: trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java
===================================================================
--- trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java 2009-09-16 23:13:07 UTC (rev 1228)
+++ trunk/dna-cnd/src/main/java/org/jboss/dna/cnd/CndTokenizer.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -1,9 +1,9 @@
package org.jboss.dna.cnd;
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.CharacterStream;
-import org.jboss.dna.common.text.TokenStream.ParsingException;
-import org.jboss.dna.common.text.TokenStream.Position;
import org.jboss.dna.common.text.TokenStream.Tokenizer;
import org.jboss.dna.common.text.TokenStream.Tokens;
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-09-16 23:13:07 UTC (rev 1228)
+++ trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndImporterTest.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -36,7 +36,7 @@
import java.util.Set;
import org.jboss.dna.common.collection.Problem;
import org.jboss.dna.common.collection.SimpleProblems;
-import org.jboss.dna.common.text.TokenStream.ParsingException;
+import org.jboss.dna.common.text.ParsingException;
import org.jboss.dna.graph.DnaLexicon;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
Modified: trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndTokenizerTest.java
===================================================================
--- trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndTokenizerTest.java 2009-09-16 23:13:07 UTC (rev 1228)
+++ trunk/dna-cnd/src/test/java/org/jboss/dna/cnd/CndTokenizerTest.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -26,9 +26,9 @@
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import java.util.LinkedList;
+import org.jboss.dna.common.text.ParsingException;
+import org.jboss.dna.common.text.Position;
import org.jboss.dna.common.text.TokenStream.CharacterArrayStream;
-import org.jboss.dna.common.text.TokenStream.ParsingException;
-import org.jboss.dna.common.text.TokenStream.Position;
import org.jboss.dna.common.text.TokenStream.Tokens;
import org.junit.Before;
import org.junit.Test;
Added: trunk/dna-common/src/main/java/org/jboss/dna/common/text/ParsingException.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/ParsingException.java (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/ParsingException.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -0,0 +1,47 @@
+package org.jboss.dna.common.text;
+
+/**
+ * An exception representing a problem during parsing of text.
+ */
+public class ParsingException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private final Position position;
+
+ /**
+ * @param position the position of the error; never null
+ */
+ public ParsingException( Position position ) {
+ super();
+ this.position = position;
+ }
+
+ /**
+ * @param position the position of the error; never null
+ * @param message the message
+ * @param cause the underlying cause
+ */
+ public ParsingException( Position position,
+ String message,
+ Throwable cause ) {
+ super(message, cause);
+ this.position = position;
+ }
+
+ /**
+ * @param position the position of the error; never null
+ * @param message the message
+ */
+ public ParsingException( Position position,
+ String message ) {
+ super(message);
+ this.position = position;
+ }
+
+ /**
+ * @return position
+ */
+ public Position getPosition() {
+ return position;
+ }
+}
Property changes on: trunk/dna-common/src/main/java/org/jboss/dna/common/text/ParsingException.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -0,0 +1,56 @@
+package org.jboss.dna.common.text;
+
+import net.jcip.annotations.Immutable;
+
+/**
+ * A class that represents the position of a particular character in terms of the lines and columns of a character sequence.
+ */
+@Immutable
+public final class Position {
+ private final int line;
+ private final int column;
+
+ protected Position( int line,
+ int column ) {
+ this.line = line;
+ this.column = column;
+ }
+
+ /**
+ * Get the 1-based column number of the character.
+ *
+ * @return the column number; always positive
+ */
+ public int getColumn() {
+ return column;
+ }
+
+ /**
+ * Get the 1-based line number of the character.
+ *
+ * @return the line number; always positive
+ */
+ public int getLine() {
+ return line;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return line;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "" + line + ':' + column;
+ }
+}
Property changes on: trunk/dna-common/src/main/java/org/jboss/dna/common/text/Position.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
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-09-16 23:13:07 UTC (rev 1228)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/text/TokenStream.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -1585,7 +1585,7 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.common.text.TokenStream.Tokens#addToken(org.jboss.dna.common.text.TokenStream.Position, int)
+ * @see org.jboss.dna.common.text.TokenStream.Tokens#addToken(Position, int)
*/
public void addToken( Position position,
int index ) {
@@ -1794,95 +1794,6 @@
}
/**
- * A class that represents the position of a particular character in terms of the lines and columns of a character sequence.
- */
- @Immutable
- public final static class Position {
- private final int line;
- private final int column;
-
- protected Position( int line,
- int column ) {
- this.line = line;
- this.column = column;
- }
-
- /**
- * Get the 1-based column number of the character.
- *
- * @return the column number; always positive
- */
- public int getColumn() {
- return column;
- }
-
- /**
- * Get the 1-based line number of the character.
- *
- * @return the line number; always positive
- */
- public int getLine() {
- return line;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "" + line + ':' + column;
- }
- }
-
- /**
- * An exception representing a problem during parsing.
- */
- public static class ParsingException extends RuntimeException {
- private static final long serialVersionUID = 1L;
-
- private final Position position;
-
- /**
- * @param position the position of the error; never null
- */
- public ParsingException( Position position ) {
- super();
- this.position = position;
- }
-
- /**
- * @param position the position of the error; never null
- * @param message the message
- * @param cause the underlying cause
- */
- public ParsingException( Position position,
- String message,
- Throwable cause ) {
- super(message, cause);
- this.position = position;
- }
-
- /**
- * @param position the position of the error; never null
- * @param message the message
- */
- public ParsingException( Position position,
- String message ) {
- super(message);
- this.position = position;
- }
-
- /**
- * @return position
- */
- public Position getPosition() {
- return position;
- }
- }
-
- /**
* Obtain a basic {@link Tokenizer} implementation that ignores whitespace but includes tokens for individual symbols, the
* period ('.'), single-quoted strings, double-quoted strings, whitespace-delimited words, and optionally comments.
* <p>
Modified: trunk/dna-common/src/test/java/org/jboss/dna/common/text/SampleSqlParser.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/text/SampleSqlParser.java 2009-09-16 23:13:07 UTC (rev 1228)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/text/SampleSqlParser.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -26,7 +26,6 @@
import java.util.LinkedList;
import java.util.List;
import net.jcip.annotations.Immutable;
-import org.jboss.dna.common.text.TokenStream.ParsingException;
/**
*
Modified: trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamBasicTokenizerTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamBasicTokenizerTest.java 2009-09-16 23:13:07 UTC (rev 1228)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamBasicTokenizerTest.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -28,8 +28,6 @@
import java.util.LinkedList;
import org.jboss.dna.common.text.TokenStream.BasicTokenizer;
import org.jboss.dna.common.text.TokenStream.CharacterArrayStream;
-import org.jboss.dna.common.text.TokenStream.ParsingException;
-import org.jboss.dna.common.text.TokenStream.Position;
import org.jboss.dna.common.text.TokenStream.Tokens;
import org.junit.Before;
import org.junit.Test;
Modified: trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java
===================================================================
--- trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java 2009-09-16 23:13:07 UTC (rev 1228)
+++ trunk/dna-common/src/test/java/org/jboss/dna/common/text/TokenStreamTest.java 2009-09-17 14:01:59 UTC (rev 1229)
@@ -25,7 +25,6 @@
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
-import org.jboss.dna.common.text.TokenStream.ParsingException;
import org.jboss.dna.common.text.TokenStream.Tokenizer;
import org.junit.Before;
import org.junit.Test;
14 years, 7 months
DNA SVN: r1228 - trunk.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-09-16 19:13:07 -0400 (Wed, 16 Sep 2009)
New Revision: 1228
Modified:
trunk/pom.xml
Log:
Changed several database URLs
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2009-09-16 20:56:22 UTC (rev 1227)
+++ trunk/pom.xml 2009-09-16 23:13:07 UTC (rev 1228)
@@ -368,7 +368,7 @@
<database>hsqldb</database>
<jpaSource.dialect>org.hibernate.dialect.HSQLDialect</jpaSource.dialect>
<jpaSource.driverClassName>org.hsqldb.jdbcDriver</jpaSource.driverClassName>
- <jpaSource.url>jdbc:hsqldb:target/test/db/hsqldb/hibernate</jpaSource.url>
+ <jpaSource.url>jdbc:hsqldb:target/test/db/hsqldb/dna</jpaSource.url>
<jpaSource.username>sa</jpaSource.username>
<jpaSource.password />
</properties>
@@ -394,7 +394,7 @@
<database>hsqldb</database>
<jpaSource.dialect>org.hibernate.dialect.HSQLDialect</jpaSource.dialect>
<jpaSource.driverClassName>org.hsqldb.jdbcDriver</jpaSource.driverClassName>
- <jpaSource.url>jdbc:hsqldb:target/test/db/hsqldb/hibernate</jpaSource.url>
+ <jpaSource.url>jdbc:hsqldb:target/test/db/hsqldb/dna</jpaSource.url>
<jpaSource.username>sa</jpaSource.username>
<jpaSource.password />
</properties>
@@ -420,7 +420,7 @@
<properties>
<jpaSource.dialect>org.hibernate.dialect.H2Dialect</jpaSource.dialect>
<jpaSource.driverClassName>org.h2.Driver</jpaSource.driverClassName>
- <jpaSource.url>jdbc:h2:mem:target/test/db/h2/hibernate</jpaSource.url>
+ <jpaSource.url>jdbc:h2:mem:target/test/db/h2/dna</jpaSource.url>
<jpaSource.username>sa</jpaSource.username>
<jpaSource.password />
</properties>
14 years, 7 months