de.iteratec.iteraplan.businesslogic.reports.query.node.ExtensionNode.java Source code

Java tutorial

Introduction

Here is the source code for de.iteratec.iteraplan.businesslogic.reports.query.node.ExtensionNode.java

Source

/*
 * iteraplan is an IT Governance web application developed by iteratec, GmbH
 * Copyright (C) 2004 - 2014 iteratec, GmbH
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY ITERATEC, ITERATEC DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact iteratec GmbH headquarters at Inselkammerstr. 4
 * 82008 Munich - Unterhaching, Germany, or at email address info@iteratec.de.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "iteraplan" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by iteraplan".
 */
package de.iteratec.iteraplan.businesslogic.reports.query.node;

import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;

import de.iteratec.iteraplan.businesslogic.reports.query.type.Extension;
import de.iteratec.iteraplan.businesslogic.reports.query.type.Type;
import de.iteratec.iteraplan.common.GeneralHelper;
import de.iteratec.iteraplan.common.error.IteraplanErrorMessages;
import de.iteratec.iteraplan.common.error.IteraplanTechnicalException;
import de.iteratec.iteraplan.model.BuildingBlock;
import de.iteratec.iteraplan.persistence.util.CriteriaUtil;

/**
 * Intermediary Node for all extension queries.
 */
public class ExtensionNode extends Node {

    /** The building block type of the result. */
    private Type<?> resultType;

    /** The extension to use for this node. */
    private Extension extension;

    /** Only one child is allowed for an ExtensionNode. */
    private Node child;

    private Set<? extends BuildingBlock> leafNodeBuildingBlocks;

    /**
     * Constructor.
     */
    public ExtensionNode(Type<?> resultType, Extension extension) {
        this.resultType = resultType;
        this.extension = extension;
    }

    public Type<?> getResultType() {
        return resultType;
    }

    public Extension getExtension() {
        return extension;
    }

    /**
     * Returns the distance between result type and leaf type.
     */
    public int getExtensionSize() {
        if (extension == null) {
            return 0;
        }

        return extension.getTypesWithJoinProperties().size();
    }

    /**
     * @return The type of the query leaf.
     */
    private Type<?> getLeafType() {
        if (getExtensionSize() > 0) {
            return getExtension().getRequestedType();
        }

        return getResultType();
    }

    public Node getChild() {
        return child;
    }

    public void setChild(Node child) {
        this.child = child;
    }

    public Set<? extends BuildingBlock> getLeafNodeBuildingBlocks() {
        return leafNodeBuildingBlocks;
    }

    public void setLeafNodeBuildingBlocks(Set<? extends BuildingBlock> leafNodeBuildingBlocks) {
        this.leafNodeBuildingBlocks = leafNodeBuildingBlocks;
    }

    /**
     * Returns the short name of the result type concatenated with a positional suffix.
     * 
     * @return The suffixed type name.
     */
    private String getResultTypeDBNameShortWithSuffix() {
        return resultType.getTypeNameDBShort() + "0";
    }

    /**
     * Returns the short name of an intermediary type concatenated with a positional suffix.
     * 
     * @param index
     *          The index of the intermediary type in the chain from result to leaf type.
     * @return The suffixed type name.
     */
    private String getIntermediaryTypeDBNameShortWithSuffix(int index) {
        int suffix = index + 1;
        return getExtension().getIntermediaryType(index).getTypeNameDBShort() + suffix;
    }

    /**
     * Returns the short name of the leaf type concatenated with a positional suffix.
     * 
     * @return The suffixed type name.
     */
    String getLeafTypeDBNameShortWithSuffix() {
        return getLeafType().getTypeNameDBShort() + getExtensionSize();
    }

    private Class<?> getCriteriaClass() {
        try {
            return Class.forName("de.iteratec.iteraplan.model." + getResultType().getTypeNameDB());
        } catch (ClassNotFoundException e) {
            throw new IteraplanTechnicalException(IteraplanErrorMessages.INTERNAL_ERROR, e);
        }
    }

    /**
     * Returns the {@link DetachedCriteria} instance with the restrictions, required to filter the data, 
     * loaded from Database. The extending classes must implement the {@link #getSelectCriteria(Class)}, 
     * {@link #getFromCriteria(DetachedCriteria)} and {@link #getWhereCriteria(DetachedCriteria)} methods, 
     * in order to create valid query.
     * 
     * @return the {@link DetachedCriteria} instance
     */
    public DetachedCriteria getCriteria() {
        DetachedCriteria criteria = getSelectCriteria(getCriteriaClass());
        criteria = getFromCriteria(criteria);
        criteria = getWhereCriteria(criteria);

        return criteria;
    }

    /**
     * Creates the newly created instance of the {@link DetachedCriteria} for the specified {@code resultTypeClass} and 
     * alias from {@link #getResultTypeDBNameShortWithSuffix()}.
     * 
     * @param resultTypeClass the class to be selected in a query
     * @return the newly created instance of the {@link DetachedCriteria}
     */
    private DetachedCriteria getSelectCriteria(Class<?> resultTypeClass) {
        return DetachedCriteria.forClass(resultTypeClass, getResultTypeDBNameShortWithSuffix() /* alias */);
    }

    /**
     * Adds the alias's to the specified {@code criteria} and specifies the join types, used for those 
     * alias. The alias can be later used in {@link #getWhereCriteria(DetachedCriteria)} method to filter
     * the data.
     *
     * @param criteria to be ammended
     * @return criteria to be executed within session scope
     */
    private DetachedCriteria getFromCriteria(DetachedCriteria criteria) {
        String previousTypeNameShort = getResultTypeDBNameShortWithSuffix();
        for (int i = 0; i < getExtensionSize(); i++) {
            String currentTypeNameShort = getIntermediaryTypeDBNameShortWithSuffix(i);
            String path = String.format("%s.%s", previousTypeNameShort,
                    getExtension().getIntermediaryTypeJoinProperty(i));
            criteria.createAlias(path, currentTypeNameShort, Criteria.INNER_JOIN);
            previousTypeNameShort = currentTypeNameShort;
        }

        return criteria;
    }

    /**
     * Adds the restrictions to the specified {@code criteria} in order to filter the data.
     *
     * @param criteria the criteria instance
     * @return criteria to be executed within session scope
     */
    private DetachedCriteria getWhereCriteria(DetachedCriteria criteria) {
        addIdRestriction(criteria);
        return criteria;
    }

    /**
     * Adds the restriction on the ids, taken from the {@link #getLeafNodeBuildingBlocks()}.
     * 
     * <p>If the element list is longer than 950 items, it is split up into two (or more) lists. This is
     * because Oracle refuses to check against lists with more than 1000 elements. In such a case, the
     * result will be <tt>prop.id in (<b>less than 1000 elements</b>) or prop.id in (<b>remaining elements</b>)</tt>.
     * Thus the list-items are split up into several in-clauses. This will make the query a bit slower.
     * 
     * @param criteria the criteria to add the restriction for
     */
    private void addIdRestriction(DetachedCriteria criteria) {
        Set<Integer> createIdSetFromIdEntities = GeneralHelper.createIdSetFromIdEntities(leafNodeBuildingBlocks);
        String propName = getLeafTypeDBNameShortWithSuffix() + ".id";

        criteria.add(CriteriaUtil.createInRestrictions(propName, createIdSetFromIdEntities));
    }

    public String toString() {
        return "Extension: " + getExtension();
    }
}