package org.geogebra.common.gui.view.data;
//import geogebra.gui.GuiManagerD;
import java.util.ArrayList;
import org.geogebra.common.gui.view.spreadsheet.CellRange;
import org.geogebra.common.gui.view.spreadsheet.CellRangeProcessor;
import org.geogebra.common.gui.view.spreadsheet.MyTable;
import org.geogebra.common.gui.view.spreadsheet.RelativeCopy;
import org.geogebra.common.gui.view.spreadsheet.SpreadsheetViewInterface;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.algos.AlgoDependentList;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.main.App;
import org.geogebra.common.plugin.GeoClass;
import org.geogebra.common.util.debug.Log;
/**
* A DataItem maintains a reference to an existing GeoList or a set of
* GeoElements that can be used to generate a single list of data values.
*
* DataItems are the basic elements used by the classes DataVariable and
* DataSource to manage data for DataAnalysisView.
*
* @author G. Sturr
*
*/
public class DataItem {
/**
* Identifiers for the possible sources of a DataItem
*/
public static enum SourceType {
SPREADSHEET, LIST, CLASS, INTERNAL, EMPTY
}
private SourceType sourceType;
private GeoClass geoClass = GeoClass.NUMERIC;
// source objects
private ArrayList<CellRange> rangeList;
private GeoList geoList;
private Double[] leftBorder;
private String[] strInternal;
private String description = " ";
// =======================================
// Constructors
// =======================================
/**
* Constructs a DataItem from a GeoList.
*
* @param geoList
*/
public DataItem(GeoList geoList) {
this.geoList = geoList;
this.sourceType = SourceType.LIST;
}
/**
* Constructs a DataItem from a list of spreadsheet cell ranges.
*
* @param rangeList
*/
public DataItem(ArrayList<CellRange> rangeList) {
this.rangeList = rangeList;
this.sourceType = SourceType.SPREADSHEET;
}
/**
* Constructs a DataItem from a single spreadsheet cell range.
*
* @param cellRange
*/
public DataItem(CellRange cellRange) {
rangeList = new ArrayList<CellRange>();
rangeList.add(cellRange);
this.sourceType = SourceType.SPREADSHEET;
}
/**
* Constructs a DataItem from an array of double (used for class borders
* when classes are generated automatically).
*
* @param leftBorder
*/
public DataItem(Double[] leftBorder) {
this.leftBorder = leftBorder;
this.sourceType = SourceType.CLASS;
}
/**
* @param internalData
*/
public DataItem(String[] internalData) {
this.sourceType = SourceType.INTERNAL;
this.strInternal = internalData;
}
/**
* Constructs a DataItem without a given source object.
*/
public DataItem() {
sourceType = SourceType.EMPTY;
}
// ============================================
// Getters/Setters
// ============================================
/**
* Clears this DataItem and sets the source to the given list of spreadsheet
* cell ranges.
*
* @param rangeList
*/
public void setDataItem(ArrayList<CellRange> rangeList) {
clearItem();
this.rangeList = rangeList;
this.sourceType = SourceType.SPREADSHEET;
}
/**
* Clears this DataItem and sets the source to the given GeoList.
*
* @param geoList
*/
public void setDataItem(GeoList geoList) {
clearItem();
this.geoList = geoList;
this.sourceType = SourceType.LIST;
}
/**
* Clears this DataItem and sets the source to the given array of double.
* (used for class borders when classes are generated automatically).
*
* @param leftBorder
*/
public void setDataItem(Double[] leftBorder) {
clearItem();
this.leftBorder = leftBorder;
this.sourceType = SourceType.CLASS;
}
public ArrayList<CellRange> getRangeList() {
return rangeList;
}
public GeoList getGeoList() {
return geoList;
}
public Double[] getLeftBorder() {
return leftBorder;
}
public String[] getStrInternal() {
return strInternal;
}
public SourceType getType() {
return sourceType;
}
public GeoClass getGeoClass() {
return geoClass;
}
public void setGeoClass(GeoClass geoClass) {
this.geoClass = geoClass;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
// ==============================================
// Utility methods
// ==============================================
public void clearItem() {
// TODO: dereference the geo fields -- needed ??
}
public boolean containsGeoClass(GeoClass geoClass) {
if (sourceType == SourceType.EMPTY) {
return false;
}
switch (sourceType) {
case LIST:
if (geoList == null) {
return false;
}
for (int i = 0; i < geoList.size(); i++) {
if (geoList.get(i).getGeoClassType() == geoClass) {
return true;
}
}
break;
case SPREADSHEET:
if (rangeList == null) {
return false;
}
for (CellRange cr : rangeList) {
if (cr.containsGeoClass(geoClass)) {
return true;
}
}
break;
}
return false;
}
/**
* @return true if this DataItem has a null source
*/
private boolean isEmptyItem() {
// TODO also check for empty sources?
/*
* if(getGeoCount() == 0){ return false; }
*/
switch (sourceType) {
case LIST:
return geoList == null;
case SPREADSHEET:
return rangeList == null;
case CLASS:
return leftBorder == null;
case INTERNAL:
return strInternal == null;
case EMPTY:
return true;
default:
return false;
}
}
/**
* @return number of GeoElements referenced by this DataItem
*/
public int getGeoCount() {
switch (sourceType) {
case LIST:
return geoList.size();
case SPREADSHEET:
int count = 0;
for (CellRange cr : rangeList) {
count += cr.getGeoCount(null);
}
return count;
case CLASS:
return leftBorder.length;
case INTERNAL:
return strInternal.length;
case EMPTY:
return 0;
default:
return 0;
}
}
/**
* Returns a string description of the data source
*
* @param app
*
* @return either a spreadsheet cell range name or a GeoList label
*/
public String getSourceString(App app) {
String sourceString;
switch (sourceType) {
case LIST:
sourceString = getGeoList()
.getLabel(StringTemplate.defaultTemplate);
break;
case SPREADSHEET:
sourceString = spreadsheetTable(app).getCellRangeProcessor()
.getCellRangeString(getRangeList());
break;
case INTERNAL:
sourceString = "Untitled";
break;
default:
case CLASS:
sourceString = " ";
break;
}
return sourceString;
}
/**
* @param app
* @param spreadsheetTable
* @param enableHeader
* @return DataItem title
*/
public String getDataTitle(App app, boolean enableHeader) {
if (!enableHeader || sourceType == SourceType.LIST) {
return (getSourceString(app));
} else if (enableHeader && sourceType == SourceType.SPREADSHEET) {
StringTemplate tpl = StringTemplate.defaultTemplate;
CellRange range = getRangeList().get(0);
if (range.isColumn() || range.isPartialColumn()) {
GeoElement geo = RelativeCopy.getValue(app,
range.getMinColumn(), range.getMinRow());
if (geo != null) {
return geo.toDefinedValueString(tpl);
}
}
}
return app.getLocalization().getMenu("Untitled");
}
/**
* Converts DataItem into a GeoList
*
* @param app
* @param enableHeader
* @param leftToRight
* @param doCopy
*
* @return A GeoList containing elements corresponding to this DataItem
*/
protected GeoList toGeoList(App app, boolean enableHeader,
boolean leftToRight, boolean doCopy) {
if (sourceType == SourceType.EMPTY) {
return null;
}
Construction cons = app.getKernel().getConstruction();
GeoList list;
switch (sourceType) {
case LIST:
if (doCopy) {
list = dependentListCopy(cons, getGeoList());
} else {
list = getGeoList();
}
if (!leftToRight) {
swapXYCoords(list);
}
break;
case SPREADSHEET:
boolean scanByColumn = true;
boolean copyByValue = doCopy; // allows dynamic changes
boolean doStoreUndo = false;
boolean isSorted = false;
boolean setLabel = false;
try {
ArrayList<CellRange> rangeListCopy = rangeListCopy(
getRangeList(), enableHeader);
list = crProcessor(app).createList(rangeListCopy, scanByColumn,
copyByValue, isSorted, doStoreUndo, geoClass, setLabel);
} catch (Exception e) {
e.printStackTrace();
return null;
}
break;
case CLASS:
list = new GeoList(cons);
for (int i = 0; i < getLeftBorder().length; i++) {
list.add(new GeoNumeric(cons, getLeftBorder()[i]));
}
break;
case INTERNAL:
String[] s = getStrInternal();
boolean oldSuppress = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
list = new GeoList(cons);
for (int i = 0; i < s.length; i++) {
try {
double num = Double.parseDouble(s[i]);
// System.out.println(num);
GeoElement geo = new GeoNumeric(cons);
((GeoNumeric) geo).setValue(num);
list.add(geo);
} catch (Exception e) {
// e.printStackTrace();
Log.error(e.getMessage());
}
}
cons.setSuppressLabelCreation(oldSuppress);
break;
default:
return null;
}
if (!leftToRight && list.getElementType() == GeoClass.POINT) {
swapXYCoords(list);
}
return list;
}
/**
* Copies a list of cell ranges with option to remove header cell
*
* @param list
* @param removeHeaderCell
* @return
*/
private static ArrayList<CellRange> rangeListCopy(ArrayList<CellRange> list,
boolean removeHeaderCell) {
ArrayList<CellRange> list2 = new ArrayList<CellRange>();
list2.add(rangeCopy(list.get(0), removeHeaderCell));
for (int i = 1; i < list.size(); i++) {
list2.add(list.get(i).duplicate());
}
return list2;
}
/**
* Copies a cell range with option to remove header cell
*
* @param cr
* @param removeHeaderCell
* @return
*/
private static CellRange rangeCopy(CellRange cr, boolean removeHeaderCell) {
CellRange cr2 = cr.duplicate();
if (removeHeaderCell) {
cr2.setCellRange(cr.getMinColumn(), cr.getMinRow() + 1,
cr.getMaxColumn(), cr.getMaxRow());
}
return cr2;
}
private static GeoList dependentListCopy(Construction cons,
GeoList geoList) {
ArrayList<GeoElement> copyList = new ArrayList<GeoElement>();
for (int i = 0; i < geoList.size(); i++) {
copyList.add(geoList.get(i).copy());
}
AlgoDependentList algo = new AlgoDependentList(cons, copyList, false);
cons.removeFromConstructionList(algo);
return (GeoList) algo.getGeoElements()[0];
}
private static void swapXYCoords(GeoList geoList) {
if (geoList.getElementType() != GeoClass.POINT) {
return;
}
for (int i = 0; i < geoList.size(); i++) {
double x = ((GeoPoint) geoList.get(i)).x;
double y = ((GeoPoint) geoList.get(i)).y;
((GeoPoint) geoList.get(i)).setCoords(y, x, 1.0);
}
geoList.updateCascade();
}
/**
* @param enableHeader
* @return
*/
public String[] toStringArray(boolean enableHeader) {
ArrayList<String> list = toStringList(enableHeader);
if (list == null) {
return new String[0];
}
return list.toArray(new String[list.size()]);
}
/**
* @param enableHeader
* if true then the first cell of a spreadsheet range is ignored
* @return list of String descriptions of the data represented by this
* DataItem
*/
public ArrayList<String> toStringList(boolean enableHeader) {
if (isEmptyItem()) {
return null;
}
ArrayList<String> strList = new ArrayList<String>();
try {
switch (sourceType) {
case LIST:
for (int i = 0; i < geoList.size(); i++) {
if (geoList.get(i) == null
|| !(geoList.get(i).isDefined())) {
continue;
}
if (isValidDataType(geoList.get(i))) {
strList.add(i, geoList.get(i).getValueForInputBar());
} else {
strList.add(i,
"<html><i><font color = gray>"
+ geoList.get(i).getValueForInputBar()
+ "</font></i></html>");
}
}
break;
case SPREADSHEET:
boolean skipFirstCell = enableHeader;
for (CellRange cr : rangeList) {
ArrayList<GeoElement> list = cr.toGeoList();
// iterate through the list and set the row values
for (int i = 0; i < list.size(); i++) {
if (skipFirstCell) {
skipFirstCell = false;
continue;
}
if (list.get(i) == null || !(list.get(i).isDefined())) {
continue;
}
if (isValidDataType(list.get(i))) {
strList.add(list.get(i).getValueForInputBar());
} else {
strList.add("<html><i><font color = gray>"
+ list.get(i).getValueForInputBar()
+ "</font></i></html>");
}
}
}
break;
case INTERNAL:
String[] str = getStrInternal();
// load the array into the column
for (int i = 0; i < str.length; i++) {
if (i < str.length && str[i] != null) {
strList.add(i, str[i]);
} else {
strList.add(i, " ");
}
}
break;
case CLASS:
Double[] leftBorder1 = getLeftBorder();
// System.out.println("=====> " + Arrays.toString(leftBorder));
// load the array into the column
for (int i = 0; i < leftBorder1.length - 1; i++) {
if (i < leftBorder1.length && leftBorder1[i] != null) {
String interval = leftBorder1[i] + " - "
+ leftBorder1[i + 1];
strList.add(i, interval);
} else {
strList.add(i, " ");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return strList;
}
/**
* @return true if the given GeoElement is a valid element for this DataItem
*/
private boolean isValidDataType(GeoElement geo) {
return geo.getGeoClassType() == geoClass;
}
private static CellRangeProcessor crProcessor(App app) {
return spreadsheetTable(app).getCellRangeProcessor();
}
private static MyTable spreadsheetTable(App app) {
SpreadsheetViewInterface spvi = app
.getGuiManager().getSpreadsheetView();
return (MyTable) spvi.getSpreadsheetTable();
}
}