/*
 * 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.openjpa.persistence.query;

import java.util.LinkedList;

/**
 * An abstract path is formed by two parts : the first part is a parent path.
 * The second part can be an attribute or an operation (e.g. KEY() or VALUE())
 * or a join type operation. Based on the exact nature of the second part,
 * concrete derivation of this class combines the two constituent parts to
 * arrive at complete path name.
 * For example, a navigation path adds the two part with a navigation '.'
 * operator, while a OperatorPath will combine the parts as KEY(parent).
 *
 * The constituent parts are immutable and supplied at construction. Hence
 * concrete implementations know what exact type they are dealing with, but
 * this receiver maintains it state as more generic type to accommodate
 * concrete types to cast/interpret these state variables.
 *
 * @author Pinaki Poddar
 *
 */
abstract class AbstractPath extends ExpressionImpl implements
		PathExpression {
	
    private static final long serialVersionUID = 1L;
    protected final AbstractPath  _parent;
	protected final Object 		  _part2;
	protected final PathOperator  _operator;
	protected final QueryDefinitionImpl _owner;

	protected AbstractPath(QueryDefinitionImpl owner, AbstractPath parent,
	    PathOperator op, Object part2) {
		_owner = owner;
		_parent = parent;
		_part2  = part2;
		_operator = op;
	}

    // ------------------------------------------------------------------------
    // Path related functions.
    // ------------------------------------------------------------------------

	final QueryDefinitionImpl getOwner() {
		return _owner;
	}
	/**
     * Gets the parent from which this receiver has been derived. Can be null
	 * for a root path.
	 */
	public AbstractPath getParent() {
		return _parent;
	}

	/**
	 * Gets operator that derived this receiver from its parent.
	 */
	public PathOperator getOperator() {
		return _operator;
	}

	/**
	 * Gets the last segment of this path.
	 * Concrete implementation should return a covariant type.
	 */
	public Object getLastSegment() {
		return _part2;
	}

    // -----------------------------------------------------------------------
    // Implementation of PathExpression
    // -----------------------------------------------------------------------
	@Override
    public Aggregate avg() {
		return new AverageExpression(this);
	}

	@Override
    public Aggregate count() {
		return new CountExpression(this);
	}

	@Override
    public Predicate isEmpty() {
		return new IsEmptyExpression(this);
	}

	@Override
    public Aggregate max() {
		return new MaxExpression(this);
	}

	@Override
    public Aggregate min() {
		return new MinExpression(this);
	}

	@Override
    public Expression size() {
		return new SizeExpression(this);
	}

	@Override
    public Aggregate sum() {
		return new SumExpression(this);
	}

	@Override
    public Expression type() {
		return new TypeExpression(this);
	}

	LinkedList<AbstractPath> split() {
		return _split(this, new LinkedList<>());
	}

	private LinkedList<AbstractPath> _split(AbstractPath path,
		LinkedList<AbstractPath> list) {
		if (path == null)
			return list;
		_split(path.getParent(), list);
		list.add(path);
		return list;
	}
}
