gov.nih.nci.rembrandt.web.helper.ReportGeneratorHelper.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.rembrandt.web.helper.ReportGeneratorHelper.java

Source

/*L
 * Copyright (c) 2006 SAIC, SAIC-F.
 *
 * Distributed under the OSI-approved BSD 3-Clause License.
 * See http://ncip.github.com/rembrandt/LICENSE.txt for details.
 */

package gov.nih.nci.rembrandt.web.helper;

import gov.nih.nci.caintegrator.dto.critieria.SampleCriteria;
import gov.nih.nci.caintegrator.dto.de.SampleIDDE;
import gov.nih.nci.caintegrator.dto.query.OperatorType;
import gov.nih.nci.caintegrator.dto.view.View;
import gov.nih.nci.caintegrator.dto.view.Viewable;
import gov.nih.nci.caintegrator.service.findings.ClassComparisonFinding;
import gov.nih.nci.caintegrator.service.findings.FTestFinding;
import gov.nih.nci.caintegrator.service.findings.Finding;
import gov.nih.nci.rembrandt.cache.RembrandtContextListener;
import gov.nih.nci.rembrandt.cache.RembrandtPresentationTierCache;
import gov.nih.nci.rembrandt.dto.lookup.LookupManager;
import gov.nih.nci.rembrandt.dto.query.CompoundQuery;
import gov.nih.nci.rembrandt.dto.query.Queriable;
import gov.nih.nci.rembrandt.dto.query.Query;
import gov.nih.nci.rembrandt.queryservice.ResultsetManager;
import gov.nih.nci.rembrandt.queryservice.resultset.DimensionalViewContainer;
import gov.nih.nci.rembrandt.queryservice.resultset.Resultant;
import gov.nih.nci.rembrandt.queryservice.resultset.ResultsContainer;
import gov.nih.nci.rembrandt.queryservice.resultset.sample.SampleResultset;
import gov.nih.nci.rembrandt.queryservice.resultset.sample.SampleViewResultsContainer;
import gov.nih.nci.rembrandt.queryservice.validation.DataValidator;
import gov.nih.nci.rembrandt.util.ApplicationContext;
import gov.nih.nci.rembrandt.util.MoreStringUtils;
import gov.nih.nci.rembrandt.util.RembrandtConstants;
import gov.nih.nci.rembrandt.web.bean.FindingReportBean;
import gov.nih.nci.rembrandt.web.bean.ReportBean;
import gov.nih.nci.rembrandt.web.factory.ApplicationFactory;
import gov.nih.nci.rembrandt.web.xml.ClassComparisonReport;
import gov.nih.nci.rembrandt.web.xml.FTestReport;
import gov.nih.nci.rembrandt.web.xml.ReportGenerator;
import gov.nih.nci.rembrandt.web.xml.ReportGeneratorFactory;
import gov.nih.nci.rembrandt.web.xml.Transformer;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

import javax.naming.OperationNotSupportedException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspWriter;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.io.HTMLWriter;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

/**
 * The ReportGeneratorHelper was written to act as a Report Generation manager
 * for the UI. It provides a single avenue where, if a UI element has a Query to
 * execute or the cache key to a previously stored Resultant, the necesary calls
 * are made to generate an XML document representing the report. The generated
 * XML will then be stored in a ReportBean that will also contain the cache key
 * where the resultant can be called again, if needed.
 * 
 * @author BauerD, LandyR 
 * Feb 8, 2005
 * 
 */

/**
* caIntegrator License
* 
* Copyright 2001-2005 Science Applications International Corporation ("SAIC"). 
* The software subject to this notice and license includes both human readable source code form and machine readable, 
* binary, object code form ("the caIntegrator Software"). The caIntegrator Software was developed in conjunction with 
* the National Cancer Institute ("NCI") by NCI employees and employees of SAIC. 
* To the extent government employees are authors, any rights in such works shall be subject to Title 17 of the United States
* Code, section 105. 
* This caIntegrator Software License (the "License") is between NCI and You. "You (or "Your") shall mean a person or an 
* entity, and all other entities that control, are controlled by, or are under common control with the entity. "Control" 
* for purposes of this definition means (i) the direct or indirect power to cause the direction or management of such entity,
*  whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) 
* beneficial ownership of such entity. 
* This License is granted provided that You agree to the conditions described below. NCI grants You a non-exclusive, 
* worldwide, perpetual, fully-paid-up, no-charge, irrevocable, transferable and royalty-free right and license in its rights 
* in the caIntegrator Software to (i) use, install, access, operate, execute, copy, modify, translate, market, publicly 
* display, publicly perform, and prepare derivative works of the caIntegrator Software; (ii) distribute and have distributed 
* to and by third parties the caIntegrator Software and any modifications and derivative works thereof; 
* and (iii) sublicense the foregoing rights set out in (i) and (ii) to third parties, including the right to license such 
* rights to further third parties. For sake of clarity, and not by way of limitation, NCI shall have no right of accounting
* or right of payment from You or Your sublicensees for the rights granted under this License. This License is granted at no
* charge to You. 
* 1. Your redistributions of the source code for the Software must retain the above copyright notice, this list of conditions
*    and the disclaimer and limitation of liability of Article 6, below. Your redistributions in object code form must reproduce 
*    the above copyright notice, this list of conditions and the disclaimer of Article 6 in the documentation and/or other materials
*    provided with the distribution, if any. 
* 2. Your end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This 
*    product includes software developed by SAIC and the National Cancer Institute." If You do not include such end-user 
*    documentation, You shall include this acknowledgment in the Software itself, wherever such third-party acknowledgments 
*    normally appear.
* 3. You may not use the names "The National Cancer Institute", "NCI" "Science Applications International Corporation" and 
*    "SAIC" to endorse or promote products derived from this Software. This License does not authorize You to use any 
*    trademarks, service marks, trade names, logos or product names of either NCI or SAIC, except as required to comply with
*    the terms of this License. 
* 4. For sake of clarity, and not by way of limitation, You may incorporate this Software into Your proprietary programs and 
*    into any third party proprietary programs. However, if You incorporate the Software into third party proprietary 
*    programs, You agree that You are solely responsible for obtaining any permission from such third parties required to 
*    incorporate the Software into such third party proprietary programs and for informing Your sublicensees, including 
*    without limitation Your end-users, of their obligation to secure any required permissions from such third parties 
*    before incorporating the Software into such third party proprietary software programs. In the event that You fail 
*    to obtain such permissions, You agree to indemnify NCI for any claims against NCI by such third parties, except to 
*    the extent prohibited by law, resulting from Your failure to obtain such permissions. 
* 5. For sake of clarity, and not by way of limitation, You may add Your own copyright statement to Your modifications and 
*    to the derivative works, and You may provide additional or different license terms and conditions in Your sublicenses 
*    of modifications of the Software, or any derivative works of the Software as a whole, provided Your use, reproduction, 
*    and distribution of the Work otherwise complies with the conditions stated in this License.
* 6. THIS SOFTWARE IS PROVIDED "AS IS," AND ANY EXPRESSED OR IMPLIED WARRANTIES, (INCLUDING, BUT NOT LIMITED TO, 
*    THE IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 
*    IN NO EVENT SHALL THE NATIONAL CANCER INSTITUTE, SAIC, OR THEIR AFFILIATES BE LIABLE FOR ANY DIRECT, INDIRECT, 
*    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
*    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
*    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
*    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* 
*/

public class ReportGeneratorHelper {
    //This is the general purpose Nautilus Logger
    private static Logger logger = Logger.getLogger(ReportGeneratorHelper.class);
    //this is the XML logger for the reports
    private static Logger xmlLogger;
    //This is the element that is used to store all relevant report data
    //it sores the resultant, the sessionId, the latest reportXML
    private ReportBean _reportBean;
    private CompoundQuery _cQuery;
    private String _queryName = null;
    private String _sessionId = null;
    private boolean newQueryName = false;
    private static Properties applicationResources = ApplicationContext.getLabelProperties();
    private RembrandtPresentationTierCache presentationTierCache = ApplicationFactory.getPresentationTierCache();
    //Check the applications resource file and turn on report xml logging if 
    //property nautilus.xml_logging = true, else then no xml report logging.
    private static boolean xmlLogging;
    static {
        String property = (String) applicationResources.get("nautilus.xml_logging");
        if ("true".equals(property)) {
            xmlLogging = true;
            //Get the XML_LOGGER specified in log4j.properties
            xmlLogger = Logger.getLogger("XML_LOGGER");
        } else {
            xmlLogging = false;
        }

    }

    /**
     * This constructor is intended to be used by the UI when it needs to apply
     * a filter on a previously run report that requires the regeneration of the
     * report XML.  At this time it is only a "Show All Values" and "Copy Number
     * Filter" report.
     * 
     * This means that there must be a previous query that has been executed and
     * stored in the cache that can retrieveed.  The ReportBean given should be
     * the bean for the report that was previously generated and the user would
     * like to filter.
     * 
     * @param reportBean
     * @param filterParams -- this is all the XML filterParam values 
     */
    public ReportGeneratorHelper(ReportBean reportBean, Map filterParams) {
        logger.debug("Calling ReportGeneratorHelper to filter resultant");
        Resultant oldResultant = reportBean.getResultant();
        Resultant newResultant;
        //Get the sessionId and name for this resultant, this works because,
        //at the present time there is only one type of query that is ever
        //run, and that is a CompoundQuery if that changes than it will be
        //necesary to modify the code here and many other places
        String sessionId = ((CompoundQuery) (reportBean.getAssociatedQuery())).getSessionId();
        String oldQueryName = ((CompoundQuery) (reportBean.getAssociatedQuery())).getQueryName();
        String newQueryName = "";
        String filter_element = (String) filterParams.get("filter_element");
        try {
            //Apply the copy_number filter
            if ("copy_number".equals(filter_element)) {
                logger.debug("Performing CopyNumber filter");
                //execute copy number filter query
                OperatorType operator = (OperatorType) filterParams.get("filter_value4");
                Integer consecutiveCalls = (Integer) filterParams.get("filter_value5");
                Integer percentCalls = (Integer) filterParams.get("filter_value6");
                logger.debug("Old QueryName: " + oldQueryName);
                newResultant = ResultsetManager.filterCopyNumber(oldResultant, consecutiveCalls, percentCalls,
                        operator);
                newQueryName = nameFilterQuery(oldQueryName, RembrandtConstants.FILTER_REPORT_SUFFIX);
                logger.debug("New QueryName: " + newQueryName);
            } else {
                logger.debug("Performing a Show All Values Query");
                /*
                 * This is a hack...  we need to make sure that this isn't
                 * something other than a show all values query
                 */
                logger.debug("Old QueryName: " + oldQueryName);
                newResultant = ResultsetManager.executeShowAllQuery(oldResultant);
                newQueryName = nameFilterQuery(oldQueryName, RembrandtConstants.SHOW_ALL_VALUES_SUFFIX);
                logger.debug("New QueryName: " + newQueryName);
            }
            /*
             * We have to rename the resultant using the new query name with the
             * added suffix
             */
            if (newResultant != null) {
                logger.debug("Setting newQueryName in resultant's associated query");
                newResultant.getAssociatedQuery().setQueryName(newQueryName);
            }
            /*
             * At present the executeShowAllQuery(Resultant) method does not 
             * pass through the sessionId or the queryName, in the associated
             * Query in the showAllResults. So it is necesary to get the query
             * and set the sessionId and the queryName.  
             */
            CompoundQuery query = (CompoundQuery) reportBean.getAssociatedQuery();
            //check the associated query to make sure that it is a compound query
            //also sets the _cQuery attribute

            checkCompoundQuery(query);
            //check the sessionId that it isn't null or empty and set _sessionId in _cQuery
            checkSessionId(sessionId);
            //check that we have an old QueryName and set the class variable _queryName,
            //adding that it is a all values report
            checkQueryName(newQueryName);
            //         create a new ReportBean
            _reportBean = new ReportBean();
            //store the annotated query in the reportBean
            _reportBean.setAssociatedQuery(_cQuery);
            //store the results into the report bean
            _reportBean.setResultant(newResultant);
            //store the cache key that can be used to retrieve this bean later
            _reportBean.setResultantCacheKey(_queryName);
            //send the filterParamMap for processing and store in _reportBean
            _reportBean.setFilterParams(processFilterParamMap(filterParams));
            //generate the reportXML and store in the ReportBean
            generateReportXML();
            //drop this ReportBean in the session cache, use the _queryName as the 
            //parameter
            presentationTierCache.addNonPersistableToSessionCache(_sessionId, _queryName, _reportBean);

        } catch (Exception e) {
            logger.error("Exception when trying to generate a Show All Values Report");

            logger.error(e);
        }
        //All done...
    }

    /**
     * This is intended to be used to generate a ReportBean when you have a
     * query and want to reduce the result set by limiting the results to 
     * sample ids listed in the String[] sampleIds. As it is currently implemented
     * it does not check the cache for any result sets, because at present, we
     * execute the query all over again.  
     * 
     * In the future if performance is important we may consider just taking the
     * already existing result set and just extract the relevant sampleIds 
     * 
     * @param query --currently a CompoundQuery that you are selecting sample
     * ids from. No other Queriable object will actually work.  I know,I know
     * bad design. But this is to allow for the possibility of other types 
     * in the future.  We may later decide to constrain this to a CompoundQuery
     * but for now... well it is just going to have to be the way it is.
     * @param sampleIds --this is the array of sample ids that you would like to
     * contstrain
     * @throws Exception
     */
    public ReportGeneratorHelper(Queriable query, String[] sampleIds, boolean newQueryName)
            throws IllegalStateException {
        this.newQueryName = newQueryName;
        //Clone the original query so that we do not modify it when we add samples to it
        query = (CompoundQuery) query.clone();
        //check the query to make sure that it is a compound query
        checkCompoundQuery(query);
        //check to make sure that we have a sessionId
        checkSessionId(_cQuery.getSessionId());
        //check that we have a queryName
        checkQueryName(_cQuery.getQueryName());
        //create a new ReportBean
        _reportBean = new ReportBean();
        Resultant sampleIdResults = null;
        if (sampleIds != null) {
            Set<String> samples = new HashSet<String>(Arrays.asList(sampleIds));
            //get the samples associated with these specimens
            List<String> sampleIDList = LookupManager.getSampleIDs(samples);
            //Add back any samples that were just sampleIds to start with
            if (sampleIDList != null) {
                samples.addAll(sampleIDList);
            }

            //get the specimenNames associated with these samples
            List<String> specimenNames = LookupManager.getSpecimenNames(samples);
            if (specimenNames != null) {
                samples.addAll(specimenNames);
            }

            sampleIds = samples.toArray(new String[0]);
            try {
                if (_cQuery.isAllGenesQuery()) {
                    //Check for specimen_names for all genes Query
                    Collection<String> specimans;
                    specimans = DataValidator.validateSpecimanNames(samples);
                    List<String> list = new ArrayList<String>();
                    list.addAll(specimans);
                    sampleIds = list.toArray(new String[0]);
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        try {
            sampleIdResults = ResultsetManager.executeCompoundQuery(_cQuery, sampleIds);
        } catch (Exception e) {
            logger.error("The ResultsetManager threw some exception");
            logger.error(e);
        }
        /*
         * Make sure we got a result set back and then store all the information
         * in the bean.
         */
        if (sampleIdResults != null && sampleIdResults.getResultsContainer() != null) {
            if (newQueryName)
                sampleIdResults.getAssociatedQuery().setQueryName("previewResultsBySample");
            else
                sampleIdResults.getAssociatedQuery().setQueryName(_cQuery.getQueryName());
            //store the results and query into the report bean
            _reportBean.setAssociatedQuery(sampleIdResults.getAssociatedQuery());
            _reportBean.setResultant(sampleIdResults);
        } else {
            throw new IllegalStateException("ResultsetManager returned an empty resultant or threw an Exception");
        }
        //store the cache key that can be used to retrieve this bean later
        if (newQueryName) {//it's a new report bean
            _reportBean.setResultantCacheKey("previewResultsBySample");
            //presentationTierCache.addNonPersistableToSessionCache(_sessionId, "previewResultsBySample", _reportBean);
        } else
            _reportBean.setResultantCacheKey(_queryName);
        //this is a result set
        _reportBean.setSampleSetQuery(true);
        //generate the reportXML and store in the ReportBean
        generateReportXML();
    }

    /**
     * This constructor uses a TemplateMethod pattern to perform the following
     * tasks (They must happen in this order, though some may be omitted if the
     * use case does not require it): 
     *       1-Check to see if the query is a CompoundQuery and cast if is
     *       2-Check that there is a sessionId specified for the query
     *       3-Check that we have a query name for the CompoundQuery
     *       4-Check the SessionCache for any stored results for this query
     *      to avoid running to the database if not needed.
     *      5-Execute the query if there is no result set to use in the cache
     *      6-Generate the report xml based on the desired view
     *             
     * @param query --the query that you want some new view (Report) of
     */
    public ReportGeneratorHelper(Queriable query, Map filterParams) throws Exception {
        //      try {

        //check the query to make sure that it is a compound query
        checkCompoundQuery(query);
        //check to make sure that we have a sessionId
        checkSessionId(_cQuery.getSessionId());
        //check that we have a queryName
        checkQueryName(_cQuery.getQueryName());
        //System.out.println(_cQuery.toString());
        /*
         * If the _reportBean is null then we know that we could not
         * find an appropriate result set in the session cache. So
         * lets run a query and get a result. Always run a query
         * if it is a preview report.
         * 
         */
        if (_reportBean == null || RembrandtConstants.PREVIEW_RESULTS.equals(_queryName)) {
            logger.debug("Executing Query");
            if (RembrandtConstants.PREVIEW_RESULTS.equals(_queryName)) {
                //put this in the map for preview
                filterParams.put("showSampleSelect", new String("false"));
            }
            executeQuery();
        }
        if (_reportBean != null) {//if its a valid query
            _reportBean.setFilterParams(processFilterParamMap(filterParams));
            this.generateReportXML();
        }
        /*      }catch(Exception e) {
                 logger.error("Unable to create the ReportBean");
                 logger.error(e);
                  
                    
              } */
    }

    public ReportGeneratorHelper(Finding finding) {

    }

    /**
     * Checks the Queriable object and determines if it is a CompoundQuery.  If
     * so then set the class variable _cQuery = (CompoundQuery)query else
     * throw a new UnsupportedOperationException as we can not currently handle 
     * anything but CompoundQueries at this time.  I know that it doesn't make 
     * much sense to only allow a single implementation of the Queriable Interface
     * to really be passed when we specify in the signature that any Queriable
     * object will do.  It is just that in order to avoid tons of casting and
     * maintain flexibility for later implementations I did it this way.
     *  
     * @param query
     * @throws UnsupportedOperationException
     */
    private void checkCompoundQuery(Queriable query) throws UnsupportedOperationException {
        if (query != null && query instanceof CompoundQuery) {
            //sets the class variable _cQuery 
            _cQuery = (CompoundQuery) query;
        } else {
            /*
             * Some other object implementing the Queriable interface has been
             * passed. Currently we only support the compound query. But that is
             * not to say that we won't later support others. Hence, the if-else
             * statement here. I'll bet we want the run report button to use
             * this class so I better add some code here if we just get a query.
             */
            logger.error("Non compound query submitted");
            throw new UnsupportedOperationException("You must pass a CompoundQuery at this time");
        }
    }

    /**
     * Check the sessionId in the Queriable object that we have gotten
     * @param sessionId
     * @throws IllegalStateException
     */
    private void checkSessionId(String sessionId) throws IllegalStateException {
        if (sessionId != null && !sessionId.equals("")) {
            _sessionId = sessionId;
            _cQuery.setSessionId(sessionId);
        } else {
            /*
             * We can not store the resultSet without a unique cache to
             * store it in. And since we are currently implementing a
             * session based cache system, that unique cache id should
             * be the session id. If there is no session id, than we can
             * assume that something is really wrong so throw a new
             * IllegalStateException
             */
            logger.error("sessionId is empty for the compoundQuery");
            throw new IllegalStateException("There does not appear to be a session associated with this query");
        }
    }

    /**
     * make sure that we have a name for our report
     * @param queryName
     */
    private void checkQueryName(String queryName) {
        if (!"".equals(queryName) && queryName != null) {
            _queryName = queryName;
        } else {
            /*
             * If the query name is empty than we can assume that
             * the user has no interest in storing the query for
             * later use. However the user may still want to use the
             * current query report in other ways, like changing the
             * view or downloading it. SO we should probably keep it
             * around for a little bit. We do this by giving it a
             * fixed temp name to use as a session cache key.
             * 
             */
            _queryName = presentationTierCache.getTempReportName(_sessionId);

        }
        _cQuery.setQueryName(_queryName);
    }

    /**
     * check the session cache for the results sets for the query
     * @param view
     */
    private void checkCache(View view) {
        /*
         * Use the sessionId to get the cache and see if 
         * a result set already exists for the queryName we have been 
         * given.
         */
        _reportBean = presentationTierCache.getReportBean(_sessionId, _queryName, view);
    }

    /**
     * This method will take the class variable ReportBean _reportBean and 
     * create the correct XML representation for the desired view of the results.  When
     * completed it adds the XML to the _reportBean and drops the _reportBean
     * into the sessionCache for later retrieval when needed
     * 
     * @throws IllegalStateException this is thrown when there is no resultant
     * found in _reportBean
     */
    private void generateReportXML() throws IllegalStateException {

        /*
         * Get the correct report XML generator for the desired view
         * of the results.
         * 
         */
        Document reportXML = null;
        if (_reportBean != null) {
            /*
             * We need to check the resultant to make sure that
             * the database has actually return something for the associated
             * query or filter.
             */
            Resultant resultant = _reportBean.getResultant();
            if (resultant != null) {
                try {
                    Viewable oldView = resultant.getAssociatedView();
                    //make sure old view is not null, if so, set it to the same as new view.
                    if (oldView == null) {
                        oldView = _cQuery.getAssociatedView();
                    }
                    Viewable newView = _cQuery.getAssociatedView();
                    Map filterParams = _reportBean.getFilterParams();
                    /*
                     * Make sure that we change the view on the resultSet
                     * if the user changes the desired view from already
                     * stored view of the results
                     */
                    if (!oldView.equals(newView) || _reportBean.getReportXML() == null) {
                        //we must generate a new XML document for the
                        //view they want
                        logger.debug("Generating XML");
                        ReportGenerator reportGen = ReportGeneratorFactory.getReportGenerator(newView);
                        resultant.setAssociatedView(newView);
                        reportXML = reportGen.getReportXML(resultant, filterParams);
                        logger.debug("Completed Generating XML");
                    } else {
                        //Old view is the current view
                        logger.debug("Fetching report XML from reportBean");
                        reportXML = _reportBean.getReportXML();
                    }
                } catch (NullPointerException npe) {
                    logger.error("The resultant has a null value for something that was needed");
                    logger.error(npe);
                }
            }

            //XML Report Logging
            if (xmlLogging) {
                try {
                    StringWriter out = new StringWriter();
                    OutputFormat outformat = OutputFormat.createPrettyPrint();
                    XMLWriter writer = new XMLWriter(out, outformat);
                    writer.write(reportXML);
                    writer.flush();
                    xmlLogger.debug(out.getBuffer());
                } catch (IOException ioe) {
                    logger.error("There was an error writing the XML to log");
                    logger.error(ioe);
                } catch (NullPointerException npe) {
                    logger.debug("There is no XML to log!");
                }
            }
            _reportBean.setReportXML(reportXML);
            if (newQueryName) {//it's a new report bean
                presentationTierCache.addNonPersistableToSessionCache(_sessionId, "previewResultsBySample",
                        _reportBean);
            } else
                presentationTierCache.addNonPersistableToSessionCache(_sessionId, _queryName, _reportBean);

        } else {
            throw new IllegalStateException("There is no resultant to create report");
        }
    }

    public static void generateReportXML(Finding finding) {
        //TODO: shouldnt be called until findings are populated (!= running)
        //TODO: is threadsafe?
        //TODO: instance of
        Document xmlDocument = null;
        if (finding instanceof ClassComparisonFinding) {
            xmlDocument = ClassComparisonReport.getReportXML(finding, new HashMap());
        } else if (finding instanceof FTestFinding) {
            xmlDocument = FTestReport.getReportXML(finding, new HashMap());
        }
        FindingReportBean frb = new FindingReportBean();
        frb.setFinding(finding);
        frb.setXmlDoc(xmlDocument);
        //TODO: check cache for collision - second param is key
        ApplicationFactory.getPresentationTierCache().addNonPersistableToSessionCache(finding.getSessionId(),
                finding.getTaskId(), frb);
    }

    /**
     * This executes the current Compound Query that is referenced in the _cQuery
     * class scope variable.  Stores this _cQuery in the ReportBean as the
     * associatedQuery
     * @throws Exception
     */
    private void executeQuery() throws Exception {
        //empty the cache before executing the query again
        presentationTierCache.addNonPersistableToSessionCache(_sessionId, _queryName, null);

        if (_cQuery != null) {
            Resultant resultant = ResultsetManager.executeCompoundQuery(_cQuery);
            /*
             * Create the _reportBean that will store everything we 
             * may need later when messing with the reports associated
             * with this result set.  This _reportBean will also 
             * be stored in the cache.
             */
            if (resultant != null) {
                _reportBean = new ReportBean();
                _reportBean.setAssociatedQuery(_cQuery);
                _reportBean.setResultant(resultant);
                //The cache key will always be the compound query name
                _reportBean.setResultantCacheKey(_cQuery.getQueryName());
            } else {
                logger.debug("resultant is Null.  no results returned!");
                //_reportBean = null;
                _reportBean = new ReportBean();
                _reportBean.setAssociatedQuery(_cQuery);
                //_reportBean.setResultant(resultant);
                //The cache key will always be the compound query name
                _reportBean.setResultantCacheKey(_cQuery.getQueryName());
            }
        } else {
            logger.error("Compound Query is Null.  Can not execute!");
            throw new NullPointerException("CompoundQuery is null.");
        }
    }

    /**
     * Returns the ReportBean that was generated by the constructor
     * @return --constructed ReportBean
     */
    public ReportBean getReportBean() {
        return _reportBean;
    }

    public static void renderReport(HttpServletRequest request, Document reportXML, String xsltFilename,
            JspWriter out) {
        renderReport(request, reportXML, xsltFilename, out, "false");
    }

    /**
     * This static method will render a query report of the passed reportXML, in
     * HTML using the XSLT whose name has been passed, to the jsp whose 
     * jspWriter has been passed. The request is used to acquire any filter 
     * params that may have been added to the request and that may be applicable
     * to the XSLT.
     *  
     * @param request --the request that will contain any parameters you want applied
     * to the XSLT that you specify
     * @param reportXML -- this is the XML that you want transformed to HTML
     * @param xsltFilename --this the XSLT that you want to use
     * @param out --the JSPWriter you want the transformed document to go to...
     */
    public static void renderReport(HttpServletRequest request, Document reportXML, String xsltFilename,
            JspWriter out, String isIGV) {
        File styleSheet = new File(RembrandtContextListener.getContextPath() + "/XSL/" + xsltFilename);
        // load the transformer using JAX
        logger.debug("Applying XSLT " + xsltFilename);

        Transformer transformer;
        try {

            // if the xsltFiname is pathway xlst, then do this
            if ((xsltFilename.equals(RembrandtConstants.DEFAULT_PATHWAY_XSLT_FILENAME))
                    || (xsltFilename.equals(RembrandtConstants.DEFAULT_PATHWAY_DESC_XSLT_FILENAME))
                    || (xsltFilename.equals(RembrandtConstants.DEFAULT_GENE_XSLT_FILENAME))) {
                transformer = new Transformer(styleSheet);
            }
            // otherwise do this

            else {
                transformer = new Transformer(styleSheet,
                        (HashMap) request.getAttribute(RembrandtConstants.FILTER_PARAM_MAP));
            }

            Document transformedDoc = transformer.transform(reportXML);

            /*
             * right now this assumes that we will only have one XSL for CSV
             * and it checks for that as we do not want to "pretty print" the CSV, we
             * only want to spit it out as a string, or formatting gets messed up
             * we will of course want to pretty print the XHTML for the graphical reports
             * later we can change this to handle mult XSL CSVs
             * RCL
             */
            if (!xsltFilename.equals(RembrandtConstants.DEFAULT_XSLT_CSV_FILENAME)) {
                OutputFormat format = OutputFormat.createPrettyPrint();
                XMLWriter writer;
                writer = new XMLWriter(out, format);
                writer.write(transformedDoc);
                if (!xsltFilename.equals(RembrandtConstants.DEFAULT_PATHWAY_DESC_XSLT_FILENAME)) {
                    //writer.close();
                }
            } else {
                String csv = transformedDoc.getStringValue();
                csv.trim();
                if (isIGV != null && isIGV.equals("true")) {
                    csv = csv.replaceAll(",\\s+", "\t");
                }
                out.println(csv);
            }
        } catch (UnsupportedEncodingException uee) {
            logger.error("UnsupportedEncodingException");
            logger.error(uee);
        } catch (IOException ioe) {
            logger.error("IOException");
            logger.error(ioe);
        }
    }

    /**
     * Method to compose gene symbol report for bioDBNet pathways
     * @param host
     * @param db
     * @param pathwayName
     * @return 
     */
    public static String composeGeneReportUrl(String host, String db, String pathwayName) {
        String urlStr = host + "/biodbnetRestApi.xml?input=" + db + "pathwayname&inputValues=";
        //logger.debug("Params: " + db + " | " + pathwayName);

        //'+', '#' and '&' are the ones that cause problem without specific encoding
        //URLEncoder doesn't work for these.
        String pathwayEncoded = pathwayName.replace("+", "%2B").replace("#", "%23").replace("&", "%26");
        urlStr += pathwayEncoded + "&outputs=geneinfo," + db;

        if (db.equalsIgnoreCase("ncipid"))
            urlStr += "pathwaytitle&taxonId=9606";
        else
            urlStr += "pathwaydescription&taxonId=9606";

        return urlStr;
    }

    /**
     * Render report with paging params
     * @param request
     * @param reportXMLParam
     * @param xsltFilename
     * @param out
     * @param params
     */
    public static void renderReportWithParams(HttpServletRequest request, Document reportXMLParam,
            String xsltFilename, JspWriter out, HashMap<String, String> params) {
        File styleSheet = new File(RembrandtContextListener.getContextPath() + "/XSL/" + xsltFilename);
        // load the transformer using JAX
        logger.debug("Applying XSLT with paging " + xsltFilename);
        Transformer transformer;
        Document reportXML;
        try {
            if (reportXMLParam != null)
                reportXML = reportXMLParam;
            else
                reportXML = (Document) request.getSession()
                        .getAttribute(RembrandtConstants.SESSION_ATTR_PATHWAY_XML);

            transformer = new Transformer(styleSheet, params);
            Document transformedDoc = transformer.transform(reportXML);

            OutputFormat format = OutputFormat.createPrettyPrint();
            XMLWriter writer;
            writer = new XMLWriter(out, format);
            writer.write(transformedDoc);

        } catch (UnsupportedEncodingException uee) {
            logger.error("UnsupportedEncodingException");
            logger.error(uee);
        } catch (IOException ioe) {
            logger.error("IOException");
            logger.error(ioe);
        }
    }

    public static String renderReport(Map params, Document reportXML, String xsltFilename) {
        //used only for finding based AJAX

        File styleSheet = new File(RembrandtContextListener.getContextPath() + "/XSL/" + xsltFilename);
        // load the transformer using JAXP
        logger.debug("Applying XSLT " + xsltFilename);
        Transformer transformer;
        try {
            transformer = new Transformer(styleSheet, params);
            Document transformedDoc = transformer.transform(reportXML);

            /*
             * right now this assumes that we will only have one XSL for CSV
             * and it checks for that as we do not want to "pretty print" the CSV, we
             * only want to spit it out as a string, or formatting gets messed up
             * we will of course want to pretty print the XHTML for the graphical reports
             * later we can change this to handle mult XSL CSVs
             * RCL
             */

            if (!xsltFilename.equals(RembrandtConstants.DEFAULT_XSLT_CSV_FILENAME)) {

                StringBuffer sb = new StringBuffer();
                OutputFormat format = OutputFormat.createCompactFormat();
                StringWriter sw = new StringWriter(1024);
                HTMLWriter writer = new HTMLWriter(sw, format);
                writer.write(transformedDoc);
                sb = sw.getBuffer();
                return sb.toString();

            } else {
                String csv = transformedDoc.getStringValue();
                csv.trim();
                return csv;
            }
        } catch (UnsupportedEncodingException uee) {
            logger.error("UnsupportedEncodingException");
            logger.error(uee);
        } catch (IOException ioe) {
            logger.error("IOException");
            logger.error(ioe);
        }

        return "Reporting Error";
    }

    /**
     * 
     * @param container --this is the container that you want the sample ids from
     * @return --A SampleCriteria of all the ResultsContainer sampleIds
     */
    public static SampleCriteria extractSampleIds(ResultsContainer container)
            throws OperationNotSupportedException {
        SampleCriteria theCriteria = new SampleCriteria();
        Collection sampleIds = new ArrayList();
        SampleViewResultsContainer svrContainer = null;
        /*
         * These are currently the only two results containers that we have to
         * worry about at this time, I believe.
         */
        if (container instanceof DimensionalViewContainer) {
            //Get the SampleViewResultsContainer from the DimensionalViewContainer
            DimensionalViewContainer dvContainer = (DimensionalViewContainer) container;
            svrContainer = dvContainer.getSampleViewResultsContainer();
        } else if (container instanceof SampleViewResultsContainer) {
            svrContainer = (SampleViewResultsContainer) container;
        }
        //Handle the SampleViewResultsContainers if that is what we got
        if (svrContainer != null) {
            Collection<SampleResultset> bioSpecimen = svrContainer.getSampleResultsets();
            for (Iterator i = bioSpecimen.iterator(); i.hasNext();) {
                SampleResultset sampleResultset = (SampleResultset) i.next();
                sampleIds.add(new SampleIDDE(sampleResultset.getSampleIDDE().getValueObject()));
            }
        } else {
            throw new OperationNotSupportedException(
                    "We are not able to able to extract SampleIds from: " + container.getClass());
        }
        //Drop the sampleIds into the SampleCriteria
        theCriteria.setSampleIDs(sampleIds);
        return theCriteria;
    }

    /**
     * will recurse through the compound query adding the SampleCriteria to each
     * of the associated queries.
     *  
     * @param query
     * @param sampleCriteria
     */
    public static Queriable addSampleCriteriaToCompoundQuery(Queriable query, SampleCriteria sampleCriteria,
            String resultSetName) {
        if (query != null) {
            if (query instanceof CompoundQuery) {
                CompoundQuery newQuery = (CompoundQuery) query;
                try {
                    newQuery.setLeftQuery(addSampleCriteriaToCompoundQuery(newQuery.getLeftQuery(), sampleCriteria,
                            resultSetName));
                    newQuery.setRightQuery(addSampleCriteriaToCompoundQuery(newQuery.getRightQuery(),
                            sampleCriteria, resultSetName));
                } catch (Exception e) {
                    logger.error(e);
                }
            } else {
                Query newQuery = (Query) query;
                newQuery.setQueryName("(" + newQuery.getQueryName() + " AND " + resultSetName + ")");
                newQuery.setSampleIDCrit(sampleCriteria);
                return newQuery;
            }

        }
        return query;
    }

    /**
     * This is used to take the RefineQueryForm "filter_string" and create a 
     * list from the comma seperated tokens contained in the string. It also
     * removes any unacceptable characters and creates an acceptable string...
     * In truth it is utilizing the MoreStringUtils class to clean the strings.
     * @param filterParams
     */
    private Map processFilterParamMap(Map filterParams) {
        String test = "";
        List tokens = null;
        //String unallowableCharacters = " <>!:\"@#\\$%^&*()-+=/{}[]|?~`";
        String unallowableCharacters = " <>!\"@#\\$%^&*()+=/{}[]|?~`";
        if (filterParams != null && filterParams.containsKey("filter_string")) {
            //tokenize the string
            StringTokenizer tokenizer = new StringTokenizer((String) filterParams.get("filter_string"), ",", false);
            tokens = new ArrayList();
            while (tokenizer.hasMoreTokens()) {
                String cleanToken = MoreStringUtils.cleanString(unallowableCharacters, tokenizer.nextToken());
                cleanToken = cleanToken.toUpperCase();
                tokens.add(cleanToken);
            }
            filterParams.put("filter_string", tokens);
        }
        return filterParams;

    }

    /**
     * This will verify that the desired suffix is not already in the report
     * @param oldQueryName
     * @param suffix
     * @return
     */
    private String nameFilterQuery(String oldQueryName, String suffix) {
        String newQueryName;
        if (oldQueryName.lastIndexOf(suffix) < 0) {
            newQueryName = oldQueryName + suffix;
        } else {
            newQueryName = oldQueryName;
        }
        return newQueryName;
    }

}