/* 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 com.google.gdata.util.XmlParser.ElementHandler;
import org.xml.sax.Attributes;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* Extension that globs together all "gsx:" tags into one.
*
* Designed for row-based access to spreadsheets.
*
* extra tags. Another option is overriding ListEntry's getExtensionHandler
* method. Finally, this might be embedded as a private class inside of
* ListEntry.
*
*
*/
public class CustomElementCollection implements Extension {
/**
* Internal representation of a custom element. This allows
* for the addition of attributes.
*/
private class CustomElement {
/** The value of the custom element. */
private String value;
/**
* Creates a custom element with only the value set.
*
* @param value the value of this custom element
*/
public CustomElement(String value) {
this.value = value;
}
/**
* Creates a custom element with the value and the comment set.
*
* @param value the value of the custom element
*/
public CustomElement(String value, String comment) {
this.value = value;
}
/**
* Returns the value of the custom element.
*
* @return value of the custom element
*/
public String getValue() {
return value;
}
}
/**
* All values, indexed by the header column.
*
* For instance, the pair "name", "bill" would mean that the cell in
* the column labelled "name" has the text "bill".
*/
private Map<String, CustomElement> values =
new LinkedHashMap<String, CustomElement>();
/**
* Gets the text at the cell, whose column is named columnHeader.
*
* For instance, if the spreadsheet has a column "Name", then calling
* getValue("name") will return the selected row's cell in that name
* column.
*
* @param columnHeader the lowercase, stripped version of the column header
* @return the contents of the cell
*/
public String getValue(String columnHeader) {
CustomElement element = values.get(columnHeader.toLowerCase());
if (element == null) {
return null;
}
return element.getValue();
}
/**
* Locally sets the value at the particular cell, specified by the
* column name.
*
* For instance, setValueLocal("name", "Ensulato") will change the
* name column of this row to "Ensulato". To commit the change,
* you must .
*
* The particular column you specify must already exist
* in the spreadsheet; new columns are not automatically added.
*
* @param columnHeader the header column (must already exist)
* @param newContents the new contents; may not start with an '=' sign
* @throws IllegalArgumentException if the contents begins with an equals
* sign
*
* if a non-existent columnHeader is used. But, when adding a new
* entry, there needs to be a way to locally know the schema.
*/
public void setValueLocal(String columnHeader, String newContents) {
if (newContents.startsWith("=")) {
throw new IllegalArgumentException("Formulas are not supported.");
} else {
values.put(columnHeader.toLowerCase(),
new CustomElement(newContents));
}
}
/**
* Locally clears the particular value.
*
* Note that if the entire row is cleared, the entry will be rejected.
* You must actually delete the row.
*
* @param columnHeader the column header to clear.
*/
public void clearValueLocal(String columnHeader) {
values.remove(columnHeader.toLowerCase());
}
/**
* Locally clears all existing values and copies the contents of the other
* element collection over.
*
* @param other the custom element collection
*/
public void replaceWithLocal(CustomElementCollection other) {
values.clear();
values.putAll(other.values);
}
/**
* Gets a list of all tags that are set for this entry.
* The initial order of the tags is preserved.
*
* For instance, this might return an iterable with "name", "address",
* "manager", "employeeid".
*
* @return an Iterable with all the different tags for getValue
*/
public Set<String> getTags() {
return values.keySet();
}
/**
* Returns the suggested extension description.
*/
public static ExtensionDescription getDefaultDescription() {
ExtensionDescription desc = new ExtensionDescription();
desc.setExtensionClass(CustomElementCollection.class);
desc.setNamespace(Namespaces.gSpreadCustomNs);
desc.setLocalName("*");
desc.setAggregate(true);
return desc;
}
/**
* Writes this cell as XML, omitting any unspecified fields.
*/
public void generate(XmlWriter w, ExtensionProfile extProfile)
throws IOException {
for (Map.Entry<String, CustomElement> entry : values.entrySet()) {
ArrayList<XmlWriter.Attribute> attrs =
new ArrayList<XmlWriter.Attribute>();
// Attributes - if there were any
w.simpleElement(Namespaces.gSpreadCustomNs, entry.getKey(),
attrs, entry.getValue().getValue());
}
}
/**
* 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 CustomElementHandler(localName);
}
/**
* Parses custom gx: tags.
*
*
*/
private class CustomElementHandler extends ElementHandler {
/**
* The name of the custom tag.
*/
private String tagName;
/**
* Constructs the handler, given the name of the element in use.
*/
public CustomElementHandler(String tagName) {
this.tagName = tagName;
}
/**
* Processes an attribute.
*
* Currently, no attributes are used (only the main text).
*/
public void processAttribute(String namespace,
String localName,
String value)
throws ParseException {
// If there were any
}
public void processEndElement() throws ParseException {
if (value == null) {
values.put(tagName, new CustomElement(null));
} else {
values.put(tagName, new CustomElement(value));
}
}
}
}