/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2014 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.server.ngclient.property;
import com.servoy.j2db.dataprocessing.FoundSetEvent;
import com.servoy.j2db.dataprocessing.IFoundSetEventListener;
import com.servoy.j2db.dataprocessing.IFoundSetInternal;
import com.servoy.j2db.dataprocessing.IRecordInternal;
/**
* Holds the client used viewport info for this foundset.
*
* @author acostescu
*/
public class FoundsetTypeViewport
{
protected int startIndex = 0;
protected int size = 0;
protected FoundsetTypeChangeMonitor changeMonitor;
protected IFoundSetInternal foundset;
protected IFoundSetEventListener foundsetEventListener;
private int preferredViewPortSize = 15;
/**
* Creates a new viewport object.
* @param changeMonitor change monitor can be used to announce changes in viewport (bounds).
*/
public FoundsetTypeViewport(FoundsetTypeChangeMonitor changeMonitor)
{
this.changeMonitor = changeMonitor;
}
protected void setFoundset(IFoundSetInternal newFoundset)
{
if (foundset != null) foundset.removeFoundSetEventListener(getFoundsetEventListener());
if (newFoundset != null) newFoundset.addFoundSetEventListener(getFoundsetEventListener());
this.foundset = newFoundset;
// reset to the preferred viewport size if that is set
setBounds(0, foundset != null ? Math.min(preferredViewPortSize, foundset.getSize()) : 0);
changeMonitor.viewPortCompletelyChanged();
}
public int getStartIndex()
{
return startIndex;
}
public int getSize()
{
return size;
}
/**
* The viewPort needs to change to the new startIndex/size.
*/
public void setBounds(int startIndex, int size)
{
int oldStartIndex = this.startIndex;
int oldSize = this.size;
correctAndSetViewportBoundsInternal(startIndex, size);
if (oldStartIndex != this.startIndex || oldSize != this.size) changeMonitor.viewPortCompletelyChanged();
}
/**
* Extends the viewport - useful for sending more records to client without re-sending the whole viewport.
*
* @param positiveOrNegativeRecordNo the number of records to extend the viewPort with. A positive value
* will append records at the end of the viewPort and a negative one will prepend (add to the beginning).
*/
public void loadExtraRecords(int positiveOrNegativeRecordNo)
{
int oldStartIndex = this.startIndex;
int oldSize = this.size;
if (positiveOrNegativeRecordNo >= 0)
{
correctAndSetViewportBoundsInternal(oldStartIndex, oldSize + positiveOrNegativeRecordNo);
changeMonitor.recordsInserted(this.startIndex + oldSize, this.startIndex + this.size - 1, this, true);
}
else
{
this.startIndex = Math.max(positiveOrNegativeRecordNo + startIndex, 0);
this.size += (oldStartIndex - startIndex);
changeMonitor.recordsInserted(this.startIndex, oldStartIndex - 1, this, true);
}
if (oldStartIndex != startIndex || oldSize != size) changeMonitor.viewPortBoundsOnlyChanged();
}
/**
* Corrects bounds given new bounds to be valid and then applies the to current viewport.
*
* This method can also load more records into the foundset (thus firing foundset events) in case of large foundsets with 'hadMoreRecords' true,
* in case the give new bounds require new records.
*/
protected void correctAndSetViewportBoundsInternal(int newStartIndex, int newSize)
{
if (foundset != null)
{
IRecordInternal firstRec = foundset.getRecord(newStartIndex); // this can trigger a query for more records if foundset hadMoreRows is true; that in turn can update through listener serverSize and hadMoreRows related flags on the change monitor
if (firstRec != null)
{
if (newSize > 0)
{
IRecordInternal lastRec = foundset.getRecord(newStartIndex + newSize - 1); // this can trigger a query for more records if foundset hadMoreRows is true; that in turn can update through listener serverSize and hadMoreRows related flags on the change monitor
startIndex = newStartIndex; // do this after getRecord above would potentially load more records, trigger inserted event and potentially wrongly adjust current viewport bounds
if (lastRec == null)
{
size = foundset.getSize() - startIndex;
}
else
{
size = newSize;
}
}
else
{
startIndex = newStartIndex;
size = 0;
}
}
else
{
startIndex = 0;
size = 0;
}
}
else
{
startIndex = 0;
size = 0;
}
}
protected IFoundSetEventListener getFoundsetEventListener()
{
if (foundsetEventListener == null)
{
foundsetEventListener = new IFoundSetEventListener()
{
@Override
public void foundSetChanged(FoundSetEvent event)
{
if (event.getType() == FoundSetEvent.FIND_MODE_CHANGE) changeMonitor.findModeChanged(foundset.isInFindMode());
else if (event.getType() == FoundSetEvent.FOUNDSET_INVALIDATED) changeMonitor.foundsetInvalidated();
else if (event.getType() == FoundSetEvent.CONTENTS_CHANGED)
{
// partial change only push the changes.
if (event.getChangeType() == FoundSetEvent.CHANGE_DELETE)
{
changeMonitor.recordsDeleted(event.getFirstRow(), event.getLastRow(), FoundsetTypeViewport.this);
}
else if (event.getChangeType() == FoundSetEvent.CHANGE_INSERT)
{
if (size == 0)
{
// reset to the preferred viewport size if that is set
setBounds(0, Math.min(preferredViewPortSize, foundset.getSize()));
changeMonitor.viewPortCompletelyChanged();
}
else changeMonitor.recordsInserted(event.getFirstRow(), event.getLastRow(), FoundsetTypeViewport.this, false); // true - slide if first so that viewPort follows the first record
}
else if (event.getChangeType() == FoundSetEvent.CHANGE_UPDATE)
{
changeMonitor.recordsUpdated(event.getFirstRow(), event.getLastRow(), foundset.getSize(), FoundsetTypeViewport.this);
}
changeMonitor.checkHadMoreRows();
}
}
};
}
return foundsetEventListener;
}
protected void dispose()
{
if (foundset != null) foundset.removeFoundSetEventListener(getFoundsetEventListener());
}
/**
* Slides the viewPort (startIndex) to higher or lower values and then corrects viewPort bounds (if they became invalid due to foundset changes).<br/>
* Call this only when the viewPort data remains the same or when viewPort data will be updated through granular add/remove operations.
*
* @param delta can be a positive or negative value.
*/
protected void slideAndCorrect(int delta)
{
int oldStartIndex = startIndex;
int oldSize = size;
correctAndSetViewportBoundsInternal(oldStartIndex + delta, oldSize);
if (oldStartIndex != startIndex || oldSize != size) changeMonitor.viewPortBoundsOnlyChanged();
}
/**
* Sets the preferred viewport size
* @param int1
*/
public void setPreferredViewportSize(int preferredViewPortSize)
{
this.preferredViewPortSize = preferredViewPortSize;
}
}