net.sf.jasperreports.engine.design.JRVerifier.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jasperreports.engine.design.JRVerifier.java

Source

/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports 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 JasperReports. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.jasperreports.engine.design;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.jasperreports.annotations.properties.Property;
import net.sf.jasperreports.annotations.properties.PropertyScope;
import net.sf.jasperreports.charts.JRCategoryDataset;
import net.sf.jasperreports.charts.JRCategorySeries;
import net.sf.jasperreports.charts.JRGanttDataset;
import net.sf.jasperreports.charts.JRGanttSeries;
import net.sf.jasperreports.charts.JRHighLowDataset;
import net.sf.jasperreports.charts.JRPieDataset;
import net.sf.jasperreports.charts.JRPieSeries;
import net.sf.jasperreports.charts.JRTimePeriodDataset;
import net.sf.jasperreports.charts.JRTimePeriodSeries;
import net.sf.jasperreports.charts.JRTimeSeries;
import net.sf.jasperreports.charts.JRTimeSeriesDataset;
import net.sf.jasperreports.charts.JRValueDataset;
import net.sf.jasperreports.charts.JRXyDataset;
import net.sf.jasperreports.charts.JRXySeries;
import net.sf.jasperreports.charts.JRXyzDataset;
import net.sf.jasperreports.charts.JRXyzSeries;
import net.sf.jasperreports.crosstabs.JRCellContents;
import net.sf.jasperreports.crosstabs.JRCrosstab;
import net.sf.jasperreports.crosstabs.JRCrosstabBucket;
import net.sf.jasperreports.crosstabs.JRCrosstabCell;
import net.sf.jasperreports.crosstabs.JRCrosstabColumnGroup;
import net.sf.jasperreports.crosstabs.JRCrosstabDataset;
import net.sf.jasperreports.crosstabs.JRCrosstabGroup;
import net.sf.jasperreports.crosstabs.JRCrosstabMeasure;
import net.sf.jasperreports.crosstabs.JRCrosstabParameter;
import net.sf.jasperreports.crosstabs.JRCrosstabRowGroup;
import net.sf.jasperreports.crosstabs.design.JRDesignCrosstab;
import net.sf.jasperreports.crosstabs.fill.JRPercentageCalculator;
import net.sf.jasperreports.crosstabs.fill.JRPercentageCalculatorFactory;
import net.sf.jasperreports.crosstabs.type.CrosstabPercentageEnum;
import net.sf.jasperreports.crosstabs.type.CrosstabTotalPositionEnum;
import net.sf.jasperreports.engine.CommonReturnValue;
import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.ExpressionReturnValue;
import net.sf.jasperreports.engine.JRAnchor;
import net.sf.jasperreports.engine.JRBand;
import net.sf.jasperreports.engine.JRChart;
import net.sf.jasperreports.engine.JRChartDataset;
import net.sf.jasperreports.engine.JRComponentElement;
import net.sf.jasperreports.engine.JRConditionalStyle;
import net.sf.jasperreports.engine.JRDataset;
import net.sf.jasperreports.engine.JRDatasetParameter;
import net.sf.jasperreports.engine.JRDatasetRun;
import net.sf.jasperreports.engine.JRElement;
import net.sf.jasperreports.engine.JRElementDataset;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRExpressionChunk;
import net.sf.jasperreports.engine.JRExpressionCollector;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRFrame;
import net.sf.jasperreports.engine.JRGenericElement;
import net.sf.jasperreports.engine.JRGenericElementParameter;
import net.sf.jasperreports.engine.JRGenericElementType;
import net.sf.jasperreports.engine.JRGroup;
import net.sf.jasperreports.engine.JRHyperlink;
import net.sf.jasperreports.engine.JRHyperlinkParameter;
import net.sf.jasperreports.engine.JRImage;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRPart;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRPropertyExpression;
import net.sf.jasperreports.engine.JRQuery;
import net.sf.jasperreports.engine.JRQueryChunk;
import net.sf.jasperreports.engine.JRReport;
import net.sf.jasperreports.engine.JRReportTemplate;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRSection;
import net.sf.jasperreports.engine.JRSortField;
import net.sf.jasperreports.engine.JRStaticText;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JRSubreport;
import net.sf.jasperreports.engine.JRSubreportParameter;
import net.sf.jasperreports.engine.JRSubreportReturnValue;
import net.sf.jasperreports.engine.JRTemplate;
import net.sf.jasperreports.engine.JRTextField;
import net.sf.jasperreports.engine.JRVariable;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.ReturnValue;
import net.sf.jasperreports.engine.VariableReturnValue;
import net.sf.jasperreports.engine.analytics.dataset.MultiAxisData;
import net.sf.jasperreports.engine.component.Component;
import net.sf.jasperreports.engine.component.ComponentCompiler;
import net.sf.jasperreports.engine.component.ComponentKey;
import net.sf.jasperreports.engine.component.ComponentsEnvironment;
import net.sf.jasperreports.engine.fill.JRExtendedIncrementerFactory;
import net.sf.jasperreports.engine.part.PartComponent;
import net.sf.jasperreports.engine.part.PartComponentCompiler;
import net.sf.jasperreports.engine.part.PartComponentManager;
import net.sf.jasperreports.engine.part.PartComponentsEnvironment;
import net.sf.jasperreports.engine.part.PartEvaluationTime;
import net.sf.jasperreports.engine.query.QueryExecuterFactory;
import net.sf.jasperreports.engine.type.CalculationEnum;
import net.sf.jasperreports.engine.type.EvaluationTimeEnum;
import net.sf.jasperreports.engine.type.IncrementTypeEnum;
import net.sf.jasperreports.engine.type.PartEvaluationTimeType;
import net.sf.jasperreports.engine.type.ResetTypeEnum;
import net.sf.jasperreports.engine.type.SectionTypeEnum;
import net.sf.jasperreports.engine.type.SortFieldTypeEnum;
import net.sf.jasperreports.engine.type.SplitTypeEnum;
import net.sf.jasperreports.engine.util.FormatFactory;
import net.sf.jasperreports.engine.util.JRClassLoader;
import net.sf.jasperreports.engine.util.JRQueryExecuterUtils;
import net.sf.jasperreports.properties.PropertyConstants;

/**
 * A report verifier.
 *
 * <p>
 * The verifier checks that a report design meets certain rules in order to pass
 * report compilation.  
 * 
 * @author Teodor Danciu (teodord@users.sourceforge.net)
 */
public class JRVerifier {

    private static final Log log = LogFactory.getLog(JRVerifier.class);

    /**
     * A property that determines whether elements are allowed to overlap.
     * 
     * <p>
     * If this value is set to <code>false</code>, the report is verified not
     * to contain elements that overlap.  This is useful when the report is
     * meant to be exported to grid-based formats such as HTML, XLS or CSV.
     * Setting this property to <code>false</code> ensures that element overlap
     * issues are caught at report compile time.
     * 
     * <p>
     * Additionally, when this property is set to false <code>false</code>, the
     * report is verified not to have any content in the background section as
     * this content would likely be overlapped by other sections and would not
     * show in grid-based exporters.
     * 
     * <p>
     * By default, the property is set to <code>true</code> which means that
     * no element overlap checks are performed.
     * 
     * <p>
     * The property can be set at the following levels:
     * <ul>
     *    <li>At global level (in jasperreports.properties) to provide a default
     * value.</li>
     *    <li>At report level, to indicate whether element overlap checks are to
     * be performed for the report.  If not set, the global property value is
     * used.</li>
     *    <li>At report element level to specify that the particular element is
     * allowed to overlap or be overlapped by other elements, when the report
     * or global property determines report element overlap verification.
     * The element level property is only effective when set to <code>true</code>;
     * setting the property to <code>false</code> does not make the verifier
     * check for overlaps when the report is not set to be checked for element
     * overlaps.</li>
     * </ul>
     * 
     * <p>
     * Note that print when expressions or export filters cannot be taken into
     * consideration while checking for overlapping elements as this check is
     * performed at report compilation time.
     * If a report contains two elements that overlap but have print when
     * expressions that guarantee that only one of them will be printed,
     * or if export filters are in place to exclude one of the elements,
     * one of them should be explicitly marked to allow element overlap
     * when the report is configured to check for overlaps.
     */
    @Property(category = PropertyConstants.CATEGORY_DESIGN, defaultValue = PropertyConstants.BOOLEAN_TRUE, scopes = {
            PropertyScope.CONTEXT, PropertyScope.REPORT,
            PropertyScope.ELEMENT }, sinceVersion = PropertyConstants.VERSION_3_5_0, valueType = Boolean.class)
    public static final String PROPERTY_ALLOW_ELEMENT_OVERLAP = JRPropertiesUtil.PROPERTY_PREFIX
            + "allow.element.overlap";

    @Property(category = PropertyConstants.CATEGORY_DESIGN, valueType = Boolean.class, defaultValue = PropertyConstants.BOOLEAN_FALSE, scopes = {
            PropertyScope.CONTEXT, PropertyScope.REPORT }, sinceVersion = PropertyConstants.VERSION_3_7_1)
    public static final String PROPERTY_ALLOW_ELEMENT_NEGATIVE_WIDTH = JRPropertiesUtil.PROPERTY_PREFIX
            + "allow.element.negative.width";

    @Property(category = PropertyConstants.CATEGORY_DESIGN, valueType = Boolean.class, defaultValue = PropertyConstants.BOOLEAN_TRUE, scopes = {
            PropertyScope.CONTEXT, PropertyScope.REPORT,
            PropertyScope.ELEMENT }, sinceVersion = PropertyConstants.VERSION_6_1_1)
    public static final String PROPERTY_ALLOW_ELEMENT_NEGATIVE_X = JRPropertiesUtil.PROPERTY_PREFIX
            + "allow.element.negative.x";

    /**
     * Property that determines whether elements positioned at negative Y offsets 
     * on bands, frames and other element containers are allowed in a report.
     *
     * <p>
     * Elements placed at negative Y offsets can cause unexpected problems in
     * grid-based exporters where they can overlap elements from previous
     * bands/element containers.
     * </p>
     * 
     * <p>
     * If the property is set to <code>false</code>, elements in the report are 
     * verified to have positive Y offsets.  Otherwise, no check is performed
     * on element Y offsets.
     * </p>
     * 
     * <p>
     * The property can be set at element, report and global levels.
     * By default the property is set to <code>true</code>.
     * </p>
     * 
     * @see JRElement#getY()
     * @since 3.7.3
     */
    @Property(category = PropertyConstants.CATEGORY_DESIGN, defaultValue = PropertyConstants.BOOLEAN_TRUE, scopes = {
            PropertyScope.CONTEXT, PropertyScope.REPORT,
            PropertyScope.ELEMENT }, sinceVersion = PropertyConstants.VERSION_3_7_3, valueType = Boolean.class)
    public static final String PROPERTY_ALLOW_ELEMENT_NEGATIVE_Y = JRPropertiesUtil.PROPERTY_PREFIX
            + "allow.element.negative.y";

    /**
     * @deprecated To be removed.
     */
    private static Class<?>[] templateTypes = new Class[] { String.class, java.io.File.class, java.net.URL.class,
            java.io.InputStream.class, JRTemplate.class };

    /**
     *
     */
    private JasperReportsContext jasperReportsContext;
    private JasperDesign jasperDesign;
    private SectionTypeEnum sectionType;
    private Collection<JRValidationFault> brokenRules;

    private JRExpressionCollector expressionCollector;

    private LinkedList<JRComponentElement> currentComponentElementStack = new LinkedList<JRComponentElement>();
    private LinkedList<String> datasetContextStack = new LinkedList<>();

    private boolean allowElementNegativeWidth;
    private final boolean allowElementNegativeX;
    private final boolean allowElementNegativeY;

    /**
     * 
     */
    protected JRVerifier(JasperReportsContext jasperReportsContext, JasperDesign jasperDesign,
            JRExpressionCollector expressionCollector) {
        this.jasperReportsContext = jasperReportsContext;
        this.jasperDesign = jasperDesign;
        this.sectionType = jasperDesign.getSectionType() == null ? SectionTypeEnum.BAND
                : jasperDesign.getSectionType();
        brokenRules = new ArrayList<JRValidationFault>();

        if (expressionCollector != null) {
            this.expressionCollector = expressionCollector;
        } else {
            this.expressionCollector = JRExpressionCollector.collector(jasperReportsContext, jasperDesign);
        }

        allowElementNegativeWidth = JRPropertiesUtil.getInstance(jasperReportsContext)
                .getBooleanProperty(jasperDesign, PROPERTY_ALLOW_ELEMENT_NEGATIVE_WIDTH, false);
        allowElementNegativeX = JRPropertiesUtil.getInstance(jasperReportsContext).getBooleanProperty(jasperDesign,
                PROPERTY_ALLOW_ELEMENT_NEGATIVE_X, true);
        allowElementNegativeY = JRPropertiesUtil.getInstance(jasperReportsContext).getBooleanProperty(jasperDesign,
                PROPERTY_ALLOW_ELEMENT_NEGATIVE_Y, true);
    }

    public JasperDesign getReportDesign() {
        return jasperDesign;
    }

    /**
     * Logs a broken rule for the report.
     * 
     * @param message the message
     * @param source the source object to which the rule applies; can be null
     * if not available
     */
    public void addBrokenRule(String message, Object source) {
        addBrokenRule(brokenRules, message, source);
    }

    protected static void addBrokenRule(Collection<JRValidationFault> brokenRules, String message, Object source) {
        JRValidationFault fault = new JRValidationFault();
        fault.setMessage(message);
        fault.setSource(source);
        brokenRules.add(fault);
    }

    /**
     * Logs a broken report rule which was caused by an exception.
     * 
     * @param e the exception that caused the broken rule
     * @param source the source object if available
     */
    public void addBrokenRule(Exception e, Object source) {
        JRValidationFault fault = new JRValidationFault();
        fault.setMessage(e.getMessage());
        fault.setSource(source);
        brokenRules.add(fault);
    }

    /**
     * Validates a {@link JasperDesign report design}.
     *
     * @param jasperDesign the report design
     * @param expressionCollector a collector which was used to collect expressions from the report design;
     *    if null, a new collector will be created and used to collect the expressions
     *
     * @return a list of {@link JRValidationFault design faults};
     *    the report design is valid if and only if the list is empty
     */
    public static Collection<JRValidationFault> verifyDesign(JasperReportsContext jasperReportsContext,
            JasperDesign jasperDesign, JRExpressionCollector expressionCollector) {
        JRVerifier verifier = new JRVerifier(jasperReportsContext, jasperDesign, expressionCollector);
        return verifier.verifyDesign();
    }

    /**
     * Validates a {@link JasperDesign report design}.
     *
     * @param jasperDesign the report design
     *
     * @return a list of {@link JRValidationFault design faults};
     *    the report design is valid if and only if the list is empty
     */
    public static Collection<JRValidationFault> verifyDesign(JasperDesign jasperDesign) {
        return verifyDesign(DefaultJasperReportsContext.getInstance(), jasperDesign, null);
    }

    /**
     *
     */
    protected Collection<JRValidationFault> verifyDesign() {
        /*   */
        jasperDesign.preprocess();//FIXME either calculate twice or use change listeners

        /*   */
        verifyDesignAttributes();

        verifyReportTemplates();

        verifyDataset(jasperDesign.getMainDesignDataset());

        verifyDatasets();

        /*   */
        verifyStyles();

        if (toVerifyElementOverlap()) {
            verifyEmptyBackground();
        }

        /*   */
        verifyBand(jasperDesign.getBackground());
        verifyBand(jasperDesign.getTitle());
        verifyBand(jasperDesign.getPageHeader());
        verifyBand(jasperDesign.getColumnHeader());
        verifySection(jasperDesign.getDetailSection());
        verifyBand(jasperDesign.getColumnFooter());
        verifyBand(jasperDesign.getPageFooter());
        verifyBand(jasperDesign.getLastPageFooter());
        verifyBand(jasperDesign.getSummary());
        verifyBand(jasperDesign.getNoData());

        return brokenRules;
    }

    protected void verifyEmptyBackground() {
        // if element overlapping checks are on, do not allow any content
        // in the background band
        JRBand background = jasperDesign.getBackground();
        if (background != null && background.getHeight() > 0) {
            JRElement[] elements = background.getElements();
            if (elements != null && elements.length > 0) {
                boolean foundContent = false;
                for (int i = 0; i < elements.length; i++) {
                    if (elements[i].getWidth() > 0 && elements[i].getHeight() > 0) {
                        foundContent = true;
                        break;
                    }
                }

                if (foundContent) {
                    addBrokenRule("Use of the background section is not recommended "
                            + "for reports that are supposed to be exported using grid exporters such as HTML and XLS "
                            + "because the background content would likely be overlapped by other sections "
                            + "resulting in it not showing up.", background);
                }
            }
        }
    }

    /**
     *
     */
    private void verifyDesignAttributes() {
        if (jasperDesign.getName() == null || jasperDesign.getName().trim().length() == 0) {
            addBrokenRule("Report name is missing.", jasperDesign);
        }

        if (jasperDesign.getColumnCount() <= 0) {
            addBrokenRule("Column count must be greater than zero.", jasperDesign);
        }

        if (jasperDesign.getPageWidth() < 0) {
            addBrokenRule("Page width must be positive.", jasperDesign);
        }

        if (jasperDesign.getPageHeight() < 0) {
            addBrokenRule("Page height must be positive.", jasperDesign);
        }

        if (jasperDesign.getColumnWidth() < 0) {
            addBrokenRule("Column width must be positive.", jasperDesign);
        }

        if (jasperDesign.getColumnSpacing() < 0) {
            addBrokenRule("Column spacing must be positive.", jasperDesign);
        }

        if (jasperDesign.getLeftMargin() < 0) {
            addBrokenRule("Left margin must be positive.", jasperDesign);
        }

        if (jasperDesign.getRightMargin() < 0) {
            addBrokenRule("Right margin must be positive.", jasperDesign);
        }

        if (jasperDesign.getTopMargin() < 0) {
            addBrokenRule("Top margin must be positive.", jasperDesign);
        }

        if (jasperDesign.getBottomMargin() < 0) {
            addBrokenRule("Bottom margin must be positive.", jasperDesign);
        }

        if (jasperDesign.getLeftMargin() + jasperDesign.getColumnCount() * jasperDesign.getColumnWidth()
                + (jasperDesign.getColumnCount() - 1) * jasperDesign.getColumnSpacing()
                + jasperDesign.getRightMargin() > jasperDesign.getPageWidth()) {
            addBrokenRule("The columns and the margins do not fit the page width.", jasperDesign);
        }

        verifyBandHeights(brokenRules, jasperDesign, jasperDesign.getPageHeight(), jasperDesign.getTopMargin(),
                jasperDesign.getBottomMargin());

        verifyFormatFactoryClass();
    }

    /**
     * Validates that the report band heights fit on a page of certain size.
     * 
     * @param brokenRules the list of rules to which
     * the validation failures are to be added
     * @param report the report whose bands are to be validated
     * @param pageHeight the height of the page
     * @param topMargin the page top margin
     * @param bottomMargin the page bottom margin
     */
    public static void verifyBandHeights(Collection<JRValidationFault> brokenRules, JRReport report, int pageHeight,
            int topMargin, int bottomMargin) {
        if (topMargin + (report.getBackground() != null ? report.getBackground().getHeight() : 0)
                + bottomMargin > pageHeight) {
            addBrokenRule(brokenRules, "The background section and the margins do not fit the page height.",
                    report);
        }

        if (report.isTitleNewPage()) {
            if (topMargin + getBreakHeight(report.getTitle()) + bottomMargin > pageHeight) {
                addBrokenRule(brokenRules, "The title section and the margins do not fit the page height.", report);
            }
        } else {
            if (topMargin + getBreakHeight(report.getTitle())
                    + (report.getPageHeader() != null ? report.getPageHeader().getHeight() : 0)
                    + (report.getColumnHeader() != null ? report.getColumnHeader().getHeight() : 0)
                    + (report.getColumnFooter() != null ? report.getColumnFooter().getHeight() : 0)
                    + (report.getPageFooter() != null ? report.getPageFooter().getHeight() : 0)
                    + bottomMargin > pageHeight) {
                addBrokenRule(brokenRules,
                        "The title section, the page and column headers and footers and the margins do not fit the page height.",
                        report);
            }
        }

        if (topMargin + (report.getPageHeader() != null ? report.getPageHeader().getHeight() : 0)
                + (report.getColumnHeader() != null ? report.getColumnHeader().getHeight() : 0)
                + (report.getColumnFooter() != null ? report.getColumnFooter().getHeight() : 0)
                + (report.getPageFooter() != null ? report.getPageFooter().getHeight() : 0)
                + bottomMargin > pageHeight) {
            addBrokenRule(brokenRules,
                    "The page and column headers and footers and the margins do not fit the page height.", report);
        }

        if (topMargin + (report.getPageHeader() != null ? report.getPageHeader().getHeight() : 0)
                + (report.getColumnHeader() != null ? report.getColumnHeader().getHeight() : 0)
                + (report.getColumnFooter() != null ? report.getColumnFooter().getHeight() : 0)
                + (report.getLastPageFooter() != null ? report.getLastPageFooter().getHeight() : 0)
                + bottomMargin > pageHeight) {
            addBrokenRule(brokenRules,
                    "The page and column headers and footers and the margins do not fit the last page height.",
                    report);
        }

        if (topMargin + getBreakHeight(report.getSummary()) + bottomMargin > pageHeight) {
            addBrokenRule(brokenRules, "The summary section and the margins do not fit the page height.", report);
        }

        JRSection detailSection = report.getDetailSection();
        if (detailSection != null) {
            JRBand[] detailBands = detailSection.getBands();
            if (detailBands != null && detailBands.length > 0) {
                for (int i = 0; i < detailBands.length; i++) {
                    JRBand detailBand = detailBands[i];
                    if (topMargin + (report.getPageHeader() != null ? report.getPageHeader().getHeight() : 0)
                            + (report.getColumnHeader() != null ? report.getColumnHeader().getHeight() : 0)
                            + getBreakHeight(detailBand)
                            + (report.getColumnFooter() != null ? report.getColumnFooter().getHeight() : 0)
                            + (report.getPageFooter() != null ? report.getPageFooter().getHeight() : 0)
                            + bottomMargin > pageHeight) {
                        addBrokenRule(brokenRules,
                                "The detail section, the page and column headers and footers and the margins do not fit the page height.",
                                report);
                    }
                }
            }
        }

        if (topMargin + getBreakHeight(report.getNoData()) + bottomMargin > pageHeight) {
            addBrokenRule(brokenRules, "The noData section and the margins do not fit the page height.", report);
        }
    }

    protected void verifyFormatFactoryClass() {
        String formatFactoryClassName = jasperDesign.getFormatFactoryClass();
        if (formatFactoryClassName != null) {
            try {
                Class<?> formatFactoryClass = JRClassLoader.loadClassForName(formatFactoryClassName);
                if (!FormatFactory.class.isAssignableFrom(formatFactoryClass)) {
                    addBrokenRule("The report format factory class is not compatible with "
                            + FormatFactory.class.getName(), jasperDesign);
                }
            } catch (ClassNotFoundException e) {
                addBrokenRule(e.toString(), jasperDesign);
            }
        }
    }

    /**
     *
     */
    private void verifyQuery(JRDesignDataset dataset) {
        JRQuery query = dataset.getQuery();
        if (query != null) {
            String language = query.getLanguage();
            QueryExecuterFactory queryExecuterFactory = null;
            if (language == null || language.length() == 0) {
                addBrokenRule("Query language not set.", query);
            } else {
                try {
                    queryExecuterFactory = JRQueryExecuterUtils.getInstance(jasperReportsContext)
                            .getExecuterFactory(query.getLanguage());
                } catch (JRException e1) {
                    addBrokenRule("Query executer factory for " + language + " cannot be created.", query);
                }
            }

            JRQueryChunk[] chunks = query.getChunks();
            if (chunks != null && chunks.length > 0) {
                Map<String, JRParameter> parametersMap = dataset.getParametersMap();

                for (int j = 0; j < chunks.length; j++) {
                    JRQueryChunk queryChunk = chunks[j];
                    switch (queryChunk.getType()) {
                    case JRQueryChunk.TYPE_PARAMETER: {
                        JRParameter parameter = parametersMap.get(queryChunk.getText());
                        if (parameter == null) {
                            addBrokenRule("Query parameter not found : " + queryChunk.getText(), query);
                        } else if (queryExecuterFactory != null) {
                            String parameterType = null;
                            try {
                                parameterType = parameter.getValueClassName();
                            } catch (JRRuntimeException e) {
                                // ignore, already added when the parameter got verified
                            }
                            if (parameterType != null
                                    && !queryExecuterFactory.supportsQueryParameterType(parameterType)) {
                                addBrokenRule("Parameter type not supported in query : " + queryChunk.getText()
                                        + " class " + parameterType, query);
                            }
                        }

                        break;
                    }
                    case JRQueryChunk.TYPE_PARAMETER_CLAUSE: {
                        if (!parametersMap.containsKey(queryChunk.getText())) {
                            addBrokenRule("Query parameter not found : " + queryChunk.getText(), query);
                        }
                        break;
                    }
                    case JRQueryChunk.TYPE_TEXT:
                    default: {
                    }
                    }
                }
            }
        }
    }

    /**
     *
     */
    private void verifyExpressions(List<JRExpression> expressions, Map<String, ? extends JRParameter> parametersMap,
            Map<String, JRField> fieldsMap, Map<String, JRVariable> variablesMap) {
        if (expressions != null && expressions.size() > 0) {
            for (Iterator<JRExpression> it = expressions.iterator(); it.hasNext();) {
                JRExpression expression = it.next();
                JRExpressionChunk[] chunks = expression.getChunks();
                if (chunks != null && chunks.length > 0) {
                    for (int j = 0; j < chunks.length; j++) {
                        JRExpressionChunk expressionChunk = chunks[j];
                        switch (expressionChunk.getType()) {
                        case JRExpressionChunk.TYPE_VARIABLE: {
                            if (!variablesMap.containsKey(expressionChunk.getText())) {
                                addBrokenRule("Variable not found : " + expressionChunk.getText(), expression);
                            }
                            break;
                        }
                        case JRExpressionChunk.TYPE_FIELD: {
                            if (!fieldsMap.containsKey(expressionChunk.getText())) {
                                addBrokenRule("Field not found : " + expressionChunk.getText(), expression);
                            }
                            break;
                        }
                        case JRExpressionChunk.TYPE_PARAMETER: {
                            if (!parametersMap.containsKey(expressionChunk.getText())) {
                                addBrokenRule("Parameter not found : " + expressionChunk.getText(), expression);
                            }
                            break;
                        }
                        case JRExpressionChunk.TYPE_RESOURCE:
                        case JRExpressionChunk.TYPE_TEXT:
                        default: {
                        }
                        }
                    }
                }
            }
        }
    }

    private void verifyExpressions(JRDesignDataset dataset) {
        verifyExpressions(expressionCollector.getExpressions(dataset), dataset.getParametersMap(),
                dataset.getFieldsMap(), dataset.getVariablesMap());
    }

    protected void verifyReportTemplates() {
        JRReportTemplate[] templates = jasperDesign.getTemplates();
        if (templates != null) {
            for (int i = 0; i < templates.length; i++) {
                JRReportTemplate template = templates[i];
                verifyTemplate(template);
            }
        }
    }

    protected void verifyTemplate(JRReportTemplate template) {
        JRExpression sourceExpression = template.getSourceExpression();
        if (sourceExpression == null) {
            addBrokenRule("Template source expression missing.", template);
        }
    }

    /**
     * @deprecated To be removed.
     */
    protected boolean verifyTemplateSourceType(Class<?> valueClass) {
        boolean valid = false;
        for (int i = 0; i < templateTypes.length; i++) {
            Class<?> type = templateTypes[i];
            if (type.isAssignableFrom(valueClass)) {
                valid = true;
                break;
            }
        }
        return valid;
    }

    /**
     *
     */
    private void verifyStyles() {
        JRStyle[] styles = jasperDesign.getStyles();
        if (styles != null && styles.length > 0) {
            for (int index = 0; index < styles.length; index++) {
                JRStyle style = styles[index];

                if (style.getName() == null || style.getName().trim().length() == 0) {
                    addBrokenRule("Report style name missing.", style);
                }

                verifyConditionalStyles(style);
            }
        }
    }

    /**
     *
     */
    private void verifyConditionalStyles(JRStyle style) {
        JRConditionalStyle[] condStyles = style.getConditionalStyles();
        if (condStyles != null && condStyles.length > 0) {
            for (int index = 0; index < condStyles.length; index++) {
                JRConditionalStyle condStyle = condStyles[index];

                if (log.isWarnEnabled()) {
                    if (condStyle.getName() != null) {
                        log.warn("Conditional style should not have a name.");
                    }

                    if (condStyle.isDefault()) {
                        log.warn("Conditional style can't be the default style.");
                    }
                }
            }
        }
    }

    /**
     *
     */
    private void verifyParameters(JRDesignDataset dataset) {
        JRParameter[] parameters = dataset.getParameters();
        if (parameters != null && parameters.length > 0) {
            for (int index = 0; index < parameters.length; index++) {
                JRParameter parameter = parameters[index];

                Object errorSource = parameter;
                if (parameter.isSystemDefined()) {
                    errorSource = jasperDesign;
                }

                if (parameter.getName() == null || parameter.getName().trim().length() == 0) {
                    addBrokenRule("Parameter name missing.", errorSource);
                }

                if (parameter.getValueClassName() == null) {
                    addBrokenRule("Class not set for parameter : " + parameter.getName(), errorSource);
                }
            }
        }
    }

    /**
     *
     */
    private void verifyFields(JRDesignDataset dataset) {
        JRField[] fields = dataset.getFields();
        if (fields != null && fields.length > 0) {
            for (int index = 0; index < fields.length; index++) {
                JRField field = fields[index];

                if (field.getName() == null || field.getName().trim().length() == 0) {
                    addBrokenRule("Field name missing.", field);
                }

                try {
                    Class<?> fieldType = field.getValueClass();
                    if (fieldType == null) {
                        addBrokenRule("Class not set for field : " + field.getName(), field);
                    }
                } catch (JRRuntimeException e) {
                    addBrokenRule(e, field);
                }

                verifyProperyExpressions(field.getPropertyExpressions());
            }
        }
    }

    /**
     *
     */
    private void verifySortFields(JRDesignDataset dataset) {
        JRField[] fields = dataset.getFields();
        JRVariable[] variables = dataset.getVariables();
        JRSortField[] sortFields = dataset.getSortFields();
        if (sortFields != null && sortFields.length > 0) {
            for (int index = 0; index < sortFields.length; index++) {
                JRSortField sortField = sortFields[index];
                String sortFieldName = sortField.getName();

                if (sortFieldName == null || sortFieldName.trim().length() == 0) {
                    addBrokenRule("Sort field name missing.", sortField);
                } else {
                    boolean isFound = false;

                    if (sortField.getType() == SortFieldTypeEnum.VARIABLE) {
                        if (variables != null) {
                            int j = 0;
                            while (!isFound && j < variables.length) {
                                isFound = sortFieldName.equals(variables[j].getName());
                                j++;
                            }
                        }
                    } else {
                        if (fields != null) {
                            int j = 0;
                            while (!isFound && j < fields.length) {
                                isFound = sortFieldName.equals(fields[j].getName());
                                j++;
                            }
                        }
                    }

                    if (!isFound) {
                        addBrokenRule("Sort " + sortField.getType().getName().toLowerCase() + " \'" + sortFieldName
                                + "\' not found in dataset.", sortField);
                    }
                }
            }
        }
    }

    /**
     *
     */
    private void verifyVariables(JRDesignDataset dataset) throws JRRuntimeException {
        JRVariable[] variables = dataset.getVariables();
        if (variables != null && variables.length > 0) {
            for (int index = 0; index < variables.length; index++) {
                JRVariable variable = variables[index];

                if (variable.getName() == null || variable.getName().trim().length() == 0) {
                    addBrokenRule("Variable name missing.", variable);
                }

                try {
                    Class<?> valueClass = variable.getValueClass();
                    if (valueClass == null) {
                        addBrokenRule("Class not set for variable : " + variable.getName(), variable);
                    }
                } catch (JRRuntimeException e) {
                    addBrokenRule(e, variable);
                }

                ResetTypeEnum resetType = variable.getResetTypeValue();
                if (resetType == ResetTypeEnum.GROUP) {
                    if (variable.getResetGroup() == null) {
                        addBrokenRule("Reset group missing for variable : " + variable.getName(), variable);
                    } else {
                        Map<String, JRGroup> groupsMap = dataset.getGroupsMap();

                        if (!groupsMap.containsKey(variable.getResetGroup().getName())) {
                            addBrokenRule("Reset group \"" + variable.getResetGroup().getName()
                                    + "\" not found for variable : " + variable.getName(), variable);
                        }
                    }
                }

                IncrementTypeEnum incrementType = variable.getIncrementTypeValue();
                if (incrementType == IncrementTypeEnum.GROUP) {
                    if (variable.getIncrementGroup() == null) {
                        addBrokenRule("Increment group missing for variable : " + variable.getName(), variable);
                    } else {
                        Map<String, JRGroup> groupsMap = dataset.getGroupsMap();

                        if (!groupsMap.containsKey(variable.getIncrementGroup().getName())) {
                            addBrokenRule("Increment group \"" + variable.getIncrementGroup().getName()
                                    + "\" not found for variable : " + variable.getName(), variable);
                        }
                    }
                }
            }
        }
    }

    /**
     *
     */
    private void verifyGroups(JRDesignDataset dataset) {
        JRGroup[] groups = dataset.getGroups();
        if (groups != null && groups.length > 0) {
            boolean isMainDataset = dataset.isMainDataset();
            for (int index = 0; index < groups.length; index++) {
                JRGroup group = groups[index];

                if (group.getName() == null || group.getName().trim().length() == 0) {
                    addBrokenRule("Group name missing.", group);
                }

                if (isMainDataset) {
                    verifyGroupHeaderAndFooter(group);
                } else {
                    if ((group.getGroupHeaderSection() != null && group.getGroupHeaderSection().getBands() != null
                            && group.getGroupHeaderSection().getBands().length > 0)
                            || (group.getGroupFooterSection() != null
                                    && group.getGroupFooterSection().getBands() != null
                                    && group.getGroupFooterSection().getBands().length > 0)) {
                        addBrokenRule("Group " + group.getName() + " cannot have header or footer sections.",
                                group);
                    }
                }

                if (isMainDataset) {
                    verifySection(group.getGroupHeaderSection());
                    verifySection(group.getGroupFooterSection());
                }
            }
        }
    }

    private void verifyGroupHeaderAndFooter(JRGroup group) {
        if (jasperDesign.isTitleNewPage()) {
            JRSection groupHeaderSection = group.getGroupHeaderSection();
            if (groupHeaderSection != null) {
                JRBand[] groupHeaderBands = groupHeaderSection.getBands();
                if (groupHeaderBands != null && groupHeaderBands.length > 0) {
                    for (int i = 0; i < groupHeaderBands.length; i++) {
                        JRBand groupHeaderBand = groupHeaderBands[i];
                        if (jasperDesign.getTopMargin()
                                + (jasperDesign.getPageHeader() != null ? jasperDesign.getPageHeader().getHeight()
                                        : 0)
                                + (jasperDesign.getColumnHeader() != null
                                        ? jasperDesign.getColumnHeader().getHeight()
                                        : 0)
                                + getBreakHeight(groupHeaderBand)
                                + (jasperDesign.getColumnFooter() != null
                                        ? jasperDesign.getColumnFooter().getHeight()
                                        : 0)
                                + (jasperDesign.getPageFooter() != null ? jasperDesign.getPageFooter().getHeight()
                                        : 0)
                                + jasperDesign.getBottomMargin() > jasperDesign.getPageHeight()) {
                            addBrokenRule("The '" + group.getName()
                                    + "' group header section, the page and column headers and footers and the margins do not fit the page height.",
                                    groupHeaderBand);
                        }
                    }
                }
            }

            JRSection groupFooterSection = group.getGroupFooterSection();
            if (groupFooterSection != null) {
                JRBand[] groupFooterBands = groupFooterSection.getBands();
                if (groupFooterBands != null && groupFooterBands.length > 0) {
                    for (int i = 0; i < groupFooterBands.length; i++) {
                        JRBand groupFooterBand = groupFooterBands[i];
                        if (jasperDesign.getTopMargin()
                                + (jasperDesign.getPageHeader() != null ? jasperDesign.getPageHeader().getHeight()
                                        : 0)
                                + (jasperDesign.getColumnHeader() != null
                                        ? jasperDesign.getColumnHeader().getHeight()
                                        : 0)
                                + getBreakHeight(groupFooterBand)
                                + (jasperDesign.getColumnFooter() != null
                                        ? jasperDesign.getColumnFooter().getHeight()
                                        : 0)
                                + (jasperDesign.getPageFooter() != null ? jasperDesign.getPageFooter().getHeight()
                                        : 0)
                                + jasperDesign.getBottomMargin() > jasperDesign.getPageHeight()) {
                            addBrokenRule("The '" + group.getName()
                                    + "' group footer section, the page and column headers and footers and the margins do not fit the page height.",
                                    groupFooterBand);
                        }
                    }
                }
            }
        } else {
            JRSection groupHeaderSection = group.getGroupHeaderSection();
            if (groupHeaderSection != null) {
                JRBand[] groupHeaderBands = groupHeaderSection.getBands();
                if (groupHeaderBands != null && groupHeaderBands.length > 0) {
                    for (int i = 0; i < groupHeaderBands.length; i++) {
                        JRBand groupHeaderBand = groupHeaderBands[i];
                        if (jasperDesign.getTopMargin()
                                + (jasperDesign.getTitle() != null ? jasperDesign.getTitle().getHeight() : 0)
                                + (jasperDesign.getPageHeader() != null ? jasperDesign.getPageHeader().getHeight()
                                        : 0)
                                + (jasperDesign.getColumnHeader() != null
                                        ? jasperDesign.getColumnHeader().getHeight()
                                        : 0)
                                + getBreakHeight(groupHeaderBand)
                                + (jasperDesign.getColumnFooter() != null
                                        ? jasperDesign.getColumnFooter().getHeight()
                                        : 0)
                                + (jasperDesign.getPageFooter() != null ? jasperDesign.getPageFooter().getHeight()
                                        : 0)
                                + jasperDesign.getBottomMargin() > jasperDesign.getPageHeight()) {
                            addBrokenRule("The '" + group.getName()
                                    + "' group header section, the title, the page and column headers and footers and the margins do not fit the first page height.",
                                    groupHeaderBand);
                        }
                    }
                }
            }

            JRSection groupFooterSection = group.getGroupFooterSection();
            if (groupFooterSection != null) {
                JRBand[] groupFooterBands = groupFooterSection.getBands();
                if (groupFooterBands != null && groupFooterBands.length > 0) {
                    for (int i = 0; i < groupFooterBands.length; i++) {
                        JRBand groupFooterBand = groupFooterBands[i];
                        if (jasperDesign.getTopMargin()
                                + (jasperDesign.getTitle() != null ? jasperDesign.getTitle().getHeight() : 0)
                                + (jasperDesign.getPageHeader() != null ? jasperDesign.getPageHeader().getHeight()
                                        : 0)
                                + (jasperDesign.getColumnHeader() != null
                                        ? jasperDesign.getColumnHeader().getHeight()
                                        : 0)
                                + getBreakHeight(groupFooterBand)
                                + (jasperDesign.getColumnFooter() != null
                                        ? jasperDesign.getColumnFooter().getHeight()
                                        : 0)
                                + (jasperDesign.getPageFooter() != null ? jasperDesign.getPageFooter().getHeight()
                                        : 0)
                                + jasperDesign.getBottomMargin() > jasperDesign.getPageHeight()) {
                            addBrokenRule("The '" + group.getName()
                                    + "' group footer section, the title, the page and column headers and footers and the margins do not fit the first page height.",
                                    groupFooterBand);
                        }
                    }
                }
            }
        }
    }

    protected boolean toVerifyElementOverlap() {
        return !JRPropertiesUtil.getInstance(jasperReportsContext).getBooleanProperty(jasperDesign,
                PROPERTY_ALLOW_ELEMENT_OVERLAP, true);
    }

    protected boolean isAllowedToOverlap(JRElement element) {
        // check whether the element has been marked to allow to overwrite
        return element.hasProperties() && JRPropertiesUtil
                .asBoolean(element.getPropertiesMap().getProperty(PROPERTY_ALLOW_ELEMENT_OVERLAP));
    }

    protected void verifyElementOverlap(JRElement element1, JRElement element2) {
        if (element1.getWidth() <= 0 || element1.getHeight() <= 0 || element2.getWidth() <= 0
                || element2.getHeight() <= 0) {
            // no-space element -> no overlap
            return;
        }

        if ((element1.getX() < element2.getX() + element2.getWidth()
                && element2.getX() < element1.getX() + element1.getWidth())
                && (element1.getY() < element2.getY() + element2.getHeight()
                        && element2.getY() < element1.getY() + element1.getHeight())) {
            // we have an overlap
            StringBuilder message = new StringBuilder();
            message.append("Element ");
            if (element2.getKey() != null) {
                message.append("\"");
                message.append(element2.getKey());
                message.append("\" ");
            }
            message.append("at ");
            message.append(getElementPositionText(element2));
            message.append(" overlaps element ");
            if (element1.getKey() != null) {
                message.append("\"");
                message.append(element1.getKey());
                message.append("\" ");
            }
            message.append("at ");
            message.append(getElementPositionText(element1));

            // using the element on top (in z-order) as source 
            addBrokenRule(message.toString(), element2);
        }
    }

    protected String getElementPositionText(JRElement element) {
        return "[x = " + element.getX() + ", y = " + element.getY() + ", width = " + element.getWidth()
                + ", height = " + element.getHeight() + "]";
    }

    protected void verifyElementsOverlap(JRElement[] elements) {
        if (!toVerifyElementOverlap()) {
            return;
        }

        for (int index = 1; index < elements.length; index++) {
            JRElement element = elements[index];
            if (!isAllowedToOverlap(element)) {
                for (int overlapIndex = 0; overlapIndex < index; ++overlapIndex) {
                    if (!isAllowedToOverlap(elements[overlapIndex])) {
                        verifyElementOverlap(elements[overlapIndex], element);
                    }
                }
            }
        }
    }

    /**
     *
     */
    private void verifySection(JRSection section) {
        if (section != null) {
            JRBand[] bands = section.getBands();
            if (bands != null && bands.length > 0) {
                if (sectionType == SectionTypeEnum.PART) {
                    addBrokenRule("Part reports cannot contain bands", section);
                } else {
                    for (int i = 0; i < bands.length; i++) {
                        verifyBand(bands[i]);
                    }
                }
            }

            JRPart[] parts = section.getParts();
            if (parts != null && parts.length > 0) {
                if (sectionType == SectionTypeEnum.BAND) {
                    addBrokenRule("Band reports cannot contain parts", section);
                } else {
                    for (int i = 0; i < parts.length; i++) {
                        verifyPart(parts[i]);
                    }
                }
            }
        }
    }

    /**
     *
     */
    private void verifyBand(JRBand band) {
        if (band != null) {
            if (sectionType == SectionTypeEnum.PART) {
                addBrokenRule("Part reports cannot contain bands", band);
                return;
            }

            JRElement[] elements = band.getElements();
            if (elements != null && elements.length > 0) {
                for (int index = 0; index < elements.length; index++) {
                    JRElement element = elements[index];

                    /*
                    if (element.getY() < 0)
                    {
                    //                  if (log.isWarnEnabled())
                    //                     log.warn(
                    "Warning : Element placed outside band area : y=" + element.getY()
                    );
                       //addBrokenRule("Element placed outside band area.");
                    }
                    else if (element.getY() + element.getHeight() > band.getHeight())
                    */
                    if (element.getY() + element.getHeight() > band.getHeight()) {
                        //                  if (log.isWarnEnabled())
                        //                     log.warn(
                        //                        "Warning : Element bottom reaches outside band area : y=" + element.getY() +
                        //                        " height=" + element.getHeight() +
                        //                        " band-height=" + band.getHeight()
                        //                        );
                        addBrokenRule(
                                "Warning : Element bottom reaches outside band area : y=" + element.getY()
                                        + " height=" + element.getHeight() + " band-height=" + band.getHeight(),
                                element);
                    }

                    verifyElement(element);
                }

                verifyElementsOverlap(elements);
            }

            List<ExpressionReturnValue> returnValues = band.getReturnValues();
            if (returnValues != null && !returnValues.isEmpty()) {
                for (ExpressionReturnValue returnValue : returnValues) {
                    verifyReturnValue(returnValue);
                }
            }
        }
    }

    public void verifyElement(JRElement element) {
        if (element instanceof JRStaticText) {
            verifyStaticText((JRStaticText) element);
        } else if (element instanceof JRTextField) {
            verifyTextField((JRTextField) element);
        } else if (element instanceof JRImage) {
            verifyImage((JRImage) element);
        } else if (element instanceof JRSubreport) {
            verifySubreport((JRSubreport) element);
        } else if (element instanceof JRCrosstab) {
            verifyCrosstab((JRDesignCrosstab) element);
        } else if (element instanceof JRChart) {
            verifyChart((JRChart) element);
        } else if (element instanceof JRFrame) {
            verifyFrame((JRFrame) element);
        } else if (element instanceof JRComponentElement) {
            verifyComponentElement((JRComponentElement) element);
        } else if (element instanceof JRGenericElement) {
            verifyGenericElement((JRGenericElement) element);
        }
    }

    /**
     *
     */
    private void verifyStaticText(JRStaticText staticText) {
        verifyReportElement(staticText);
    }

    /**
     *
     */
    private void verifyTextField(JRTextField textField) {
        verifyReportElement(textField);
        verifyAnchor(textField);
        verifyHyperlink(textField);
    }

    /**
     *
     */
    private void verifyAnchor(JRAnchor anchor) {
        if (anchor != null) {
            if (anchor.getBookmarkLevel() != JRAnchor.NO_BOOKMARK && anchor.getBookmarkLevel() < 1) {
                addBrokenRule("Bookmark level should be " + JRAnchor.NO_BOOKMARK + " or greater than 0", anchor);
            }
        }
    }

    /**
     *
     */
    public void verifyHyperlink(JRHyperlink hyperlink) {
        if (hyperlink != null) {
            JRHyperlinkParameter[] parameters = hyperlink.getHyperlinkParameters();
            if (parameters != null) {
                for (int i = 0; i < parameters.length; i++) {
                    JRHyperlinkParameter parameter = parameters[i];
                    verifyHyperlinkParameter(parameter);
                }
            }
        }
    }

    protected void verifyHyperlinkParameter(JRHyperlinkParameter parameter) {
        if (parameter != null) {
            String name = parameter.getName();
            if (name == null || name.length() == 0) {
                addBrokenRule("Hyperlink parameter name missing.", parameter);
            }
        }
    }

    /**
     *
     */
    private void verifyImage(JRImage image) {
        verifyReportElement(image);
        verifyAnchor(image);
        verifyHyperlink(image);
    }

    /**
     *
     */
    private void verifySubreport(JRSubreport subreport) {
        if (subreport != null) {
            verifyReportElement(subreport);

            JRSubreportParameter[] parameters = subreport.getParameters();
            if (parameters != null && parameters.length > 0) {
                for (int index = 0; index < parameters.length; index++) {
                    JRSubreportParameter parameter = parameters[index];

                    if (parameter.getName() == null || parameter.getName().trim().length() == 0) {
                        addBrokenRule("Subreport parameter name missing.", parameter);
                    }
                }
            }

            if (subreport.getConnectionExpression() != null && subreport.getDataSourceExpression() != null) {
                addBrokenRule("Subreport cannot have both connection expresion and data source expression.",
                        subreport);
            }

            JRSubreportReturnValue[] returnValues = subreport.getReturnValues();
            if (returnValues != null && returnValues.length > 0) {
                for (int i = 0; i < returnValues.length; i++) {
                    JRSubreportReturnValue returnValue = returnValues[i];

                    if (returnValue.getFromVariable() == null
                            || returnValue.getFromVariable().trim().length() == 0) {
                        addBrokenRule("Subreport return value variable name missing.", returnValue);
                    }

                    if (returnValue.getToVariable() == null || returnValue.getToVariable().trim().length() == 0) {
                        addBrokenRule("Subreport return value to variable name missing.", returnValue);
                    }

                    JRDesignDataset dataset = currentDataset();
                    if (dataset == null || !dataset.getVariablesMap().containsKey(returnValue.getToVariable())) {
                        addBrokenRule("Subreport return value to variable not found.", returnValue);
                    }
                }
            }
        }
    }

    protected void verifyReturnValue(VariableReturnValue returnValue) {
        if (returnValue.getFromVariable() == null || returnValue.getFromVariable().trim().length() == 0) {
            addBrokenRule("Return value source variable name missing.", returnValue);
        }

        verifyCommonReturnValue(returnValue);
    }

    protected void verifyReturnValue(ExpressionReturnValue returnValue) {
        if (returnValue.getExpression() == null) {
            addBrokenRule("Return value expression missing.", returnValue);
        }

        verifyCommonReturnValue(returnValue);
    }

    protected void verifyCommonReturnValue(CommonReturnValue returnValue) {
        if (returnValue.getToVariable() == null || returnValue.getToVariable().trim().length() == 0) {
            addBrokenRule("Return value destination variable name missing.", returnValue);
        }

        JRDesignDataset dataset = currentDataset();
        if (dataset == null || !dataset.getVariablesMap().containsKey(returnValue.getToVariable())) {
            addBrokenRule("Return value destination variable not found.", returnValue);
        }
    }

    private void verifyCrosstab(JRDesignCrosstab crosstab) {
        verifyReportElement(crosstab);
        verifyParameters(crosstab);

        JRCrosstabDataset dataset = crosstab.getDataset();
        if (dataset == null) {
            addBrokenRule("Crosstab dataset missing.", crosstab);
        } else {
            verifyElementDataset(dataset);
        }

        if (crosstab.getTitleCell() != null) {
            verifyCellContents(crosstab.getTitleCell().getCellContents(), "crosstab title cell");
        }

        verifyCellContents(crosstab.getHeaderCell(), "crosstab cell");

        JRCrosstabRowGroup[] rowGroups = crosstab.getRowGroups();
        if (rowGroups == null || rowGroups.length == 0) {
            addBrokenRule("Crosstab should have at least one row group.", crosstab);
        } else {
            for (int i = 0; i < rowGroups.length; i++) {
                verifyCrosstabRowGroup(rowGroups[i]);

                if (i + 1 < rowGroups.length) {
                    verifyCrosstabNextGroup(rowGroups[i], rowGroups[i + 1]);
                }
            }
        }

        JRCrosstabColumnGroup[] colGroups = crosstab.getColumnGroups();
        if (colGroups == null || colGroups.length == 0) {
            addBrokenRule("Crosstab should have at least one column group.", crosstab);
        } else {
            for (int i = 0; i < colGroups.length; i++) {
                verifyCrosstabColumnGroup(colGroups[i]);

                if (i + 1 < colGroups.length) {
                    verifyCrosstabNextGroup(colGroups[i], colGroups[i + 1]);
                }
            }
        }

        JRCrosstabMeasure[] measures = crosstab.getMeasures();
        if (measures == null || measures.length == 0) {
            addBrokenRule("Crosstab should have at least one measure.", crosstab);
        } else {
            for (int i = 0; i < measures.length; i++) {
                verifyCrosstabMeasure(measures[i]);
            }
        }

        verifyCrosstabCells(crosstab);

        verifyCellContents(crosstab.getWhenNoDataCell(), "when no data cell");

        verifyExpressions(crosstab);
    }

    protected void verifyCrosstabNextGroup(JRCrosstabGroup group, JRCrosstabGroup nextGroup) {
        if (Boolean.FALSE.equals(group.getMergeHeaderCells())
                && nextGroup.getTotalPositionValue() != CrosstabTotalPositionEnum.NONE) {
            addBrokenRule("Row crosstab group has repeating header cells but the next group has a total row",
                    group);
        }
    }

    private void verifyParameters(JRDesignCrosstab crosstab) {
        JRCrosstabParameter[] parameters = crosstab.getParameters();
        if (parameters != null) {
            for (int i = 0; i < parameters.length; i++) {
                JRCrosstabParameter parameter = parameters[i];

                String paramName = parameter.getName();
                if (paramName == null || paramName.length() == 0) {
                    addBrokenRule("Missing parameter name for crosstab.", parameter);
                }

                try {
                    Class<?> valueClass = parameter.getValueClass();
                    if (valueClass == null) {
                        addBrokenRule("Class not set for crosstab parameter " + paramName + ".", parameter);
                    }
                } catch (Exception e) {
                    addBrokenRule(e, parameter);
                }
            }
        }
    }

    private void verifyCrosstabRowGroup(JRCrosstabRowGroup group) {
        verifyCrosstabGroup(group);
    }

    private void verifyCrosstabColumnGroup(JRCrosstabColumnGroup group) {
        verifyCrosstabGroup(group);

        verifyCellContents(group.getCrosstabHeader(), group.getName() + " crosstab header");
    }

    private void verifyCrosstabGroup(JRCrosstabGroup group) {
        String groupName = group.getName();
        if (groupName == null || groupName.length() == 0) {
            addBrokenRule("Crosstab group name missing.", group);
        }

        verifyCrosstabBucket(group);
        verifyCellContents(group.getHeader(), groupName + " header");
        if (group.hasTotal()) {
            verifyCellContents(group.getTotalHeader(), groupName + " total header");
        }
    }

    private void verifyCrosstabBucket(JRCrosstabGroup group) {
        JRCrosstabBucket bucket = group.getBucket();

        try {
            Class<?> valueClass = bucket.getValueClass();
            if (valueClass == null) {
                addBrokenRule("Class not set for bucket : " + group.getName(), bucket);
            }
        } catch (JRRuntimeException e) {
            addBrokenRule(e, bucket);
        }

        JRExpression expression = bucket.getExpression();
        if (expression == null) {
            addBrokenRule("Crosstab bucket expression missing for group " + group.getName() + ".", bucket);
        }
    }

    private void verifyCrosstabCells(JRDesignCrosstab crosstab) {
        JRCrosstabCell[][] cells = crosstab.getCells();
        JRCrosstabRowGroup[] rowGroups = crosstab.getRowGroups();
        JRCrosstabColumnGroup[] columnGroups = crosstab.getColumnGroups();

        JRCrosstabCell baseCell = cells[rowGroups.length][columnGroups.length];
        if (baseCell == null || baseCell.getWidth() == null) {
            addBrokenRule("Crosstab base cell width not specified.", crosstab);
        }

        if (baseCell == null || baseCell.getHeight() == null) {
            addBrokenRule("Crosstab base cell height not specified.", crosstab);
        }

        for (int i = rowGroups.length; i >= 0; --i) {
            for (int j = columnGroups.length; j >= 0; --j) {
                JRCrosstabCell cell = cells[i][j];

                String cellText = getCrosstabCellText(rowGroups, columnGroups, i, j);

                if (cell != null) {
                    JRCellContents contents = cell.getContents();

                    if (i < rowGroups.length) {
                        JRCrosstabCell colCell = cells[rowGroups.length][j];
                        if (colCell != null && colCell.getContents().getWidth() != contents.getWidth()) {
                            addBrokenRule("Crosstab " + cellText + " width should be "
                                    + colCell.getContents().getWidth() + ".", cell);
                        }
                    }

                    if (j < columnGroups.length) {
                        JRCrosstabCell rowCell = cells[i][columnGroups.length];
                        if (rowCell != null && rowCell.getContents().getHeight() != contents.getHeight()) {
                            addBrokenRule("Crosstab " + cellText + " height should be "
                                    + rowCell.getContents().getHeight() + ".", cell);
                        }
                    }

                    verifyCellContents(contents, cellText);
                }
            }
        }
    }

    private String getCrosstabCellText(JRCrosstabRowGroup[] rowGroups, JRCrosstabColumnGroup[] columnGroups,
            int rowIndex, int columnIndex) {
        String text;

        if (rowIndex == rowGroups.length) {
            if (columnIndex == columnGroups.length) {
                text = "cell";
            } else {
                text = columnGroups[columnIndex].getName() + " total cell";
            }
        } else {
            if (columnIndex == columnGroups.length) {
                text = rowGroups[rowIndex].getName() + " total cell";
            } else {
                text = rowGroups[rowIndex].getName() + "," + columnGroups[columnIndex].getName() + " total cell";
            }
        }

        return text;
    }

    private void verifyCrosstabMeasure(JRCrosstabMeasure measure) {
        String measureName = measure.getName();
        if (measureName == null || measureName.trim().length() == 0) {
            addBrokenRule("Measure name missing.", measure);
        }

        CalculationEnum calculation = measure.getCalculationValue();
        if (calculation == CalculationEnum.SYSTEM) {
            addBrokenRule("Crosstab mesures cannot have system calculation", measure);
        }

        JRExpression valueExpression = measure.getValueExpression();
        if (valueExpression == null) {
            addBrokenRule("Missing expression for measure " + measureName, measure);
        }

        try {
            Class<?> valueClass = measure.getValueClass();
            if (valueClass == null) {
                addBrokenRule("Measure value class missing.", measure);
            }
            if (measure.getPercentageType() != CrosstabPercentageEnum.NONE) {
                Class<?> percentageCalculatorClass = measure.getPercentageCalculatorClass();
                if (percentageCalculatorClass == null) {
                    if (valueClass != null && !JRPercentageCalculatorFactory.hasBuiltInCalculator(valueClass)) {
                        addBrokenRule("Percentage calculator class needs to be specified for measure " + measureName
                                + ".", measure);
                    }
                } else {
                    if (!JRPercentageCalculator.class.isAssignableFrom(percentageCalculatorClass)) {
                        addBrokenRule("Incompatible percentage calculator class for measure " + measureName + ".",
                                measure);
                    }
                }
            }
        } catch (JRRuntimeException e) {
            addBrokenRule(e, measure);
        }

        try {
            Class<?> incrementerFactoryClass = measure.getIncrementerFactoryClass();
            if (incrementerFactoryClass != null
                    && !JRExtendedIncrementerFactory.class.isAssignableFrom(incrementerFactoryClass)) {
                addBrokenRule(
                        "Crosstab measures need extended incrementers (net.sf.jasperreports.engine.fill.JRExtendedIncrementerFactory).",
                        measure);
            }
        } catch (JRRuntimeException e) {
            addBrokenRule(e, measure);
        }
    }

    private void verifyExpressions(JRDesignCrosstab crosstab) {
        verifyExpressions(expressionCollector.getExpressions(crosstab), crosstab.getParametersMap(),
                new HashMap<String, JRField>(), crosstab.getVariablesMap());
    }

    private void verifyChart(JRChart chart) {
        verifyReportElement(chart);

        if (chart.getEvaluationTimeValue() == EvaluationTimeEnum.AUTO) {
            addBrokenRule("Charts do not support Auto evaluation time.", chart);
        }

        JRChartDataset dataset = chart.getDataset();
        if (dataset == null) {
            addBrokenRule("Chart dataset missing.", chart);
        } else {
            dataset.validate(this);
        }
    }

    private void verifyCellContents(JRCellContents contents, String cellText) {
        if (contents != null) {
            JRElement[] elements = contents.getElements();
            if (elements != null && elements.length > 0) {
                int topPadding = 0;
                int leftPadding = 0;
                int bottomPadding = 0;
                int rightPadding = 0;

                JRLineBox box = contents.getLineBox();
                if (box != null) {
                    topPadding = box.getTopPadding();
                    leftPadding = box.getLeftPadding();
                    bottomPadding = box.getBottomPadding();
                    rightPadding = box.getRightPadding();
                }

                int cellWidth = contents.getWidth();
                boolean widthCalculated = cellWidth != JRCellContents.NOT_CALCULATED;
                int avlblWidth = cellWidth - leftPadding - rightPadding;
                int cellHeight = contents.getHeight();
                boolean heightCalculated = cellHeight != JRCellContents.NOT_CALCULATED;
                int avlblHeight = cellHeight - topPadding - bottomPadding;

                for (int i = 0; i < elements.length; i++) {
                    JRElement element = elements[i];

                    if (widthCalculated && element.getX() + element.getWidth() > avlblWidth) {
                        addBrokenRule("Element reaches outside " + cellText + " width: x=" + element.getX()
                                + ", width=" + element.getWidth() + ", available width=" + avlblWidth + ".",
                                element);
                    }

                    if (heightCalculated && element.getY() + element.getHeight() > avlblHeight) {
                        addBrokenRule("Element reaches outside " + cellText + " height: y=" + element.getY()
                                + ", height=" + element.getHeight() + ", available height=" + avlblHeight + ".",
                                element);
                    }

                    if (element instanceof JRStaticText) {
                        verifyStaticText((JRStaticText) element);
                    } else if (element instanceof JRTextField) {
                        JRTextField textField = (JRTextField) element;

                        if (textField.getEvaluationTimeValue() != EvaluationTimeEnum.NOW) {
                            addBrokenRule(
                                    "Elements with delayed evaluation time are not supported inside crosstab cells.",
                                    textField);
                        }

                        verifyTextField(textField);
                    } else if (element instanceof JRImage) {
                        JRImage image = (JRImage) element;

                        if (image.getEvaluationTimeValue() != EvaluationTimeEnum.NOW) {
                            addBrokenRule(
                                    "Elements with delayed evaluation time are not supported inside crosstab cells.",
                                    image);
                        }

                        verifyImage(image);
                    } else if (element instanceof JRFrame) {
                        verifyFrame((JRFrame) element);
                    } else if (element instanceof JRSubreport) {
                        verifySubreport((JRSubreport) element);
                    } else if (element instanceof JRCrosstab) {
                        addBrokenRule("Crosstabs are not allowed inside crosstab cells.", element);
                    } else if (element instanceof JRChart) {
                        addBrokenRule("Charts are not allowed inside crosstab cells.", element);
                    }
                }

                verifyElementsOverlap(elements);
            }
        }
    }

    public void verifyElementDataset(JRElementDataset dataset) {
        JRDatasetRun datasetRun = dataset.getDatasetRun();

        if (datasetRun != null) {
            IncrementTypeEnum incrementType = dataset.getIncrementTypeValue();
            if (incrementType == IncrementTypeEnum.PAGE || incrementType == IncrementTypeEnum.COLUMN) {
                addBrokenRule("Chart datasets with dataset run cannont have Column or Page increment type.",
                        dataset);
            }

            ResetTypeEnum resetType = dataset.getResetTypeValue();
            if (resetType == ResetTypeEnum.PAGE || resetType == ResetTypeEnum.COLUMN) {
                addBrokenRule("Chart datasets with dataset run cannont have Column or Page reset type.", dataset);
            }
            //         else if (resetType != ResetTypeEnum.REPORT)
            //         {
            //            //doesn't make sense, but let it go
            //         }

            verifyDatasetRun(datasetRun);
        }
    }

    /**
     * Verifies a subdataset run object.
     * 
     * @param datasetRun the subdataset run
     */
    public void verifyDatasetRun(JRDatasetRun datasetRun) {
        JRDesignDataset dataset = null;

        String datasetName = datasetRun.getDatasetName();
        if (datasetName == null || datasetName.length() == 0) {
            addBrokenRule("Dataset name is missing for dataset run.", datasetRun);
        } else {
            dataset = (JRDesignDataset) jasperDesign.getDatasetMap().get(datasetName);

            if (dataset == null) {
                addBrokenRule("Unknown dataset name " + datasetName + ".", datasetRun);
            }
        }

        JRDatasetParameter[] parameters = datasetRun.getParameters();
        if (parameters != null && parameters.length > 0) {
            for (int index = 0; index < parameters.length; index++) {
                JRDatasetParameter parameter = parameters[index];

                String paramName = parameter.getName();
                if (paramName == null || paramName.trim().length() == 0) {
                    addBrokenRule("Dataset " + datasetName + " parameter name missing.", parameter);
                }

                JRParameter datasetParam = null;
                if (dataset != null) {
                    datasetParam = dataset.getParametersMap().get(paramName);

                    if (datasetParam == null) {
                        addBrokenRule("Unknown parameter " + paramName + " in dataset " + datasetName + ".",
                                parameter);
                    }
                }
            }
        }

        JRExpression connectionExpression = datasetRun.getConnectionExpression();
        JRExpression dataSourceExpression = datasetRun.getDataSourceExpression();

        if (connectionExpression != null && dataSourceExpression != null) {
            addBrokenRule(
                    "Dataset " + datasetName + " cannot have both connection expresion and data source expression.",
                    datasetRun);
        }

        List<ReturnValue> returnValues = datasetRun.getReturnValues();
        if (returnValues != null && !returnValues.isEmpty()) {
            for (ReturnValue returnValue : returnValues) {
                verifyReturnValue(returnValue);
            }
        }
    }

    private void verifyDatasets() {
        JRDataset[] datasets = jasperDesign.getDatasets();
        if (datasets != null && datasets.length > 0) {
            for (int i = 0; i < datasets.length; ++i) {
                JRDesignDataset dataset = (JRDesignDataset) datasets[i];

                if (dataset.getName() == null || dataset.getName().trim().length() == 0) {
                    addBrokenRule("Dataset name is missing.", dataset);
                }

                verifyDataset(dataset);
            }
        }
    }

    private void verifyDataset(JRDesignDataset dataset) {
        verifyExpressions(dataset);

        verifyProperyExpressions(dataset.getPropertyExpressions());

        verifyParameters(dataset);

        verifyQuery(dataset);

        verifyFields(dataset);

        verifySortFields(dataset);

        verifyVariables(dataset);

        verifyGroups(dataset);
    }

    private void verifyFrame(JRFrame frame) {
        verifyReportElement(frame);

        JRElement[] elements = frame.getElements();
        if (elements != null && elements.length > 0) {
            int leftPadding = frame.getLineBox().getLeftPadding();
            int rightPadding = frame.getLineBox().getRightPadding();

            int avlblWidth = frame.getWidth() - leftPadding - rightPadding;

            for (int i = 0; i < elements.length; i++) {
                JRElement element = elements[i];

                if (element.getX() + element.getWidth() > avlblWidth) {
                    addBrokenRule("Element reaches outside frame width: x=" + element.getX() + ", width="
                            + element.getWidth() + ", available width=" + avlblWidth + ".", element);
                }

                verifyElement(element);
            }

            verifyElementsOverlap(elements);
        }
    }

    public void verify(JRCategoryDataset dataset) {
        verifyElementDataset(dataset);

        JRCategorySeries[] series = dataset.getSeries();
        if (series != null) {
            for (int i = 0; i < series.length; i++) {
                verify(series[i]);
            }
        }
    }

    protected void verify(JRCategorySeries series) {
        verifyHyperlink(series.getItemHyperlink());
    }

    public void verify(JRPieDataset dataset) {
        verifyElementDataset(dataset);

        JRPieSeries[] series = dataset.getSeries();
        if (series != null) {
            for (int i = 0; i < series.length; i++) {
                verify(series[i]);
            }
        }

        verifyHyperlink(dataset.getOtherSectionHyperlink());
    }

    protected void verify(JRPieSeries series) {
        verifyHyperlink(series.getSectionHyperlink());
    }

    public void verify(JRHighLowDataset dataset) {
        verifyElementDataset(dataset);
        verifyHyperlink(dataset.getItemHyperlink());
    }

    public void verify(JRTimePeriodDataset dataset) {
        verifyElementDataset(dataset);

        JRTimePeriodSeries[] series = dataset.getSeries();
        if (series != null) {
            for (int i = 0; i < series.length; i++) {
                verify(series[i]);
            }
        }
    }

    protected void verify(JRTimePeriodSeries series) {
        verifyHyperlink(series.getItemHyperlink());
    }

    public void verify(JRTimeSeriesDataset dataset) {
        verifyElementDataset(dataset);

        JRTimeSeries[] series = dataset.getSeries();
        if (series != null) {
            for (int i = 0; i < series.length; i++) {
                verify(series[i]);
            }
        }
    }

    protected void verify(JRTimeSeries series) {
        verifyHyperlink(series.getItemHyperlink());
    }

    /**
     * Verify the design of a value dataset.  Since value dataset's only
     * contain a single value and do not support hyperlinks there is nothing
     * to verify.
     */
    public void verify(JRValueDataset dataset) {
    }

    public void verify(JRXyDataset dataset) {
        verifyElementDataset(dataset);

        JRXySeries[] series = dataset.getSeries();
        if (series != null) {
            for (int i = 0; i < series.length; i++) {
                verify(series[i]);
            }
        }
    }

    protected void verify(JRXySeries series) {
        verifyHyperlink(series.getItemHyperlink());
    }

    protected void verify(JRGanttSeries series) {
        verifyHyperlink(series.getItemHyperlink());
    }

    public void verify(JRXyzDataset dataset) {
        verifyElementDataset(dataset);

        JRXyzSeries[] series = dataset.getSeries();
        if (series != null) {
            for (int i = 0; i < series.length; i++) {
                verify(series[i]);
            }
        }
    }

    public void verify(JRGanttDataset dataset) {
        verifyElementDataset(dataset);

        JRGanttSeries[] series = dataset.getSeries();

        if (series != null) {
            for (int i = 0; i < series.length; i++) {
                verify(series[i]);
            }
        }
    }

    protected void verify(JRXyzSeries series) {
        verifyHyperlink(series.getItemHyperlink());
    }

    protected void verifyReportElement(JRElement element) {
        if (element.getWidth() < 0) {
            if (allowElementNegativeWidth) {
                if (log.isWarnEnabled()) {
                    log.warn("Element has negative width: " + element.getWidth());
                }
            } else {
                addBrokenRule("Element cannot have negative width.", element);
            }
        }

        if (element.getX() < 0 && !allowElementNegativeX(element)) {
            addBrokenRule("Element negative X " + element.getX() + " not allowed", element);
        }

        if (element.getY() < 0 && !allowElementNegativeY(element)) {
            addBrokenRule("Element negative Y " + element.getY() + " not allowed", element);
        }

        verifyProperyExpressions(element.getPropertyExpressions());
    }

    protected boolean allowElementNegativeX(JRElement element) {
        // default to report/global property
        boolean allow = allowElementNegativeX;
        if (element.hasProperties()) {
            JRPropertiesMap properties = element.getPropertiesMap();
            if (properties.containsProperty(PROPERTY_ALLOW_ELEMENT_NEGATIVE_X)) {
                // use element level property
                allow = JRPropertiesUtil.asBoolean(properties.getProperty(PROPERTY_ALLOW_ELEMENT_NEGATIVE_X));
            }
        }
        return allow;
    }

    protected boolean allowElementNegativeY(JRElement element) {
        // default to report/global property
        boolean allow = allowElementNegativeY;
        if (element.hasProperties()) {
            JRPropertiesMap properties = element.getPropertiesMap();
            if (properties.containsProperty(PROPERTY_ALLOW_ELEMENT_NEGATIVE_Y)) {
                // use element level property
                allow = JRPropertiesUtil.asBoolean(properties.getProperty(PROPERTY_ALLOW_ELEMENT_NEGATIVE_Y));
            }
        }
        return allow;
    }

    protected void verifyProperyExpressions(JRPropertyExpression[] propertyExpressions) {
        if (propertyExpressions != null) {
            for (int i = 0; i < propertyExpressions.length; i++) {
                verifyPropertyExpression(propertyExpressions[i]);
            }
        }
    }

    protected void verifyPropertyExpression(JRPropertyExpression propertyExpression) {
        String name = propertyExpression.getName();
        if (name == null) {
            addBrokenRule("Property name missing.", propertyExpression);
        }

        JRExpression expr = propertyExpression.getValueExpression();
        if (expr == null) {
            addBrokenRule("Property value expression missing.", propertyExpression);
        }
    }

    protected void verifyComponentElement(JRComponentElement element) {
        verifyReportElement(element);

        ComponentKey componentKey = element.getComponentKey();
        if (componentKey == null) {
            addBrokenRule("No component key set for component element", element);
        }

        Component component = element.getComponent();
        if (component == null) {
            addBrokenRule("No component set for component element", element);
        }

        if (componentKey != null && component != null) {
            ComponentCompiler compiler = ComponentsEnvironment.getInstance(jasperReportsContext)
                    .getManager(componentKey).getComponentCompiler(jasperReportsContext);
            pushCurrentComponentElement(element);
            try {
                compiler.verify(component, this);
            } finally {
                popCurrentComponentElement();
            }
        }
    }

    /**
     * Returns the component element which is currently verified, if any.
     *
     * <p>
     * This method can be used in the {@link ComponentCompiler#verify(Component, JRVerifier)}
     * method to get a handle of the wrapping componenet element.
     * </p>
     * 
     * @return the currently verified component element
     */
    public JRComponentElement getCurrentComponentElement() {
        if (currentComponentElementStack.isEmpty()) {
            return null;
        }
        return currentComponentElementStack.getFirst();
    }

    protected void pushCurrentComponentElement(JRComponentElement element) {
        currentComponentElementStack.addFirst(element);
    }

    protected void popCurrentComponentElement() {
        currentComponentElementStack.removeFirst();
    }

    protected void verifyGenericElement(JRGenericElement element) {
        verifyReportElement(element);

        if (element.getEvaluationTimeValue() == EvaluationTimeEnum.GROUP) {
            String groupName = element.getEvaluationGroupName();
            if (groupName == null) {
                addBrokenRule("Evaluation group not set for generic element", element);
            } else {
                if (!jasperDesign.getGroupsMap().containsKey(groupName)) {
                    addBrokenRule("Generic element evaluation group " + groupName + " not found in report",
                            element);
                }
            }
        }

        JRGenericElementType type = element.getGenericType();
        if (type == null) {
            addBrokenRule("No type set for generic element", element);
        } else {
            if (type.getNamespace() == null) {
                addBrokenRule("No namespace set for generic element type", type);
            }

            if (type.getName() == null) {
                addBrokenRule("No name set for generic element type", type);
            }
        }

        JRGenericElementParameter[] parameters = element.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            JRGenericElementParameter parameter = parameters[i];

            if (parameter.getName() == null) {
                addBrokenRule("No name set for generic element parameter", parameter);
            }
        }
    }

    private static int getBreakHeight(JRBand band) {
        int breakHeight = 0;

        if (band != null) {
            breakHeight = band.getHeight();
            JRElement[] elements = band.getElements();
            if (SplitTypeEnum.IMMEDIATE == band.getSplitTypeValue() && elements != null && elements.length > 0) {
                for (int i = 0; i < elements.length; i++) {
                    JRElement element = elements[i];
                    int bottom = element.getY() + element.getHeight();
                    breakHeight = bottom < breakHeight ? bottom : breakHeight;
                }
            }
        }

        return breakHeight;
    }

    public void verifyExpression(JRExpression expression, Object parent, String mandatoryMessage) {
        if (expression == null) {
            if (mandatoryMessage != null) {
                addBrokenRule(mandatoryMessage, parent);
            }
        }
    }

    public void verify(MultiAxisData data) {
        // TODO lucianc 
    }

    protected void verifyPart(JRPart part) {
        PartEvaluationTime evaluationTime = part.getEvaluationTime();
        if (evaluationTime != null && evaluationTime.getEvaluationTimeType() == PartEvaluationTimeType.GROUP) {
            String evaluationGroup = evaluationTime.getEvaluationGroup();
            if (evaluationGroup == null) {
                addBrokenRule("Evaluation group not set for part", part);
            } else {
                Map<String, JRGroup> groups = jasperDesign.getGroupsMap();
                if (!groups.containsKey(evaluationGroup)) {
                    addBrokenRule("Part evaluation group \"" + evaluationGroup + "\" not found in report", part);
                }
            }
        }

        ComponentKey componentKey = part.getComponentKey();
        if (componentKey == null) {
            addBrokenRule("No component key set for part", part);
        }

        PartComponent component = part.getComponent();
        if (component == null) {
            addBrokenRule("No component set for part", part);
        }

        if (componentKey != null && component != null) {
            PartComponentManager manager = PartComponentsEnvironment.getInstance(jasperReportsContext)
                    .getManager(componentKey);
            if (manager == null) {
                addBrokenRule("No component manager found for part component \"" + componentKey.getName() + "\"",
                        part);
            } else {
                PartComponentCompiler compiler = manager.getComponentCompiler(jasperReportsContext);
                compiler.verify(component, this);
            }
        }
    }

    public void pushSubdatasetContext(String subdatasetName) {
        datasetContextStack.addFirst(subdatasetName);
    }

    public void popSubdatasetContext() {
        datasetContextStack.removeFirst();
    }

    protected JRDesignDataset currentDataset() {
        String subdatasetName = datasetContextStack.isEmpty() ? null : datasetContextStack.getFirst();
        JRDesignDataset dataset = subdatasetName == null ? jasperDesign.getMainDesignDataset()
                : (JRDesignDataset) jasperDesign.getDatasetMap().get(subdatasetName);
        return dataset;
    }

}