<commit_before>/*!
* Copyright 2002 - 2013 Webdetails, a Pentaho company.  All rights reserved.
*
* This software was developed by Webdetails and is provided under the terms
* of the Mozilla Public License, Version 2.0, or any later version. You may not use
* this file except in compliance with the license. If you need a copy of the license,
* please go to  http://mozilla.org/MPL/2.0/. The Initial Developer is Webdetails.
*
* Software distributed under the Mozilla Public License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or  implied. Please refer to
* the license for the specific language governing your rights and limitations.
*/

package pt.webdetails.cda;

import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.APPLICATION_XML;

import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.QueryParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.MultivaluedMap;

import com.sun.jersey.core.util.MultivaluedMapImpl;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.apache.commons.lang.StringUtils;
import pt.webdetails.cda.dataaccess.AbstractDataAccess;
import pt.webdetails.cda.dataaccess.DataAccessConnectionDescriptor;
import pt.webdetails.cda.exporter.ExportOptions;
import pt.webdetails.cda.exporter.ExportedQueryResult;
import pt.webdetails.cda.exporter.Exporter;
import pt.webdetails.cda.exporter.ExporterException;
import pt.webdetails.cda.exporter.UnsupportedExporterException;
import pt.webdetails.cda.services.CacheManager;
import pt.webdetails.cda.services.Editor;
import pt.webdetails.cda.services.ExtEditor;
import pt.webdetails.cda.services.Previewer;

import org.pentaho.platform.api.engine.IPentahoSession;

import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.security.SecurityHelper;

import pt.webdetails.cda.utils.DoQueryParameters;
import pt.webdetails.cpf.PluginEnvironment;
import pt.webdetails.cpf.messaging.JsonGeneratorSerializable;
import pt.webdetails.cpf.messaging.JsonResult;
import pt.webdetails.cpf.utils.CharsetHelper;
import pt.webdetails.cpf.utils.JsonHelper;
import pt.webdetails.cpf.utils.MimeTypes;

@Path("/{plugin}/api")
public class CdaUtils {
  private static final Log logger = LogFactory.getLog(CdaUtils.class);

  public static final String PLUGIN_NAME = "cda";
  private static final String PREFIX_PARAMETER = "param";
  private static final String PREFIX_SETTING = "setting";

//  private static final String[] EXPORT_TYPES = {MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV};//TODO
  
  private static final Pattern CDA_PATH = Pattern.compile( "^[^:]*([^/]+)[^?]*" );//TODO: safer to get trom repos?
  
  public CdaUtils() {
  }

  protected static String getEncoding() { return CharsetHelper.getEncoding(); }

  //TODO: wildcard for exported types?
  @GET
  @Path( "/doQuery" )
  @Produces( {MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML} )
  public StreamingOutput doQueryGet( @Context UriInfo urii ) throws WebApplicationException {
    return doQuery( urii.getQueryParameters() );
  }

  @POST
  @Path( "/doQuery" )
  @Consumes ( APPLICATION_FORM_URLENCODED )
  @Produces( {MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML} )
  public StreamingOutput doQueryPost( MultivaluedMap<String, String> formParams ) throws WebApplicationException {
    return doQuery( formParams );
  }

  protected ExportedQueryResult doQueryInternal( DoQueryParameters parameters ) throws Exception {
    CdaCoreService core = getCdaCoreService();
    return core.doQuery( parameters );
  }


  public StreamingOutput doQuery( MultivaluedMap<String, String> params ) throws WebApplicationException {
    try {
        DoQueryParameters parameters = getDoQueryParameters( params );
        if( parameters.isWrapItUp() ) {
            return wrapQuery( parameters );
        }
      return toStreamingOutput( doQueryInternal(parameters) );
    } catch ( Exception e ) {
      throw new WebApplicationException( e, 501 );// TODO:
    }
  }

  private StreamingOutput wrapQuery( DoQueryParameters parameters ) throws Exception {
      final String uuid = getCdaCoreService().wrapQuery( parameters );
    return new StreamingOutput() {
      public void write( OutputStream out ) throws IOException, WebApplicationException {
        IOUtils.write( uuid, out );
      }
    };
  }

  private DoQueryParameters getDoQueryParameters( MultivaluedMap<String, String> parameters ) throws Exception {
    DoQueryParameters doQueryParams = new DoQueryParameters( );

    //should populate everything but prefixed parameters TODO: recheck defaults
    BeanUtils.populate( doQueryParams, parameters );

    Map<String, Object> params = new HashMap<String, Object>();
    Map<String, Object> extraSettings = new HashMap<String, Object>();
    for ( String name : parameters.keySet() ) {
      if ( name.startsWith( PREFIX_PARAMETER ) ) {
        params.put( name.substring( PREFIX_PARAMETER.length() ), getParam( parameters.get( name ) ) );
      } else if ( name.startsWith( PREFIX_SETTING ) ) {
        extraSettings.put( name.substring( PREFIX_SETTING.length() ), getParam( parameters.get( name ) ) );
      }
    }
    doQueryParams.setParameters( params );
    doQueryParams.setExtraSettings( extraSettings );

    return doQueryParams;
  }

  private Object getParam( List<String> paramValues ) {
    if( paramValues == null ) return null;
    if (paramValues.size() == 1) return paramValues.get( 0 );
    return paramValues;
  }

  @GET
  @Path( "/unwrapQuery" )
  @Produces( { MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML } )
  public StreamingOutput unwrapQuery( @QueryParam( "path" ) String path, @QueryParam( "uuid" ) String uuid,
      @Context HttpServletResponse servletResponse, @Context HttpServletRequest servletRequest )
    throws WebApplicationException {
    try {
      return toStreamingOutput( getCdaCoreService().unwrapQuery( path, uuid ) );
    } catch ( Exception e ) {
      logger.error( e );
      throw new WebApplicationException( e, Response.Status.INTERNAL_SERVER_ERROR );
    }
  }

  @GET
  @Path("/listQueries")
  @Produces( {MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML} )
  public StreamingOutput listQueries(
      @QueryParam("path") String path, 
      @DefaultValue("json") @QueryParam("outputType") String outputType, 
      @Context HttpServletResponse servletResponse) throws WebApplicationException
  {
    if(StringUtils.isEmpty(path)){
      throw new IllegalArgumentException("No path provided");
    }

    logger.debug("Do Query: getSolPath:" + PentahoSystem.getApplicationContext().getSolutionPath(path));
    ExportedQueryResult result;
    try {
      result = getCdaCoreService().listQueries( path, getSimpleExportOptions( outputType ));
    } catch ( Exception e ) {
      logger.error(e);
      throw new WebApplicationException( e );
    }
    return toStreamingOutput( result );
  }

  private StreamingOutput toStreamingOutput (final ExportedQueryResult result ) {
    return new StreamingOutput() {
      
      public void write( OutputStream out ) throws IOException, WebApplicationException {
        try {
          result.writeOut( out );
        } catch ( ExporterException e ) {
          throw new WebApplicationException( e );
        }
      }
    };
  }

  private StreamingOutput toStreamingOutput( final JsonGeneratorSerializable json ) {
    return new StreamingOutput() {
      public void write( OutputStream out ) throws IOException, WebApplicationException {
         JsonHelper.writeJson( json, out );
      }
    };
  }

  private StreamingOutput toErrorResult( final Exception e ) {
    logger.error( e.getLocalizedMessage(), e );
    return new StreamingOutput() {
      public void write( OutputStream out ) throws IOException, WebApplicationException {
         JsonHelper.writeJson( new JsonResult( false, e.getLocalizedMessage() ), out );
      }
    };
  }

  private ExportOptions getSimpleExportOptions( final String outputType ) {
    return new ExportOptions() {
      
      public String getOutputType() {
        return outputType;
      }
      
      public Map<String, String> getExtraSettings() {
        return Collections.<String,String>emptyMap();
      }
    };
  }

  @GET
  @Path( "/listParameters" )
  @Produces( { MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML } )
  public StreamingOutput listParameters( @QueryParam( "path" ) String path,
      @QueryParam( "dataAccessId" ) String dataAccessId,
      @DefaultValue( "json" ) @QueryParam( "outputType" ) String outputType ) throws WebApplicationException {
    logger.debug( "Do Query: getSolPath:" + path );
    try {
      return toStreamingOutput( getCdaCoreService().listParameters( path, dataAccessId,
          getSimpleExportOptions( outputType ) ) );
    } catch ( Exception e ) {
      logger.error( e );
      throw new WebApplicationException( e );
    }
  }

  private Exporter useExporter( final CdaEngine engine, final String outputType, HttpServletResponse servletResponse )
    throws UnsupportedExporterException {
    return useExporter( engine, new ExportOptions() {

      public String getOutputType() {
        return outputType;
      }

      public Map<String, String> getExtraSettings() {
        return null;
      }
    }, servletResponse );
  }

  private Exporter useExporter( final CdaEngine engine, ExportOptions opts, HttpServletResponse servletResponse )
      throws UnsupportedExporterException {
      // Handle the query itself and its output format...
      Exporter exporter = engine.getExporter( opts );
      String mimeType = exporter.getMimeType();
      if(mimeType != null)
      {
        servletResponse.setHeader("Content-Type", mimeType);
      }

      String attachmentName = exporter.getAttachmentName();
      if ( attachmentName != null ) {
        servletResponse.setHeader( "content-disposition", "attachment; filename=" + attachmentName );
      }
      return exporter;
  }

  @GET
  @Path("/getCdaFile")
  @Produces( MimeTypes.JSON )
  public StreamingOutput getCdaFile(@QueryParam("path") String path) throws WebApplicationException
  {
    String filePath = StringUtils.replace(path, "///", "/");
    JsonGeneratorSerializable json;
    try {
      String document = getEditor().getFile( filePath );
      if ( document != null ) {
        json = new JsonResult( true, document );
      }
      else {
        json = new JsonResult( false, "Unable to read file." );
      }
    }
    catch (Exception e) {
      return toErrorResult( e );
    }
    return toStreamingOutput( json );
  }

  @GET
  @Path("/canEdit")
  @Produces( MimeTypes.JSON )
  public StreamingOutput canEdit(@QueryParam("path") String path) {
    boolean canEdit = getEditor().canEdit( path );
    return toStreamingOutput(new JsonResult( true, JsonHelper.toJson( canEdit ) ) );
  }

  private Editor getEditor() {
    return new Editor();
  }

  @POST
  @Path("/writeCdaFile")
  @Produces( MimeTypes.JSON )
  public StreamingOutput writeCdaFile(@FormParam("path") String path, 
                                      @FormParam("data") String data ) throws Exception
  {  
    //TODO: Validate the filename in some way, shape or form!
    if ( data == null ) {
      logger.error( "writeCdaFile: no data to save provided " + path );
      return toStreamingOutput( new JsonResult( false, "No Data!" ) );
    }

    try {
      return toStreamingOutput( new JsonResult( getEditor().writeFile( path, data ), path ) );
    }
    catch (Exception e) {
      return toErrorResult( e );
    }
  }

  @GET
  @Path("/getCdaList")
  @Consumes({ APPLICATION_XML, APPLICATION_JSON })
  public void getCdaList(@DefaultValue("json") @QueryParam("outputType") String outputType, 
                            
                         @Context HttpServletResponse servletResponse, 
                         @Context HttpServletRequest servletRequest) throws Exception
  {
    final CdaEngine engine = CdaEngine.getInstance();
    Exporter exporter = useExporter( engine, outputType, servletResponse );
    exporter.export( servletResponse.getOutputStream(), engine.getCdaList() );
  }

  @GET
  @Path("/clearCache")
  @Produces("text/plain")
  @Consumes({ APPLICATION_XML, APPLICATION_JSON })
  public void clearCache(@Context HttpServletResponse servletResponse, 
                         @Context HttpServletRequest servletRequest) throws Exception
  {
    // Check if user is admin
    Boolean accessible = SecurityHelper.getInstance().isPentahoAdministrator(getPentahoSession());
    if(!accessible){
      String msg = "Method clearCache not exposed or user does not have required permissions."; 
      logger.error(msg);
      servletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
    }
    
    CdaEngine.getInstance().getSettingsManager().clearCache();
    AbstractDataAccess.clearCache();

    servletResponse.getOutputStream().write("Cache cleared".getBytes());
  }

  @GET
  @Path("/editFile")
  @Produces( MimeTypes.HTML )
  public String editFile( @QueryParam("path") String path ) throws WebApplicationException, IOException {
    if (StringUtils.isEmpty( path )) {
      throw new WebApplicationException( 400 );
    }<commit_msg>merging pull request<commit_after>/*!
* Copyright 2002 - 2013 Webdetails, a Pentaho company.  All rights reserved.
*
* This software was developed by Webdetails and is provided under the terms
* of the Mozilla Public License, Version 2.0, or any later version. You may not use
* this file except in compliance with the license. If you need a copy of the license,
* please go to  http://mozilla.org/MPL/2.0/. The Initial Developer is Webdetails.
*
* Software distributed under the Mozilla Public License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or  implied. Please refer to
* the license for the specific language governing your rights and limitations.
*/

package pt.webdetails.cda;

import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.APPLICATION_XML;

import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.QueryParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.MultivaluedMap;

import com.sun.jersey.core.util.MultivaluedMapImpl;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.apache.commons.lang.StringUtils;
import pt.webdetails.cda.dataaccess.AbstractDataAccess;
import pt.webdetails.cda.dataaccess.DataAccessConnectionDescriptor;
import pt.webdetails.cda.exporter.ExportOptions;
import pt.webdetails.cda.exporter.ExportedQueryResult;
import pt.webdetails.cda.exporter.Exporter;
import pt.webdetails.cda.exporter.TableExporter;
import pt.webdetails.cda.exporter.ExporterException;
import pt.webdetails.cda.exporter.UnsupportedExporterException;
import pt.webdetails.cda.services.CacheManager;
import pt.webdetails.cda.services.Editor;
import pt.webdetails.cda.services.ExtEditor;
import pt.webdetails.cda.services.Previewer;

import org.pentaho.platform.api.engine.IPentahoSession;

import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.security.SecurityHelper;

import pt.webdetails.cda.utils.DoQueryParameters;
import pt.webdetails.cpf.PluginEnvironment;
import pt.webdetails.cpf.messaging.JsonGeneratorSerializable;
import pt.webdetails.cpf.messaging.JsonResult;
import pt.webdetails.cpf.utils.CharsetHelper;
import pt.webdetails.cpf.utils.JsonHelper;
import pt.webdetails.cpf.utils.MimeTypes;

@Path("/{plugin}/api")
public class CdaUtils {
  private static final Log logger = LogFactory.getLog(CdaUtils.class);

  public static final String PLUGIN_NAME = "cda";
  private static final String PREFIX_PARAMETER = "param";
  private static final String PREFIX_SETTING = "setting";

//  private static final String[] EXPORT_TYPES = {MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV};//TODO
  
  private static final Pattern CDA_PATH = Pattern.compile( "^[^:]*([^/]+)[^?]*" );//TODO: safer to get trom repos?
  
  public CdaUtils() {
  }

  protected static String getEncoding() { return CharsetHelper.getEncoding(); }

  //TODO: wildcard for exported types?
  @GET
  @Path( "/doQuery" )
  @Produces( {MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML} )
  public StreamingOutput doQueryGet( @Context UriInfo urii ) throws WebApplicationException {
    return doQuery( urii.getQueryParameters() );
  }

  @POST
  @Path( "/doQuery" )
  @Consumes ( APPLICATION_FORM_URLENCODED )
  @Produces( {MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML} )
  public StreamingOutput doQueryPost( MultivaluedMap<String, String> formParams ) throws WebApplicationException {
    return doQuery( formParams );
  }

  protected ExportedQueryResult doQueryInternal( DoQueryParameters parameters ) throws Exception {
    CdaCoreService core = getCdaCoreService();
    return core.doQuery( parameters );
  }


  public StreamingOutput doQuery( MultivaluedMap<String, String> params ) throws WebApplicationException {
    try {
        DoQueryParameters parameters = getDoQueryParameters( params );
        if( parameters.isWrapItUp() ) {
            return wrapQuery( parameters );
        }
      return toStreamingOutput( doQueryInternal(parameters) );
    } catch ( Exception e ) {
      throw new WebApplicationException( e, 501 );// TODO:
    }
  }

  private StreamingOutput wrapQuery( DoQueryParameters parameters ) throws Exception {
      final String uuid = getCdaCoreService().wrapQuery( parameters );
    return new StreamingOutput() {
      public void write( OutputStream out ) throws IOException, WebApplicationException {
        IOUtils.write( uuid, out );
      }
    };
  }

  private DoQueryParameters getDoQueryParameters( MultivaluedMap<String, String> parameters ) throws Exception {
    DoQueryParameters doQueryParams = new DoQueryParameters( );

    //should populate everything but prefixed parameters TODO: recheck defaults
    BeanUtils.populate( doQueryParams, parameters );

    Map<String, Object> params = new HashMap<String, Object>();
    Map<String, Object> extraSettings = new HashMap<String, Object>();
    for ( String name : parameters.keySet() ) {
      if ( name.startsWith( PREFIX_PARAMETER ) ) {
        params.put( name.substring( PREFIX_PARAMETER.length() ), getParam( parameters.get( name ) ) );
      } else if ( name.startsWith( PREFIX_SETTING ) ) {
        extraSettings.put( name.substring( PREFIX_SETTING.length() ), getParam( parameters.get( name ) ) );
      }
    }
    doQueryParams.setParameters( params );
    doQueryParams.setExtraSettings( extraSettings );

    return doQueryParams;
  }

  private Object getParam( List<String> paramValues ) {
    if( paramValues == null ) return null;
    if (paramValues.size() == 1) return paramValues.get( 0 );
    return paramValues;
  }

  @GET
  @Path( "/unwrapQuery" )
  @Produces( { MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML } )
  public StreamingOutput unwrapQuery( @QueryParam( "path" ) String path, @QueryParam( "uuid" ) String uuid,
      @Context HttpServletResponse servletResponse, @Context HttpServletRequest servletRequest )
    throws WebApplicationException {
    try {
      return toStreamingOutput( getCdaCoreService().unwrapQuery( path, uuid ) );
    } catch ( Exception e ) {
      logger.error( e );
      throw new WebApplicationException( e, Response.Status.INTERNAL_SERVER_ERROR );
    }
  }

  @GET
  @Path("/listQueries")
  @Produces( {MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML} )
  public StreamingOutput listQueries(
      @QueryParam("path") String path, 
      @DefaultValue("json") @QueryParam("outputType") String outputType, 
      @Context HttpServletResponse servletResponse) throws WebApplicationException
  {
    if(StringUtils.isEmpty(path)){
      throw new IllegalArgumentException("No path provided");
    }

    logger.debug("Do Query: getSolPath:" + PentahoSystem.getApplicationContext().getSolutionPath(path));
    ExportedQueryResult result;
    try {
      result = getCdaCoreService().listQueries( path, getSimpleExportOptions( outputType ));
    } catch ( Exception e ) {
      logger.error(e);
      throw new WebApplicationException( e );
    }
    return toStreamingOutput( result );
  }

  private StreamingOutput toStreamingOutput (final ExportedQueryResult result ) {
    return new StreamingOutput() {
      
      public void write( OutputStream out ) throws IOException, WebApplicationException {
        try {
          result.writeOut( out );
        } catch ( ExporterException e ) {
          throw new WebApplicationException( e );
        }
      }
    };
  }

  private StreamingOutput toStreamingOutput( final JsonGeneratorSerializable json ) {
    return new StreamingOutput() {
      public void write( OutputStream out ) throws IOException, WebApplicationException {
         JsonHelper.writeJson( json, out );
      }
    };
  }

  private StreamingOutput toErrorResult( final Exception e ) {
    logger.error( e.getLocalizedMessage(), e );
    return new StreamingOutput() {
      public void write( OutputStream out ) throws IOException, WebApplicationException {
         JsonHelper.writeJson( new JsonResult( false, e.getLocalizedMessage() ), out );
      }
    };
  }

  private ExportOptions getSimpleExportOptions( final String outputType ) {
    return new ExportOptions() {
      
      public String getOutputType() {
        return outputType;
      }
      
      public Map<String, String> getExtraSettings() {
        return Collections.<String,String>emptyMap();
      }
    };
  }

  @GET
  @Path( "/listParameters" )
  @Produces( { MimeTypes.JSON, MimeTypes.XML, MimeTypes.CSV, MimeTypes.XLS, MimeTypes.PLAIN_TEXT, MimeTypes.HTML } )
  public StreamingOutput listParameters( @QueryParam( "path" ) String path,
      @QueryParam( "dataAccessId" ) String dataAccessId,
      @DefaultValue( "json" ) @QueryParam( "outputType" ) String outputType ) throws WebApplicationException {
    logger.debug( "Do Query: getSolPath:" + path );
    try {
      return toStreamingOutput( getCdaCoreService().listParameters( path, dataAccessId,
          getSimpleExportOptions( outputType ) ) );
    } catch ( Exception e ) {
      logger.error( e );
      throw new WebApplicationException( e );
    }
  }

  private TableExporter useExporter( final CdaEngine engine, final String outputType, HttpServletResponse servletResponse )
    throws UnsupportedExporterException {
    return useExporter( engine, new ExportOptions() {

      public String getOutputType() {
        return outputType;
      }

      public Map<String, String> getExtraSettings() {
        return null;
      }
    }, servletResponse );
  }

  private TableExporter useExporter( final CdaEngine engine, ExportOptions opts, HttpServletResponse servletResponse )
      throws UnsupportedExporterException {
      // Handle the query itself and its output format...
      Exporter exporter = engine.getExporter( opts );
      String mimeType = exporter.getMimeType();
      if(mimeType != null)
      {
        servletResponse.setHeader("Content-Type", mimeType);
      }

      String attachmentName = exporter.getAttachmentName();
      if ( attachmentName != null ) {
        servletResponse.setHeader( "content-disposition", "attachment; filename=" + attachmentName );
      }
      return (TableExporter)exporter;
  }

  @GET
  @Path("/getCdaFile")
  @Produces( MimeTypes.JSON )
  public StreamingOutput getCdaFile(@QueryParam("path") String path) throws WebApplicationException
  {
    String filePath = StringUtils.replace(path, "///", "/");
    JsonGeneratorSerializable json;
    try {
      String document = getEditor().getFile( filePath );
      if ( document != null ) {
        json = new JsonResult( true, document );
      }
      else {
        json = new JsonResult( false, "Unable to read file." );
      }
    }
    catch (Exception e) {
      return toErrorResult( e );
    }
    return toStreamingOutput( json );
  }

  @GET
  @Path("/canEdit")
  @Produces( MimeTypes.JSON )
  public StreamingOutput canEdit(@QueryParam("path") String path) {
    boolean canEdit = getEditor().canEdit( path );
    return toStreamingOutput(new JsonResult( true, JsonHelper.toJson( canEdit ) ) );
  }

  private Editor getEditor() {
    return new Editor();
  }

  @POST
  @Path("/writeCdaFile")
  @Produces( MimeTypes.JSON )
  public StreamingOutput writeCdaFile(@FormParam("path") String path, 
                                      @FormParam("data") String data ) throws Exception
  {  
    //TODO: Validate the filename in some way, shape or form!
    if ( data == null ) {
      logger.error( "writeCdaFile: no data to save provided " + path );
      return toStreamingOutput( new JsonResult( false, "No Data!" ) );
    }

    try {
      return toStreamingOutput( new JsonResult( getEditor().writeFile( path, data ), path ) );
    }
    catch (Exception e) {
      return toErrorResult( e );
    }
  }

  @GET
  @Path("/getCdaList")
  @Consumes({ APPLICATION_XML, APPLICATION_JSON })
  public void getCdaList(@DefaultValue("json") @QueryParam("outputType") String outputType, 
                            
                         @Context HttpServletResponse servletResponse, 
                         @Context HttpServletRequest servletRequest) throws Exception
  {
    final CdaEngine engine = CdaEngine.getInstance();
    TableExporter exporter = useExporter( engine, outputType, servletResponse );
    exporter.export( servletResponse.getOutputStream(), engine.getCdaList() );
    servletResponse.getOutputStream().flush();
  }

  @GET
  @Path("/clearCache")
  @Produces("text/plain")
  @Consumes({ APPLICATION_XML, APPLICATION_JSON })
  public void clearCache(@Context HttpServletResponse servletResponse, 
                         @Context HttpServletRequest servletRequest) throws Exception
  {
    // Check if user is admin
    Boolean accessible = SecurityHelper.getInstance().isPentahoAdministrator(getPentahoSession());
    if(!accessible){
      String msg = "Method clearCache not exposed or user does not have required permissions."; 
      logger.error(msg);
      servletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
    }
    
    CdaEngine.getInstance().getSettingsManager().clearCache();
    AbstractDataAccess.clearCache();

    servletResponse.getOutputStream().write("Cache cleared".getBytes());
  }

  @GET
  @Path("/editFile")
  @Produces( MimeTypes.HTML )
  public String editFile( @QueryParam("path") String path ) throws WebApplicationException, IOException {
    if (StringUtils.isEmpty( path )) {
      throw new WebApplicationException( 400 );
    }
{'fraction_non_alphanumeric': 0.10254263890342158, 'fraction_numerical': 0.0013951379442642391, 'mean_word_length': 3.747019867549669, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2133560', 'n_tokens_mistral': 8320, 'n_tokens_neox': 8189, 'n_words': 2021}