/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008 jOpenDocument, by ILM Informatique. All rights reserved. * * The contents of this file are subject to the terms of the GNU * General Public License Version 3 only ("GPL"). * You may not use this file except in compliance with the License. * You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html * See the License for the specific language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each file. * */ /* * Cell created on 10 décembre 2005 */ package org.jopendocument.dom.spreadsheet; import org.jopendocument.dom.ODDocument; import org.jopendocument.dom.ODValueType; import org.jopendocument.dom.XMLVersion; import org.jopendocument.util.CollectionUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; import org.jdom.Element; import org.jdom.Namespace; import org.jdom.Text; /** * A cell in a calc document. If you want to change a cell value you must obtain a MutableCell. * * @author Sylvain * @param <D> type of document */ public class Cell<D extends ODDocument> extends TableCalcNode<CellStyle, D> { // see 5.1.1 private static final Pattern multiSpacePattern = Pattern.compile("[\t\r\n ]+"); private static boolean OO_MODE = true; /** * Set whether {@link #getTextValue()} parses strings using the standard way or using the * OpenOffice.org way. * * @param ooMode <code>true</code> if strings should be parsed the OO way. * @see #getTextValue(boolean) */ public static void setTextValueMode(boolean ooMode) { OO_MODE = ooMode; } public static boolean getTextValueMode() { return OO_MODE; } static final Element createEmpty(XMLVersion ns) { return createEmpty(ns, 1); } static final Element createEmpty(XMLVersion ns, int count) { final Element e = new Element("table-cell", ns.getTABLE()); if (count > 1) e.setAttribute("number-columns-repeated", count + "", ns.getTABLE()); return e; } private final Row<D> row; Cell(Row<D> parent, Element elem) { super(parent.getODDocument(), elem, CellStyle.class); this.row = parent; } protected final Row<D> getRow() { return this.row; } protected final XMLVersion getNS() { return this.getODDocument().getVersion(); } protected final Namespace getValueNS() { final XMLVersion ns = this.getNS(); return ns == XMLVersion.OD ? ns.getOFFICE() : ns.getTABLE(); } protected final String getType() { return this.getElement().getAttributeValue("value-type", getValueNS()); } public final ODValueType getValueType() { final String type = this.getType(); return type == null ? null : ODValueType.get(type); } // cannot resolve our style since a single instance of Cell is used for all // repeated and thus if we need to check table-column table:default-cell-style-name // we wouldn't know which column to check. @Override protected String getStyleName() { throw new UnsupportedOperationException("cannot resolve our style, use MutableCell"); } String getStyleAttr() { return this.getElement().getAttributeValue("style-name", getNS().getTABLE()); } private final String getValue(String attrName) { return this.getElement().getAttributeValue(attrName, getValueNS()); } public Object getValue() { final ODValueType vt = this.getValueType(); if (vt == null || vt == ODValueType.STRING) { // ATTN oo generates string value-types w/o any @string-value final String attr = vt == null ? null : this.getValue(vt.getValueAttribute()); if (attr != null) return attr; else { return getTextValue(); } } else { return vt.parse(this.getValue(vt.getValueAttribute())); } } /** * Calls {@link #getTextValue(boolean)} using {@link #getTextValueMode()}. * * @return a string for the content of this cell. */ public String getTextValue() { return this.getTextValue(getTextValueMode()); } /** * Return the text value of this cell. This is often the formatted string of a value, e.g. * "11 novembre 2009" for a date. This method doesn't just return the text content it also * parses XML elements (like paragraphs, tabs and line-breaks). For the differences between the * OO way (as of 3.1) and the OpenDocument way see section 5.1.1 White-space Characters of * OpenDocument-v1.0-os and OpenDocument-v1.2-part1. In essence OpenOffice never trim strings. * * @param ooMode whether to use the OO way or the standard way. * @return a string for the content of this cell. */ public String getTextValue(final boolean ooMode) { final List<String> ps = new ArrayList<String>(); for (final Object o : this.getElement().getChildren()) { final Element child = (Element) o; if ((child.getName().equals("p") || child.getName().equals("h")) && child.getNamespacePrefix().equals("text")) { ps.add(getStringValue(child, ooMode)); } } return CollectionUtils.join(ps, "\n"); } private String getStringValue(final Element pElem, final boolean ooMode) { final StringBuilder sb = new StringBuilder(); final Namespace textNS = pElem.getNamespace(); // true if the string ends with a space that wasn't expanded from an XML element (e.g. // <tab/> or <text:s/>) boolean spaceSuffix = false; final Iterator iter = pElem.getDescendants(); while (iter.hasNext()) { final Object o = iter.next(); if (o instanceof Text) { final String text = multiSpacePattern.matcher(((Text) o).getText()).replaceAll(" "); // trim leading if (!ooMode && text.startsWith(" ") && (spaceSuffix || sb.length() == 0)) sb.append(text.substring(1)); else sb.append(text); spaceSuffix = text.endsWith(" "); } else if (o instanceof Element) { final Element elem = (Element) o; if (elem.getName().equals("tab") && elem.getNamespace().equals(textNS)) { sb.append("\t"); } else if (elem.getName().equals("line-break") && elem.getNamespace().equals(textNS)) { sb.append("\n"); } else if (elem.getName().equals("s") && elem.getNamespace().equals(textNS)) { final int count = Integer.valueOf(elem.getAttributeValue("c", textNS, "1")); final char[] toAdd = new char[count]; Arrays.fill(toAdd, ' '); sb.append(toAdd); } } } // trim trailing if (!ooMode && spaceSuffix) sb.deleteCharAt(sb.length() - 1); return sb.toString(); } public boolean isValid() { return !this.getElement().getName().equals("covered-table-cell"); } public final int getColumnsSpanned() { // from 8.1.3 Table Cell return Integer.parseInt(this.getElement().getAttributeValue("number-columns-spanned", getNS().getTABLE(), "1")); } public final int getRowsSpanned() { // from 8.1.3 Table Cell return Integer.parseInt(this.getElement().getAttributeValue("number-rows-spanned", getNS().getTABLE(), "1")); } protected final boolean coversOtherCells() { return getColumnsSpanned() > 1 || getRowsSpanned() > 1; } }