org.jopendocument.dom.spreadsheet.CellStyle Maven / Gradle / Ivy
/*
* 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.
*
*/
package org.jopendocument.dom.spreadsheet;
import org.jopendocument.dom.Log;
import org.jopendocument.dom.ODPackage;
import org.jopendocument.dom.ODValueType;
import org.jopendocument.dom.Style;
import org.jopendocument.dom.StyleStyle;
import org.jopendocument.dom.StyleStyleDesc;
import org.jopendocument.dom.StyledNode;
import org.jopendocument.dom.XMLVersion;
import org.jopendocument.dom.style.RelationalOperator;
import org.jopendocument.dom.style.SideStyleProperties;
import org.jopendocument.dom.style.data.BooleanStyle;
import org.jopendocument.dom.style.data.DataStyle;
import org.jopendocument.dom.style.data.NumberStyle;
import org.jopendocument.dom.text.ParagraphStyle.StyleParagraphProperties;
import org.jopendocument.dom.text.TextStyle.StyleTextProperties;
import org.jopendocument.util.CompareUtils;
import org.jopendocument.util.Tuple3;
import org.jopendocument.util.JDOMUtils;
import java.awt.Color;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jdom.Attribute;
import org.jdom.Element;
public class CellStyle extends StyleStyle {
private static final Pattern numberPatrn = Pattern.compile("-?\\d+(?:\\.\\d+)?");
private static final Pattern escapedQuotePatrn = Pattern.compile("\"\"", Pattern.LITERAL);
private static final Pattern stringPatrn = Pattern.compile("\"(?:[^\\p{Cntrl}\"]|\\p{Space}|" + escapedQuotePatrn.pattern() + ")*\"");
private static final String valuePatrn = "(" + numberPatrn.pattern() + "|" + stringPatrn.pattern() + ")";
private static final Pattern cellContentPatrn = Pattern.compile("cell-content\\(\\) *(" + RelationalOperator.OR_PATTERN + ") *" + valuePatrn + "");
private static final Pattern cellContentBetweenPatrn = Pattern.compile("cell-content-is(?:-not)?-between\\(" + valuePatrn + ", *" + valuePatrn + "\\)");
// from section 18.728 in v1.2-part1
public static final StyleStyleDesc DESC = new StyleStyleDesc(CellStyle.class, XMLVersion.OD, "table-cell", "ce", "table", Arrays.asList("table:body",
"table:covered-table-cell", "table:even-rows", "table:first-column", "table:first-row", "table:last-column", "table:last-row", "table:odd-columns", "table:odd-rows", "table:table-cell")) {
{
this.getMultiRefElementsMap().putAll("table:default-cell-style-name", "table:table-column", "table:table-row");
}
@Override
public CellStyle create(ODPackage pkg, Element e) {
return new CellStyle(pkg, e);
}
@Override
protected boolean supportConditions() {
return true;
}
@Override
protected Element evaluateConditions(final StyledNode styledNode, final List styleMaps) {
final Cell> cell = (Cell>) styledNode;
final Object cellValue = cell.getValue();
for (final Element styleMap : styleMaps) {
final String condition = styleMap.getAttributeValue("condition", getVersion().getSTYLE()).trim();
Matcher matcher = cellContentPatrn.matcher(condition);
if (matcher.matches()) {
if (RelationalOperator.getInstance(matcher.group(1)).compare(cellValue, parse(matcher.group(2))))
return styleMap;
} else if ((matcher = cellContentBetweenPatrn.matcher(condition)).matches()) {
final boolean wantBetween = condition.startsWith("cell-content-is-between");
assert wantBetween ^ condition.startsWith("cell-content-is-not-between");
final Object o1 = parse(matcher.group(1));
final Object o2 = parse(matcher.group(2));
final boolean isBetween = CompareUtils.compare(cellValue, o1) >= 0 && CompareUtils.compare(cellValue, o2) <= 0;
if (isBetween == wantBetween)
return styleMap;
} else {
// If a consumer does not recognize a condition, it shall ignore the
// element containing the condition.
Log.get().fine("Ignoring " + JDOMUtils.output(styleMap));
}
}
return null;
}
};
private static final Pattern conditionPatrn = Pattern.compile("value\\(\\) *(" + RelationalOperator.OR_PATTERN + ") *(true|false|" + numberPatrn.pattern() + ")");
// from style:condition :
// "n is a number for non-Boolean data styles and true or false for Boolean data styles"
private static final Object convertForCondition(final Object value, final DataStyle style) {
final Object castedValue;
if (style instanceof BooleanStyle) {
castedValue = BooleanStyle.toBoolean(value);
} else {
castedValue = NumberStyle.toNumber(value, style.getEpoch());
}
return castedValue;
}
private StyleTableCellProperties cellProps;
private StyleTextProperties textProps;
private StyleParagraphProperties pProps;
public CellStyle(final ODPackage pkg, Element tableColElem) {
super(pkg, tableColElem);
}
private final DataStyle getDataStyle(final Attribute name) {
return (DataStyle) Style.getReferencedStyle(getPackage(), name);
}
// return value since it can be changed depending on the data style.
// e.g. in OO if we input 12:30 in an empty cell, it will have value-type="time"
// but if we had previously set a number style (like 0,00) it would have been converted to 0,52
// value-type="float"
final Tuple3 getDataStyle(final Object cellValue, final ODValueType valueType, final boolean onlyCast) {
DataStyle res = getDataStyle(this.getElement().getAttribute("data-style-name", this.getSTYLE()));
ODValueType returnValueType = valueType;
Object returnCellValue = cellValue;
// if the type is null, then the cell is empty so don't try to convert the cell value or
// evaluate conditions
if (res != null && valueType != null) {
if (!onlyCast) {
final Object convertedForStyle = res.convert(cellValue);
// if conversion is successful
if (convertedForStyle != null) {
returnCellValue = convertedForStyle;
returnValueType = res.getDataType();
}
}
final List> styleMaps = res.getElement().getChildren("map", getSTYLE());
if (styleMaps.size() > 0) {
final Object converted = convertForCondition(returnCellValue, res);
// we can't compare() so don't try
if (converted != null) {
for (Object child : styleMaps) {
final Element styleMap = (Element) child;
final Matcher matcher = conditionPatrn.matcher(styleMap.getAttributeValue("condition", getSTYLE()).trim());
if (!matcher.matches())
throw new IllegalStateException("Cannot parse " + JDOMUtils.output(styleMap));
if (RelationalOperator.getInstance(matcher.group(1)).compare(converted, parse(matcher.group(2)))) {
res = getDataStyle(styleMap.getAttribute("apply-style-name", getSTYLE()));
break;
}
}
}
}
}
// if the type is null, then the cell is empty, we cannot make up some value, otherwise
// don't change it to null
assert (valueType == null) == (returnValueType == null) : "don't change type to null";
assert !onlyCast || (returnValueType == valueType && returnCellValue == cellValue) : "Requested to only cast, but different object";
// if res is null, the document is incoherent (non existing style name)
return res == null ? null : Tuple3.create(res, returnValueType, returnCellValue);
}
static private Object parse(String val) {
if (val.equalsIgnoreCase("true"))
return Boolean.TRUE;
else if (val.equalsIgnoreCase("false"))
return Boolean.FALSE;
else if (val.charAt(0) == '"')
return escapedQuotePatrn.matcher(val.substring(1, val.length() - 1)).replaceAll("\"");
else
return new BigDecimal(val);
}
public final Color getBackgroundColor() {
return getTableCellProperties().getBackgroundColor();
}
public final StyleTableCellProperties getTableCellProperties() {
if (this.cellProps == null)
this.cellProps = new StyleTableCellProperties(this);
return this.cellProps;
}
public final StyleTextProperties getTextProperties() {
if (this.textProps == null)
this.textProps = new StyleTextProperties(this);
return this.textProps;
}
public final StyleParagraphProperties getParagraphProperties() {
if (this.pProps == null)
this.pProps = new StyleParagraphProperties(this);
return this.pProps;
}
/**
* See section 15.11 of OpenDocument v1.1 : Table Cell Formatting Properties.
*
* @author Sylvain CUAZ
*/
public static class StyleTableCellProperties extends SideStyleProperties {
public StyleTableCellProperties(StyleStyle style) {
super(style, DESC.getFamily());
}
public final int getRotationAngle() {
final String s = this.getAttributeValue("rotation-angle", this.getElement().getNamespace("style"));
return parseInt(s, 0);
}
public final boolean isContentPrinted() {
return parseBoolean(this.getAttributeValue("print-content", this.getElement().getNamespace("style")), true);
}
public final boolean isContentRepeated() {
return parseBoolean(this.getAttributeValue("repeat-content", this.getElement().getNamespace("style")), false);
}
public final boolean isShrinkToFit() {
return parseBoolean(this.getAttributeValue("shrink-to-fit", this.getElement().getNamespace("style")), false);
}
public final Integer getDecimalPlaces() {
return parseInteger(this.getAttributeValue("decimal-places", this.getElement().getNamespace("style")));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy