Added more features - this allows a client app to fully display table styling without resorting to CT* classes, and has some helper logic for the additional processing needed for things like regional/area borders. As far as I can tell I only added stuff, didn't change any existing behavior. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1795648 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_17_BETA1
@@ -314,4 +314,12 @@ public final class HSSFConditionalFormattingRule implements ConditionalFormattin | |||
} | |||
return HSSFFormulaParser.toFormulaString(workbook, parsedExpression); | |||
} | |||
/** | |||
* Conditional format rules don't define stripes, so always 0 | |||
* @see org.apache.poi.ss.usermodel.DifferentialStyleProvider#getStripeSize() | |||
*/ | |||
public int getStripeSize() { | |||
return 0; | |||
} | |||
} |
@@ -46,4 +46,11 @@ public interface DifferentialStyleProvider { | |||
*/ | |||
PatternFormatting getPatternFormatting(); | |||
/** | |||
* This is the number of rows or columns in a band or stripe. | |||
* For styles that represent stripes, it must be > 1, for all others it is 0. | |||
* Not the greatest overloading by the OOXML spec. | |||
* @return number of rows/columns in a stripe for stripe styles, 0 for all others | |||
*/ | |||
int getStripeSize(); | |||
} |
@@ -59,6 +59,12 @@ public interface Table { | |||
*/ | |||
String getName(); | |||
/** | |||
* @return name of the table style, if there is one. May be a built-in name or user-defined. | |||
* @since 3.17 beta 1 | |||
*/ | |||
String getStyleName(); | |||
/** | |||
* Returns the index of a given named column in the table (names are case insensitive in XSSF). | |||
* Note this list is lazily loaded and cached for performance. | |||
@@ -70,16 +76,46 @@ public interface Table { | |||
int findColumnIndex(String columnHeader); | |||
/** | |||
* Returns the sheet name that the table belongs to. | |||
* @return sheet name | |||
*/ | |||
String getSheetName(); | |||
/** | |||
* Returns true iff the table has a 'Totals' row | |||
* Note: This is misleading. The OOXML spec indicates this is true if the totals row | |||
* has <b><i>ever</i></b> been shown, not whether or not it is currently displayed. | |||
* Use {@link #getTotalsRowCount()} > 0 to decide whether or not the totals row is visible. | |||
* @return true if a totals row has ever been shown for this table | |||
* @since 3.15 beta 2 | |||
* @see #getTotalsRowCount() | |||
*/ | |||
boolean isHasTotalsRow(); | |||
/** | |||
* @return 0 for no totals rows, 1 for totals row shown. | |||
* Values > 1 are not currently used by Excel up through 2016, and the OOXML spec | |||
* doesn't define how they would be implemented. | |||
* @since 3.17 beta 1 | |||
*/ | |||
int getTotalsRowCount(); | |||
/** | |||
* @return 0 for no header rows, 1 for table headers shown. | |||
* Values > 1 might be used by Excel for pivot tables? | |||
* @since 3.17 beta 1 | |||
*/ | |||
int getHeaderRowCount(); | |||
/** | |||
* @return TableStyleInfo for this instance | |||
* @since 3.17 beta 1 | |||
*/ | |||
TableStyleInfo getStyle(); | |||
/** | |||
* checks if the given cell is part of the table. Includes checking that they are on the same sheet. | |||
* @param cell | |||
* @return true if the table and cell are on the same sheet and the cell is within the table range. | |||
* @since 3.17 beta 1 | |||
*/ | |||
boolean contains(Cell cell); | |||
} |
@@ -27,10 +27,25 @@ package org.apache.poi.ss.usermodel; | |||
public interface TableStyle { | |||
/** | |||
* @return name (may be a builtin name) | |||
* @return name (may be a built-in name) | |||
*/ | |||
String getName(); | |||
/** | |||
* Some clients may care where in the table style list this definition came from, so we'll track it. | |||
* The spec only references these by name, unlike Dxf records, which these definitions reference by index | |||
* (XML definition order). Nice of MS to be consistent when defining the ECMA standard. | |||
* Use org.apache.poi.xssf.usermodel.XSSFBuiltinTableStyle.isBuiltinStyle(TableStyle) to determine whether the index is for a built-in style or explicit user style | |||
* @return index from org.apache.poi.xssf.model.StylesTable.getExplicitTableStyle(String) or org.apache.poi.xssf.usermodel.XSSFBuiltinTableStyle.ordinal() | |||
*/ | |||
int getIndex(); | |||
/** | |||
* | |||
* @return true if this is a built-in style defined in the OOXML specification, false if it is a user style | |||
*/ | |||
boolean isBuiltin(); | |||
/** | |||
* | |||
* @param type |
@@ -17,6 +17,11 @@ | |||
package org.apache.poi.ss.usermodel; | |||
import java.util.EnumSet; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.ss.util.CellRangeAddressBase; | |||
/** | |||
* Ordered list of table style elements, for both data tables and pivot tables. | |||
* Some elements only apply to pivot tables, but any style definition can omit any number, | |||
@@ -33,34 +38,243 @@ package org.apache.poi.ss.usermodel; | |||
* @since 3.17 beta 1 | |||
*/ | |||
public enum TableStyleType { | |||
/***/ | |||
wholeTable { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
return new CellRangeAddress(table.getStartRowIndex(), table.getEndRowIndex(), table.getStartColIndex(), table.getEndColIndex()); | |||
} | |||
}, | |||
/***/ | |||
pageFieldLabels, // pivot only | |||
/***/ | |||
pageFieldValues, // pivot only | |||
/***/ | |||
firstColumnStripe{ | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
TableStyleInfo info = table.getStyle(); | |||
if (! info.isShowColumnStripes()) return null; | |||
DifferentialStyleProvider c1Style = info.getStyle().getStyle(firstColumnStripe); | |||
DifferentialStyleProvider c2Style = info.getStyle().getStyle(secondColumnStripe); | |||
int c1Stripe = c1Style == null ? 1 : Math.max(1, c1Style.getStripeSize()); | |||
int c2Stripe = c2Style == null ? 1 : Math.max(1, c2Style.getStripeSize()); | |||
int firstStart = table.getStartColIndex(); | |||
int secondStart = firstStart + c1Stripe; | |||
int c = cell.getColumnIndex(); | |||
// look for the stripe containing c, accounting for the style element stripe size | |||
// could do fancy math, but tables can't be that wide, a simple loop is fine | |||
// if not in this type of stripe, return null | |||
while (true) { | |||
if (firstStart > c) break; | |||
if (c >= firstStart && c <= secondStart -1) return new CellRangeAddress(table.getStartRowIndex(), table.getEndRowIndex(), firstStart, secondStart - 1); | |||
firstStart = secondStart + c2Stripe; | |||
secondStart = firstStart + c1Stripe; | |||
} | |||
return null; | |||
} | |||
}, | |||
/***/ | |||
secondColumnStripe{ | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
TableStyleInfo info = table.getStyle(); | |||
if (! info.isShowColumnStripes()) return null; | |||
DifferentialStyleProvider c1Style = info.getStyle().getStyle(firstColumnStripe); | |||
DifferentialStyleProvider c2Style = info.getStyle().getStyle(secondColumnStripe); | |||
int c1Stripe = c1Style == null ? 1 : Math.max(1, c1Style.getStripeSize()); | |||
int c2Stripe = c2Style == null ? 1 : Math.max(1, c2Style.getStripeSize()); | |||
int firstStart = table.getStartColIndex(); | |||
int secondStart = firstStart + c1Stripe; | |||
int c = cell.getColumnIndex(); | |||
// look for the stripe containing c, accounting for the style element stripe size | |||
// could do fancy math, but tables can't be that wide, a simple loop is fine | |||
// if not in this type of stripe, return null | |||
while (true) { | |||
if (firstStart > c) break; | |||
if (c >= secondStart && c <= secondStart + c2Stripe -1) return new CellRangeAddress(table.getStartRowIndex(), table.getEndRowIndex(), secondStart, secondStart + c2Stripe - 1); | |||
firstStart = secondStart + c2Stripe; | |||
secondStart = firstStart + c1Stripe; | |||
} | |||
return null; | |||
} | |||
}, | |||
/***/ | |||
firstRowStripe { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
TableStyleInfo info = table.getStyle(); | |||
if (! info.isShowRowStripes()) return null; | |||
DifferentialStyleProvider c1Style = info.getStyle().getStyle(firstRowStripe); | |||
DifferentialStyleProvider c2Style = info.getStyle().getStyle(secondRowStripe); | |||
int c1Stripe = c1Style == null ? 1 : Math.max(1, c1Style.getStripeSize()); | |||
int c2Stripe = c2Style == null ? 1 : Math.max(1, c2Style.getStripeSize()); | |||
int firstStart = table.getStartRowIndex() + table.getHeaderRowCount(); | |||
int secondStart = firstStart + c1Stripe; | |||
int c = cell.getRowIndex(); | |||
// look for the stripe containing c, accounting for the style element stripe size | |||
// could do fancy math, but tables can't be that wide, a simple loop is fine | |||
// if not in this type of stripe, return null | |||
while (true) { | |||
if (firstStart > c) break; | |||
if (c >= firstStart && c <= secondStart -1) return new CellRangeAddress(firstStart, secondStart - 1, table.getStartColIndex(), table.getEndColIndex()); | |||
firstStart = secondStart + c2Stripe; | |||
secondStart = firstStart + c1Stripe; | |||
} | |||
return null; | |||
} | |||
}, | |||
/***/ | |||
secondRowStripe{ | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
TableStyleInfo info = table.getStyle(); | |||
if (! info.isShowRowStripes()) return null; | |||
DifferentialStyleProvider c1Style = info.getStyle().getStyle(firstRowStripe); | |||
DifferentialStyleProvider c2Style = info.getStyle().getStyle(secondRowStripe); | |||
int c1Stripe = c1Style == null ? 1 : Math.max(1, c1Style.getStripeSize()); | |||
int c2Stripe = c2Style == null ? 1 : Math.max(1, c2Style.getStripeSize()); | |||
wholeTable, | |||
headerRow, | |||
totalRow, | |||
firstColumn, | |||
lastColumn, | |||
firstRowStripe, | |||
secondRowStripe, | |||
firstColumnStripe, | |||
secondColumnStripe, | |||
firstHeaderCell, | |||
lastHeaderCell, | |||
firstTotalCell, | |||
lastTotalCell, | |||
int firstStart = table.getStartRowIndex() + table.getHeaderRowCount(); | |||
int secondStart = firstStart + c1Stripe; | |||
int c = cell.getRowIndex(); | |||
// look for the stripe containing c, accounting for the style element stripe size | |||
// could do fancy math, but tables can't be that wide, a simple loop is fine | |||
// if not in this type of stripe, return null | |||
while (true) { | |||
if (firstStart > c) break; | |||
if (c >= secondStart && c <= secondStart +c2Stripe -1) return new CellRangeAddress(secondStart, secondStart + c2Stripe - 1, table.getStartColIndex(), table.getEndColIndex()); | |||
firstStart = secondStart + c2Stripe; | |||
secondStart = firstStart + c1Stripe; | |||
} | |||
return null; | |||
} | |||
}, | |||
/***/ | |||
lastColumn { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
if (! table.getStyle().isShowLastColumn()) return null; | |||
return new CellRangeAddress(table.getStartRowIndex(), table.getEndRowIndex(), table.getEndColIndex(), table.getEndColIndex()); | |||
} | |||
}, | |||
/***/ | |||
firstColumn { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
if (! table.getStyle().isShowFirstColumn()) return null; | |||
return new CellRangeAddress(table.getStartRowIndex(), table.getEndRowIndex(), table.getStartColIndex(), table.getStartColIndex()); | |||
} | |||
}, | |||
/***/ | |||
headerRow { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
if (table.getHeaderRowCount() < 1) return null; | |||
return new CellRangeAddress(table.getStartRowIndex(), table.getStartRowIndex() + table.getHeaderRowCount() -1, table.getStartColIndex(), table.getEndColIndex()); | |||
} | |||
}, | |||
/***/ | |||
totalRow { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
if (table.getTotalsRowCount() < 1) return null; | |||
return new CellRangeAddress(table.getEndRowIndex() - table.getTotalsRowCount() +1, table.getEndRowIndex(), table.getStartColIndex(), table.getEndColIndex()); | |||
} | |||
}, | |||
/***/ | |||
firstHeaderCell { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
if (table.getHeaderRowCount() < 1) return null; | |||
return new CellRangeAddress(table.getStartRowIndex(), table.getStartRowIndex(), table.getStartColIndex(), table.getStartColIndex()); | |||
} | |||
}, | |||
/***/ | |||
lastHeaderCell { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
if (table.getHeaderRowCount() < 1) return null; | |||
return new CellRangeAddress(table.getStartRowIndex(), table.getStartRowIndex(), table.getEndColIndex(), table.getEndColIndex()); | |||
} | |||
}, | |||
/***/ | |||
firstTotalCell { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
if (table.getTotalsRowCount() < 1) return null; | |||
return new CellRangeAddress(table.getEndRowIndex() - table.getTotalsRowCount() +1, table.getEndRowIndex(), table.getStartColIndex(), table.getStartColIndex()); | |||
} | |||
}, | |||
/***/ | |||
lastTotalCell { | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
if (table.getTotalsRowCount() < 1) return null; | |||
return new CellRangeAddress(table.getEndRowIndex() - table.getTotalsRowCount() +1, table.getEndRowIndex(), table.getEndColIndex(), table.getEndColIndex()); | |||
} | |||
}, | |||
/* these are for pivot tables only */ | |||
/***/ | |||
firstSubtotalColumn, | |||
/***/ | |||
secondSubtotalColumn, | |||
/***/ | |||
thirdSubtotalColumn, | |||
/***/ | |||
blankRow, | |||
/***/ | |||
firstSubtotalRow, | |||
/***/ | |||
secondSubtotalRow, | |||
/***/ | |||
thirdSubtotalRow, | |||
blankRow, | |||
/***/ | |||
firstColumnSubheading, | |||
/***/ | |||
secondColumnSubheading, | |||
/***/ | |||
thirdColumnSubheading, | |||
/***/ | |||
firstRowSubheading, | |||
/***/ | |||
secondRowSubheading, | |||
/***/ | |||
thirdRowSubheading, | |||
pageFieldLabels, | |||
pageFieldValues, | |||
; | |||
/** | |||
* A range is returned only for the part of the table matching this enum instance and containing the given cell. | |||
* Null is returned for all other cases, such as: | |||
* <ul> | |||
* <li>Cell on a different sheet than the table | |||
* <li>Cell outside the table | |||
* <li>this Enum part is not included in the table (i.e. no header/totals row) | |||
* <li>this Enum is for a table part not yet implemented in POI, such as pivot table elements | |||
* </ul> | |||
* The returned range can be used to determine how style options may or may not apply to this cell. | |||
* For example, {@link #wholeTable} borders only apply to the outer boundary of a table, while the | |||
* rest of the styling, such as font and color, could apply to all the interior cells as well. | |||
* | |||
* @param table table to evaluate | |||
* @param cell to evaluate | |||
* @return range in the table representing this class of cells, if it contains the given cell, or null if not applicable. | |||
* Stripe style types return only the stripe range containing the given cell, or null. | |||
*/ | |||
public CellRangeAddressBase appliesTo(Table table, Cell cell) { | |||
if (table == null || cell == null) return null; | |||
if ( ! cell.getSheet().getSheetName().equals(table.getSheetName())) return null; | |||
if ( ! table.contains(cell)) return null; | |||
final CellRangeAddressBase range = getRange(table, cell); | |||
if (range != null && range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) return range; | |||
// else | |||
return null; | |||
} | |||
/** | |||
* @param table | |||
* @param cell | |||
* @return default is unimplemented/null | |||
*/ | |||
CellRangeAddressBase getRange(Table table, Cell cell) { | |||
return null; | |||
} | |||
} |
@@ -17,6 +17,9 @@ | |||
package org.apache.poi.ss.util; | |||
import java.util.EnumSet; | |||
import java.util.Set; | |||
import org.apache.poi.ss.SpreadsheetVersion; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
@@ -28,6 +31,23 @@ import org.apache.poi.ss.usermodel.Cell; | |||
*/ | |||
public abstract class CellRangeAddressBase { | |||
/** | |||
* Indicates a cell or range is in the given relative position in a range. | |||
* More than one of these may apply at once. | |||
*/ | |||
public static enum CellPosition { | |||
/** range starting rows are equal */ | |||
TOP, | |||
/** range ending rows are equal */ | |||
BOTTOM, | |||
/** range starting columns are equal */ | |||
LEFT, | |||
/** range ending columns are equal */ | |||
RIGHT, | |||
/** a cell or range is completely inside another range, without touching any edges (a cell in this position can't be in any others) */ | |||
INSIDE, | |||
; | |||
} | |||
private int _firstRow; | |||
private int _firstCol; | |||
private int _lastRow; | |||
@@ -188,6 +208,28 @@ public abstract class CellRangeAddressBase { | |||
other._firstCol <= this._lastCol; | |||
} | |||
/** | |||
* Useful for logic like table/range styling, where some elements apply based on relative position in a range. | |||
* @param rowInd | |||
* @param colInd | |||
* @return set of {@link CellPosition}s occupied by the given coordinates. Empty if the coordinates are not in the range, never null. | |||
* @since 3.17 beta 1 | |||
*/ | |||
public Set<CellPosition> getPosition(int rowInd, int colInd) { | |||
Set<CellPosition> positions = EnumSet.noneOf(CellPosition.class); | |||
if (rowInd > getFirstRow() && rowInd < getLastRow() && colInd > getFirstColumn() && colInd < getLastColumn()) { | |||
positions.add(CellPosition.INSIDE); | |||
return positions; // entirely inside, matches no boundaries | |||
} | |||
// check edges | |||
if (rowInd == getFirstRow()) positions.add(CellPosition.TOP); | |||
if (rowInd == getLastRow()) positions.add(CellPosition.BOTTOM); | |||
if (colInd == getFirstColumn()) positions.add(CellPosition.LEFT); | |||
if (colInd == getLastColumn()) positions.add(CellPosition.RIGHT); | |||
return positions; | |||
} | |||
/** | |||
* @param firstCol column number for the upper left hand corner | |||
*/ |
@@ -29,6 +29,7 @@ import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
import java.util.Set; | |||
import java.util.SortedMap; | |||
import java.util.TreeMap; | |||
@@ -246,8 +247,10 @@ public class StylesTable extends POIXMLDocumentPart { | |||
CTTableStyles ctTableStyles = styleSheet.getTableStyles(); | |||
if (ctTableStyles != null) { | |||
int idx = 0; | |||
for (CTTableStyle style : Arrays.asList(ctTableStyles.getTableStyleArray())) { | |||
tableStyles.put(style.getName(), new XSSFTableStyle(styleDxfs, style)); | |||
tableStyles.put(style.getName(), new XSSFTableStyle(idx, styleDxfs, style)); | |||
idx++; | |||
} | |||
} | |||
@@ -792,6 +795,14 @@ public class StylesTable extends POIXMLDocumentPart { | |||
return tableStyles.get(name); | |||
} | |||
/** | |||
* @return names of all explicitly defined table styles in the workbook | |||
* @since 3.17 beta 1 | |||
*/ | |||
public Set<String> getExplicitTableStyleNames() { | |||
return tableStyles.keySet(); | |||
} | |||
/** | |||
* @param name of the table style | |||
* @return defined style, either explicit or built-in, or null if not found |
@@ -20,16 +20,15 @@ package org.apache.poi.xssf.usermodel; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.InputStream; | |||
import java.nio.charset.Charset; | |||
import java.util.HashMap; | |||
import java.util.EnumMap; | |||
import java.util.Map; | |||
import org.apache.poi.ss.usermodel.DifferentialStyleProvider; | |||
import org.apache.poi.ss.usermodel.TableStyle; | |||
import org.apache.poi.ss.usermodel.TableStyleType; | |||
import org.apache.poi.util.DocumentHelper; | |||
import org.apache.poi.util.IOUtils; | |||
import org.apache.poi.xssf.model.StylesTable; | |||
import org.apache.xmlbeans.XmlOptions; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxfs; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyle; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.Node; | |||
@@ -332,7 +331,10 @@ public enum XSSFBuiltinTableStyle { | |||
PivotStyleDark28, | |||
; | |||
private static final Map<XSSFBuiltinTableStyle, TableStyle> styleMap = new HashMap<XSSFBuiltinTableStyle, TableStyle>(60); | |||
/** | |||
* Interestingly, this is initialized after the enum instances, so using an {@link EnumMap} works. | |||
*/ | |||
private static final Map<XSSFBuiltinTableStyle, TableStyle> styleMap = new EnumMap<XSSFBuiltinTableStyle, TableStyle>(XSSFBuiltinTableStyle.class); | |||
private XSSFBuiltinTableStyle() { | |||
} | |||
@@ -399,7 +401,8 @@ public enum XSSFBuiltinTableStyle { | |||
// - build a fake styles.xml file with just this built-in | |||
StylesTable styles = new StylesTable(); | |||
styles.readFrom(new ByteArrayInputStream(styleXML(dxfsNode, tableStyleNode).getBytes(Charset.forName("UTF-8")))); | |||
styleMap.put(XSSFBuiltinTableStyle.valueOf(styleName), styles.getExplicitTableStyle(styleName)); | |||
XSSFBuiltinTableStyle builtIn = XSSFBuiltinTableStyle.valueOf(styleName); | |||
styleMap.put(builtIn, new XSSFBuiltinTypeStyleStyle(builtIn, styles.getExplicitTableStyle(styleName))); | |||
} | |||
} finally { | |||
IOUtils.closeQuietly(is); | |||
@@ -410,6 +413,10 @@ public enum XSSFBuiltinTableStyle { | |||
} | |||
private static String styleXML(Node dxfsNode, Node tableStyleNode) { | |||
// built-ins doc uses 1-based dxf indexing, Excel uses 0 based. | |||
// add a dummy node to adjust properly. | |||
dxfsNode.insertBefore(dxfsNode.getOwnerDocument().createElement("dxf"), dxfsNode.getFirstChild()); | |||
DOMImplementationLS lsImpl = (DOMImplementationLS)dxfsNode.getOwnerDocument().getImplementation().getFeature("LS", "3.0"); | |||
LSSerializer lsSerializer = lsImpl.createLSSerializer(); | |||
lsSerializer.getDomConfig().setParameter("xml-declaration", false); | |||
@@ -425,4 +432,39 @@ public enum XSSFBuiltinTableStyle { | |||
sb.append("</styleSheet>"); | |||
return sb.toString(); | |||
} | |||
/** | |||
* implementation for built-in styles | |||
*/ | |||
protected static class XSSFBuiltinTypeStyleStyle implements TableStyle { | |||
private final XSSFBuiltinTableStyle builtIn; | |||
private final TableStyle style; | |||
/** | |||
* @param builtIn | |||
* @param style | |||
*/ | |||
protected XSSFBuiltinTypeStyleStyle(XSSFBuiltinTableStyle builtIn, TableStyle style) { | |||
this.builtIn = builtIn; | |||
this.style = style; | |||
} | |||
public String getName() { | |||
return style.getName(); | |||
} | |||
public int getIndex() { | |||
return builtIn.ordinal(); | |||
} | |||
public boolean isBuiltin() { | |||
return true; | |||
} | |||
public DifferentialStyleProvider getStyle(TableStyleType type) { | |||
return style.getStyle(type); | |||
} | |||
} | |||
} |
@@ -412,4 +412,12 @@ public class XSSFConditionalFormattingRule implements ConditionalFormattingRule | |||
public String getFormula2(){ | |||
return _cfRule.sizeOfFormulaArray() == 2 ? _cfRule.getFormulaArray(1) : null; | |||
} | |||
/** | |||
* Conditional format rules don't define stripes, so always 0 | |||
* @see org.apache.poi.ss.usermodel.DifferentialStyleProvider#getStripeSize() | |||
*/ | |||
public int getStripeSize() { | |||
return 0; | |||
} | |||
} |
@@ -34,11 +34,14 @@ public class XSSFDxfStyleProvider implements DifferentialStyleProvider { | |||
private final FontFormatting font; | |||
private final ExcelNumberFormat number; | |||
private final PatternFormatting fill; | |||
private final int stripeSize; | |||
/** | |||
* @param dxf | |||
* @param stripeSize 0 for non-stripe styles, > 1 for stripes | |||
*/ | |||
public XSSFDxfStyleProvider(CTDxf dxf) { | |||
public XSSFDxfStyleProvider(CTDxf dxf, int stripeSize) { | |||
this.stripeSize = stripeSize; | |||
if (dxf == null) { | |||
border = null; | |||
font = null; | |||
@@ -72,5 +75,9 @@ public class XSSFDxfStyleProvider implements DifferentialStyleProvider { | |||
public PatternFormatting getPatternFormatting() { | |||
return fill; | |||
} | |||
public int getStripeSize() { | |||
return stripeSize; | |||
} | |||
} |
@@ -30,6 +30,7 @@ import java.util.Locale; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.usermodel.Table; | |||
import org.apache.poi.ss.usermodel.TableStyleInfo; | |||
import org.apache.poi.ss.util.CellReference; | |||
@@ -62,14 +63,20 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { | |||
private transient CellReference startCellReference; | |||
private transient CellReference endCellReference; | |||
private transient String commonXPath; | |||
private transient String name; | |||
private transient String styleName; | |||
/** | |||
* empty implementation, not attached to a workbook/worksheet yet | |||
*/ | |||
public XSSFTable() { | |||
super(); | |||
ctTable = CTTable.Factory.newInstance(); | |||
} | |||
/** | |||
* @param part | |||
* @throws IOException | |||
* @since POI 3.14-Beta1 | |||
*/ | |||
public XSSFTable(PackagePart part) throws IOException { | |||
@@ -77,6 +84,11 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { | |||
readFrom(part.getInputStream()); | |||
} | |||
/** | |||
* read table XML | |||
* @param is | |||
* @throws IOException | |||
*/ | |||
public void readFrom(InputStream is) throws IOException { | |||
try { | |||
TableDocument doc = TableDocument.Factory.parse(is, DEFAULT_XML_OPTIONS); | |||
@@ -86,10 +98,18 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { | |||
} | |||
} | |||
/** | |||
* @return owning sheet | |||
*/ | |||
public XSSFSheet getXSSFSheet(){ | |||
return (XSSFSheet) getParent(); | |||
} | |||
/** | |||
* write table XML to stream | |||
* @param out | |||
* @throws IOException | |||
*/ | |||
public void writeTo(OutputStream out) throws IOException { | |||
updateHeaders(); | |||
@@ -108,6 +128,7 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { | |||
/** | |||
* get the underlying CTTable XML bean | |||
* @return underlying OOXML object | |||
*/ | |||
@Internal(since="POI 3.15 beta 3") | |||
public CTTable getCTTable() { | |||
@@ -209,20 +230,57 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { | |||
* @return the name of the Table, if set | |||
*/ | |||
public String getName() { | |||
return ctTable.getName(); | |||
if (name == null) { | |||
setName(ctTable.getName()); | |||
} | |||
return name; | |||
} | |||
/** | |||
* Changes the name of the Table | |||
* @param newName | |||
*/ | |||
public void setName(String name) { | |||
if (name == null) { | |||
public void setName(String newName) { | |||
if (newName == null) { | |||
ctTable.unsetName(); | |||
name = null; | |||
return; | |||
} | |||
ctTable.setName(name); | |||
ctTable.setName(newName); | |||
name = newName; | |||
} | |||
/** | |||
* @return the table style name, if set | |||
* @since 3.17 beta 1 | |||
*/ | |||
public String getStyleName() { | |||
if (styleName == null && ctTable.isSetTableStyleInfo()) { | |||
setStyleName(ctTable.getTableStyleInfo().getName()); | |||
} | |||
return styleName; | |||
} | |||
/** | |||
* Changes the name of the Table | |||
* @param newStyleName | |||
* @since 3.17 beta 1 | |||
*/ | |||
public void setStyleName(String newStyleName) { | |||
if (newStyleName == null) { | |||
if (ctTable.isSetTableStyleInfo()) { | |||
ctTable.getTableStyleInfo().unsetName(); | |||
} | |||
styleName = null; | |||
return; | |||
} | |||
if (! ctTable.isSetTableStyleInfo()) { | |||
ctTable.addNewTableStyleInfo(); | |||
} | |||
ctTable.getTableStyleInfo().setName(newStyleName); | |||
styleName = newStyleName; | |||
} | |||
/** | |||
* @return the display name of the Table, if set | |||
*/ | |||
@@ -232,6 +290,7 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { | |||
/** | |||
* Changes the display name of the Table | |||
* @param name to use | |||
*/ | |||
public void setDisplayName(String name) { | |||
ctTable.setDisplayName(name); | |||
@@ -412,12 +471,35 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { | |||
} | |||
/** | |||
* Note: This is misleading. The Spec indicates this is true if the totals row | |||
* has <b><i>ever</i></b> been shown, not whether or not it is currently displayed. | |||
* Use {@link #getTotalsRowCount()} > 0 to decide whether or not the totals row is visible. | |||
* @since 3.15 beta 2 | |||
* @see #getTotalsRowCount() | |||
*/ | |||
public boolean isHasTotalsRow() { | |||
return ctTable.getTotalsRowShown(); | |||
} | |||
/** | |||
* @return 0 for no totals rows, 1 for totals row shown. | |||
* Values > 1 are not currently used by Excel up through 2016, and the OOXML spec | |||
* doesn't define how they would be implemented. | |||
* @since 3.17 beta 1 | |||
*/ | |||
public int getTotalsRowCount() { | |||
return (int) ctTable.getTotalsRowCount(); | |||
} | |||
/** | |||
* @return 0 for no header rows, 1 for table headers shown. | |||
* Values > 1 might be used by Excel for pivot tables? | |||
* @since 3.17 beta 1 | |||
*/ | |||
public int getHeaderRowCount() { | |||
return (int) ctTable.getHeaderRowCount(); | |||
} | |||
/** | |||
* @since 3.15 beta 2 | |||
*/ | |||
@@ -447,11 +529,27 @@ public class XSSFTable extends POIXMLDocumentPart implements Table { | |||
} | |||
/** | |||
* | |||
* @since 3.17 beta 1 | |||
*/ | |||
public TableStyleInfo getStyle() { | |||
if (! ctTable.isSetTableStyleInfo()) return null; | |||
return new XSSFTableStyleInfo(((XSSFSheet) getParent()).getWorkbook().getStylesSource(), ctTable.getTableStyleInfo()); | |||
} | |||
} | |||
/** | |||
* @see org.apache.poi.ss.usermodel.Table#contains(org.apache.poi.ss.usermodel.Cell) | |||
* @since 3.17 beta 1 | |||
*/ | |||
public boolean contains(Cell cell) { | |||
if (cell == null) return false; | |||
// check if cell is on the same sheet as the table | |||
if ( ! getSheetName().equals(cell.getSheet().getSheetName())) return false; | |||
// check if the cell is inside the table | |||
if (cell.getRowIndex() >= getStartRowIndex() | |||
&& cell.getRowIndex() <= getEndRowIndex() | |||
&& cell.getColumnIndex() >= getStartColIndex() | |||
&& cell.getColumnIndex() <= getEndColIndex()) { | |||
return true; | |||
} | |||
return false; | |||
}} |
@@ -35,26 +35,32 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyleElement; | |||
public class XSSFTableStyle implements TableStyle { | |||
private final String name; | |||
private final int index; | |||
private final Map<TableStyleType, DifferentialStyleProvider> elementMap = new EnumMap<TableStyleType, DifferentialStyleProvider>(TableStyleType.class); | |||
/** | |||
* @param index style definition index or built-in ordinal depending on use | |||
* @param dxfs | |||
* @param tableStyle | |||
* @see TableStyle#getIndex() | |||
*/ | |||
public XSSFTableStyle(CTDxfs dxfs, CTTableStyle tableStyle) { | |||
public XSSFTableStyle(int index, CTDxfs dxfs, CTTableStyle tableStyle) { | |||
this.name = tableStyle.getName(); | |||
this.index = index; | |||
for (CTTableStyleElement element : tableStyle.getTableStyleElementList()) { | |||
TableStyleType type = TableStyleType.valueOf(element.getType().toString()); | |||
DifferentialStyleProvider dstyle = null; | |||
if (element.isSetDxfId()) { | |||
int idx = (int) element.getDxfId() -1; | |||
int idx = (int) element.getDxfId(); | |||
CTDxf dxf; | |||
if (idx >= 0 && idx < dxfs.getCount()) { | |||
dxf = dxfs.getDxfArray(idx); | |||
} else { | |||
dxf = null; | |||
} | |||
if (dxf != null) dstyle = new XSSFDxfStyleProvider(dxf); | |||
int stripeSize = 0; | |||
if (element.isSetSize()) stripeSize = (int) element.getSize(); | |||
if (dxf != null) dstyle = new XSSFDxfStyleProvider(dxf, stripeSize); | |||
} | |||
elementMap.put(type, dstyle); | |||
} | |||
@@ -64,6 +70,17 @@ public class XSSFTableStyle implements TableStyle { | |||
return name; | |||
} | |||
public int getIndex() { | |||
return index; | |||
} | |||
/** | |||
* Always false for these, these are user defined styles | |||
*/ | |||
public boolean isBuiltin() { | |||
return false; | |||
} | |||
public DifferentialStyleProvider getStyle(TableStyleType type) { | |||
return elementMap.get(type); | |||
} |