/* Copyright (c) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gdata.data.spreadsheet;
import com.google.gdata.util.common.xml.XmlWriter;
import com.google.gdata.data.Extension;
import com.google.gdata.data.ExtensionDescription;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.util.ParseException;
import com.google.gdata.util.XmlParser;
import org.xml.sax.Attributes;
import java.io.IOException;
import java.util.ArrayList;
/**
* GData schema extension describing a spreadsheet formula.
*
*
*
*/
public class Cell implements Extension {
/** The positional row number starting with 1 (-1 for unspecified) */
private int row = -1;
/** The positional column number starting with 1 (-1 for unspecified). */
private int col = -1;
/** The formula of the cell (null if the formula is omitted). */
private String inputValue = null;
/** The calculated numeric value of this cell. */
private Number numericValue = null;
/**
* The evaluated value of the cell in String from (null if unspecified).
*/
private String value = null;
/**
* Initializes to blank for XML parsing.
*/
public Cell() {
}
/**
* Constructs this Cell with all fields, but since some of these fields
* cannot be written to the server, this constructor is private.
*
* @param inRow row number
* @param inCol column number
* @param inNumericValue the input of the cell
* @param inValue the result of its calculation
*/
private Cell(int inRow, int inCol,
String inInputValue, Number inNumericValue, String inValue) {
row = inRow;
col = inCol;
inputValue = inInputValue;
value = inValue;
numericValue = inNumericValue;
}
/**
* Initializes a cell where the column is known.
*
* @param inRow the row number starting with 1 (-1 for unspecified)
* @param inCol the column number starting with 1 (-1 for unspecified)
* @param inInputValue the formula (null for unspecified)
*/
public Cell(int inRow, int inCol, String inInputValue) {
this(inRow, inCol, inInputValue, null, null);
}
/**
* Creates a cell for the server library; it is not appropriate for
* client side use (the server may reject these cells).
*/
public static Cell createFullCell(int inRow, int inCol,
String inInputValue, Number inCalculatedValue, String inValue) {
return new Cell(inRow, inCol, inInputValue,
inCalculatedValue, inValue);
}
/**
* Yields the positional row number starting with 1.
*
* @return the row number, or -1 if the row is not specified
*/
public int getRow() {
return row;
}
/**
* Yields the column number starting with 1.
*
* @return the positional column number, or -1 if the column is not specified
*/
public int getCol() {
return col;
}
/**
* Yields the formula reference of the cell.
*
*
* An "=" sign signifies that there is a formula computed on the fly.
* Otherwise it is simply data that is entered into the sheet.
*/
public String getInputValue() {
return inputValue;
}
/**
* Gets the calculated numeric value.
*
* @return the raw numeric value, or null if it is non-numeric
*/
public Number getNumericValue() {
return numericValue;
}
/**
* Gets the double-precision value.
*
* @return the double value, or Double.NaN if no number specified
*/
public double getDoubleValue() {
if (numericValue == null) {
return Double.NaN;
} else {
return numericValue.doubleValue();
}
}
/**
* Yields the evaluated, formatted value of this cell.
*
*
* @return the evaluated and formatted value (null if not specified)
*/
public String getValue() {
return value;
}
/**
* Creates a new cell with a new input value, for the purpose of updating.
*
* The new cell cannot contain a calculation result value, because values
* cannot be updated.
*
* @param newInputValue the new input value, starting with '=' for a formula,
* otherwise just a plain string
* @return a newly created "cell" object
*/
public Cell withNewInputValue(String newInputValue) {
return new Cell(row, col, newInputValue, null, null);
}
/**
* Returns the suggested extension description.
* @param repeats whether this cell might be repeated in parent context
*/
public static ExtensionDescription getDefaultDescription(boolean repeats) {
ExtensionDescription desc = new ExtensionDescription();
desc.setExtensionClass(Cell.class);
desc.setNamespace(Namespaces.gSpreadNs);
desc.setLocalName("cell");
desc.setRepeatable(repeats);
return desc;
}
/**
* Writes this cell as XML, omitting any unspecified fields.
*/
public void generate(XmlWriter w, ExtensionProfile extProfile)
throws IOException {
ArrayList<XmlWriter.Attribute> attrs =
new ArrayList<XmlWriter.Attribute>();
if (row > 0) {
attrs.add(new XmlWriter.Attribute("row", String.valueOf(row)));
}
if (col > 0) {
attrs.add(new XmlWriter.Attribute("col", String.valueOf(col)));
}
if (inputValue != null) {
attrs.add(new XmlWriter.Attribute("inputValue", inputValue));
}
if (numericValue != null) {
attrs.add(new XmlWriter.Attribute("numericValue",
numericValue.toString()));
}
w.simpleElement(Namespaces.gSpreadNs, "cell", attrs, value);
}
/**
* Yields an XML handler for parsing a Cell element.
*/
public XmlParser.ElementHandler getHandler(ExtensionProfile extProfile,
String namespace,
String localName,
Attributes attrs)
throws ParseException, IOException {
return new Handler();
}
/**
* Parser for a <gd:cell>.
*/
private class Handler extends XmlParser.ElementHandler {
/**
* Initializes this instance given the extension profile.
*/
public Handler() {
}
/**
* Handles an attribute (such as row, col, inputValue).
*/
public void processAttribute(String namespace,
String localName,
String attributeData)
throws ParseException {
if (namespace.equals("")) {
if (localName.equals("row")) {
Cell.this.row = Integer.parseInt(attributeData);
} else if (localName.equals("col")) {
Cell.this.col = Integer.parseInt(attributeData);
} else if (localName.equals("inputValue")) {
Cell.this.inputValue = attributeData;
} else if (localName.equals("numericValue")) {
try {
// (right now their backend is entirely double anyway)
Cell.this.numericValue = Double.valueOf(attributeData);
} catch (NumberFormatException nfe) {
throw new ParseException("Invalid numericValue.");
}
}
}
}
/**
* Processes the end of the tag.
*/
public void processEndElement() throws ParseException {
/*
* This insidious-looking line of code copies
* this handler's value (<gd:cell>..stuff..</gd:cell>)
* into the cell value.
*/
Cell.this.value = this.value;
// Take empty fields and make them null.
if (Cell.this.value != null && Cell.this.value.equals("")) {
Cell.this.value = null;
}
}
}
}