/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.printing;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.servoy.base.query.BaseQueryTable;
import com.servoy.base.query.IBaseSQLCondition;
import com.servoy.base.util.ITagResolver;
import com.servoy.j2db.IApplication;
import com.servoy.j2db.dataprocessing.FoundSet;
import com.servoy.j2db.dataprocessing.FoundSetManager;
import com.servoy.j2db.dataprocessing.IDataServer;
import com.servoy.j2db.dataprocessing.IDataSet;
import com.servoy.j2db.dataprocessing.IDisplay;
import com.servoy.j2db.dataprocessing.IDisplayData;
import com.servoy.j2db.dataprocessing.IFoundSetInternal;
import com.servoy.j2db.dataprocessing.ISwingFoundSet;
import com.servoy.j2db.dataprocessing.SQLGenerator;
import com.servoy.j2db.dataprocessing.SortColumn;
import com.servoy.j2db.dataprocessing.SubSummaryFoundSet;
import com.servoy.j2db.persistence.AggregateVariable;
import com.servoy.j2db.persistence.Column;
import com.servoy.j2db.persistence.Form;
import com.servoy.j2db.persistence.IDataProvider;
import com.servoy.j2db.persistence.IPersist;
import com.servoy.j2db.persistence.Part;
import com.servoy.j2db.persistence.Relation;
import com.servoy.j2db.persistence.RepositoryException;
import com.servoy.j2db.persistence.Table;
import com.servoy.j2db.query.AbstractBaseQuery;
import com.servoy.j2db.query.CompareCondition;
import com.servoy.j2db.query.IQuerySelectValue;
import com.servoy.j2db.query.IQuerySort;
import com.servoy.j2db.query.ISQLTableJoin;
import com.servoy.j2db.query.Placeholder;
import com.servoy.j2db.query.QueryAggregate;
import com.servoy.j2db.query.QueryColumn;
import com.servoy.j2db.query.QuerySelect;
import com.servoy.j2db.query.QuerySort;
import com.servoy.j2db.query.TablePlaceholderKey;
import com.servoy.j2db.smart.dataui.DataRenderer;
import com.servoy.j2db.ui.IDisplayTagText;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.RendererParentWrapper;
import com.servoy.j2db.util.Text;
/**
* Node used in chain
*
* @author jblok
*/
public class PartNode
{
private PartNode child;//child node if any
private final Part part;//the part this node is meant for (can be null in case of virtual body)
private final DataRenderer renderer;// the renderer (can be null in case of virtual body)
private final List<AggregateVariable> allAggregates;//all aggregates used for group by
private final SortColumn[] sortColumns;//sort columns subtracted from all sort columns to be able to make related(!) query
private final RendererParentWrapper renderParent;
//it is possible to have a leading and trailing part on same column(s)
private boolean isLeadingAndTrailingSubsummary = false;//must be true for leading and trailing on same column
private Part second_part; //always the trailing part if IS_LEADING_AND_TRIALING_SUBSUMMARY
private DataRenderer second_renderer; //always the trailing part renderer if IS_LEADING_AND_TRIALING_SUBSUMMARY
private final IApplication application;
void setSecondPartAsTrailingRenderer(Part p, DataRenderer r) throws RepositoryException
{
isLeadingAndTrailingSubsummary = true;
second_part = p;
second_renderer = r;
if (second_renderer != null)
{
getAggregatesFromRenderer(allAggregates, (Form)p.getParent(), second_renderer);
}
}
private void getAggregatesFromRenderer(final List<AggregateVariable> aggregates, final Form f, DataRenderer a_renderer) throws RepositoryException
{
Map<IPersist, IDisplay> allFields = a_renderer.getFieldComponents();
Iterator<IDisplay> it = allFields.values().iterator();
while (it.hasNext())
{
IDisplay display = it.next();
if (display instanceof IDisplayData)
{
String dataProviderID = ((IDisplayData)display).getDataProviderID();
if (dataProviderID != null)
{
IDataProvider dp = application.getFlattenedSolution().getDataproviderLookup(application.getFoundSetManager(), f).getDataProvider(
dataProviderID);
if (dp instanceof AggregateVariable)
{
if (!aggregates.contains(dp)) aggregates.add((AggregateVariable)dp);
}
}
else if (display instanceof IDisplayTagText)
{
String tagText = ((IDisplayTagText)display).getTagText();
Text.processTags(tagText, new ITagResolver()
{
public String getStringValue(String name)
{
try
{
IDataProvider dp = application.getFlattenedSolution().getDataproviderLookup(application.getFoundSetManager(), f).getDataProvider(
name);
if (dp instanceof AggregateVariable)
{
if (!aggregates.contains(dp)) aggregates.add((AggregateVariable)dp);
}
}
catch (Exception e)
{
Debug.error(e);
}
return null;
}
});
}
}
}
}
PartNode(FormPreviewPanel fpp, Part p, DataRenderer r, RendererParentWrapper renderParent, SortColumn[] scs) throws Exception
{
application = fpp.getApplication();
sortColumns = scs;
part = p;
renderer = r;
this.renderParent = renderParent;
//get aggregates from part
allAggregates = new ArrayList<AggregateVariable>();
if (renderer != null)
{
getAggregatesFromRenderer(allAggregates, (Form)p.getParent(), renderer);
}
}
/**
* Sets the child.
*
* @param child The child to set
*/
public void setChild(PartNode child)
{
this.child = child;
}
public PartNode getChild()
{
return child;
}
public SortColumn[] getSortColumns()
{
return sortColumns;
}
public boolean isLeading()
{
return (part.getPartType() == Part.LEADING_SUBSUMMARY);
}
public List<DataRendererDefinition> process(FormPreviewPanel fpp, FoundSet fs, Table table, QuerySelect sqlString) throws Exception
{
//Selection model must be in print mode to be able to set the selection to -1 . Otherwise is not allowed by the selectionModel
((ISwingFoundSet)fs).getSelectionModel().hideSelectionForPrinting();
FoundSet rootSet = (FoundSet)fs.copy(false);//this is needed because we must keep sql the same in foundset during printing
foundSets.add(rootSet);
IApplication app = fpp.getApplication();
List<DataRendererDefinition> list = new ArrayList<DataRendererDefinition>();//retval
if (part != null && (part.getPartType() == Part.LEADING_SUBSUMMARY || part.getPartType() == Part.TRAILING_SUBSUMMARY || isLeadingAndTrailingSubsummary))
{
QuerySelect newSQLString = AbstractBaseQuery.deepClone(sqlString);
IDataServer server = app.getDataServer();
//build the sql parts based on sort columns
ArrayList<IQuerySelectValue> selectCols = new ArrayList<IQuerySelectValue>();
ArrayList<QueryColumn> groupbyCols = new ArrayList<QueryColumn>();
ArrayList<QuerySort> sortbyCols = new ArrayList<QuerySort>();
for (SortColumn element : sortColumns)
{
BaseQueryTable queryTable = sqlString.getTable();
Relation[] relations = element.getRelations();
if (relations != null)
{
for (Relation relation : relations)
{
ISQLTableJoin join = (ISQLTableJoin)sqlString.getJoin(queryTable, relation.getName());
if (join == null)
{
Debug.log("Missing relation " + relation.getName() + " in join condition for form on table " + table.getName()); //$NON-NLS-1$ //$NON-NLS-2$
}
else
{
queryTable = join.getForeignTable();
}
}
}
Column column = (Column)element.getColumn();
QueryColumn queryColumn = new QueryColumn(queryTable, column.getID(), column.getSQLName(), column.getType(), column.getLength(),
column.getScale(), column.getFlags());
selectCols.add(queryColumn);
groupbyCols.add(queryColumn);
sortbyCols.add(new QuerySort(queryColumn, element.getSortOrder() == SortColumn.ASCENDING));
}
//make sql
for (int i = 0; i < allAggregates.size(); i++)
{
AggregateVariable ag = allAggregates.get(i);
selectCols.add(new QueryAggregate(ag.getType(), new QueryColumn(newSQLString.getTable(), -1, ag.getColumnNameToAggregate(),
ag.getDataProviderType(), ag.getLength(), 0, ag.getFlags()), ag.getName()));
}
newSQLString.setColumns(selectCols);
newSQLString.setGroupBy(groupbyCols);
ArrayList<IQuerySort> oldSort = newSQLString.getSorts();
newSQLString.setSorts(sortbyCols);//fix the sort (if columns not are selected of used in groupby they cannot be used in sort)
FoundSetManager foundSetManager = ((FoundSetManager)app.getFoundSetManager());
String transaction_id = foundSetManager.getTransactionID(table.getServerName());
IDataSet data = server.performQuery(app.getClientID(), table.getServerName(), transaction_id, newSQLString,
foundSetManager.getTableFilterParams(table.getServerName(), newSQLString), false, 0, foundSetManager.pkChunkSize * 4, IDataServer.PRINT_QUERY);
SubSummaryFoundSet newSet = new SubSummaryFoundSet(app.getFoundSetManager(), rootSet, sortColumns, allAggregates, data, table);//create a new FoundSet with 'data' and with right 'table', 'where','whereArgs'
newSQLString.setSorts(oldSort);//restore the sort for child body parts
//make new where for use in sub queries
for (int i = 0; i < sortbyCols.size(); i++)
{
QueryColumn sc = (QueryColumn)(sortbyCols.get(i)).getColumn();
newSQLString.addCondition(SQLGenerator.CONDITION_SEARCH, new CompareCondition(IBaseSQLCondition.EQUALS_OPERATOR, sc, new Placeholder(
new TablePlaceholderKey(sc.getTable(), '#' + sc.getName()))));
}
int count = newSet.getSize();
for (int ii = 0; ii < count; ii++)
{
QuerySelect newSQLStringCopy = AbstractBaseQuery.deepClone(newSQLString);//make copy for setting sort column
//handle the child first, this puts the rootset in the right state! for use of related(!) fields in the subsums
//THIS is EXTREMELY important for correct printing, see also SubSummaryFoundSet.queryForRelatedFoundSet
List<DataRendererDefinition> childRetval = null;
IFoundSetInternal curLeafFoundSet = null;
if (child != null)
{
for (int i = 0; i < sortbyCols.size(); i++)
{
QueryColumn sc = (QueryColumn)(sortbyCols.get(i)).getColumn();
TablePlaceholderKey placeholderKey = new TablePlaceholderKey(sc.getTable(), '#' + sc.getName());
if (!newSQLStringCopy.setPlaceholderValue(placeholderKey, data.getRow(ii)[i]))
{
Debug.error(new RuntimeException("Could not set placeholder " + placeholderKey + " in query " + newSQLStringCopy + "-- continuing")); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
}
}
childRetval = child.process(fpp, rootSet, table, newSQLStringCopy);
curLeafFoundSet = child.getCurrentLeafFoundSet();
}
SubSummaryFoundSet.PrintState state = (SubSummaryFoundSet.PrintState)newSet.getRecord(ii);
state.setDelegate(curLeafFoundSet);
if (part.getPartType() == Part.LEADING_SUBSUMMARY)
{
state.doAggregatesLookup();
list.add(new DataRendererDefinition(fpp, renderParent, part, renderer, state));
}
if (childRetval != null)
{
list.addAll(childRetval);
}
if (isLeadingAndTrailingSubsummary)
{
state.doAggregatesLookup();
list.add(new DataRendererDefinition(fpp, renderParent, second_part, second_renderer, state));
}
else if (part.getPartType() == Part.TRAILING_SUBSUMMARY)
{
state.doAggregatesLookup();
list.add(new DataRendererDefinition(fpp, renderParent, part, renderer, state));
}
}
}
else
//for handeling (virtual) body part
{
rootSet.browseAll(sqlString);
int count = app.getFoundSetManager().getFoundSetCount(rootSet);
for (int ii = 0; ii < count; ii++)
{
currentLeafFoundSet = rootSet;
list.add(new DataRendererDefinition(fpp, renderParent, part, renderer, rootSet, ii));
}
}
return list;
}
private IFoundSetInternal currentLeafFoundSet;
public IFoundSetInternal getCurrentLeafFoundSet()
{
if (child != null)
{
return child.getCurrentLeafFoundSet();
}
else
{
return currentLeafFoundSet;
}
}
@Override
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append("Node "); //$NON-NLS-1$
sb.append((part == null ? "Virtual Body Part" : part.getEditorName())); //$NON-NLS-1$
if (isLeadingAndTrailingSubsummary)
{
sb.append(" && "); //$NON-NLS-1$
sb.append(second_part.getEditorName());
}
sb.append(" "); //$NON-NLS-1$
sb.append(allAggregates.size());
sb.append(" aggregates"); //$NON-NLS-1$
sb.append("\n\t"); //$NON-NLS-1$
if (child != null) sb.append(child);
return sb.toString();
}
private final List<FoundSet> foundSets = new ArrayList<FoundSet>();
public void removePrintedStatesFromFoundSets()
{
for (int i = 0; i < foundSets.size(); i++)
{
FoundSet fs = foundSets.get(i);
if (fs.getSize() > 0 && fs.getSelectedIndex() == -1) fs.setSelectedIndex(0);
fs.flushAllCachedItems();
}
if (child != null) child.removePrintedStatesFromFoundSets();
}
}