<commit_before>/*
 * Copyright 2008-2010 Microarray Informatics Team, EMBL-European Bioinformatics Institute
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 * For further details of the Gene Expression Atlas project, including source code,
 * downloads and documentation, please see:
 *
 * http://gxa.github.com/gxa
 */

package ae3.service;

import ae3.dao.AtlasSolrDAO;
import ae3.model.*;
import ae3.util.HtmlHelper;
import ae3.service.structuredquery.UpdownCounter;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import uk.ac.ebi.gxa.dao.AtlasDAO;
import uk.ac.ebi.gxa.index.GeneExpressionAnalyticsTable;
import uk.ac.ebi.gxa.properties.AtlasProperties;
import uk.ac.ebi.gxa.utils.EfvTree;
import uk.ac.ebi.microarray.atlas.model.ExpressionAnalysis;
import uk.ac.ebi.mydas.controller.CacheManager;
import uk.ac.ebi.mydas.controller.DataSourceConfiguration;
import uk.ac.ebi.mydas.datasource.AnnotationDataSource;
import uk.ac.ebi.mydas.exceptions.BadReferenceObjectException;
import uk.ac.ebi.mydas.exceptions.DataSourceException;
import uk.ac.ebi.mydas.exceptions.UnimplementedFeatureException;
import uk.ac.ebi.mydas.model.*;
import org.apache.commons.lang.StringUtils;

import javax.servlet.ServletContext;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

/**
 * Created Using IntelliJ IDEA. Date: 18-Jul-2007 Time: 16:51:37
 *
 * @author <NAME>, EMBL-EBI, <EMAIL>
 *         <p/>
 *         NOTE TO DATA SOURCE DEVELOPERS:
 *         <p/>
 *         This template is based upon the AnnotationDataSource interface, there are however three other interfaces
 *         available that may be more appropriate for your needs, described here:
 *         <p/>
 *         <a href="http://code.google.com/p/mydas/wiki/HOWTO_WritePluginIntro"> Writing a MyDas Data Source - Selecting
 *         the Best Inteface </a>
 */

public class GxaS4DasDataSource implements AnnotationDataSource {

    private static AtlasDAO atlasDao;
    public void setDao(AtlasDAO atlasDao){
        GxaS4DasDataSource.atlasDao = atlasDao;
    }

    CacheManager cacheManager = null;
    ServletContext svCon;
    Map<String, String> globalParameters;
    DataSourceConfiguration config;
    AtlasSolrDAO atlasSolrDAO;
    AtlasProperties atlasProperties;

    protected String getDasBaseUrl(){
        return atlasProperties.getProperty("atlas.dasbase");
    }


    /**
     * This method is called by the MydasServlet class at Servlet initialisation.
     * <p/>
     * The AnnotationDataSource is passed the servletContext, a handle to globalParameters in the form of a Map
     * &lt;String, String&gt; and a DataSourceConfiguration object.
     * <p/>
     * The latter two parameters contain all of the pertinent information in the ServerConfig.xml file relating to the
     * server as a whole and specifically to this data source.  This mechanism allows the datasource author to set up
     * required configuration in one place, including AnnotationDataSource specific configuration.
     * <p/>
     * <bold>It is highly desirable for the implementation to test itself in this init method and throw a
     * DataSourceException if it fails, e.g. to attempt to get a Connection to a database and read a record.</bold>
     *
     * @param servletContext   being the ServletContext of the servlet container that the Mydas servlet is running in.
     * @param globalParameters being a Map &lt;String, String&gt; of keys and values as defined in the ServerConfig.xml
     *                         file.
     * @param dataSourceConfig containing the pertinent information frmo the ServerConfig.xml file for this datasource,
     *                         including (optionally) a Map of datasource specific configuration.
     * @throws uk.ac.ebi.mydas.exceptions.DataSourceException
     *          should be thrown if there is any fatal problem with loading this data source.  <bold>It is highly
     *          desirable for the implementation to test itself in this init method and throw a DataSourceException if
     *          it fails, e.g. to attempt to get a Connection to a database and read a record.</bold>
     */
    public void init(ServletContext servletContext, Map<String, String> globalParameters,
                     DataSourceConfiguration dataSourceConfig) throws DataSourceException {
        this.svCon = servletContext;
        this.globalParameters = globalParameters;
        this.config = dataSourceConfig;

        WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(svCon);
        atlasSolrDAO = (AtlasSolrDAO)context.getBean("atlasSolrDAO");
        atlasProperties = (AtlasProperties)context.getBean("atlasProperties");
    }

    /**
     * This method is called when the DAS server is shut down and should be used to clean up resources such as database
     * connections as required.
     */
    public void destroy() {
    }


    private int iCountTypes = 0;
    private String currentType = "";

    private String getSortableCaption(String caption) {
        iCountTypes += 1;
        if (0 != caption.compareTo(currentType)) {
            //iCountTypes+=1; Sept-8-2009 let's number all
            currentType = caption;
        }
        return (iCountTypes < 100 ? "0" : "") + (iCountTypes < 10 ? "0" : "") + String.valueOf(iCountTypes) + ". " +
                caption;
    }

    public static int SortOrd(String val) {
        if (val.equalsIgnoreCase("gene")) {
            return 1;
        }
        else if (val.equalsIgnoreCase("organism part")) {
            return 2;
        }
        else if (val.equalsIgnoreCase("disease state")) {
            return 3;
        }
        else if (val.equalsIgnoreCase("cell type")) {
            return 4;
        }
        else if (val.equalsIgnoreCase("cell line")) {
            return 5;
        }
        else if (val.equalsIgnoreCase("compound treatment")) {
            return 6;
        }
        else if (val.equalsIgnoreCase("experiment")) {
            return 8;
        }
        else {
            return 7;
        }
    }


    public DasFeature getGeneDasFeature(AtlasGene gene) throws DataSourceException {
        try {
            String notes = String.format("%1$s differential expression summary",gene.getGeneName());
            return (new DasFeature(
                    gene.getGeneIdentifier(),
                    "differential expression summary", // ,gene.getGeneIdentifier(),
                    "description",
                    "description",
                    getSortableCaption("Gene"),
                    "ExperimentalFactor",
                    "Experimental Factor",
                    0,
                    0,
                    0.0,
                    DasFeatureOrientation.ORIENTATION_NOT_APPLICABLE,
                    DasPhase.PHASE_NOT_APPLICABLE,
                    Collections.singleton(notes),
                    Collections.singletonMap(new URL(getDasBaseUrl() + "/gene/" + gene.getGeneIdentifier()),
                                             "view " + gene.getGeneName() + " expression in Gene Expression Atlas"),
                    null,
                    null
            ));
        }
        catch (MalformedURLException e) {
            throw new DataSourceException("Tried to create an invalid URL for a LINK element.", e);
        }
    }

    public String getHeatmapString(AtlasGene atlasGene, EfvTree.EfEfv<UpdownCounter> row) throws DataSourceException {
            String notes = "";
            if (row.getPayload().getUps() > 0) {
                notes += "up in " + row.getPayload().getUps();
            }

            if (row.getPayload().getDowns() > 0) {
                if (row.getPayload().getUps() > 0) {
                    notes += " and ";
                }
                notes += "down in " + row.getPayload().getDowns();
            }
            //notes+=" experiments";

            String featureLabel;

            String FactorValue = row.getEfv();
            String ExperimentFactor = row.getEf();

            featureLabel = ExperimentFactor + ":" + FactorValue;

            notes = "[" + row.getPayload().getUps() + " up/" + row.getPayload().getDowns() + " dn]";

            //replace last "; "  with "."
            if (notes.endsWith("; ")) {
                notes = notes.substring(0, notes.lastIndexOf("; ")) + ".";
            }

            return FactorValue + ' ' + notes;
   }

   public DasFeature getFactorDasFeature(AtlasGene atlasGene, String factor, List<EfvTree.EfEfv<UpdownCounter>> all_rows) throws DataSourceException {

       try{
        List<EfvTree.EfEfv<UpdownCounter>> my_rows = new ArrayList<EfvTree.EfEfv<UpdownCounter>>();

        for(EfvTree.EfEfv<UpdownCounter> r : all_rows){
            if(r.getEf().equals(factor)){
                my_rows.add(r);
            }
        }

        String notes = "";
        int iCount = 0;

        for(EfvTree.EfEfv<UpdownCounter> r : my_rows){
            ++iCount;
            if(iCount>=5){
              continue;
            }
            if(notes.length() > 0){
                notes += ", ";
            }
            notes += r.getEfv();
        }

        if( iCount > 5){
            notes += String.format(", ... (%1$d more)", (iCount-5));
        }

        return new DasFeature(
                    atlasProperties.getCuratedEf(factor),
                    atlasProperties.getCuratedEf(factor),
                    "summary",
                    "summary",
                    getSortableCaption(atlasProperties.getCuratedEf(factor)),
                    "ExperimentalFactor",
                    "Experimental Factor",
                    0,
                    0,
                    0.0,
                    DasFeatureOrientation.ORIENTATION_NOT_APPLICABLE,
                    DasPhase.PHASE_NOT_APPLICABLE,
                    Collections.singleton(notes), //notes -- do not show notes
                    Collections.singletonMap(
                            new URL(getDasBaseUrl() + "/gene/" + atlasGene.getGeneIdentifier()),
                            "view all"),
                    null,
                    null
            );
       }
       catch (MalformedURLException e) {
            throw new DataSourceException("Tried to create an invalid URL for a LINK element.", e);
       }
       catch (Exception e) {
            throw new DataSourceException("Error creating DasFeature.", e);
       }
    }

    public DasFeature getPlainTextDasFeature(String caption, String description) throws DataSourceException {

        try{

         return new DasFeature(
                     caption,
                     caption,
                     "summary",
                     "summary",
                     caption,
                     "ExperimentalFactor",
                     "Experimental Factor",
                     0,
                     0,
                     0.0,
                     DasFeatureOrientation.ORIENTATION_NOT_APPLICABLE,
                     DasPhase.PHASE_NOT_APPLICABLE,
                     Collections.singleton(description), //notes -- do not show notes
                     null, //no links
                     null,
                     null
             );
        }
        catch (Exception e) {
             throw new DataSourceException("Error creating DasFeature.", e);
        }
     }


    public DasFeature getImageDasFeature(AtlasGene atlasGene) throws DataSourceException {
        try{

            return new DasFeature(
                     atlasGene.getGeneIdentifier() //String featureId,
                     ,atlasGene.getGeneIdentifier()//String featureLabel,
                     ,"image"                      //String typeId,
                     ,"image"                      //String typeCategory,
                     ,"image"                      //String typeLabel,
                     ,"image"                      //String methodId,
                     ,"image"                      //String methodLabel,
                     ,0                            //int startCoordinate,
                     ,0                            //int endCoordinate,
                     ,0.0                          //Double score,
                     ,DasFeatureOrientation.ORIENTATION_NOT_APPLICABLE //DasFeatureOrientation orientation,
                     ,DasPhase.PHASE_NOT_APPLICABLE  //DasPhase phase,
                     ,Collections.singleton("anatomogram")                //Collection<String> notes,
                     ,Collections.singletonMap(new URL(getDasBaseUrl() + "/anatomogram/" + atlasGene.getGeneIdentifier() + ".png"), "")  //Map<URL, String> links,
                     ,null                              //Collection<DasTarget> targets,
                     ,null                              //Collection<DasGroup> groups
                    );
        }
        catch (Exception e){
            throw new DataSourceException("Error creating Image DasFeature.", e);
        }
    }

    /**
     * This method returns a List of DasAnnotatedSegment objects, describing the annotated segment and the features of
     * the segmentId passed in as argument.
     *
     * @param segmentReference being the reference of the segment requested in the DAS request (not including start and
     *                         stop coordinates)
     *                         <p/>
     *                         If your datasource implements only this interface, the MydasServlet will handle
     *                         restricting the features returned to the start / stop coordinates in the request and you
     *                         will only need to implement this method to return Features.  If on the other hand, your
     *                         data source includes massive segments, you may wish to implement the {@link
     *                         uk.ac.ebi.mydas.datasource.RangeHandlingAnnotationDataSource} interface.  It will then be
     *                         the responsibility of your AnnotationDataSource plugin to restrict the features returned
     *                         for the requested range.
     * @return A DasAnnotatedSegment object.  This describes the segment that is annotated, limited to the information
     *         required for the /DASGFF/GFF/SEGMENT element.  References a Collection of DasFeature objects.   Note that
     *         this is a basic Collection - this gives you complete control over the details of the Collection type - so
     *         you can create your own comparators etc.
     * @throws uk.ac.ebi.mydas.exceptions.BadReferenceObjectException
     *          in the event that your server does not include information about this segment.
     * @throws uk.ac.ebi.mydas.exceptions.DataSourceException
     *          should be thrown if there is any fatal problem with loading this data source.  <bold>It is highly
     *          desirable for the implementation to test itself in this init method and throw a DataSourceException if
     *          it fails, e.g. to attempt to get a Connection to a database and read a record.</bold>
     */
    public DasAnnotatedSegment getFeatures(String segmentReference)
            throws BadReferenceObjectException, DataSourceException {
        iCountTypes = 0;

        String geneId = segmentReference;

        AtlasGene atlasGene = atlasSolrDAO.getGeneByIdentifier(geneId).getGene();

        if (null == atlasGene) {
            throw new BadReferenceObjectException("can not find gene with ID=" + geneId, "DAS");
        }

        ArrayList<DasFeature> feat = new ArrayList<DasFeature>();

        feat.add(getGeneDasFeature(atlasGene)); //first row - gene

        /*
        feat.add(getPlainTextDasFeature("Anatomogram","The anatomogram on the right shows up to nine organism parts where this gene" +
                " was differentially expressed. The squares indicate the number of independent experiments where this" +
                "gene was over- (red) or under- (blue) expressed"));
        */        


        List<EfvTree.EfEfv<UpdownCounter>> heatmaps = atlasGene.getHeatMap(atlasProperties.getGeneHeatmapIgnoredEfs()).getValueSortedList();
<commit_msg>DAS: logging<commit_after>/*
 * Copyright 2008-2010 Microarray Informatics Team, EMBL-European Bioinformatics Institute
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 * For further details of the Gene Expression Atlas project, including source code,
 * downloads and documentation, please see:
 *
 * http://gxa.github.com/gxa
 */

package ae3.service;

import ae3.dao.AtlasSolrDAO;
import ae3.model.*;
import ae3.util.HtmlHelper;
import ae3.service.structuredquery.UpdownCounter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import uk.ac.ebi.gxa.dao.AtlasDAO;
import uk.ac.ebi.gxa.index.GeneExpressionAnalyticsTable;
import uk.ac.ebi.gxa.properties.AtlasProperties;
import uk.ac.ebi.gxa.utils.EfvTree;
import uk.ac.ebi.microarray.atlas.model.ExpressionAnalysis;
import uk.ac.ebi.mydas.controller.CacheManager;
import uk.ac.ebi.mydas.controller.DataSourceConfiguration;
import uk.ac.ebi.mydas.datasource.AnnotationDataSource;
import uk.ac.ebi.mydas.exceptions.BadReferenceObjectException;
import uk.ac.ebi.mydas.exceptions.DataSourceException;
import uk.ac.ebi.mydas.exceptions.UnimplementedFeatureException;
import uk.ac.ebi.mydas.model.*;
import org.apache.commons.lang.StringUtils;

import javax.servlet.ServletContext;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

/**
 * Created Using IntelliJ IDEA. Date: 18-Jul-2007 Time: 16:51:37
 *
 * @author <NAME>, EMBL-EBI, <EMAIL>
 *         <p/>
 *         NOTE TO DATA SOURCE DEVELOPERS:
 *         <p/>
 *         This template is based upon the AnnotationDataSource interface, there are however three other interfaces
 *         available that may be more appropriate for your needs, described here:
 *         <p/>
 *         <a href="http://code.google.com/p/mydas/wiki/HOWTO_WritePluginIntro"> Writing a MyDas Data Source - Selecting
 *         the Best Inteface </a>
 */

public class GxaS4DasDataSource implements AnnotationDataSource {

    private static AtlasDAO atlasDao;
    public void setDao(AtlasDAO atlasDao){
        GxaS4DasDataSource.atlasDao = atlasDao;
    }

    CacheManager cacheManager = null;
    ServletContext svCon;
    Map<String, String> globalParameters;
    DataSourceConfiguration config;
    AtlasSolrDAO atlasSolrDAO;
    AtlasProperties atlasProperties;
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    protected String getDasBaseUrl(){
        return atlasProperties.getProperty("atlas.dasbase");
    }


    /**
     * This method is called by the MydasServlet class at Servlet initialisation.
     * <p/>
     * The AnnotationDataSource is passed the servletContext, a handle to globalParameters in the form of a Map
     * &lt;String, String&gt; and a DataSourceConfiguration object.
     * <p/>
     * The latter two parameters contain all of the pertinent information in the ServerConfig.xml file relating to the
     * server as a whole and specifically to this data source.  This mechanism allows the datasource author to set up
     * required configuration in one place, including AnnotationDataSource specific configuration.
     * <p/>
     * <bold>It is highly desirable for the implementation to test itself in this init method and throw a
     * DataSourceException if it fails, e.g. to attempt to get a Connection to a database and read a record.</bold>
     *
     * @param servletContext   being the ServletContext of the servlet container that the Mydas servlet is running in.
     * @param globalParameters being a Map &lt;String, String&gt; of keys and values as defined in the ServerConfig.xml
     *                         file.
     * @param dataSourceConfig containing the pertinent information frmo the ServerConfig.xml file for this datasource,
     *                         including (optionally) a Map of datasource specific configuration.
     * @throws uk.ac.ebi.mydas.exceptions.DataSourceException
     *          should be thrown if there is any fatal problem with loading this data source.  <bold>It is highly
     *          desirable for the implementation to test itself in this init method and throw a DataSourceException if
     *          it fails, e.g. to attempt to get a Connection to a database and read a record.</bold>
     */
    public void init(ServletContext servletContext, Map<String, String> globalParameters,
                     DataSourceConfiguration dataSourceConfig) throws DataSourceException {
        this.svCon = servletContext;
        this.globalParameters = globalParameters;
        this.config = dataSourceConfig;

        WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(svCon);
        atlasSolrDAO = (AtlasSolrDAO)context.getBean("atlasSolrDAO");
        atlasProperties = (AtlasProperties)context.getBean("atlasProperties");
    }

    /**
     * This method is called when the DAS server is shut down and should be used to clean up resources such as database
     * connections as required.
     */
    public void destroy() {
    }


    private int iCountTypes = 0;
    private String currentType = "";

    private String getSortableCaption(String caption) {
        iCountTypes += 1;
        if (0 != caption.compareTo(currentType)) {
            //iCountTypes+=1; Sept-8-2009 let's number all
            currentType = caption;
        }
        return (iCountTypes < 100 ? "0" : "") + (iCountTypes < 10 ? "0" : "") + String.valueOf(iCountTypes) + ". " +
                caption;
    }

    public static int SortOrd(String val) {
        if (val.equalsIgnoreCase("gene")) {
            return 1;
        }
        else if (val.equalsIgnoreCase("organism part")) {
            return 2;
        }
        else if (val.equalsIgnoreCase("disease state")) {
            return 3;
        }
        else if (val.equalsIgnoreCase("cell type")) {
            return 4;
        }
        else if (val.equalsIgnoreCase("cell line")) {
            return 5;
        }
        else if (val.equalsIgnoreCase("compound treatment")) {
            return 6;
        }
        else if (val.equalsIgnoreCase("experiment")) {
            return 8;
        }
        else {
            return 7;
        }
    }


    public DasFeature getGeneDasFeature(AtlasGene gene) throws DataSourceException {
        try {
            String notes = String.format("%1$s differential expression summary",gene.getGeneName());
            return (new DasFeature(
                    gene.getGeneIdentifier(),
                    "differential expression summary", // ,gene.getGeneIdentifier(),
                    "description",
                    "description",
                    getSortableCaption("Gene"),
                    "ExperimentalFactor",
                    "Experimental Factor",
                    0,
                    0,
                    0.0,
                    DasFeatureOrientation.ORIENTATION_NOT_APPLICABLE,
                    DasPhase.PHASE_NOT_APPLICABLE,
                    Collections.singleton(notes),
                    Collections.singletonMap(new URL(getDasBaseUrl() + "/gene/" + gene.getGeneIdentifier()),
                                             "view " + gene.getGeneName() + " expression in Gene Expression Atlas"),
                    null,
                    null
            ));
        }
        catch (MalformedURLException e) {
            throw new DataSourceException("Tried to create an invalid URL for a LINK element.", e);
        }
    }

    public String getHeatmapString(AtlasGene atlasGene, EfvTree.EfEfv<UpdownCounter> row) throws DataSourceException {
            String notes = "";
            if (row.getPayload().getUps() > 0) {
                notes += "up in " + row.getPayload().getUps();
            }

            if (row.getPayload().getDowns() > 0) {
                if (row.getPayload().getUps() > 0) {
                    notes += " and ";
                }
                notes += "down in " + row.getPayload().getDowns();
            }
            //notes+=" experiments";

            String featureLabel;

            String FactorValue = row.getEfv();
            String ExperimentFactor = row.getEf();

            featureLabel = ExperimentFactor + ":" + FactorValue;

            notes = "[" + row.getPayload().getUps() + " up/" + row.getPayload().getDowns() + " dn]";

            //replace last "; "  with "."
            if (notes.endsWith("; ")) {
                notes = notes.substring(0, notes.lastIndexOf("; ")) + ".";
            }

            return FactorValue + ' ' + notes;
   }

   public DasFeature getFactorDasFeature(AtlasGene atlasGene, String factor, List<EfvTree.EfEfv<UpdownCounter>> all_rows) throws DataSourceException {

       try{
        List<EfvTree.EfEfv<UpdownCounter>> my_rows = new ArrayList<EfvTree.EfEfv<UpdownCounter>>();

        for(EfvTree.EfEfv<UpdownCounter> r : all_rows){
            if(r.getEf().equals(factor)){
                my_rows.add(r);
            }
        }

        String notes = "";
        int iCount = 0;

        for(EfvTree.EfEfv<UpdownCounter> r : my_rows){
            ++iCount;
            if(iCount>=5){
              continue;
            }
            if(notes.length() > 0){
                notes += ", ";
            }
            notes += r.getEfv();
        }

        if( iCount > 5){
            notes += String.format(", ... (%1$d more)", (iCount-5));
        }

        return new DasFeature(
                    atlasProperties.getCuratedEf(factor),
                    atlasProperties.getCuratedEf(factor),
                    "summary",
                    "summary",
                    getSortableCaption(atlasProperties.getCuratedEf(factor)),
                    "ExperimentalFactor",
                    "Experimental Factor",
                    0,
                    0,
                    0.0,
                    DasFeatureOrientation.ORIENTATION_NOT_APPLICABLE,
                    DasPhase.PHASE_NOT_APPLICABLE,
                    Collections.singleton(notes), //notes -- do not show notes
                    Collections.singletonMap(
                            new URL(getDasBaseUrl() + "/gene/" + atlasGene.getGeneIdentifier()),
                            "view all"),
                    null,
                    null
            );
       }
       catch (MalformedURLException e) {
            throw new DataSourceException("Tried to create an invalid URL for a LINK element.", e);
       }
       catch (Exception e) {
            throw new DataSourceException("Error creating DasFeature.", e);
       }
    }

    public DasFeature getPlainTextDasFeature(String caption, String description) throws DataSourceException {

        try{

         return new DasFeature(
                     caption,
                     caption,
                     "summary",
                     "summary",
                     caption,
                     "ExperimentalFactor",
                     "Experimental Factor",
                     0,
                     0,
                     0.0,
                     DasFeatureOrientation.ORIENTATION_NOT_APPLICABLE,
                     DasPhase.PHASE_NOT_APPLICABLE,
                     Collections.singleton(description), //notes -- do not show notes
                     null, //no links
                     null,
                     null
             );
        }
        catch (Exception e) {
             throw new DataSourceException("Error creating DasFeature.", e);
        }
     }


    public DasFeature getImageDasFeature(AtlasGene atlasGene) throws DataSourceException {
        try{

            return new DasFeature(
                     atlasGene.getGeneIdentifier() //String featureId,
                     ,atlasGene.getGeneIdentifier()//String featureLabel,
                     ,"image"                      //String typeId,
                     ,"image"                      //String typeCategory,
                     ,"image"                      //String typeLabel,
                     ,"image"                      //String methodId,
                     ,"image"                      //String methodLabel,
                     ,0                            //int startCoordinate,
                     ,0                            //int endCoordinate,
                     ,0.0                          //Double score,
                     ,DasFeatureOrientation.ORIENTATION_NOT_APPLICABLE //DasFeatureOrientation orientation,
                     ,DasPhase.PHASE_NOT_APPLICABLE  //DasPhase phase,
                     ,Collections.singleton("anatomogram")                //Collection<String> notes,
                     ,Collections.singletonMap(new URL(getDasBaseUrl() + "/anatomogram/" + atlasGene.getGeneIdentifier() + ".png"), "")  //Map<URL, String> links,
                     ,null                              //Collection<DasTarget> targets,
                     ,null                              //Collection<DasGroup> groups
                    );
        }
        catch (Exception e){
            throw new DataSourceException("Error creating Image DasFeature.", e);
        }
    }

    /**
     * This method returns a List of DasAnnotatedSegment objects, describing the annotated segment and the features of
     * the segmentId passed in as argument.
     *
     * @param segmentReference being the reference of the segment requested in the DAS request (not including start and
     *                         stop coordinates)
     *                         <p/>
     *                         If your datasource implements only this interface, the MydasServlet will handle
     *                         restricting the features returned to the start / stop coordinates in the request and you
     *                         will only need to implement this method to return Features.  If on the other hand, your
     *                         data source includes massive segments, you may wish to implement the {@link
     *                         uk.ac.ebi.mydas.datasource.RangeHandlingAnnotationDataSource} interface.  It will then be
     *                         the responsibility of your AnnotationDataSource plugin to restrict the features returned
     *                         for the requested range.
     * @return A DasAnnotatedSegment object.  This describes the segment that is annotated, limited to the information
     *         required for the /DASGFF/GFF/SEGMENT element.  References a Collection of DasFeature objects.   Note that
     *         this is a basic Collection - this gives you complete control over the details of the Collection type - so
     *         you can create your own comparators etc.
     * @throws uk.ac.ebi.mydas.exceptions.BadReferenceObjectException
     *          in the event that your server does not include information about this segment.
     * @throws uk.ac.ebi.mydas.exceptions.DataSourceException
     *          should be thrown if there is any fatal problem with loading this data source.  <bold>It is highly
     *          desirable for the implementation to test itself in this init method and throw a DataSourceException if
     *          it fails, e.g. to attempt to get a Connection to a database and read a record.</bold>
     */
    public DasAnnotatedSegment getFeatures(String segmentReference)
            throws BadReferenceObjectException, DataSourceException {
        iCountTypes = 0;

        log.info(String.format("DAS query: %s" ,segmentReference));

        String geneId = segmentReference;

        AtlasGene atlasGene = atlasSolrDAO.getGeneByIdentifier(geneId).getGene();

        if (null == atlasGene) {
            log.warn(String.format("DAS segment not found: %s" ,segmentReference));
            throw new BadReferenceObjectException("can not find gene with ID=" + geneId, "DAS");
        }

        ArrayList<DasFeature> feat = new ArrayList<DasFeature>();

        feat.add(getGeneDasFeature(atlasGene)); //first row - gene

        /*
        feat.add(getPlainTextDasFeature("Anatomogram","The anatomogram on the right shows up to nine organism parts where this gene" +
                " was differentially expressed. The squares indicate the number of independent experiments where this" +
                "gene was over- (red) or under- (blue) expressed"));
        */        


        List<EfvTree.EfEfv<UpdownCounter>> heatmaps = atlasGene.getHeatMap(atlasProperties.getGeneHeatmapIgnoredEfs()).getValueSortedList();

{'fraction_non_alphanumeric': 0.0838445204671464, 'fraction_numerical': 0.00495458298926507, 'mean_word_length': 1.909894447781687, 'pattern_counts': {'":': 2, '<': 79, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '12650', 'n_tokens_mistral': 8949, 'n_tokens_neox': 8294, 'n_words': 2504}