blob: 6195af78e7b51fbcd7e5a72e12948c3dec7cf02d [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2008 The University of York.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.epsilon.emc.spreadsheets.google;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.epsilon.common.util.StringUtil;
import org.eclipse.epsilon.emc.spreadsheets.ISpreadsheetMetadata.SpreadsheetColumnMetadata;
import org.eclipse.epsilon.emc.spreadsheets.SpreadsheetColumn;
import org.eclipse.epsilon.emc.spreadsheets.SpreadsheetRow;
import org.eclipse.epsilon.emc.spreadsheets.SpreadsheetWorksheet;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
import com.google.gdata.data.spreadsheet.CellEntry;
import com.google.gdata.data.spreadsheet.CellFeed;
import com.google.gdata.data.spreadsheet.ListEntry;
import com.google.gdata.data.spreadsheet.ListFeed;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.util.ServiceException;
public class GSWorksheet extends SpreadsheetWorksheet {
private GSModel model;
private WorksheetEntry worksheetEntry;
private ListFeed listFeed;
private HashSet<GSRow> rows;
/*
* This field indicates whether the Google worksheet has any value set in the
* header. Required because when header is blank the first row written using
* ListFeed is written to the header row. Bug reported to Google:
* https://code.google.com/a/google.com/p/apps-api-issues/issues/detail?id=3355
*/
private boolean headerInWorksheetIsEmpty;
/*
* Each Google Spreadsheet column is identified by some unique Google-assigned
* id. We may want to manage columns that do not have a title (value in header
* row cell) thus this id is required, for example, to write a row.
*/
private boolean hasGoogleIdsSet;
public GSWorksheet(final GSModel model, final WorksheetEntry worksheetEntry, final boolean existsInSpreadsheet)
throws Exception {
super(model, worksheetEntry.getTitle().getPlainText(), existsInSpreadsheet);
this.model = model;
this.worksheetEntry = worksheetEntry;
this.headerInWorksheetIsEmpty = true;
this.hasGoogleIdsSet = false;
this.rows = null;
this.initialise();
}
private void initialise() throws Exception {
if (super.existsInSpreadsheet) {
this.initialiseListFeed();
this.loadHeader();
}
}
private void initialiseListFeed() throws Exception {
final URL listFeedUrl = new URL(this.worksheetEntry.getListFeedUrl().toString());// + "?return-empty=true");
this.listFeed = this.model.getWorksheetListFeed(listFeedUrl);
}
@Override
protected void loadHeader() throws EolModelLoadingException {
try {
final CellFeed headerRow = this.getHeaderCellFeed();
if (headerRow != null) {
for (final CellEntry headerCell : headerRow.getEntries()) {
final String name = headerCell.getCell().getValue();
if (!StringUtil.isEmpty(name)) {
int i = headerCell.getCell().getCol();
super.addColumn(i - getColumnOffset(), name);
this.headerInWorksheetIsEmpty = false;
}
}
this.writeValueInFirstCellIfHeaderIsEmpty();
}
else {
throw new EolRuntimeException("Cell feed failed to load the header row");
}
}
catch (Exception e) {
e.printStackTrace();
throw new EolModelLoadingException(e, this.model);
}
}
private CellFeed getHeaderCellFeed() throws Exception {
final StringBuilder uri = new StringBuilder(this.worksheetEntry.getCellFeedUrl().toString());
uri.append("?min-row=" + Integer.toString(GSConstants.HEADER_ROW_INDEX));
uri.append("&max-row=" + Integer.toString(GSConstants.HEADER_ROW_INDEX));
uri.append("&return-empty=true");
final URL cellFeedUrl = new URI(uri.toString()).toURL();
return this.model.getWorksheetCellFeed(cellFeedUrl);
}
private void writeValueInFirstCellIfHeaderIsEmpty() throws Exception {
if (this.headerInWorksheetIsEmpty) {
this.writeHeaderCell(1, GSConstants.DEFAULT_COLUMN_VALUE);
super.addColumn(0, GSConstants.DEFAULT_COLUMN_VALUE);
this.headerInWorksheetIsEmpty = false;
}
}
public void writeHeaderCell(final int columnIndex, final String columnName) throws Exception {
final CellFeed cellFeed = this.getHeaderCellFeed();
final CellEntry cellEntry = new CellEntry(GSConstants.HEADER_ROW_INDEX, columnIndex, columnName);
cellFeed.insert(cellEntry);
}
/**
* Deletes this worksheet from the containing spreadsheet.
*
* @throws IOException
* @throws ServiceException
*/
public void delete() throws Exception {
this.worksheetEntry.delete();
}
@Override
public SpreadsheetColumn addColumn(final SpreadsheetColumnMetadata metadata) {
final SpreadsheetColumn column = super.addColumn(metadata);
if (!StringUtil.isEmpty(column.getName())) {
this.headerInWorksheetIsEmpty = false;
}
return column;
}
@Override
public SpreadsheetColumn createColumn(final int columnIndex) {
return new GSColumn(this, columnIndex);
}
@Override
public List<SpreadsheetRow> getRows() {
this.checkThatWorksheetExists();
if (this.rows == null) {
try {
this.rows = new LinkedHashSet<>();
this.listFeed = this.listFeed.getSelf();
for (final ListEntry listEntry : this.listFeed.getEntries()) {
final GSRow row = new GSRow(this, listEntry);
this.rows.add(row);
}
}
catch (Exception e) {
e.printStackTrace();
throw new IllegalStateException(e.getMessage());
}
}
return new ArrayList<>(this.rows);
}
@Override
protected SpreadsheetRow insertRow(final Map<SpreadsheetColumn, String> values) {
this.checkThatWorksheetExists();
try {
ListEntry listEntry = new ListEntry();
for (final Map.Entry<SpreadsheetColumn, String> entry : values.entrySet()) {
final GSColumn column = (GSColumn) entry.getKey();
String value = entry.getValue().equals("") ? this.getDefaultEmptyCellValue() : entry.getValue();
listEntry.getCustomElements().setValueLocal(column.getGoogleColumnId(), value);
}
listEntry = this.model.insertRow(worksheetEntry, listEntry);
this.worksheetEntry = this.worksheetEntry.getSelf();
final GSRow row = new GSRow(this, listEntry);
this.rows.add(row);
return row;
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
@Override
public void removeRow(final SpreadsheetRow row) {
this.checkThatWorksheetExists();
try {
final int rowIndex = new ArrayList<>(this.rows).indexOf(row);
this.listFeed = this.listFeed.getSelf();
this.listFeed.getEntries().get(rowIndex).delete();
this.worksheetEntry = this.worksheetEntry.getSelf();
this.rows.remove(row);
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
@Override
protected void checkThatWorksheetExists() {
this.createInSpreadsheet();
try {
this.getGoogleColumnIds();
}
catch (EolRuntimeException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
@Override
public String getDefaultEmptyCellValue() {
return " ";
}
public int getColumnOffset() {
return 1;
}
/**
* Returns the List Feed URL for working with this worksheet.
*
* @return URL
* @throws ServiceException
* @throws IOException
*/
public URL getListFeedURL() throws IOException, ServiceException {
this.worksheetEntry = this.worksheetEntry.getSelf();
return this.worksheetEntry.getListFeedUrl();
}
public int getRowIndex(GSRow row) {
return new ArrayList<>(this.rows).indexOf(row);
}
public ListEntry getListEntry(final GSRow row) throws IOException, ServiceException {
this.worksheetEntry = this.worksheetEntry.getSelf();
this.listFeed = this.listFeed.getSelf();
int i = this.getRowIndex(row);
return this.listFeed.getEntries().get(i);
}
@Override
protected void createInSpreadsheet() {
try {
if (!super.existsInSpreadsheet) {
this.worksheetEntry = this.model.insertWorksheet(this.worksheetEntry);
super.existsInSpreadsheet = true;
this.writeHeader();
this.worksheetEntry = this.worksheetEntry.getSelf();
this.initialiseListFeed();
this.getGoogleColumnIds();
}
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
private void writeHeader() throws Exception {
for (final SpreadsheetColumn column : this.getHeader().getColumns()) {
if (!StringUtil.isEmpty(column.getName())) {
this.writeHeaderCell(column.getIndex() + this.getColumnOffset(), column.getName());
this.headerInWorksheetIsEmpty = false;
}
}
this.writeValueInFirstCellIfHeaderIsEmpty();
}
private void getGoogleColumnIds() throws EolRuntimeException {
super.checkThatWorksheetExists();
if (this.hasGoogleIdsSet) {
return;
}
try {
this.hasGoogleIdsSet = true;
final int maxColIndex = this.getHeader().getColumns().last().getIndex() + this.getColumnOffset();
final int rowIndex = this.getRows().size() + GSConstants.HEADER_ROW_INDEX + 1;
this.extendWorksheet(rowIndex);
this.writeTemporaryRow(rowIndex, maxColIndex);
final GSRow temporaryRow = this.loadGoogleColumnIds(rowIndex, maxColIndex);
this.rows.add(temporaryRow);
this.removeRow(temporaryRow);
}
catch (Exception e) {
e.printStackTrace();
throw new EolRuntimeException(e.getMessage());
}
}
private void extendWorksheet(final int rowIndex) throws IOException, ServiceException {
this.worksheetEntry = this.worksheetEntry.getSelf();
if (this.worksheetEntry.getRowCount() < rowIndex) {
this.worksheetEntry.setRowCount(rowIndex + GSConstants.DEFAULT_WORKSHEET_ROWS);
this.worksheetEntry = this.worksheetEntry.update();
}
}
private void writeTemporaryRow(final int rowIndex, final int lastColumnIndex) throws Exception {
final StringBuilder uri = new StringBuilder(this.worksheetEntry.getCellFeedUrl().toString());
uri.append("?min-row=" + Integer.toString(rowIndex));
uri.append("&max-row=" + Integer.toString(rowIndex));
uri.append("&min-col=1&max-col=" + Integer.toString(lastColumnIndex));
uri.append("&return-empty=true");
final URL cellFeedUrl = new URI(uri.toString()).toURL();
final CellFeed cellFeed = this.model.getWorksheetCellFeed(cellFeedUrl);
for (final CellEntry cell : cellFeed.getEntries()) {
cell.changeInputValueLocal(GSConstants.DEFAULT_COLUMN_VALUE);
cell.update();
}
}
private GSRow loadGoogleColumnIds(final int rowIndex, final int lastColumnIndex) throws Exception {
final StringBuilder uri = new StringBuilder(this.worksheetEntry.getListFeedUrl().toString());
uri.append("?start-index=" + Integer.toString(rowIndex - GSConstants.HEADER_ROW_INDEX));
uri.append("&max-results=1");
final URL listFeedUrl = new URL(uri.toString());
final ListFeed listFeed = this.model.getWorksheetListFeed(listFeedUrl);
final List<ListEntry> listEntries = listFeed.getEntries();
if (listEntries.size() == 1) {
final ListEntry temporaryRow = listEntries.get(0);
this.extractGoogleIdsFromRow(temporaryRow, lastColumnIndex);
return new GSRow(this, temporaryRow);
}
else {
throw new IllegalStateException("Failed to retrieve temporary row");
}
}
private void extractGoogleIdsFromRow(final ListEntry row, final int lastColumnIndex) {
int columnIndex = 1 - this.getColumnOffset();
final Iterator<String> it = row.getCustomElements().getTags().iterator(); // Google preserves column ordering
while (it.hasNext()) {
final String googleColumnId = it.next();
final GSColumn column = (GSColumn) super.getColumn(columnIndex);
if (column != null) {
column.setGoogleColumnId(googleColumnId);
}
final boolean lastColumnReached = columnIndex == lastColumnIndex;
if (lastColumnReached) {
break;
}
columnIndex++;
}
}
public void getSelf() throws Exception {
this.worksheetEntry = this.worksheetEntry.getSelf();
}
}