package org.geogebra.common.gui.view.data;
import java.util.ArrayList;
import org.geogebra.common.awt.GPoint;
import org.geogebra.common.gui.view.data.DataItem.SourceType;
import org.geogebra.common.gui.view.spreadsheet.CellRange;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.algos.AlgoDependentList;
import org.geogebra.common.kernel.algos.AlgoDependentPoint;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.MyVecNode;
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.main.Localization;
import org.geogebra.common.plugin.GeoClass;
import org.geogebra.common.plugin.Operation;
import org.geogebra.common.util.debug.Log;
/**
* A DataVariable is a collection of DataItems representing data as a list of
* raw data values, data values with corresponding frequencies, or as classes
* with corresponding frequencies.
*
* @author G. Sturr
*
*/
public class DataVariable {
/**
* Identifier for the data grouping type
*/
public enum GroupType {
RAWDATA, FREQUENCY, CLASS
}
private App app;
private GroupType groupType;
private GeoClass geoClass;
private DataItem frequency, label, classes;
private ArrayList<DataItem> values;
private boolean enableHeader = false;
private double classStart = 0.0;
private double classWidth = 1.0;
private Localization loc;
/**
* Constructs a DataVariable
*
* @param app
*/
public DataVariable(App app) {
this.app = app;
this.loc = app.getLocalization();
}
// =============================================
// Set the data
// =============================================
/**
* @param geoClass
* @param valueItemList
*/
public void setDataVariableAsRawData(GeoClass geoClass,
ArrayList valueItemList) {
setDataVariable(GroupType.RAWDATA, geoClass, valueItemList, null, null,
null);
}
/**
* @param groupType
* @param geoClass
* @param valueItemList
* @param frequency
* @param classes
* @param label
*/
public void setDataVariable(GroupType groupType, GeoClass geoClass,
ArrayList valueItemList, DataItem frequency, DataItem classes,
DataItem label) {
this.values = valueItemList;
this.frequency = frequency;
this.classes = classes;
this.label = label;
this.geoClass = geoClass;
setGroupType(groupType);
if (values != null) {
for (DataItem item : values) {
item.setGeoClass(geoClass);
}
}
}
// ========================================================
// Getters/Setters
// ========================================================
public void setGroupType(GroupType groupType) {
this.groupType = groupType;
switch (groupType) {
case RAWDATA:
frequency = null;
classes = null;
if (values.size() == 0) {
values.add(new DataItem());
}
break;
case FREQUENCY:
if (frequency == null) {
frequency = new DataItem();
frequency.setGeoClass(GeoClass.NUMERIC);
frequency.setDescription(loc.getMenu("Frequency"));
}
classes = null;
break;
case CLASS:
if (frequency == null) {
frequency = new DataItem();
frequency.setGeoClass(GeoClass.NUMERIC);
frequency.setDescription(loc.getMenu("Frequency"));
}
if (classes == null) {
classes = new DataItem(new Double[0]);
classes.setDescription(loc.getMenu("Classes"));
}
for (DataItem item : values) {
item.clearItem();
}
values.clear();
break;
}
}
public GroupType getGroupType() {
return groupType;
}
public void setGeoClass(GeoClass geoClass) {
this.geoClass = geoClass;
for (DataItem item : values) {
item.setGeoClass(geoClass);
}
}
public GeoClass getGeoClass() {
return geoClass;
}
public double getClassStart() {
return classStart;
}
public void setClassStart(double classStart) {
this.classStart = classStart;
if (groupType == GroupType.CLASS) {
updateAutomaticClasses();
}
}
public double getClassWidth() {
return classWidth;
}
public void setClassWidth(double classWidth) {
this.classWidth = classWidth;
if (groupType == GroupType.CLASS) {
updateAutomaticClasses();
}
}
private void updateAutomaticClasses() {
if (classes == null) {
return;
}
int numClasses = 0;
if (frequency != null) {
numClasses = frequency.getGeoCount();
}
Double[] leftBorder = new Double[numClasses + 1];
leftBorder[0] = classStart;
for (int i = 1; i < leftBorder.length; i++) {
leftBorder[i] = leftBorder[i - 1] + classWidth;
}
classes.setDataItem(leftBorder);
}
public DataItem getFrequency() {
return frequency;
}
public void setFrequency(DataItem frequency) {
this.frequency = frequency;
if (frequency != null) {
frequency.setGeoClass(GeoClass.NUMERIC);
frequency.setDescription(loc.getMenu("Frequency"));
}
}
public DataItem getClasses() {
return classes;
}
public boolean enableHeader() {
return enableHeader;
}
public void setEnableHeader(boolean enableHeader) {
this.enableHeader = enableHeader;
}
public ArrayList<DataItem> getValues() {
return values;
}
public void setValueItemList(ArrayList<DataItem> values) {
this.values = values;
for (DataItem item : values) {
item.setDescription(loc.getMenu("Data"));
}
}
public void setValueItems(DataItem... valueItem) {
values = new ArrayList<DataItem>();
for (DataItem item : valueItem) {
values.add(item);
item.setDescription(loc.getMenu("Data"));
}
}
public void setDataItem(int itemIndex, DataItem item) {
if (itemIndex < values.size()) {
values.set(itemIndex, item);
} else {
frequency = item;
}
}
public void removeLastValue() {
if (values == null || values.size() == 0) {
return;
}
values.get(values.size() - 1).clearItem();
values.remove(values.size() - 1);
}
public void addNewValue() {
if (values == null) {
values = new ArrayList<DataItem>();
}
DataItem item = new DataItem();
item.setGeoClass(geoClass);
values.add(item);
}
// ========================================================
// Export to GeoList or table data
// ========================================================
public ArrayList<DataItem> getItemList() {
ArrayList<DataItem> list = new ArrayList<DataItem>();
if (label != null) {
list.add(label);
}
list.addAll(values);
if (classes != null) {
list.add(classes);
}
if (frequency != null) {
list.add(frequency);
}
return list;
}
/**
* @param app
* @param item
* @param mode
* @param leftToRight
* @param doCopy
* @return
*/
public GeoList toGeoList(App app, DataItem item, int mode,
boolean leftToRight, boolean doCopy) {
return item.toGeoList(app, enableHeader, leftToRight, doCopy);
}
/**
* @param app
* @param mode
* @param leftToRight
* @param doCopy
* @return
*/
public ArrayList<GeoList> getGeoListData(App app, int mode,
boolean leftToRight, boolean doCopy) {
ArrayList<GeoList> list = new ArrayList<GeoList>();
if (label != null) {
list.add(label.toGeoList(app, enableHeader, leftToRight, doCopy));
}
if (mode == DataAnalysisModel.MODE_REGRESSION
&& geoClass == GeoClass.NUMERIC) {
list.add(getPointList(leftToRight, doCopy));
} else {
for (DataItem item : values) {
list.add(
item.toGeoList(app, enableHeader, leftToRight, doCopy));
}
}
if (classes != null) {
list.add(classes.toGeoList(app, enableHeader, leftToRight, doCopy));
}
if (frequency != null) {
list.add(frequency.toGeoList(app, enableHeader, leftToRight,
doCopy));
}
return list;
}
private GeoList getPointList(boolean leftToRight, boolean doCopy) {
if (values.size() < 2) {
return null;
}
GeoList list0 = values.get(0).toGeoList(app, enableHeader, leftToRight,
doCopy);
GeoList list1 = values.get(1).toGeoList(app, enableHeader, leftToRight,
doCopy);
return createPointGeoList(list0, list1, doCopy, leftToRight);
}
private GeoList createPointGeoList(GeoList xList, GeoList yList,
boolean byValue, boolean leftToRight) {
Construction cons = app.getKernel().getConstruction();
ArrayList<GeoElement> list = new ArrayList<GeoElement>();
try {
GeoElement xCoord, yCoord;
for (int i = 0; i < xList.size(); ++i) {
xCoord = xList.get(i);
yCoord = yList.get(i);
// don't process the point if either coordinate is null or
// non-numeric,
if (xCoord == null || yCoord == null || !xCoord.isGeoNumeric()
|| !yCoord.isGeoNumeric()) {
continue;
}
GeoPoint geoPoint;
AlgoDependentPoint pointAlgo = null;
if (byValue) {
if (leftToRight) {
geoPoint = new GeoPoint(cons,
((GeoNumeric) xCoord).getDouble(),
((GeoNumeric) yCoord).getDouble(), 1.0);
} else {
geoPoint = new GeoPoint(cons,
((GeoNumeric) yCoord).getDouble(),
((GeoNumeric) xCoord).getDouble(), 1.0);
}
} else {
MyVecNode vec = new MyVecNode(app.getKernel(),
leftToRight ? xCoord : yCoord,
leftToRight ? yCoord : xCoord);
ExpressionNode point = new ExpressionNode(app.getKernel(),
vec, Operation.NO_OPERATION, null);
point.setForcePoint();
pointAlgo = new AlgoDependentPoint(cons, point, false);
geoPoint = (GeoPoint) pointAlgo.getGeoElements()[0];
}
if (pointAlgo != null) {
cons.removeFromConstructionList(pointAlgo);
}
list.add(geoPoint);
if (yCoord.isAngle() || xCoord.isAngle()) {
geoPoint.setPolar();
}
}
}
catch (Exception ex) {
Log.debug(
"Creating list of points expression failed with exception "
+ ex);
}
AlgoDependentList dl = new AlgoDependentList(cons, list, false);
cons.removeFromConstructionList(dl);
GeoList ret = (GeoList) dl.getGeoElements()[0];
ret.setSelectionAllowed(false);
return ret;
}
public ArrayList<String[]> getStringData() {
ArrayList<String[]> list = new ArrayList<String[]>();
if (groupType == GroupType.CLASS && classes != null) {
updateAutomaticClasses();
}
for (DataItem item : getItemList()) {
list.add(item.toStringArray(enableHeader));
}
return list;
}
public ArrayList<String> getTitles(App app) {
ArrayList<String> list = new ArrayList<String>();
for (DataItem item : getItemList()) {
list.add(item.getDataTitle(app, enableHeader));
}
return list;
}
public ArrayList<String> getColumnNames() {
ArrayList<String> list = new ArrayList<String>();
for (DataItem item : getItemList()) {
list.add(item.getDescription());
}
return list;
}
/**
* @param mode
* @return list of column names for ???
*/
public ArrayList<String> getTableColumnDescriptions(App app, int mode,
int index) {
ArrayList<String> list = new ArrayList<String>();
for (DataItem item : values) {
switch (mode) {
default:
case DataAnalysisModel.MODE_ONEVAR:
switch (groupType) {
case RAWDATA:
list.add(loc.getMenu("Data"));
break;
case FREQUENCY:
list.add(loc.getMenu("Data"));
list.add(loc.getMenu("Frequency"));
break;
case CLASS:
list.add(loc.getMenu("Classes"));
list.add(loc.getMenu("Frequency"));
break;
}
break;
case DataAnalysisModel.MODE_REGRESSION:
if (item.getGeoClass() == GeoClass.POINT) {
list.add("(" + loc.getMenu("Column.X") + ","
+ loc.getMenu("Column.Y") + ")");
} else {
list.add(loc.getMenu("Column.X"));
list.add(loc.getMenu("Column.Y"));
}
break;
case DataAnalysisModel.MODE_MULTIVAR:
list.add("# " + index);
break;
}
}
return list;
}
/**
* Returns true if this contains the specified GeoElement
*/
public boolean isInDataSource(GeoElement geo) {
if (geo == null) {
return false;
}
ArrayList<DataItem> itemList = getItemList();
// CASE 1: GeoList
if (geo instanceof GeoList) {
for (DataItem item : itemList) {
if (item.getGeoList() != null && item.getGeoList() == geo) {
return true;
}
}
}
// CASE 2: spreadsheet cell
GPoint location = geo.getSpreadsheetCoords();
boolean isCell = (location != null
&& location.x < Kernel.MAX_SPREADSHEET_COLUMNS_DESKTOP
&& location.y < Kernel.MAX_SPREADSHEET_ROWS_DESKTOP);
if (isCell) {
for (DataItem dataItem : itemList) {
if (dataItem.getType() == SourceType.SPREADSHEET) {
try {
if (dataItem.getRangeList() != null) {
for (CellRange cr : dataItem.getRangeList()) {
if (cr.contains(geo)) {
return true;
}
}
}
} catch (Exception e) {
// e.printStackTrace();
}
}
}
}
return false;
}
public void getXML(StringBuilder sb) {
// save these fields to XML:
// groupType, enableHeader
sb.append("<variable>\n");
// save the DataItems to XML
for (DataItem item : values) {
sb.append("<item ranges=\"");
ArrayList<CellRange> crList = item.getRangeList();
if (crList != null) {
boolean first = true;
for (CellRange cr : crList) {
sb.append(first ? "" : ",");
sb.append(cr.getLabel());
}
}
sb.append("\"/>\n");
}
if (frequency != null) {
// write item XML
}
if (classes != null) {
// write item XML
}
if (label != null) {
// write item XML
}
sb.append("</variable>\n");
}
}