Cell.java
- /*
- * Copyright 2012 The Apache Software Foundation.
- *
- * 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 org.odftoolkit.odfdom.changes;
- import static org.odftoolkit.odfdom.changes.JsonOperationConsumer.addParagraph;
- import static org.odftoolkit.odfdom.changes.JsonOperationConsumer.addStyle;
- import static org.odftoolkit.odfdom.changes.JsonOperationConsumer.addText;
- import static org.odftoolkit.odfdom.changes.OperationConstants.OPK_STYLE_ID;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import org.json.JSONException;
- import org.json.JSONObject;
- import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
- import org.odftoolkit.odfdom.dom.element.number.DataStyleElement;
- import org.odftoolkit.odfdom.dom.element.number.NumberBooleanStyleElement;
- import org.odftoolkit.odfdom.dom.element.number.NumberTextStyleElement;
- import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement;
- import org.odftoolkit.odfdom.dom.element.text.TextAElement;
- import org.odftoolkit.odfdom.dom.element.text.TextPElement;
- import org.odftoolkit.odfdom.dom.element.text.TextParagraphElementBase;
- import org.odftoolkit.odfdom.dom.style.props.OdfStylePropertiesSet;
- import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberCurrencyStyle;
- import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberDateStyle;
- import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberPercentageStyle;
- import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberStyle;
- import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberTimeStyle;
- import org.odftoolkit.odfdom.incubator.doc.style.OdfStyle;
- import org.odftoolkit.odfdom.pkg.OdfElement;
- import org.odftoolkit.odfdom.pkg.OdfFileDom;
- import org.w3c.dom.Node;
- import org.w3c.dom.NodeList;
- /**
- * A MultiCoomponent uses a single XML element to represent multiple components. This container can
- * be used for spreadsheet row and cell components using repeated elements via an attribute.
- *
- * @author svante.schubertATgmail.com
- * @param <T>
- */
- class Cell<T> extends Component {
- private static final String FORMULA_PREFIX = "of:";
- public Cell(OdfElement componentElement, Component parent) {
- super(componentElement, parent);
- }
- /**
- * A multiple components can be represented by a single XML element
- *
- * @return the number of components the elements represents
- */
- @Override
- public int repetition() {
- return mRootElement.getRepetition();
- }
- // CELL ONLY
- // Map<String, Object> mInnerCellStyle = null;
- //
- // /** The inner style of a cell will be temporary saved at the cell.
- // Whenever the cell content is deleted, the style is being merged/applied to the cell style */
- // public Map<String, Object> getInternalCellStyle(){
- // return mInnerCellStyle;
- // }
- //
- //
- // /** The inner style of a cell will be temporary saved at the cell.
- // Whenever the cell content is deleted, the style is being merged/applied to the cell style */
- // public void setInternalCellStyle(Map<String, Object> newStyles){
- // mInnerCellStyle = newStyles;
- // }
- //
- /** Adds the given component to the root element */
- @Override
- public void addChild(int index, Component c) {
- mRootElement.insert(c.getRootElement(), index);
- // 2DO: Svante: ARE THE ABOVE AND THE BELOW EQUIVALENT?
- // OdfElement rootElement = c.getRootElement();
- // if (index >= 0) {
- // mRootElement.insertBefore(rootElement, ((OdfElement) mRootElement).receiveNode(index));
- // } else {
- // mRootElement.appendChild(rootElement);
- // }
- }
- /** @return either a text node of size 1 or an element being the root element of a component */
- @Override
- public Node getChildNode(int index) {
- return mRootElement.receiveNode(index);
- }
- /**
- * Removes a component from the text element container. Removes either an element representing a
- * component or text node of size 1
- */
- @Override
- public Node remove(int index) {
- Node removedNode = null;
- Node node = this.getChildNode(index);
- if (node != null) {
- removedNode = mRootElement.removeChild(node);
- }
- return removedNode;
- }
- /**
- * All children of the root element will be traversed. If it is a text node the size is added, if
- * it is an element and a component a size of one is added, if it is a marker, for known text
- * marker elements (text:span, text:bookmark) the children are recursive checked
- *
- * @return the number of child components
- */
- @Override
- public int size() {
- return mRootElement.componentSize();
- }
- private static final String FLOAT = "float";
- private static final String STRING = "string";
- private static final String CURRENCY = "currency";
- private static final String DATE = "date";
- private static final String TIME = "time";
- private static final String PERCENTAGE = "percentage";
- private static final String BOOLEAN = "boolean";
- /**
- * Adding cell content: either as formula or paragraph and text content, latter with default
- * styles
- */
- public TableTableCellElement addCellStyleAndContent(
- Component rootComponent, Object value, JSONObject attrs) {
- // see if the cell is repeated
- TableTableCellElement cell = (TableTableCellElement) this.getRootElement();
- OdfFileDom ownerDoc = (OdfFileDom) rootComponent.getOwnerDocument();
- // save the URL as everyting else will be deleted
- String url = reuseCellHyperlink(cell, attrs);
- boolean setValueType = true;
- boolean isNumberValue = true;
- // exchanges the content if requested
- if (value != null) {
- cell.removeContent();
- // if there is new content..
- if (!value.equals(JSONObject.NULL)) {
- String valueString = value.toString();
- if (valueString.startsWith(Constants.EQUATION)) {
- // How am I able to set the other values? What is the OOXML solution for this?
- cell.setAttributeNS(OdfDocumentNamespace.OFFICE.getUri(), "office:value-type", FLOAT);
- cell.setAttributeNS(
- OdfDocumentNamespace.TABLE.getUri(),
- "table:formula",
- FORMULA_PREFIX.concat(valueString));
- } else {
- // insert a paragraph to store the text within..
- TextParagraphElementBase newParagraph = addParagraph(this, 0, attrs);
- if (url != null) {
- attrs = addUrlToCharacterProps(attrs, url);
- }
- // if the formula was masked
- if (value instanceof String
- && valueString.startsWith(Constants.APOSTROPHE_AND_EQUATION)) {
- // cut the first apostrophe
- valueString = valueString.substring(1);
- }
- // addChild the text & removes existing values
- addText(newParagraph, 0, attrs, valueString);
- if (value instanceof Integer || value instanceof Double || value instanceof Float) {
- isNumberValue = true;
- cell.setAttributeNS(OdfDocumentNamespace.OFFICE.getUri(), "office:value", valueString);
- } else if (value instanceof String) {
- cell.setAttributeNS(OdfDocumentNamespace.OFFICE.getUri(), "office:value-type", STRING);
- setValueType = false;
- }
- }
- }
- }
- if (attrs != null) {
- // Format: Adding Styles to the element
- addStyle(attrs, cell, ownerDoc);
- if (cell.hasChildNodes()) {
- NodeList children = cell.getChildNodes();
- for (int i = 0; i < children.getLength(); i++) {
- Node child = children.item(i);
- if (child instanceof OdfElement) {
- ((OdfElement) child).markText(0, Integer.MAX_VALUE - 1, attrs);
- }
- }
- } else {
- if (url != null) {
- TextPElement containerElement1 = new TextPElement(ownerDoc);
- TextAElement containerElement2 = new TextAElement(ownerDoc);
- containerElement2.setXlinkHrefAttribute(url);
- cell.appendChild(containerElement1);
- containerElement1.appendChild(containerElement2);
- }
- }
- }
- // value-type has to be set if
- // - a number is set replacing a previous string-type
- // - if a number is changed to a string (already done above ) or vice-versa
- // - if attributes have been set (that contain a number format or a new style)
- //
- if (setValueType) {
- String currentValueType = cell.getOfficeValueTypeAttribute();
- boolean isStringType = currentValueType != null && currentValueType.equals("String");
- boolean changedToNumber = isNumberValue && isStringType;
- boolean numberFormatChanged = false;
- if (attrs != null) {
- JSONObject cellAttrs = attrs.optJSONObject("cell");
- if (cellAttrs != null) {
- numberFormatChanged = cellAttrs.has("formatCode");
- }
- if (!numberFormatChanged) {
- String styleId = attrs.optString(OPK_STYLE_ID);
- numberFormatChanged = styleId != null;
- }
- }
- if (currentValueType == null || changedToNumber || numberFormatChanged) {
- DataStyleElement dataStyle = cell.getOwnDataStyle();
- if (dataStyle != null) {
- String valueType = "";
- String currencySymbol = "";
- if (dataStyle instanceof OdfNumberStyle) {
- valueType = FLOAT;
- } else if (dataStyle instanceof OdfNumberCurrencyStyle) {
- currencySymbol =
- ((OdfNumberCurrencyStyle) dataStyle).getCurrencySymbolElement().getTextContent();
- valueType = CURRENCY;
- } else if (dataStyle instanceof NumberTextStyleElement) {
- valueType = STRING;
- } else if (dataStyle instanceof OdfNumberDateStyle) {
- valueType = DATE;
- } else if (dataStyle instanceof OdfNumberTimeStyle) {
- valueType = TIME;
- } else if (dataStyle instanceof OdfNumberPercentageStyle) {
- valueType = PERCENTAGE;
- } else if (dataStyle instanceof NumberBooleanStyleElement) {
- valueType = BOOLEAN;
- }
- if (!valueType.isEmpty()) {
- cell.setOfficeValueTypeAttribute(valueType);
- cell.setCalcextValueTypeAttribute(valueType);
- cell.setOfficeCurrencyAttribute(currencySymbol);
- // make sure that an appropriate value is available:
- if (value == null && cell.getOfficeValueAttribute() == null) {
- String oldDateValue = cell.getOfficeDateValueAttribute();
- String oldTimeValue = cell.getOfficeTimeValueAttribute();
- Boolean oldBooleanValue = cell.getOfficeBooleanValueAttribute();
- Double newValue = null;
- if (oldDateValue != null) {
- newValue = new Double(MapHelper.dateToDouble(oldDateValue));
- } else if (oldTimeValue != null) {
- newValue = new Double(MapHelper.timeToDouble(oldTimeValue));
- } else if (oldBooleanValue != null) {
- newValue = new Double(oldBooleanValue.booleanValue() ? 1 : 0);
- }
- if (newValue != null) {
- cell.setOfficeValueAttribute(newValue);
- }
- }
- }
- }
- }
- }
- return cell;
- }
- /**
- * To be able to reuse existing style on the full table, new cell hyperlinks will be stored in the
- * cell text style properties as @xlink:href attribute and taken back when nothing new is set.
- */
- private static String reuseCellHyperlink(TableTableCellElement cell, JSONObject attrs) {
- String cellURL = null;
- if (attrs != null) { // apply style changes to the cell
- // apply new styles to the cell (modifying not overwriting)
- if (attrs.has("character")) {
- JSONObject charProps = attrs.optJSONObject("character");
- if (charProps != null) {
- if (charProps.has("url") && !charProps.get("url").equals(JSONObject.NULL)) {
- cellURL = charProps.optString("url");
- } else if (charProps.has("url")) {
- // removeAnchors();
- }
- }
- }
- }
- // if there is no new hyperlink given, check for an existing cached (in the properties)
- if (cellURL == null || cellURL.isEmpty()) {
- // check if there is still one given at the cell
- OdfStyle autoStyle = cell.getAutomaticStyle();
- if (autoStyle != null) {
- OdfElement textProps = autoStyle.getPropertiesElement(OdfStylePropertiesSet.TextProperties);
- if (textProps != null
- && textProps.hasAttributeNS(OdfDocumentNamespace.XLINK.getUri(), "href")) {
- cellURL = textProps.getAttributeNS(OdfDocumentNamespace.XLINK.getUri(), "href");
- }
- }
- }
- return cellURL;
- }
- private static JSONObject addUrlToCharacterProps(JSONObject attrs, String cellURL) {
- JSONObject charProps = null;
- if (cellURL != null && !cellURL.isEmpty()) {
- if (attrs == null) {
- attrs = new JSONObject();
- }
- if (!attrs.has("character")) {
- charProps = new JSONObject();
- try {
- attrs.put("character", charProps);
- } catch (JSONException ex) {
- Logger.getLogger(JsonOperationConsumer.class.getName()).log(Level.SEVERE, null, ex);
- }
- } else {
- charProps = attrs.optJSONObject("character");
- }
- try {
- charProps.put("url", cellURL);
- } catch (JSONException ex) {
- Logger.getLogger(JsonOperationConsumer.class.getName()).log(Level.SEVERE, null, ex);
- }
- }
- return attrs;
- }
- }