All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.jasperreports.engine.query.JRAbstractQueryExecuter Maven / Gradle / Ivy

There is a newer version: 6.21.3
Show newest version
/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2016 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see .
 */
package net.sf.jasperreports.engine.query;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.jasperreports.engine.JRConstants;
import net.sf.jasperreports.engine.JRDataset;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRQuery;
import net.sf.jasperreports.engine.JRQueryChunk;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRValueParameter;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.fill.JRFillParameter;
import net.sf.jasperreports.engine.util.JRQueryChunkHandler;
import net.sf.jasperreports.engine.util.JRQueryParser;

/**
 * Base abstract query executer.
 * 
 * @author Lucian Chirita ([email protected])
 */
public abstract class JRAbstractQueryExecuter implements JRQueryExecuter
{
	public static final String EXCEPTION_MESSAGE_KEY_NUMERIC_TYPE_REQUIRED = "query.numeric.type.required";
	public static final String EXCEPTION_MESSAGE_KEY_PARAMETER_NOT_FOUND = "query.parameter.not.found";
	public static final String EXCEPTION_MESSAGE_KEY_QUERY_CLAUSE_CIRCULARLY_NESTED_PARAMETER = "query.clause.circularly.nested.parameter";
	public static final String EXCEPTION_MESSAGE_KEY_QUERY_CLAUSE_ID_FIRST_TOKEN_MISSING = "query.clause.id.first.token.missing";
	public static final String EXCEPTION_MESSAGE_KEY_QUERY_CLAUSE_NOT_FOUND = "query.clause.not.found";
	public static final String EXCEPTION_MESSAGE_KEY_UNSUPPORTED_PARAMETER_TYPE = "query.unsupported.parameter.type";

	public static final String GET_COLLECTED = "getCollectedParameterNames()";
	
	/**
	 * @deprecated use {@link JRClauseTokens#CLAUSE_ID_POSITION} or {@link JRClauseTokens#getClauseId()} instead.
	 */
	protected static final int CLAUSE_POSITION_ID = JRClauseTokens.CLAUSE_ID_POSITION;

	protected class VisitExceptionWrapper extends RuntimeException
	{
		private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
		
		public VisitExceptionWrapper(Exception cause)
		{
			super(cause);
		}
	}
	
	protected static interface QueryParameterVisitor
	{
		void visit(QueryParameter parameter) throws VisitExceptionWrapper;

		void visit(ValuedQueryParameter valuedQueryParameter) throws VisitExceptionWrapper;
	}
	
	protected static interface QueryParameterEntry
	{
		void accept(QueryParameterVisitor visitor) throws VisitExceptionWrapper;
	}
	
	/**
	 * A parameter present in the query.
	 */
	protected static class QueryParameter implements QueryParameterEntry
	{
		protected static final int COUNT_SINGLE = -1;
		
		private final String name;
		private final int count;
		private final boolean ignoreNulls;
		
		public QueryParameter(String name)
		{
			this(name, COUNT_SINGLE, false);
		}
		
		public QueryParameter(String name, int count)
		{
			this(name, count, false);
		}
		
		public QueryParameter(String name, int count, boolean ignoreNulls)
		{
			this.name = name;
			this.count = count;
			this.ignoreNulls = ignoreNulls;
		}
		
		/**
		 * Decides whether the parameter has multiple values.
		 * 
		 * @return whether the parameter has multiple values.
		 */
		public boolean isMulti()
		{
			return count != COUNT_SINGLE;
		}
		
		/**
		 * Returns the number of parameter values.
		 * 
		 * @return the number of parameter values
		 * @see #isMulti()
		 */
		public int getCount()
		{
			return count;
		}
		
		/**
		 * Returns the name of the report parameter.
		 * 
		 * @return the name of the report parameter
		 */
		public String getName()
		{
			return name;
		}
		
		/**
		 * 
		 * @return a flag indicating if the null values in a multiparameter value should be ignored
		 * 
		 */
		public boolean isIgnoreNulls()
		{
			return ignoreNulls;
		}

		@Override
		public void accept(QueryParameterVisitor visitor) throws VisitExceptionWrapper
		{
			visitor.visit(this);
		}
	}
	
	protected static class ValuedQueryParameter implements QueryParameterEntry
	{
		private final Class type;
		private final Object value;

		public ValuedQueryParameter(Class type, Object value)
		{
			this.type = type;
			this.value = value;
		}

		@Override
		public void accept(QueryParameterVisitor visitor) throws VisitExceptionWrapper
		{
			visitor.visit(this);
		}

		public Class getType()
		{
			return type;
		}

		public Object getValue()
		{
			return value;
		}
	}
	
	/**
	 * Clause function registry.
	 */
	protected final Map clauseFunctions = new HashMap();
	
	private final JasperReportsContext jasperReportsContext;
	private final JRPropertiesUtil propertiesUtil;
	protected final JRDataset dataset;
	private final Map parametersMap;
	
	private String queryString;
	
	/**
	 * List of {@link QueryParameter query parameters}.
	 */
	private List queryParameters;
	
	private Set parameterClauseStack;
	
	/**
	 * 
	 */
	protected JRAbstractQueryExecuter(
		JasperReportsContext jasperReportsContext, 
		JRDataset dataset, 
		Map parametersMap
		)
	{
		this.jasperReportsContext = jasperReportsContext;
		this.propertiesUtil = JRPropertiesUtil.getInstance(jasperReportsContext);
		this.dataset = dataset;
		this.parametersMap = parametersMap;
		
		queryString = "";
		queryParameters = new ArrayList();
	}

	/**
	 * 
	 */
	protected JasperReportsContext getJasperReportsContext()
	{
		return jasperReportsContext;
	}

	/**
	 * 
	 */
	protected JRPropertiesUtil getPropertiesUtil()
	{
		return propertiesUtil;
	}

	/**
	 * Registers a clause function.
	 * 
	 * @param id the function ID
	 * @param function the function
	 */
	protected void registerClauseFunction(String id, JRClauseFunction function)
	{
		clauseFunctions.put(id, function);
	}
	
	/**
	 * Unregisters a clause function.
	 * 
	 * @param id the function ID
	 */
	protected void unregisterClauseFunction(String id)
	{
		clauseFunctions.remove(id);
	}
	
	/**
	 * Resolves a clause function ID to a function instance.
	 * 
	 * @param id the function ID
	 * @return the clause function registered for the ID
	 * @throws JRRuntimeException if no function for the ID is found
	 */
	protected JRClauseFunction resolveFunction(String id)
	{
		// first look in explicitly registered functions
		// this is mostly kept for backward compatibility
		JRClauseFunction function = clauseFunctions.get(id);
		if (function == null)
		{
			function = findExtensionQueryFunction(id);
			if (function == null)
			{
				throw 
					new JRRuntimeException(
						EXCEPTION_MESSAGE_KEY_QUERY_CLAUSE_NOT_FOUND,
						new Object[]{id});
			}
		}
		return function;
	}

	protected JRClauseFunction findExtensionQueryFunction(String id)
	{
		JRClauseFunction function = null;
		//FIXME should we also use the current query language?
		String queryLanguage = getCanonicalQueryLanguage();
		// look for extensions
		List functionBundles = jasperReportsContext.getExtensions(
				QueryClauseFunctionBundle.class);
		for (QueryClauseFunctionBundle functionBundle : functionBundles)
		{
			function = functionBundle.getFunction(queryLanguage, id);
			if (function != null)
			{
				// use the first found
				break;
			}
		}
		return function;
	}
	
	/**
	 * Returns a canonical query language for this query executer implementation.
	 * 
	 * 

* The canonical language is used to retrieve extensions for the query executer. *

* *

* The default implementation returns the runtime query language used in the dataset, * but query executer implementations should override this method and return a fixed * language. *

* * @return a canonical query language */ protected String getCanonicalQueryLanguage() { // by default returning the current query language because we can't force // existing external implementations to implement the method. // but internal implementations override the method and specify a fixed // language name for extensions. return dataset.getQuery().getLanguage(); } /** * Parses the query and replaces the parameter clauses by the parameter values and * the parameters by the return value of {@link #getParameterReplacement(String) getParameterReplacement}. * */ protected void parseQuery() { parameterClauseStack = new HashSet(); JRQuery query = dataset.getQuery(); if (query != null) { JRQueryChunk[] chunks = query.getChunks(); if (chunks != null && chunks.length > 0) { StringBuffer sbuffer = new StringBuffer(); for(int i = 0; i < chunks.length; i++) { JRQueryChunk chunk = chunks[i]; appendQueryChunk(sbuffer, chunk); } queryString = sbuffer.toString(); } } } protected void appendQueryChunk(StringBuffer sbuffer, JRQueryChunk chunk) { switch (chunk.getType()) { case JRQueryChunk.TYPE_PARAMETER_CLAUSE : { appendParameterClauseChunk(sbuffer, chunk.getText()); break; } case JRQueryChunk.TYPE_PARAMETER : { appendParameterChunk(sbuffer, chunk.getText()); break; } case JRQueryChunk.TYPE_CLAUSE_TOKENS : { appendClauseChunk(sbuffer, chunk.getTokens()); break; } case JRQueryChunk.TYPE_TEXT : default : { appendTextChunk(sbuffer, chunk.getText()); break; } } } protected void appendTextChunk(StringBuffer sbuffer, String text) { sbuffer.append(text); } protected void appendParameterChunk(StringBuffer sbuffer, String chunkText) { String parameterName = chunkText; checkParameter(parameterName); sbuffer.append(getParameterReplacement(parameterName)); addQueryParameter(chunkText); } /** * Records a query parameter. * * @param parameterName the parameter name * @see #getCollectedParameters() */ protected void addQueryParameter(String parameterName) { QueryParameter param = new QueryParameter(parameterName); queryParameters.add(param); } /** * Records a multi-valued query parameter. * * @param parameterName the parameter name * @param count the value count * @see #getCollectedParameters() * @see QueryParameter#isMulti() */ protected void addQueryMultiParameters(String parameterName, int count) { QueryParameter param = new QueryParameter(parameterName, count); queryParameters.add(param); } /** * Records a multi-valued query parameter which ignore null values. * * @param parameterName the parameter name * @param count the value count * @see #getCollectedParameters() * @see QueryParameter#isMulti() */ protected void addQueryMultiParameters(String parameterName, int count, boolean ignoreNulls) { QueryParameter param = new QueryParameter(parameterName, count, ignoreNulls); queryParameters.add(param); } protected void addQueryParameter(Class type, Object value) { ValuedQueryParameter param = new ValuedQueryParameter(type, value); queryParameters.add(param); } protected void appendParameterClauseChunk(final StringBuffer sbuffer, String chunkText) { String parameterName = chunkText; checkParameter(parameterName); if (!parameterClauseStack.add(parameterName)) { throw new JRRuntimeException( EXCEPTION_MESSAGE_KEY_QUERY_CLAUSE_CIRCULARLY_NESTED_PARAMETER, new Object[]{parameterName}); } try { Object parameterValue = getParameterValue(parameterName); String clauseText = String.valueOf(parameterValue); JRQueryChunkHandler nestedChunkHandler = new JRQueryChunkHandler() { @Override public void handleParameterChunk(String text) { appendParameterChunk(sbuffer, text); } @Override public void handleParameterClauseChunk(String text) { appendParameterClauseChunk(sbuffer, text); } @Override public void handleTextChunk(String text) { appendTextChunk(sbuffer, text); } @Override public void handleClauseChunk(String[] tokens, char tokenSeparator) { appendClauseChunk(sbuffer, tokens); } }; JRQueryParser.instance().parse(clauseText, nestedChunkHandler); } finally { parameterClauseStack.remove(parameterName); } } /** * Handles a {@link JRQueryChunk#TYPE_CLAUSE_TOKENS clause query chunk}. *

* The default implementation considers the first token as a * {@link JRClauseFunction clause function} ID and delegates the call to the * function. *

*

* Extending query executers can override this to implement custom query clause handling. *

* * @param sbuffer the query text buffer * @param clauseTokens clause tokens * @see #registerClauseFunction(String, JRClauseFunction) * @throws JRRuntimeException if there is no first token or no clause function is found for the ID */ protected void appendClauseChunk(final StringBuffer sbuffer, String[] clauseTokens) { JRClauseTokens tokens = new JRClauseTokens(clauseTokens); String id = tokens.getClauseId(); if (id == null) { throw new JRRuntimeException( EXCEPTION_MESSAGE_KEY_QUERY_CLAUSE_ID_FIRST_TOKEN_MISSING, (Object[])null); } JRClauseFunction function = resolveFunction(id); applyClause(function, tokens, sbuffer); } protected void applyClause(JRClauseFunction function, JRClauseTokens tokens, final StringBuffer sbuffer) { function.apply(tokens, new JRQueryClauseContext() { @Override public void addQueryMultiParameters(String parameterName, int count) { addQueryMultiParameters(parameterName, count, false); } @Override public void addQueryMultiParameters(String parameterName, int count, boolean ignoreNulls) { JRAbstractQueryExecuter.this.addQueryMultiParameters(parameterName, count, ignoreNulls); } @Override public void addQueryParameter(String parameterName) { JRAbstractQueryExecuter.this.addQueryParameter(parameterName); } @Override public void addQueryParameter(Class type, Object value) { JRAbstractQueryExecuter.this.addQueryParameter(type, value); } @Override public JRValueParameter getValueParameter(String parameterName) { return JRAbstractQueryExecuter.this.getValueParameter(parameterName); } @Override public StringBuffer queryBuffer() { return sbuffer; } @Override public JasperReportsContext getJasperReportsContext() { return jasperReportsContext; } @Override public String getCanonicalQueryLanguage() { return JRAbstractQueryExecuter.this.getCanonicalQueryLanguage(); } }); } /** * Returns the parsed query string with the parameter clauses replaced by the parameter values and * the parameters replaced by {@link #getParameterReplacement(String) getParameterReplacement}. * * @return the parsed query string */ protected String getQueryString() { return queryString; } /** * Returns the list of parameter names in the order in which they appear in the query. * * @return the list of parameter names */ protected List getCollectedParameterNames() { List parameterNames = new ArrayList(queryParameters.size()); for (Iterator it = queryParameters.iterator(); it.hasNext();) { QueryParameterEntry paramEntry = it.next(); if (!(paramEntry instanceof QueryParameter)) { throw new JRRuntimeException( EXCEPTION_MESSAGE_KEY_UNSUPPORTED_PARAMETER_TYPE, new Object[]{"getCollectedParameterNames()", paramEntry.getClass().getName()}); } QueryParameter param = (QueryParameter) paramEntry; parameterNames.add(param.getName()); } return parameterNames; } /** * Returns the list of {@link QueryParameter query parameters} in the order in which they appear in the query. * * @return the list of query parameters */ protected List getCollectedParameters() { List params = new ArrayList(queryParameters.size()); for (QueryParameterEntry parameterEntry : queryParameters) { if (!(parameterEntry instanceof QueryParameter)) { throw new JRRuntimeException( EXCEPTION_MESSAGE_KEY_UNSUPPORTED_PARAMETER_TYPE, new Object[]{"getCollectedParameters()", parameterEntry.getClass().getName()}); } params.add((QueryParameter) parameterEntry); } return Collections.unmodifiableList(params); } protected void visitQueryParameters(QueryParameterVisitor visitor) throws VisitExceptionWrapper { for (QueryParameterEntry queryParameter : queryParameters) { queryParameter.accept(visitor); } } /** * Returns the value of a fill parameter. * @param parameterName the parameter name * @param ignoreMissing if true, the method will return null for non existing parameters; * otherwise, an exception will be thrown if the parameter does not exist * @return the parameter value */ protected Object getParameterValue(String parameterName, boolean ignoreMissing) { if (ignoreMissing) { JRValueParameter parameter = getValueParameter(JRParameter.REPORT_PARAMETERS_MAP, false); @SuppressWarnings("unchecked") Map parametersMap = (Map)parameter.getValue(); return parametersMap.get(parameterName); } JRValueParameter parameter = getValueParameter(parameterName, ignoreMissing); return parameter == null ? null : parameter.getValue(); } /** * Returns the value of a fill parameter. * @param parameterName the parameter name * @return the parameter value */ protected Object getParameterValue(String parameterName) { return getParameterValue(parameterName, false); } /** * */ protected boolean parameterHasValue(String parameter) { JRValueParameter reportParametersMap = getValueParameter(JRParameter.REPORT_PARAMETERS_MAP, false); @SuppressWarnings("unchecked") Map parametersMap = (Map)reportParametersMap.getValue(); return parametersMap.containsKey(parameter); } /** * */ protected String getStringParameter(String parameter, String property) { if (parameterHasValue(parameter)) { return (String)getParameterValue(parameter, true); } else { return getPropertiesUtil().getProperty( dataset.getPropertiesMap(), property ); } } /** * */ protected String getStringParameterOrProperty(String name) { return getStringParameter(name, name); } /** * */ protected boolean getBooleanParameter(String parameter, String property, boolean defaultValue) { if (parameterHasValue(parameter)) { Boolean booleanValue = (Boolean)getParameterValue(parameter, true); if (booleanValue == null) { return getPropertiesUtil().getBooleanProperty(property); } else { return booleanValue.booleanValue(); } } else { return getPropertiesUtil().getBooleanProperty( dataset.getPropertiesMap(), property, defaultValue ); } } /** * */ protected boolean getBooleanParameterOrProperty(String name, boolean defaultValue) { return getBooleanParameter(name, name, defaultValue); } /** * */ protected Boolean getBooleanParameter(String parameter, String property) { if (parameterHasValue(parameter)) { Boolean booleanValue = (Boolean)getParameterValue(parameter, true); if (booleanValue == null) { return getPropertiesUtil().getBooleanProperty(property); } else { return booleanValue.booleanValue(); } } else { return getPropertiesUtil().getBooleanProperty( dataset.getPropertiesMap(), property ); } } /** * */ protected Boolean getBooleanParameterOrProperty(String name) { return getBooleanParameter(name, name); } /** * Return a fill parameter from the parameter map. * * @param parameterName the parameter name * @return the parameter * @deprecated {@link #getValueParameter(String) getValueParameter(String)} should be used instead */ protected JRFillParameter getParameter(String parameterName) { JRFillParameter parameter = (JRFillParameter) parametersMap.get(parameterName); if (parameter == null) { throw new JRRuntimeException( EXCEPTION_MESSAGE_KEY_PARAMETER_NOT_FOUND, new Object[]{parameterName}); } return parameter; } protected void checkParameter(String parameterName) { if (!parametersMap.containsKey(parameterName)) { throw new JRRuntimeException( EXCEPTION_MESSAGE_KEY_PARAMETER_NOT_FOUND, new Object[]{parameterName}); } } /** * Return a value parameter from the parameters map. * * @param parameterName the parameter name * @param ignoreMissing if true, the method will return null for non existing parameters; * otherwise, an exception will be thrown if the parameter does not exist * @return the parameter */ protected JRValueParameter getValueParameter(String parameterName, boolean ignoreMissing) { JRValueParameter parameter = parametersMap.get(parameterName); if (parameter == null && !ignoreMissing) { throw new JRRuntimeException( EXCEPTION_MESSAGE_KEY_PARAMETER_NOT_FOUND, new Object[]{parameterName}); } return parameter; } /** * Return a value parameter from the parameters map. * * @param parameterName the parameter name * @return the parameter */ protected JRValueParameter getValueParameter(String parameterName) { return getValueParameter(parameterName, false); } /** * Returns the replacement text for a query parameter. * * @param parameterName the parameter name * @return the replacement text * @see JRQueryChunk#TYPE_PARAMETER */ protected abstract String getParameterReplacement(String parameterName); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy