/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jackrabbit.spi.commons.query.qom; import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; import org.apache.jackrabbit.spi.Name; import javax.jcr.query.InvalidQueryException; import java.util.Map; import java.util.HashMap; import java.util.Arrays; import java.util.Iterator; /** * <code>QueryObjectModelTree</code> implements the root node of an object * query model tree. */ public class QueryObjectModelTree extends AbstractQOMNode { /** * The node-tuple source for this query. */ private final SourceImpl source; /** * The constraint for this query. */ private final ConstraintImpl constraint; /** * The orderings for this query. */ private final OrderingImpl[] orderings; /** * The columns for this query. */ private final ColumnImpl[] columns; /** * All selectors available in this query object model. Key=Name */ private final Map selectors = new HashMap(); public QueryObjectModelTree(NamePathResolver resolver, SourceImpl source, ConstraintImpl constraint, OrderingImpl[] orderings, ColumnImpl[] columns) throws InvalidQueryException { super(resolver); this.source = source; this.constraint = constraint; this.orderings = orderings; this.columns = columns; for (Iterator it = Arrays.asList(source.getSelectors()).iterator(); it.hasNext(); ) { SelectorImpl selector = (SelectorImpl) it.next(); if (selectors.put(selector.getSelectorQName(), selector) != null) { throw new InvalidQueryException("Duplicate selector name: " + selector.getSelectorName()); } } if (selectors.size() == 1) { // there is only one selector, which is also a default selector selectors.put(null, selectors.values().iterator().next()); } checkQuery(); } /** * Gets the node-tuple source for this query. * * @return the node-tuple source; non-null */ public SourceImpl getSource() { return source; } /** * Gets the constraint for this query. * * @return the constraint, or null if none */ public ConstraintImpl getConstraint() { return constraint; } /** * Gets the orderings for this query. * * @return an array of zero or more orderings; non-null */ public OrderingImpl[] getOrderings() { OrderingImpl[] temp = new OrderingImpl[orderings.length]; System.arraycopy(orderings, 0, temp, 0, orderings.length); return temp; } /** * Gets the columns for this query. * * @return an array of zero or more columns; non-null */ public ColumnImpl[] getColumns() { ColumnImpl[] temp = new ColumnImpl[columns.length]; System.arraycopy(columns, 0, temp, 0, columns.length); return temp; } /** * Returns the selector with the given <code>name</code> or * <code>null</code> if there is no selector with this name. * * @param name the name of a selector. * @return the selector or <code>null</code> if there is no such selector. */ public SelectorImpl getSelector(Name name) { return (SelectorImpl) selectors.get(name); } //-----------------------< AbstractQOMNode >-------------------------------- /** * Accepts a <code>visitor</code> and calls the appropriate visit method * depending on the type of this QOM node. * * @param visitor the visitor. */ public Object accept(QOMTreeVisitor visitor, Object data) throws Exception { return visitor.visit(this, data); } /** * Checks if this QOM is valid. * * @throws InvalidQueryException if the QOM is invalid. */ private void checkQuery() throws InvalidQueryException { // TODO: validate query completely. // checks currently implemented: // - check for selector names try { accept(new DefaultTraversingQOMTreeVisitor() { public Object visit(ChildNodeImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(ColumnImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(DescendantNodeImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(EquiJoinConditionImpl node, Object data) throws Exception { checkSelector(node.getSelector1QName()); return checkSelector(node.getSelector2QName()); } public Object visit(FullTextSearchImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(FullTextSearchScoreImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(NodeLocalNameImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(NodeNameImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(PropertyExistenceImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(PropertyValueImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(SameNodeImpl node, Object data) throws Exception { return checkSelector(node.getSelectorQName()); } public Object visit(SameNodeJoinConditionImpl node, Object data) throws Exception { checkSelector(node.getSelector1QName()); return checkSelector(node.getSelector2QName()); } private Object checkSelector(Name selectorName) throws InvalidQueryException { if (!selectors.containsKey(selectorName)) { String msg = "Unknown selector: "; if (selectorName != null) { msg += QueryObjectModelTree.this.getJCRName(selectorName); } else { msg += "<default>"; } throw new InvalidQueryException(msg); } return null; } }, null); } catch (Exception e) { throw new InvalidQueryException(e.getMessage()); } } //------------------------< Object >---------------------------------------- public String toString() { StringBuilder builder = new StringBuilder(); builder.append("SELECT "); if (columns != null && columns.length > 0) { for (int i = 0; i < columns.length; i++) { if (i > 0) { builder.append(", "); } builder.append(columns[i]); } } else { builder.append("*"); } builder.append(" FROM "); builder.append(source); if (constraint != null) { builder.append(" WHERE "); builder.append(constraint); } if (orderings != null && orderings.length > 0) { builder.append(" ORDER BY "); for (int i = 0; i < orderings.length; i++) { if (i > 0) { builder.append(", "); } builder.append(orderings[i]); } } return builder.toString(); } }