mirror of
https://github.com/apache/poi.git
synced 2025-02-19 16:35:51 +00:00
Enhanced SViewer to support most border types, cell formats, and conditional formatting. Added ToHtml example that converts a spreadsheet into HTML, See Bugzilla #49066
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@942809 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a120845220
commit
9017094231
@ -504,6 +504,11 @@ under the License.
|
||||
<pathelement path="${ooxml.output.dir}"/>
|
||||
</classpath>
|
||||
</javac>
|
||||
<copy todir="${examples.output.dir}">
|
||||
<fileset dir="${examples.src}">
|
||||
<include name="**/*.css"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="compile-ooxml" depends="compile-main,compile-scratchpad">
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
<changes>
|
||||
<release version="3.7-SNAPSHOT" date="2010-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="add">49066 - Worksheet/cell formatting, with view and HTML converter</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">49020 - Workaround Excel outputting invalid XML in button definitions by not closing BR tags</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">49050 - Improve performance of AbstractEscherHolderRecord when there are lots of Continue Records</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">49194 - Correct text size limit for OOXML .xlsx files</action>
|
||||
|
@ -16,7 +16,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.contrib.view;
|
||||
package org.apache.poi.hssf.view;
|
||||
|
||||
import java.awt.*;
|
||||
|
@ -17,7 +17,7 @@
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.contrib.view;
|
||||
package org.apache.poi.hssf.view;
|
||||
|
||||
import java.text.*;
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
|
||||
|
||||
package org.apache.poi.hssf.contrib.view;
|
||||
package org.apache.poi.hssf.view;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
241
src/examples/src/org/apache/poi/hssf/view/SVSheetTable.java
Normal file
241
src/examples/src/org/apache/poi/hssf/view/SVSheetTable.java
Normal file
@ -0,0 +1,241 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.hssf.view;
|
||||
|
||||
import org.apache.poi.hssf.view.brush.PendingPaintings;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.*;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import java.awt.*;
|
||||
import java.awt.event.HierarchyEvent;
|
||||
import java.awt.event.HierarchyListener;
|
||||
|
||||
/**
|
||||
* This class is a table that represents the values in a single worksheet.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class SVSheetTable extends JTable {
|
||||
private final HSSFSheet sheet;
|
||||
private final PendingPaintings pendingPaintings;
|
||||
private FormulaDisplayListener formulaListener;
|
||||
private JScrollPane scroll;
|
||||
|
||||
private static final Color HEADER_BACKGROUND = new Color(235, 235, 235);
|
||||
|
||||
/**
|
||||
* This field is the magic number to convert from a Character width to a java
|
||||
* pixel width.
|
||||
* <p/>
|
||||
* When the "normal" font size in a workbook changes, this effects all of the
|
||||
* heights and widths. Unfortunately there is no way to retrieve this
|
||||
* information, hence the MAGIC number.
|
||||
* <p/>
|
||||
* This number may only work for the normal style font size of Arial size 10.
|
||||
*/
|
||||
private static final int magicCharFactor = 7;
|
||||
|
||||
private class HeaderCell extends JLabel {
|
||||
private final int row;
|
||||
|
||||
public HeaderCell(Object value, int row) {
|
||||
super(value.toString(), CENTER);
|
||||
this.row = row;
|
||||
setBackground(HEADER_BACKGROUND);
|
||||
setOpaque(true);
|
||||
setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
|
||||
setRowSelectionAllowed(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension d = super.getPreferredSize();
|
||||
if (row >= 0) {
|
||||
d.height = getRowHeight(row);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
Dimension d = super.getMaximumSize();
|
||||
if (row >= 0) {
|
||||
d.height = getRowHeight(row);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
Dimension d = super.getMinimumSize();
|
||||
if (row >= 0) {
|
||||
d.height = getRowHeight(row);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
private class HeaderCellRenderer implements TableCellRenderer {
|
||||
public Component getTableCellRendererComponent(JTable table, Object value,
|
||||
boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
|
||||
return new HeaderCell(value, row);
|
||||
}
|
||||
}
|
||||
|
||||
private class FormulaDisplayListener implements ListSelectionListener {
|
||||
private final JTextComponent formulaDisplay;
|
||||
|
||||
public FormulaDisplayListener(JTextComponent formulaDisplay) {
|
||||
this.formulaDisplay = formulaDisplay;
|
||||
}
|
||||
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
int row = getSelectedRow();
|
||||
int col = getSelectedColumn();
|
||||
if (row < 0 || col < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.getValueIsAdjusting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
HSSFCell cell = (HSSFCell) getValueAt(row, col);
|
||||
String formula = "";
|
||||
if (cell != null) {
|
||||
if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
|
||||
formula = cell.getCellFormula();
|
||||
} else {
|
||||
formula = cell.toString();
|
||||
}
|
||||
if (formula == null)
|
||||
formula = "";
|
||||
}
|
||||
formulaDisplay.setText(formula);
|
||||
}
|
||||
}
|
||||
|
||||
public SVSheetTable(HSSFSheet sheet) {
|
||||
super(new SVTableModel(sheet));
|
||||
this.sheet = sheet;
|
||||
|
||||
setIntercellSpacing(new Dimension(0, 0));
|
||||
setAutoResizeMode(AUTO_RESIZE_OFF);
|
||||
JTableHeader header = getTableHeader();
|
||||
header.setDefaultRenderer(new HeaderCellRenderer());
|
||||
pendingPaintings = new PendingPaintings(this);
|
||||
|
||||
//Set the columns the correct size
|
||||
TableColumnModel columns = getColumnModel();
|
||||
for (int i = 0; i < columns.getColumnCount(); i++) {
|
||||
TableColumn column = columns.getColumn(i);
|
||||
int width = sheet.getColumnWidth(i);
|
||||
//256 is because the width is in 256ths of a character
|
||||
column.setPreferredWidth(width / 256 * magicCharFactor);
|
||||
}
|
||||
|
||||
Toolkit t = getToolkit();
|
||||
int res = t.getScreenResolution();
|
||||
TableModel model = getModel();
|
||||
for (int i = 0; i < model.getRowCount(); i++) {
|
||||
Row row = sheet.getRow(i - sheet.getFirstRowNum());
|
||||
if (row != null) {
|
||||
short h = row.getHeight();
|
||||
int height = Math.round(Math.max(1, h / (res / 70 * 20) + 3));
|
||||
System.out.printf("%d: %d (%d @ %d)%n", i, height, h, res);
|
||||
setRowHeight(i, height);
|
||||
}
|
||||
}
|
||||
|
||||
addHierarchyListener(new HierarchyListener() {
|
||||
public void hierarchyChanged(HierarchyEvent e) {
|
||||
if ((e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) != 0) {
|
||||
Container changedParent = e.getChangedParent();
|
||||
if (changedParent instanceof JViewport) {
|
||||
Container grandparent = changedParent.getParent();
|
||||
if (grandparent instanceof JScrollPane) {
|
||||
JScrollPane jScrollPane = (JScrollPane) grandparent;
|
||||
setupScroll(jScrollPane);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setupScroll(JScrollPane scroll) {
|
||||
if (scroll == this.scroll)
|
||||
return;
|
||||
|
||||
this.scroll = scroll;
|
||||
if (scroll == null)
|
||||
return;
|
||||
|
||||
SVRowHeader rowHeader = new SVRowHeader(sheet, this, 0);
|
||||
scroll.setRowHeaderView(rowHeader);
|
||||
scroll.setCorner(JScrollPane.UPPER_LEADING_CORNER, headerCell("?"));
|
||||
}
|
||||
|
||||
public void setFormulaDisplay(JTextComponent formulaDisplay) {
|
||||
ListSelectionModel rowSelMod = getSelectionModel();
|
||||
ListSelectionModel colSelMod = getColumnModel().getSelectionModel();
|
||||
|
||||
if (formulaDisplay == null) {
|
||||
rowSelMod.removeListSelectionListener(formulaListener);
|
||||
colSelMod.removeListSelectionListener(formulaListener);
|
||||
formulaListener = null;
|
||||
}
|
||||
|
||||
if (formulaDisplay != null) {
|
||||
formulaListener = new FormulaDisplayListener(formulaDisplay);
|
||||
rowSelMod.addListSelectionListener(formulaListener);
|
||||
colSelMod.addListSelectionListener(formulaListener);
|
||||
}
|
||||
}
|
||||
|
||||
public JTextComponent getFormulaDisplay() {
|
||||
if (formulaListener == null)
|
||||
return null;
|
||||
else
|
||||
return formulaListener.formulaDisplay;
|
||||
}
|
||||
|
||||
public Component headerCell(String text) {
|
||||
return new HeaderCell(text, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g1) {
|
||||
Graphics2D g = (Graphics2D) g1;
|
||||
|
||||
pendingPaintings.clear();
|
||||
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
super.paintComponent(g);
|
||||
|
||||
pendingPaintings.paint(g);
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.contrib.view;
|
||||
package org.apache.poi.hssf.view;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
@ -17,7 +17,7 @@
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.contrib.view;
|
||||
package org.apache.poi.hssf.view;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.TableCellRenderer;
|
@ -18,7 +18,7 @@
|
||||
|
||||
|
||||
|
||||
package org.apache.poi.hssf.contrib.view;
|
||||
package org.apache.poi.hssf.view;
|
||||
|
||||
import java.util.Iterator;
|
||||
import javax.swing.table.*;
|
@ -16,7 +16,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.contrib.view;
|
||||
package org.apache.poi.hssf.view;
|
||||
|
||||
import java.util.*;
|
||||
import java.awt.*;
|
@ -18,7 +18,7 @@
|
||||
|
||||
|
||||
|
||||
package org.apache.poi.hssf.contrib.view;
|
||||
package org.apache.poi.hssf.view;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
@ -15,7 +15,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.contrib.view;
|
||||
package org.apache.poi.hssf.view;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
@ -0,0 +1,72 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.hssf.view.brush;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* This is a basic brush that just draws the line with the given parameters.
|
||||
* This is a {@link BasicStroke} object that can be used as a {@link Brush}.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
* @see BasicStroke
|
||||
*/
|
||||
public class BasicBrush extends BasicStroke implements Brush {
|
||||
/**
|
||||
* Creates a new basic brush with the given width. Invokes {@link
|
||||
* BasicStroke#BasicStroke(float)}
|
||||
*
|
||||
* @param width The brush width.
|
||||
*
|
||||
* @see BasicStroke#BasicStroke(float)
|
||||
*/
|
||||
public BasicBrush(float width) {
|
||||
super(width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new basic brush with the given width, cap, and join. Invokes
|
||||
* {@link BasicStroke#BasicStroke(float,int,int)}
|
||||
*
|
||||
* @param width The brush width.
|
||||
* @param cap The capping style.
|
||||
* @param join The join style.
|
||||
*
|
||||
* @see BasicStroke#BasicStroke(float, int, int)
|
||||
*/
|
||||
public BasicBrush(float width, int cap, int join) {
|
||||
super(width, cap, join);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new basic brush with the given parameters. Invokes {@link
|
||||
* BasicStroke#BasicStroke(float,int,int,float,float[],float)} with a miter
|
||||
* limit of 11 (the normal default value).
|
||||
*
|
||||
* @param width The brush width.
|
||||
* @param cap The capping style.
|
||||
* @param join The join style.
|
||||
* @param dashes The dash intervals.
|
||||
* @param dashPos The intial dash position in the dash intervals.
|
||||
*
|
||||
* @see BasicStroke#BasicStroke(float, int, int, float, float[], float)
|
||||
*/
|
||||
public BasicBrush(float width, int cap, int join, float[] dashes,
|
||||
int dashPos) {
|
||||
super(width, cap, join, 11.0f, dashes, dashPos);
|
||||
}
|
||||
}
|
14
src/examples/src/org/apache/poi/hssf/view/brush/Brush.java
Normal file
14
src/examples/src/org/apache/poi/hssf/view/brush/Brush.java
Normal file
@ -0,0 +1,14 @@
|
||||
package org.apache.poi.hssf.view.brush;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* This is the type you must implement to create a brush that will be used for a
|
||||
* spreadsheet border.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public interface Brush extends Stroke {
|
||||
/** Returns the width of the brush. */
|
||||
float getLineWidth();
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.hssf.view.brush;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* This Stroke implementation applies a BasicStroke to a shape twice. If you
|
||||
* draw with this Stroke, then instead of outlining the shape, you're outlining
|
||||
* the outline of the shape.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class DoubleStroke implements Brush {
|
||||
BasicStroke stroke1, stroke2; // the two strokes to use
|
||||
|
||||
/**
|
||||
* Creates a new double-stroke brush. This surrounds a cell with a two
|
||||
* lines separated by white space between.
|
||||
*
|
||||
* @param width1 The width of the blank space in the middle
|
||||
* @param width2 The width of the each of the two drawn strokes.
|
||||
*/
|
||||
public DoubleStroke(float width1, float width2) {
|
||||
stroke1 = new BasicStroke(width1); // Constructor arguments specify
|
||||
stroke2 = new BasicStroke(width2); // the line widths for the strokes
|
||||
}
|
||||
|
||||
/**
|
||||
* Stroke the outline.
|
||||
*
|
||||
* @param s The shape in which to stroke.
|
||||
*
|
||||
* @return The created stroke as a new shape.
|
||||
*/
|
||||
public Shape createStrokedShape(Shape s) {
|
||||
// Use the first stroke to create an outline of the shape
|
||||
Shape outline = stroke1.createStrokedShape(s);
|
||||
// Use the second stroke to create an outline of that outline.
|
||||
// It is this outline of the outline that will be filled in
|
||||
return stroke2.createStrokedShape(outline);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public float getLineWidth() {
|
||||
return stroke1.getLineWidth() + 2 * stroke2.getLineWidth();
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.hssf.view.brush;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class is used to hold pending brush paintings. The model is that some
|
||||
* border drawing requires drawing strokes after all the cells have been
|
||||
* painted. The list of pending paintings can be put in this object during the
|
||||
* initial paint of the component, and then executed at the appropriate time,
|
||||
* such as at the end of the containing object's {@link
|
||||
* JComponent#paintChildren(Graphics)} method.
|
||||
* <p/>
|
||||
* It is up to the parent component to invoke the {@link #paint(Graphics2D)}
|
||||
* method of this objet at that appropriate time.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class PendingPaintings {
|
||||
/**
|
||||
* The name of the client property that holds this object in the parent
|
||||
* component.
|
||||
*/
|
||||
public static final String PENDING_PAINTINGS =
|
||||
PendingPaintings.class.getSimpleName();
|
||||
|
||||
private final List<Painting> paintings;
|
||||
|
||||
/** A single painting description. */
|
||||
public static class Painting {
|
||||
final Stroke stroke;
|
||||
final Color color;
|
||||
final Shape shape;
|
||||
final AffineTransform transform;
|
||||
|
||||
/**
|
||||
* Creates a new painting description.
|
||||
*
|
||||
* @param stroke The stroke to paint.
|
||||
* @param color The color of the stroke.
|
||||
* @param shape The shape of the stroke.
|
||||
* @param transform The transformation matrix to use.
|
||||
*/
|
||||
public Painting(Stroke stroke, Color color, Shape shape,
|
||||
AffineTransform transform) {
|
||||
|
||||
this.color = color;
|
||||
this.shape = shape;
|
||||
this.stroke = stroke;
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the painting.
|
||||
*
|
||||
* @param g The graphics object to use to draw with.
|
||||
*/
|
||||
public void draw(Graphics2D g) {
|
||||
g.setTransform(transform);
|
||||
g.setStroke(stroke);
|
||||
g.setColor(color);
|
||||
g.draw(shape);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new object on the given parent. The created object will be
|
||||
* stored as a client property.
|
||||
*
|
||||
* @param parent
|
||||
*/
|
||||
public PendingPaintings(JComponent parent) {
|
||||
paintings = new ArrayList<Painting>();
|
||||
parent.putClientProperty(PENDING_PAINTINGS, this);
|
||||
}
|
||||
|
||||
/** Drops all pending paintings. */
|
||||
public void clear() {
|
||||
paintings.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints all pending paintings. Once they have been painted they are
|
||||
* removed from the list of pending paintings (they aren't pending anymore,
|
||||
* after all).
|
||||
*
|
||||
* @param g The graphics object to draw with.
|
||||
*/
|
||||
public void paint(Graphics2D g) {
|
||||
g.setBackground(Color.CYAN);
|
||||
AffineTransform origTransform = g.getTransform();
|
||||
for (Painting c : paintings) {
|
||||
c.draw(g);
|
||||
}
|
||||
g.setTransform(origTransform);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new pending painting to the list on the given component. This
|
||||
* will find the first ancestor that has a {@link PendingPaintings} client
|
||||
* property, starting with the component itself.
|
||||
*
|
||||
* @param c The component for which the painting is being added.
|
||||
* @param g The graphics object to draw with.
|
||||
* @param stroke The stroke to draw.
|
||||
* @param color The color to draw with.
|
||||
* @param shape The shape to stroke.
|
||||
*/
|
||||
public static void add(JComponent c, Graphics2D g, Stroke stroke,
|
||||
Color color, Shape shape) {
|
||||
|
||||
add(c, new Painting(stroke, color, shape, g.getTransform()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new pending painting to the list on the given component. This
|
||||
* will find the first ancestor that has a {@link PendingPaintings} client
|
||||
* property, starting with the component itself.
|
||||
*
|
||||
* @param c The component for which the painting is being added.
|
||||
* @param newPainting The new painting.
|
||||
*/
|
||||
public static void add(JComponent c, Painting newPainting) {
|
||||
PendingPaintings pending = pendingPaintingsFor(c);
|
||||
if (pending != null) {
|
||||
pending.paintings.add(newPainting);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pending painting object for the given component, if any. This
|
||||
* is retrieved from the first object found that has a {@link
|
||||
* #PENDING_PAINTINGS} client property, starting with this component and
|
||||
* looking up its ancestors (parent, parent's parent, etc.)
|
||||
* <p/>
|
||||
* This allows any descendant of a component that has a {@link
|
||||
* PendingPaintings} property to add its own pending paintings.
|
||||
*
|
||||
* @param c The component for which the painting is being added.
|
||||
*
|
||||
* @return The pending painting object for that component, or <tt>null</tt>
|
||||
* if there is none.
|
||||
*/
|
||||
public static PendingPaintings pendingPaintingsFor(JComponent c) {
|
||||
for (Component parent = c;
|
||||
parent != null;
|
||||
parent = parent.getParent()) {
|
||||
if (parent instanceof JComponent) {
|
||||
JComponent jc = (JComponent) parent;
|
||||
Object pd = jc.getClientProperty(PENDING_PAINTINGS);
|
||||
if (pd != null)
|
||||
return (PendingPaintings) pd;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
This package contains some brushes that are used when drawing borders for Excel
|
||||
cells.
|
||||
|
||||
@author Ken Arnold, Industrious Media LLC
|
@ -0,0 +1,66 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.examples.html;
|
||||
|
||||
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
|
||||
import org.apache.poi.hssf.usermodel.HSSFPalette;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.hssf.util.HSSFColor;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
|
||||
import java.util.Formatter;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HtmlHelper} for HSSF files.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class HSSFHtmlHelper implements HtmlHelper {
|
||||
private final HSSFWorkbook wb;
|
||||
private final HSSFPalette colors;
|
||||
|
||||
private static final HSSFColor HSSF_AUTO = new HSSFColor.AUTOMATIC();
|
||||
|
||||
public HSSFHtmlHelper(HSSFWorkbook wb) {
|
||||
this.wb = wb;
|
||||
// If there is no custom palette, then this creates a new one that is
|
||||
// a copy of the default
|
||||
colors = wb.getCustomPalette();
|
||||
}
|
||||
|
||||
public void colorStyles(CellStyle style, Formatter out) {
|
||||
HSSFCellStyle cs = (HSSFCellStyle) style;
|
||||
out.format(" /* fill pattern = %d */%n", cs.getFillPattern());
|
||||
styleColor(out, "background-color", cs.getFillForegroundColor());
|
||||
styleColor(out, "color", cs.getFont(wb).getColor());
|
||||
styleColor(out, "border-left-color", cs.getLeftBorderColor());
|
||||
styleColor(out, "border-right-color", cs.getRightBorderColor());
|
||||
styleColor(out, "border-top-color", cs.getTopBorderColor());
|
||||
styleColor(out, "border-bottom-color", cs.getBottomBorderColor());
|
||||
}
|
||||
|
||||
private void styleColor(Formatter out, String attr, short index) {
|
||||
HSSFColor color = colors.getColor(index);
|
||||
if (index == HSSF_AUTO.getIndex() || color == null) {
|
||||
out.format(" /* %s: index = %d */%n", attr, index);
|
||||
} else {
|
||||
short[] rgb = color.getTriplet();
|
||||
out.format(" %s: #%02x%02x%02x; /* index = %d */%n", attr, rgb[0],
|
||||
rgb[1], rgb[2], index);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.apache.poi.ss.examples.html;
|
||||
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
|
||||
import java.util.Formatter;
|
||||
|
||||
/**
|
||||
* This interface is used where code wants to be independent of the workbook
|
||||
* formats. If you are writing such code, you can add a method to this
|
||||
* interface, and then implement it for both HSSF and XSSF workbooks, letting
|
||||
* the driving code stay independent of format.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public interface HtmlHelper {
|
||||
/**
|
||||
* Outputs the appropriate CSS style for the given cell style.
|
||||
*
|
||||
* @param style The cell style.
|
||||
* @param out The place to write the output.
|
||||
*/
|
||||
void colorStyles(CellStyle style, Formatter out);
|
||||
}
|
443
src/examples/src/org/apache/poi/ss/examples/html/ToHtml.java
Normal file
443
src/examples/src/org/apache/poi/ss/examples/html/ToHtml.java
Normal file
@ -0,0 +1,443 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.examples.html;
|
||||
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFFont;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.format.CellFormat;
|
||||
import org.apache.poi.ss.format.CellFormatResult;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Formatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.poi.ss.usermodel.CellStyle.*;
|
||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||
|
||||
/**
|
||||
* This example shows how to display a spreadsheet in HTML using the classes for
|
||||
* spreadsheet display.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class ToHtml {
|
||||
private final Workbook wb;
|
||||
private final Appendable output;
|
||||
private boolean completeHTML;
|
||||
private Formatter out;
|
||||
private boolean gotBounds;
|
||||
private int firstColumn;
|
||||
private int endColumn;
|
||||
private HtmlHelper helper;
|
||||
|
||||
private static final String DEFAULTS_CLASS = "excelDefaults";
|
||||
private static final String COL_HEAD_CLASS = "colHeader";
|
||||
private static final String ROW_HEAD_CLASS = "rowHeader";
|
||||
|
||||
private static final Map<Short, String> ALIGN = mapFor(ALIGN_LEFT, "left",
|
||||
ALIGN_CENTER, "center", ALIGN_RIGHT, "right", ALIGN_FILL, "left",
|
||||
ALIGN_JUSTIFY, "left", ALIGN_CENTER_SELECTION, "center");
|
||||
|
||||
private static final Map<Short, String> VERTICAL_ALIGN = mapFor(
|
||||
VERTICAL_BOTTOM, "bottom", VERTICAL_CENTER, "middle", VERTICAL_TOP,
|
||||
"top");
|
||||
|
||||
private static final Map<Short, String> BORDER = mapFor(BORDER_DASH_DOT,
|
||||
"dashed 1pt", BORDER_DASH_DOT_DOT, "dashed 1pt", BORDER_DASHED,
|
||||
"dashed 1pt", BORDER_DOTTED, "dotted 1pt", BORDER_DOUBLE,
|
||||
"double 3pt", BORDER_HAIR, "solid 1px", BORDER_MEDIUM, "solid 2pt",
|
||||
BORDER_MEDIUM_DASH_DOT, "dashed 2pt", BORDER_MEDIUM_DASH_DOT_DOT,
|
||||
"dashed 2pt", BORDER_MEDIUM_DASHED, "dashed 2pt", BORDER_NONE,
|
||||
"none", BORDER_SLANTED_DASH_DOT, "dashed 2pt", BORDER_THICK,
|
||||
"solid 3pt", BORDER_THIN, "dashed 1pt");
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private static <K, V> Map<K, V> mapFor(Object... mapping) {
|
||||
Map<K, V> map = new HashMap<K, V>();
|
||||
for (int i = 0; i < mapping.length; i += 2) {
|
||||
map.put((K) mapping[i], (V) mapping[i + 1]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new converter to HTML for the given workbook.
|
||||
*
|
||||
* @param wb The workbook.
|
||||
* @param output Where the HTML output will be written.
|
||||
*
|
||||
* @return An object for converting the workbook to HTML.
|
||||
*/
|
||||
public static ToHtml create(Workbook wb, Appendable output) {
|
||||
return new ToHtml(wb, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new converter to HTML for the given workbook. If the path ends
|
||||
* with "<tt>.xlsx</tt>" an {@link XSSFWorkbook} will be used; otherwise
|
||||
* this will use an {@link HSSFWorkbook}.
|
||||
*
|
||||
* @param path The file that has the workbook.
|
||||
* @param output Where the HTML output will be written.
|
||||
*
|
||||
* @return An object for converting the workbook to HTML.
|
||||
*/
|
||||
public static ToHtml create(String path, Appendable output)
|
||||
throws IOException {
|
||||
return create(new FileInputStream(path), output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new converter to HTML for the given workbook. This attempts to
|
||||
* detect whether the input is XML (so it should create an {@link
|
||||
* XSSFWorkbook} or not (so it should create an {@link HSSFWorkbook}).
|
||||
*
|
||||
* @param in The input stream that has the workbook.
|
||||
* @param output Where the HTML output will be written.
|
||||
*
|
||||
* @return An object for converting the workbook to HTML.
|
||||
*/
|
||||
public static ToHtml create(InputStream in, Appendable output)
|
||||
throws IOException {
|
||||
try {
|
||||
Workbook wb = WorkbookFactory.create(in);
|
||||
return create(wb, output);
|
||||
} catch (InvalidFormatException e){
|
||||
throw new IllegalArgumentException("Cannot create workbook from stream", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ToHtml(Workbook wb, Appendable output) {
|
||||
if (wb == null)
|
||||
throw new NullPointerException("wb");
|
||||
if (output == null)
|
||||
throw new NullPointerException("output");
|
||||
this.wb = wb;
|
||||
this.output = output;
|
||||
setupColorMap();
|
||||
}
|
||||
|
||||
private void setupColorMap() {
|
||||
if (wb instanceof HSSFWorkbook)
|
||||
helper = new HSSFHtmlHelper((HSSFWorkbook) wb);
|
||||
else if (wb instanceof XSSFWorkbook)
|
||||
helper = new XSSFHtmlHelper((XSSFWorkbook) wb);
|
||||
else
|
||||
throw new IllegalArgumentException(
|
||||
"unknown workbook type: " + wb.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Run this class as a program
|
||||
*
|
||||
* @param args The command line arguments.
|
||||
*
|
||||
* @throws Exception Exception we don't recover from.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
if(args.length < 2){
|
||||
System.err.println("usage: ToHtml inputWorkbook outputHtmlFile");
|
||||
return;
|
||||
}
|
||||
|
||||
ToHtml toHtml = create(args[0], new PrintWriter(new FileWriter(args[1])));
|
||||
toHtml.setCompleteHTML(true);
|
||||
toHtml.printPage();
|
||||
}
|
||||
|
||||
public void setCompleteHTML(boolean completeHTML) {
|
||||
this.completeHTML = completeHTML;
|
||||
}
|
||||
|
||||
public void printPage() throws IOException {
|
||||
try {
|
||||
ensureOut();
|
||||
if (completeHTML) {
|
||||
out.format(
|
||||
"<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?>%n");
|
||||
out.format("<html>%n");
|
||||
out.format("<head>%n");
|
||||
out.format("</head>%n");
|
||||
out.format("<body>%n");
|
||||
}
|
||||
|
||||
print();
|
||||
|
||||
if (completeHTML) {
|
||||
out.format("</body>%n");
|
||||
out.format("</html>%n");
|
||||
}
|
||||
} finally {
|
||||
if (out != null)
|
||||
out.close();
|
||||
if (output instanceof Closeable) {
|
||||
Closeable closeable = (Closeable) output;
|
||||
closeable.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void print() {
|
||||
printInlineStyle();
|
||||
printSheets();
|
||||
}
|
||||
|
||||
private void printInlineStyle() {
|
||||
//out.format("<link href=\"excelStyle.css\" rel=\"stylesheet\" type=\"text/css\">%n");
|
||||
out.format("<style type=\"text/css\">%n");
|
||||
printStyles();
|
||||
out.format("</style>%n");
|
||||
}
|
||||
|
||||
private void ensureOut() {
|
||||
if (out == null)
|
||||
out = new Formatter(output);
|
||||
}
|
||||
|
||||
public void printStyles() {
|
||||
ensureOut();
|
||||
|
||||
// First, copy the base css
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(
|
||||
getClass().getResourceAsStream("excelStyle.css")));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
out.format("%s%n", line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Reading standard css", e);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
//noinspection ThrowFromFinallyBlock
|
||||
throw new IllegalStateException("Reading standard css", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now add css for each used style
|
||||
Set<CellStyle> seen = new HashSet<CellStyle>();
|
||||
for (int i = 0; i < wb.getNumberOfSheets(); i++) {
|
||||
Sheet sheet = wb.getSheetAt(i);
|
||||
Iterator<Row> rows = sheet.rowIterator();
|
||||
while (rows.hasNext()) {
|
||||
Row row = rows.next();
|
||||
for (Cell cell : row) {
|
||||
CellStyle style = cell.getCellStyle();
|
||||
if (!seen.contains(style)) {
|
||||
printStyle(style);
|
||||
seen.add(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printStyle(CellStyle style) {
|
||||
out.format(".%s .%s {%n", DEFAULTS_CLASS, styleName(style));
|
||||
styleContents(style);
|
||||
out.format("}%n");
|
||||
}
|
||||
|
||||
private void styleContents(CellStyle style) {
|
||||
styleOut("text-align", style.getAlignment(), ALIGN);
|
||||
styleOut("vertical-align", style.getAlignment(), VERTICAL_ALIGN);
|
||||
fontStyle(style);
|
||||
borderStyles(style);
|
||||
helper.colorStyles(style, out);
|
||||
}
|
||||
|
||||
private void borderStyles(CellStyle style) {
|
||||
styleOut("border-left", style.getBorderLeft(), BORDER);
|
||||
styleOut("border-right", style.getBorderRight(), BORDER);
|
||||
styleOut("border-top", style.getBorderTop(), BORDER);
|
||||
styleOut("border-bottom", style.getBorderBottom(), BORDER);
|
||||
}
|
||||
|
||||
private void fontStyle(CellStyle style) {
|
||||
Font font = wb.getFontAt(style.getFontIndex());
|
||||
|
||||
if (font.getBoldweight() >= HSSFFont.BOLDWEIGHT_NORMAL)
|
||||
out.format(" font-weight: bold;%n");
|
||||
if (font.getItalic())
|
||||
out.format(" font-style: italic;%n");
|
||||
|
||||
int fontheight = font.getFontHeightInPoints();
|
||||
if (fontheight == 9) {
|
||||
//fix for stupid ol Windows
|
||||
fontheight = 10;
|
||||
}
|
||||
out.format(" font-size: %dpt;%n", fontheight);
|
||||
|
||||
// Font color is handled with the other colors
|
||||
}
|
||||
|
||||
private String styleName(CellStyle style) {
|
||||
if (style == null)
|
||||
style = wb.getCellStyleAt((short) 0);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Formatter fmt = new Formatter(sb);
|
||||
fmt.format("style_%02x", style.getIndex());
|
||||
return fmt.toString();
|
||||
}
|
||||
|
||||
private <K> void styleOut(String attr, K key, Map<K, String> mapping) {
|
||||
String value = mapping.get(key);
|
||||
if (value != null) {
|
||||
out.format(" %s: %s;%n", attr, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static int ultimateCellType(Cell c) {
|
||||
int type = c.getCellType();
|
||||
if (type == Cell.CELL_TYPE_FORMULA)
|
||||
type = c.getCachedFormulaResultType();
|
||||
return type;
|
||||
}
|
||||
|
||||
private void printSheets() {
|
||||
ensureOut();
|
||||
Sheet sheet = wb.getSheetAt(0);
|
||||
printSheet(sheet);
|
||||
}
|
||||
|
||||
public void printSheet(Sheet sheet) {
|
||||
ensureOut();
|
||||
out.format("<table class=%s>%n", DEFAULTS_CLASS);
|
||||
printCols(sheet);
|
||||
printSheetContent(sheet);
|
||||
out.format("</table>%n");
|
||||
}
|
||||
|
||||
private void printCols(Sheet sheet) {
|
||||
out.format("<col/>%n");
|
||||
ensureColumnBounds(sheet);
|
||||
for (int i = firstColumn; i < endColumn; i++) {
|
||||
out.format("<col/>%n");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureColumnBounds(Sheet sheet) {
|
||||
if (gotBounds)
|
||||
return;
|
||||
|
||||
Iterator<Row> iter = sheet.rowIterator();
|
||||
firstColumn = (iter.hasNext() ? Integer.MAX_VALUE : 0);
|
||||
endColumn = 0;
|
||||
while (iter.hasNext()) {
|
||||
Row row = iter.next();
|
||||
short firstCell = row.getFirstCellNum();
|
||||
if (firstCell >= 0) {
|
||||
firstColumn = Math.min(firstColumn, firstCell);
|
||||
endColumn = Math.max(endColumn, row.getLastCellNum());
|
||||
}
|
||||
}
|
||||
gotBounds = true;
|
||||
}
|
||||
|
||||
private void printColumnHeads() {
|
||||
out.format("<thead>%n");
|
||||
out.format(" <tr class=%s>%n", COL_HEAD_CLASS);
|
||||
out.format(" <th class=%s>◊</th>%n", COL_HEAD_CLASS);
|
||||
//noinspection UnusedDeclaration
|
||||
StringBuilder colName = new StringBuilder();
|
||||
for (int i = firstColumn; i < endColumn; i++) {
|
||||
colName.setLength(0);
|
||||
int cnum = i;
|
||||
do {
|
||||
colName.insert(0, (char) ('A' + cnum % 26));
|
||||
cnum /= 26;
|
||||
} while (cnum > 0);
|
||||
out.format(" <th class=%s>%s</th>%n", COL_HEAD_CLASS, colName);
|
||||
}
|
||||
out.format(" </tr>%n");
|
||||
out.format("</thead>%n");
|
||||
}
|
||||
|
||||
private void printSheetContent(Sheet sheet) {
|
||||
printColumnHeads();
|
||||
|
||||
out.format("<tbody>%n");
|
||||
Iterator<Row> rows = sheet.rowIterator();
|
||||
while (rows.hasNext()) {
|
||||
Row row = rows.next();
|
||||
|
||||
out.format(" <tr>%n");
|
||||
out.format(" <td class=%s>%d</td>%n", ROW_HEAD_CLASS,
|
||||
row.getRowNum() + 1);
|
||||
for (int i = firstColumn; i < endColumn; i++) {
|
||||
String content = " ";
|
||||
String attrs = "";
|
||||
CellStyle style = null;
|
||||
if (i >= row.getFirstCellNum() && i < row.getLastCellNum()) {
|
||||
Cell cell = row.getCell(i);
|
||||
if (cell != null) {
|
||||
style = cell.getCellStyle();
|
||||
attrs = tagStyle(cell, style);
|
||||
//Set the value that is rendered for the cell
|
||||
//also applies the format
|
||||
CellFormat cf = CellFormat.getInstance(
|
||||
style.getDataFormatString());
|
||||
CellFormatResult result = cf.apply(cell);
|
||||
content = result.text;
|
||||
if (content.equals(""))
|
||||
content = " ";
|
||||
}
|
||||
}
|
||||
out.format(" <td class=%s %s>%s</td>%n", styleName(style),
|
||||
attrs, content);
|
||||
}
|
||||
out.format(" </tr>%n");
|
||||
}
|
||||
out.format("</tbody>%n");
|
||||
}
|
||||
|
||||
private String tagStyle(Cell cell, CellStyle style) {
|
||||
if (style.getAlignment() == ALIGN_GENERAL) {
|
||||
switch (ultimateCellType(cell)) {
|
||||
case HSSFCell.CELL_TYPE_STRING:
|
||||
return "style=\"text-align: left;\"";
|
||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
||||
case HSSFCell.CELL_TYPE_ERROR:
|
||||
return "style=\"text-align: center;\"";
|
||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
||||
default:
|
||||
// "right" is the default
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.examples.html;
|
||||
|
||||
import org.apache.poi.hssf.util.HSSFColor;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
|
||||
import org.apache.poi.xssf.usermodel.XSSFColor;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
import java.util.Formatter;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HtmlHelper} for XSSF files.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class XSSFHtmlHelper implements HtmlHelper {
|
||||
private final XSSFWorkbook wb;
|
||||
|
||||
private static final Hashtable colors = HSSFColor.getIndexHash();
|
||||
|
||||
public XSSFHtmlHelper(XSSFWorkbook wb) {
|
||||
this.wb = wb;
|
||||
}
|
||||
|
||||
public void colorStyles(CellStyle style, Formatter out) {
|
||||
XSSFCellStyle cs = (XSSFCellStyle) style;
|
||||
styleColor(out, "background-color", cs.getFillForegroundXSSFColor());
|
||||
styleColor(out, "text-color", cs.getFont().getXSSFColor());
|
||||
}
|
||||
|
||||
private void styleColor(Formatter out, String attr, XSSFColor color) {
|
||||
if (color == null || color.isAuto())
|
||||
return;
|
||||
|
||||
byte[] rgb = color.getRgb();
|
||||
if (rgb == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is done twice -- rgba is new with CSS 3, and browser that don't
|
||||
// support it will ignore the rgba specification and stick with the
|
||||
// solid color, which is declared first
|
||||
out.format(" %s: #%02x%02x%02x;%n", attr, rgb[0], rgb[1], rgb[2]);
|
||||
out.format(" %s: rgba(0x%02x, 0x%02x, 0x%02x, 0x%02x);%n", attr,
|
||||
rgb[0], rgb[1], rgb[2], rgb[3]);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This is the default style sheet for html generated by ToHtml
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
.excelDefaults {
|
||||
background-color: white;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
direction: ltr;
|
||||
text-transform: none;
|
||||
text-indent: 0;
|
||||
letter-spacing: 0;
|
||||
word-spacing: 0;
|
||||
white-space: normal;
|
||||
unicode-bidi: normal;
|
||||
vertical-align: 0;
|
||||
background-image: none;
|
||||
text-shadow: none;
|
||||
list-style-image: none;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-collapse: collapse;
|
||||
white-space: pre;
|
||||
vertical-align: bottom;
|
||||
font-style: normal;
|
||||
font-family: sans-serif;
|
||||
font-variant: normal;
|
||||
font-weight: normal;
|
||||
font-size: 10pt;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.excelDefaults td {
|
||||
padding: 1px 5px;
|
||||
border: 1px solid silver;
|
||||
}
|
||||
|
||||
.excelDefaults .colHeader {
|
||||
background-color: silver;
|
||||
font-weight: bold;
|
||||
border: 1px solid black;
|
||||
text-align: center;
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
||||
.excelDefaults .rowHeader {
|
||||
background-color: silver;
|
||||
font-weight: bold;
|
||||
border: 1px solid black;
|
||||
text-align: right;
|
||||
padding: 1px 5px;
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
This package contains an example that uses POI to convert a workbook into
|
||||
an HTML representation of the data. It can use both XSSF and HSSF workbooks.
|
213
src/java/org/apache/poi/ss/format/CellDateFormatter.java
Normal file
213
src/java/org/apache/poi/ss/format/CellDateFormatter.java
Normal file
@ -0,0 +1,213 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Formatter;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* Formats a date value.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class CellDateFormatter extends CellFormatter {
|
||||
private boolean amPmUpper;
|
||||
private boolean showM;
|
||||
private boolean showAmPm;
|
||||
private final DateFormat dateFmt;
|
||||
private String sFmt;
|
||||
|
||||
private static final long EXCEL_EPOCH_TIME;
|
||||
private static final Date EXCEL_EPOCH_DATE;
|
||||
|
||||
private static final CellFormatter SIMPLE_DATE = new CellDateFormatter(
|
||||
"mm/d/y");
|
||||
|
||||
static {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.set(1904, 0, 1, 0, 0, 0);
|
||||
EXCEL_EPOCH_DATE = c.getTime();
|
||||
EXCEL_EPOCH_TIME = c.getTimeInMillis();
|
||||
}
|
||||
|
||||
private class DatePartHandler implements CellFormatPart.PartHandler {
|
||||
private int mStart = -1;
|
||||
private int mLen;
|
||||
private int hStart = -1;
|
||||
private int hLen;
|
||||
|
||||
public String handlePart(Matcher m, String part, CellFormatType type,
|
||||
StringBuffer desc) {
|
||||
|
||||
int pos = desc.length();
|
||||
char firstCh = part.charAt(0);
|
||||
switch (firstCh) {
|
||||
case 's':
|
||||
case 'S':
|
||||
if (mStart >= 0) {
|
||||
for (int i = 0; i < mLen; i++)
|
||||
desc.setCharAt(mStart + i, 'm');
|
||||
mStart = -1;
|
||||
}
|
||||
return part.toLowerCase();
|
||||
|
||||
case 'h':
|
||||
case 'H':
|
||||
mStart = -1;
|
||||
hStart = pos;
|
||||
hLen = part.length();
|
||||
return part.toLowerCase();
|
||||
|
||||
case 'd':
|
||||
case 'D':
|
||||
mStart = -1;
|
||||
if (part.length() <= 2)
|
||||
return part.toLowerCase();
|
||||
else
|
||||
return part.toLowerCase().replace('d', 'E');
|
||||
|
||||
case 'm':
|
||||
case 'M':
|
||||
mStart = pos;
|
||||
mLen = part.length();
|
||||
return part.toUpperCase();
|
||||
|
||||
case 'y':
|
||||
case 'Y':
|
||||
mStart = -1;
|
||||
if (part.length() == 3)
|
||||
part = "yyyy";
|
||||
return part.toLowerCase();
|
||||
|
||||
case '0':
|
||||
mStart = -1;
|
||||
int sLen = part.length();
|
||||
sFmt = "%0" + (sLen + 2) + "." + sLen + "f";
|
||||
return part.replace('0', 'S');
|
||||
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'p':
|
||||
case 'P':
|
||||
if (part.length() > 1) {
|
||||
// am/pm marker
|
||||
mStart = -1;
|
||||
showAmPm = true;
|
||||
showM = Character.toLowerCase(part.charAt(1)) == 'm';
|
||||
// For some reason "am/pm" becomes AM or PM, but "a/p" becomes a or p
|
||||
amPmUpper = showM || Character.isUpperCase(part.charAt(0));
|
||||
|
||||
return "a";
|
||||
}
|
||||
//noinspection fallthrough
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void finish(StringBuffer toAppendTo) {
|
||||
if (hStart >= 0 && !showAmPm) {
|
||||
for (int i = 0; i < hLen; i++) {
|
||||
toAppendTo.setCharAt(hStart + i, 'H');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new date formatter with the given specification.
|
||||
*
|
||||
* @param format The format.
|
||||
*/
|
||||
public CellDateFormatter(String format) {
|
||||
super(format);
|
||||
DatePartHandler partHandler = new DatePartHandler();
|
||||
StringBuffer descBuf = CellFormatPart.parseFormat(format,
|
||||
CellFormatType.DATE, partHandler);
|
||||
partHandler.finish(descBuf);
|
||||
dateFmt = new SimpleDateFormat(descBuf.toString());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void formatValue(StringBuffer toAppendTo, Object value) {
|
||||
if (value == null)
|
||||
value = 0.0;
|
||||
if (value instanceof Number) {
|
||||
Number num = (Number) value;
|
||||
double v = num.doubleValue();
|
||||
if (v == 0.0)
|
||||
value = EXCEL_EPOCH_DATE;
|
||||
else
|
||||
value = new Date((long) (EXCEL_EPOCH_TIME + v));
|
||||
}
|
||||
|
||||
AttributedCharacterIterator it = dateFmt.formatToCharacterIterator(
|
||||
value);
|
||||
boolean doneAm = false;
|
||||
boolean doneMillis = false;
|
||||
|
||||
it.first();
|
||||
for (char ch = it.first();
|
||||
ch != CharacterIterator.DONE;
|
||||
ch = it.next()) {
|
||||
if (it.getAttribute(DateFormat.Field.MILLISECOND) != null) {
|
||||
if (!doneMillis) {
|
||||
Date dateObj = (Date) value;
|
||||
int pos = toAppendTo.length();
|
||||
Formatter formatter = new Formatter(toAppendTo);
|
||||
long msecs = dateObj.getTime() % 1000;
|
||||
formatter.format(LOCALE, sFmt, msecs / 1000.0);
|
||||
toAppendTo.delete(pos, pos + 2);
|
||||
doneMillis = true;
|
||||
}
|
||||
} else if (it.getAttribute(DateFormat.Field.AM_PM) != null) {
|
||||
if (!doneAm) {
|
||||
if (showAmPm) {
|
||||
if (amPmUpper) {
|
||||
toAppendTo.append(Character.toUpperCase(ch));
|
||||
if (showM)
|
||||
toAppendTo.append('M');
|
||||
} else {
|
||||
toAppendTo.append(Character.toLowerCase(ch));
|
||||
if (showM)
|
||||
toAppendTo.append('m');
|
||||
}
|
||||
}
|
||||
doneAm = true;
|
||||
}
|
||||
} else {
|
||||
toAppendTo.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p/>
|
||||
* For a date, this is <tt>"mm/d/y"</tt>.
|
||||
*/
|
||||
public void simpleValue(StringBuffer toAppendTo, Object value) {
|
||||
SIMPLE_DATE.formatValue(toAppendTo, value);
|
||||
}
|
||||
}
|
215
src/java/org/apache/poi/ss/format/CellElapsedFormatter.java
Normal file
215
src/java/org/apache/poi/ss/format/CellElapsedFormatter.java
Normal file
@ -0,0 +1,215 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This class implements printing out an elapsed time format.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class CellElapsedFormatter extends CellFormatter {
|
||||
private final List<TimeSpec> specs;
|
||||
private TimeSpec topmost;
|
||||
private final String printfFmt;
|
||||
|
||||
private static final Pattern PERCENTS = Pattern.compile("%");
|
||||
|
||||
private static final double HOUR__FACTOR = 1.0 / 24.0;
|
||||
private static final double MIN__FACTOR = HOUR__FACTOR / 60.0;
|
||||
private static final double SEC__FACTOR = MIN__FACTOR / 60.0;
|
||||
|
||||
private static class TimeSpec {
|
||||
final char type;
|
||||
final int pos;
|
||||
final int len;
|
||||
final double factor;
|
||||
double modBy;
|
||||
|
||||
public TimeSpec(char type, int pos, int len, double factor) {
|
||||
this.type = type;
|
||||
this.pos = pos;
|
||||
this.len = len;
|
||||
this.factor = factor;
|
||||
modBy = 0;
|
||||
}
|
||||
|
||||
public long valueFor(double elapsed) {
|
||||
double val;
|
||||
if (modBy == 0)
|
||||
val = elapsed / factor;
|
||||
else
|
||||
val = elapsed / factor % modBy;
|
||||
if (type == '0')
|
||||
return Math.round(val);
|
||||
else
|
||||
return (long) val;
|
||||
}
|
||||
}
|
||||
|
||||
private class ElapsedPartHandler implements CellFormatPart.PartHandler {
|
||||
// This is the one class that's directly using printf, so it can't use
|
||||
// the default handling for quoted strings and special characters. The
|
||||
// only special character for this is '%', so we have to handle all the
|
||||
// quoting in this method ourselves.
|
||||
|
||||
public String handlePart(Matcher m, String part, CellFormatType type,
|
||||
StringBuffer desc) {
|
||||
|
||||
int pos = desc.length();
|
||||
char firstCh = part.charAt(0);
|
||||
switch (firstCh) {
|
||||
case '[':
|
||||
if (part.length() < 3)
|
||||
break;
|
||||
if (topmost != null)
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate '[' times in format");
|
||||
part = part.toLowerCase();
|
||||
int specLen = part.length() - 2;
|
||||
topmost = assignSpec(part.charAt(1), pos, specLen);
|
||||
return part.substring(1, 1 + specLen);
|
||||
|
||||
case 'h':
|
||||
case 'm':
|
||||
case 's':
|
||||
case '0':
|
||||
part = part.toLowerCase();
|
||||
assignSpec(part.charAt(0), pos, part.length());
|
||||
return part;
|
||||
|
||||
case '\n':
|
||||
return "%n";
|
||||
|
||||
case '\"':
|
||||
part = part.substring(1, part.length() - 1);
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
part = part.substring(1);
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if (part.length() > 1)
|
||||
part = CellFormatPart.expandChar(part);
|
||||
break;
|
||||
|
||||
// An escape we can let it handle because it can't have a '%'
|
||||
case '_':
|
||||
return null;
|
||||
}
|
||||
// Replace ever "%" with a "%%" so we can use printf
|
||||
return PERCENTS.matcher(part).replaceAll("%%");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a elapsed time formatter.
|
||||
*
|
||||
* @param pattern The pattern to parse.
|
||||
*/
|
||||
public CellElapsedFormatter(String pattern) {
|
||||
super(pattern);
|
||||
|
||||
specs = new ArrayList<TimeSpec>();
|
||||
|
||||
StringBuffer desc = CellFormatPart.parseFormat(pattern,
|
||||
CellFormatType.ELAPSED, new ElapsedPartHandler());
|
||||
|
||||
ListIterator<TimeSpec> it = specs.listIterator(specs.size());
|
||||
while (it.hasPrevious()) {
|
||||
TimeSpec spec = it.previous();
|
||||
desc.replace(spec.pos, spec.pos + spec.len, "%0" + spec.len + "d");
|
||||
if (spec.type != topmost.type) {
|
||||
spec.modBy = modFor(spec.type, spec.len);
|
||||
}
|
||||
}
|
||||
|
||||
printfFmt = desc.toString();
|
||||
}
|
||||
|
||||
private TimeSpec assignSpec(char type, int pos, int len) {
|
||||
TimeSpec spec = new TimeSpec(type, pos, len, factorFor(type, len));
|
||||
specs.add(spec);
|
||||
return spec;
|
||||
}
|
||||
|
||||
private static double factorFor(char type, int len) {
|
||||
switch (type) {
|
||||
case 'h':
|
||||
return HOUR__FACTOR;
|
||||
case 'm':
|
||||
return MIN__FACTOR;
|
||||
case 's':
|
||||
return SEC__FACTOR;
|
||||
case '0':
|
||||
return SEC__FACTOR / Math.pow(10, len);
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Uknown elapsed time spec: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private static double modFor(char type, int len) {
|
||||
switch (type) {
|
||||
case 'h':
|
||||
return 24;
|
||||
case 'm':
|
||||
return 60;
|
||||
case 's':
|
||||
return 60;
|
||||
case '0':
|
||||
return Math.pow(10, len);
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Uknown elapsed time spec: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void formatValue(StringBuffer toAppendTo, Object value) {
|
||||
double elapsed = ((Number) value).doubleValue();
|
||||
|
||||
if (elapsed < 0) {
|
||||
toAppendTo.append('-');
|
||||
elapsed = -elapsed;
|
||||
}
|
||||
|
||||
Object[] parts = new Long[specs.size()];
|
||||
for (int i = 0; i < specs.size(); i++) {
|
||||
parts[i] = specs.get(i).valueFor(elapsed);
|
||||
}
|
||||
|
||||
Formatter formatter = new Formatter(toAppendTo);
|
||||
formatter.format(printfFmt, parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p/>
|
||||
* For a date, this is <tt>"mm/d/y"</tt>.
|
||||
*/
|
||||
public void simpleValue(StringBuffer toAppendTo, Object value) {
|
||||
formatValue(toAppendTo, value);
|
||||
}
|
||||
}
|
313
src/java/org/apache/poi/ss/format/CellFormat.java
Normal file
313
src/java/org/apache/poi/ss/format/CellFormat.java
Normal file
@ -0,0 +1,313 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Format a value according to the standard Excel behavior. This "standard" is
|
||||
* not explicitly documented by Microsoft, so the behavior is determined by
|
||||
* experimentation; see the tests.
|
||||
* <p/>
|
||||
* An Excel format has up to four parts, separated by semicolons. Each part
|
||||
* specifies what to do with particular kinds of values, depending on the number
|
||||
* of parts given: <dl> <dt>One part (example: <tt>[Green]#.##</tt>) <dd>If the
|
||||
* value is a number, display according to this one part (example: green text,
|
||||
* with up to two decimal points). If the value is text, display it as is.
|
||||
* <dt>Two parts (example: <tt>[Green]#.##;[Red]#.##</tt>) <dd>If the value is a
|
||||
* positive number or zero, display according to the first part (example: green
|
||||
* text, with up to two decimal points); if it is a negative number, display
|
||||
* according to the second part (example: red text, with up to two decimal
|
||||
* points). If the value is text, display it as is. <dt>Three parts (example:
|
||||
* <tt>[Green]#.##;[Black]#.##;[Red]#.##</tt>) <dd>If the value is a positive
|
||||
* number, display according to the first part (example: green text, with up to
|
||||
* two decimal points); if it is zero, display according to the second part
|
||||
* (example: black text, with up to two decimal points); if it is a negative
|
||||
* number, display according to the third part (example: red text, with up to
|
||||
* two decimal points). If the value is text, display it as is. <dt>Four parts
|
||||
* (example: <tt>[Green]#.##;[Black]#.##;[Red]#.##;[@]</tt>) <dd>If the value is
|
||||
* a positive number, display according to the first part (example: green text,
|
||||
* with up to two decimal points); if it is zero, display according to the
|
||||
* second part (example: black text, with up to two decimal points); if it is a
|
||||
* negative number, display according to the third part (example: red text, with
|
||||
* up to two decimal points). If the value is text, display according to the
|
||||
* fourth part (example: text in the cell's usual color, with the text value
|
||||
* surround by brackets). </dl>
|
||||
* <p/>
|
||||
* In addition to these, there is a general format that is used when no format
|
||||
* is specified. This formatting is presented by the {@link #GENERAL_FORMAT}
|
||||
* object.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
@SuppressWarnings({"Singleton"})
|
||||
public class CellFormat {
|
||||
private final String format;
|
||||
private final CellFormatPart posNumFmt;
|
||||
private final CellFormatPart zeroNumFmt;
|
||||
private final CellFormatPart negNumFmt;
|
||||
private final CellFormatPart textFmt;
|
||||
|
||||
private static final Pattern ONE_PART = Pattern.compile(
|
||||
CellFormatPart.FORMAT_PAT.pattern() + "(;|$)",
|
||||
Pattern.COMMENTS | Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static final CellFormatPart DEFAULT_TEXT_FORMAT =
|
||||
new CellFormatPart("@");
|
||||
|
||||
/**
|
||||
* Format a value as it would be were no format specified. This is also
|
||||
* used when the format specified is <tt>General</tt>.
|
||||
*/
|
||||
public static final CellFormat GENERAL_FORMAT = new CellFormat("General") {
|
||||
@Override
|
||||
public CellFormatResult apply(Object value) {
|
||||
String text;
|
||||
if (value == null) {
|
||||
text = "";
|
||||
} else if (value instanceof Number) {
|
||||
text = CellNumberFormatter.SIMPLE_NUMBER.format(value);
|
||||
} else {
|
||||
text = value.toString();
|
||||
}
|
||||
return new CellFormatResult(true, text, null);
|
||||
}
|
||||
};
|
||||
|
||||
/** Maps a format string to its parsed version for efficiencies sake. */
|
||||
private static final Map<String, CellFormat> formatCache =
|
||||
new WeakHashMap<String, CellFormat>();
|
||||
|
||||
/**
|
||||
* Returns a {@link CellFormat} that applies the given format. Two calls
|
||||
* with the same format may or may not return the same object.
|
||||
*
|
||||
* @param format The format.
|
||||
*
|
||||
* @return A {@link CellFormat} that applies the given format.
|
||||
*/
|
||||
public static CellFormat getInstance(String format) {
|
||||
CellFormat fmt = formatCache.get(format);
|
||||
if (fmt == null) {
|
||||
if (format.equals("General"))
|
||||
fmt = GENERAL_FORMAT;
|
||||
else
|
||||
fmt = new CellFormat(format);
|
||||
formatCache.put(format, fmt);
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new object.
|
||||
*
|
||||
* @param format The format.
|
||||
*/
|
||||
private CellFormat(String format) {
|
||||
this.format = format;
|
||||
Matcher m = ONE_PART.matcher(format);
|
||||
List<CellFormatPart> parts = new ArrayList<CellFormatPart>();
|
||||
|
||||
while (m.find()) {
|
||||
try {
|
||||
String valueDesc = m.group();
|
||||
|
||||
// Strip out the semicolon if it's there
|
||||
if (valueDesc.endsWith(";"))
|
||||
valueDesc = valueDesc.substring(0, valueDesc.length() - 1);
|
||||
|
||||
parts.add(new CellFormatPart(valueDesc));
|
||||
} catch (RuntimeException e) {
|
||||
CellFormatter.logger.log(Level.WARNING,
|
||||
"Invalid format: " + CellFormatter.quote(m.group()), e);
|
||||
parts.add(null);
|
||||
}
|
||||
}
|
||||
|
||||
switch (parts.size()) {
|
||||
case 1:
|
||||
posNumFmt = zeroNumFmt = negNumFmt = parts.get(0);
|
||||
textFmt = DEFAULT_TEXT_FORMAT;
|
||||
break;
|
||||
case 2:
|
||||
posNumFmt = zeroNumFmt = parts.get(0);
|
||||
negNumFmt = parts.get(1);
|
||||
textFmt = DEFAULT_TEXT_FORMAT;
|
||||
break;
|
||||
case 3:
|
||||
posNumFmt = parts.get(0);
|
||||
zeroNumFmt = parts.get(1);
|
||||
negNumFmt = parts.get(2);
|
||||
textFmt = DEFAULT_TEXT_FORMAT;
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
posNumFmt = parts.get(0);
|
||||
zeroNumFmt = parts.get(1);
|
||||
negNumFmt = parts.get(2);
|
||||
textFmt = parts.get(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of applying the format to the given value. If the
|
||||
* value is a number (a type of {@link Number} object), the correct number
|
||||
* format type is chosen; otherwise it is considered a text object.
|
||||
*
|
||||
* @param value The value
|
||||
*
|
||||
* @return The result, in a {@link CellFormatResult}.
|
||||
*/
|
||||
public CellFormatResult apply(Object value) {
|
||||
if (value instanceof Number) {
|
||||
Number num = (Number) value;
|
||||
double val = num.doubleValue();
|
||||
if (val > 0)
|
||||
return posNumFmt.apply(value);
|
||||
else if (val < 0)
|
||||
return negNumFmt.apply(-val);
|
||||
else
|
||||
return zeroNumFmt.apply(value);
|
||||
} else {
|
||||
return textFmt.apply(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the appropriate value from the cell, and returns the result of
|
||||
* applying it to the appropriate format. For formula cells, the computed
|
||||
* value is what is used.
|
||||
*
|
||||
* @param c The cell.
|
||||
*
|
||||
* @return The result, in a {@link CellFormatResult}.
|
||||
*/
|
||||
public CellFormatResult apply(Cell c) {
|
||||
switch (ultimateType(c)) {
|
||||
case Cell.CELL_TYPE_BLANK:
|
||||
return apply("");
|
||||
case Cell.CELL_TYPE_BOOLEAN:
|
||||
return apply(c.getStringCellValue());
|
||||
case Cell.CELL_TYPE_NUMERIC:
|
||||
return apply(c.getNumericCellValue());
|
||||
case Cell.CELL_TYPE_STRING:
|
||||
return apply(c.getStringCellValue());
|
||||
default:
|
||||
return apply("?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the result of applying this format to the value, setting the text
|
||||
* and color of a label before returning the result.
|
||||
*
|
||||
* @param label The label to apply to.
|
||||
* @param value The value to process.
|
||||
*
|
||||
* @return The result, in a {@link CellFormatResult}.
|
||||
*/
|
||||
public CellFormatResult apply(JLabel label, Object value) {
|
||||
CellFormatResult result = apply(value);
|
||||
label.setText(result.text);
|
||||
if (result.textColor != null) {
|
||||
label.setForeground(result.textColor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the appropriate value from the cell, and uses the result, setting
|
||||
* the text and color of a label before returning the result.
|
||||
*
|
||||
* @param label The label to apply to.
|
||||
* @param c The cell.
|
||||
*
|
||||
* @return The result, in a {@link CellFormatResult}.
|
||||
*/
|
||||
public CellFormatResult apply(JLabel label, Cell c) {
|
||||
switch (ultimateType(c)) {
|
||||
case Cell.CELL_TYPE_BLANK:
|
||||
return apply(label, "");
|
||||
case Cell.CELL_TYPE_BOOLEAN:
|
||||
return apply(label, c.getStringCellValue());
|
||||
case Cell.CELL_TYPE_NUMERIC:
|
||||
return apply(label, c.getNumericCellValue());
|
||||
case Cell.CELL_TYPE_STRING:
|
||||
return apply(label, c.getStringCellValue());
|
||||
default:
|
||||
return apply(label, "?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ultimate cell type, following the results of formulas. If
|
||||
* the cell is a {@link Cell#CELL_TYPE_FORMULA}, this returns the result of
|
||||
* {@link Cell#getCachedFormulaResultType()}. Otherwise this returns the
|
||||
* result of {@link Cell#getCellType()}.
|
||||
*
|
||||
* @param cell The cell.
|
||||
*
|
||||
* @return The ultimate type of this cell.
|
||||
*/
|
||||
public static int ultimateType(Cell cell) {
|
||||
int type = cell.getCellType();
|
||||
if (type == Cell.CELL_TYPE_FORMULA)
|
||||
return cell.getCachedFormulaResultType();
|
||||
else
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if the other object is a {@link CellFormat} object
|
||||
* with the same format.
|
||||
*
|
||||
* @param obj The other object.
|
||||
*
|
||||
* @return <tt>true</tt> if the two objects are equal.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj instanceof CellFormat) {
|
||||
CellFormat that = (CellFormat) obj;
|
||||
return format.equals(that.format);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code for the format.
|
||||
*
|
||||
* @return A hash code for the format.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return format.hashCode();
|
||||
}
|
||||
}
|
121
src/java/org/apache/poi/ss/format/CellFormatCondition.java
Normal file
121
src/java/org/apache/poi/ss/format/CellFormatCondition.java
Normal file
@ -0,0 +1,121 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This object represents a condition in a cell format.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public abstract class CellFormatCondition {
|
||||
private static final int LT = 0;
|
||||
private static final int LE = 1;
|
||||
private static final int GT = 2;
|
||||
private static final int GE = 3;
|
||||
private static final int EQ = 4;
|
||||
private static final int NE = 5;
|
||||
|
||||
private static final Map<String, Integer> TESTS;
|
||||
|
||||
static {
|
||||
TESTS = new HashMap<String, Integer>();
|
||||
TESTS.put("<", LT);
|
||||
TESTS.put("<=", LE);
|
||||
TESTS.put(">", GT);
|
||||
TESTS.put(">=", GE);
|
||||
TESTS.put("=", EQ);
|
||||
TESTS.put("==", EQ);
|
||||
TESTS.put("!=", NE);
|
||||
TESTS.put("<>", NE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of a condition object.
|
||||
*
|
||||
* @param opString The operator as a string. One of <tt>"<"</tt>,
|
||||
* <tt>"<="</tt>, <tt>">"</tt>, <tt>">="</tt>,
|
||||
* <tt>"="</tt>, <tt>"=="</tt>, <tt>"!="</tt>, or
|
||||
* <tt>"<>"</tt>.
|
||||
* @param constStr The constant (such as <tt>"12"</tt>).
|
||||
*
|
||||
* @return A condition object for the given condition.
|
||||
*/
|
||||
public static CellFormatCondition getInstance(String opString,
|
||||
String constStr) {
|
||||
|
||||
if (!TESTS.containsKey(opString))
|
||||
throw new IllegalArgumentException("Unknown test: " + opString);
|
||||
int test = TESTS.get(opString);
|
||||
|
||||
final double c = Double.parseDouble(constStr);
|
||||
|
||||
switch (test) {
|
||||
case LT:
|
||||
return new CellFormatCondition() {
|
||||
public boolean pass(double value) {
|
||||
return value < c;
|
||||
}
|
||||
};
|
||||
case LE:
|
||||
return new CellFormatCondition() {
|
||||
public boolean pass(double value) {
|
||||
return value <= c;
|
||||
}
|
||||
};
|
||||
case GT:
|
||||
return new CellFormatCondition() {
|
||||
public boolean pass(double value) {
|
||||
return value > c;
|
||||
}
|
||||
};
|
||||
case GE:
|
||||
return new CellFormatCondition() {
|
||||
public boolean pass(double value) {
|
||||
return value >= c;
|
||||
}
|
||||
};
|
||||
case EQ:
|
||||
return new CellFormatCondition() {
|
||||
public boolean pass(double value) {
|
||||
return value == c;
|
||||
}
|
||||
};
|
||||
case NE:
|
||||
return new CellFormatCondition() {
|
||||
public boolean pass(double value) {
|
||||
return value != c;
|
||||
}
|
||||
};
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot create for test number " + test + "(\"" + opString +
|
||||
"\")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if the given value passes the constraint's test.
|
||||
*
|
||||
* @param value The value to compare against.
|
||||
*
|
||||
* @return <tt>true</tt> if the given value passes the constraint's test.
|
||||
*/
|
||||
public abstract boolean pass(double value);
|
||||
}
|
494
src/java/org/apache/poi/ss/format/CellFormatPart.java
Normal file
494
src/java/org/apache/poi/ss/format/CellFormatPart.java
Normal file
@ -0,0 +1,494 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import org.apache.poi.hssf.util.HSSFColor;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.apache.poi.ss.format.CellFormatter.logger;
|
||||
import static org.apache.poi.ss.format.CellFormatter.quote;
|
||||
|
||||
/**
|
||||
* Objects of this class represent a single part of a cell format expression.
|
||||
* Each cell can have up to four of these for positive, zero, negative, and text
|
||||
* values.
|
||||
* <p/>
|
||||
* Each format part can contain a color, a condition, and will always contain a
|
||||
* format specification. For example <tt>"[Red][>=10]#"</tt> has a color
|
||||
* (<tt>[Red]</tt>), a condition (<tt>>=10</tt>) and a format specification
|
||||
* (<tt>#</tt>).
|
||||
* <p/>
|
||||
* This class also contains patterns for matching the subparts of format
|
||||
* specification. These are used internally, but are made public in case other
|
||||
* code has use for them.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class CellFormatPart {
|
||||
private final Color color;
|
||||
private CellFormatCondition condition;
|
||||
private final CellFormatter format;
|
||||
|
||||
private static final Map<String, Color> NAMED_COLORS;
|
||||
|
||||
static {
|
||||
NAMED_COLORS = new TreeMap<String, Color>(
|
||||
String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
Map colors = HSSFColor.getIndexHash();
|
||||
for (Object val : colors.values()) {
|
||||
HSSFColor color = (HSSFColor) val;
|
||||
Class type = color.getClass();
|
||||
String name = type.getSimpleName();
|
||||
if (name.equals(name.toUpperCase())) {
|
||||
short[] rgb = color.getTriplet();
|
||||
Color c = new Color(rgb[0], rgb[1], rgb[2]);
|
||||
NAMED_COLORS.put(name, c);
|
||||
if (name.indexOf('_') > 0)
|
||||
NAMED_COLORS.put(name.replace('_', ' '), c);
|
||||
if (name.indexOf("_PERCENT") > 0)
|
||||
NAMED_COLORS.put(name.replace("_PERCENT", "%").replace('_',
|
||||
' '), c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Pattern for the color part of a cell format part. */
|
||||
public static final Pattern COLOR_PAT;
|
||||
/** Pattern for the condition part of a cell format part. */
|
||||
public static final Pattern CONDITION_PAT;
|
||||
/** Pattern for the format specification part of a cell format part. */
|
||||
public static final Pattern SPECIFICATION_PAT;
|
||||
/** Pattern for an entire cell single part. */
|
||||
public static final Pattern FORMAT_PAT;
|
||||
|
||||
/** Within {@link #FORMAT_PAT}, the group number for the matched color. */
|
||||
public static final int COLOR_GROUP;
|
||||
/**
|
||||
* Within {@link #FORMAT_PAT}, the group number for the operator in the
|
||||
* condition.
|
||||
*/
|
||||
public static final int CONDITION_OPERATOR_GROUP;
|
||||
/**
|
||||
* Within {@link #FORMAT_PAT}, the group number for the value in the
|
||||
* condition.
|
||||
*/
|
||||
public static final int CONDITION_VALUE_GROUP;
|
||||
/**
|
||||
* Within {@link #FORMAT_PAT}, the group number for the format
|
||||
* specification.
|
||||
*/
|
||||
public static final int SPECIFICATION_GROUP;
|
||||
|
||||
static {
|
||||
// A condition specification
|
||||
String condition = "([<>=]=?|!=|<>) # The operator\n" +
|
||||
" \\s*([0-9]+(?:\\.[0-9]*)?)\\s* # The constant to test against\n";
|
||||
|
||||
String color =
|
||||
"\\[(black|blue|cyan|green|magenta|red|white|yellow|color [0-9]+)\\]";
|
||||
|
||||
// A number specification
|
||||
// Note: careful that in something like ##, that the trailing comma is not caught up in the integer part
|
||||
|
||||
// A part of a specification
|
||||
String part = "\\\\. # Quoted single character\n" +
|
||||
"|\"([^\\\\\"]|\\\\.)*\" # Quoted string of characters (handles escaped quotes like \\\") \n" +
|
||||
"|_. # Space as wide as a given character\n" +
|
||||
"|\\*. # Repeating fill character\n" +
|
||||
"|@ # Text: cell text\n" +
|
||||
"|([0?\\#](?:[0?\\#,]*)) # Number: digit + other digits and commas\n" +
|
||||
"|e[-+] # Number: Scientific: Exponent\n" +
|
||||
"|m{1,5} # Date: month or minute spec\n" +
|
||||
"|d{1,4} # Date: day/date spec\n" +
|
||||
"|y{2,4} # Date: year spec\n" +
|
||||
"|h{1,2} # Date: hour spec\n" +
|
||||
"|s{1,2} # Date: second spec\n" +
|
||||
"|am?/pm? # Date: am/pm spec\n" +
|
||||
"|\\[h{1,2}\\] # Elapsed time: hour spec\n" +
|
||||
"|\\[m{1,2}\\] # Elapsed time: minute spec\n" +
|
||||
"|\\[s{1,2}\\] # Elapsed time: second spec\n" +
|
||||
"|[^;] # A character\n" + "";
|
||||
|
||||
String format = "(?:" + color + ")? # Text color\n" +
|
||||
"(?:\\[" + condition + "\\])? # Condition\n" +
|
||||
"((?:" + part + ")+) # Format spec\n";
|
||||
|
||||
int flags = Pattern.COMMENTS | Pattern.CASE_INSENSITIVE;
|
||||
COLOR_PAT = Pattern.compile(color, flags);
|
||||
CONDITION_PAT = Pattern.compile(condition, flags);
|
||||
SPECIFICATION_PAT = Pattern.compile(part, flags);
|
||||
FORMAT_PAT = Pattern.compile(format, flags);
|
||||
|
||||
// Calculate the group numbers of important groups. (They shift around
|
||||
// when the pattern is changed; this way we figure out the numbers by
|
||||
// experimentation.)
|
||||
|
||||
COLOR_GROUP = findGroup(FORMAT_PAT, "[Blue]@", "Blue");
|
||||
CONDITION_OPERATOR_GROUP = findGroup(FORMAT_PAT, "[>=1]@", ">=");
|
||||
CONDITION_VALUE_GROUP = findGroup(FORMAT_PAT, "[>=1]@", "1");
|
||||
SPECIFICATION_GROUP = findGroup(FORMAT_PAT, "[Blue][>1]\\a ?", "\\a ?");
|
||||
}
|
||||
|
||||
interface PartHandler {
|
||||
String handlePart(Matcher m, String part, CellFormatType type,
|
||||
StringBuffer desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an object to represent a format part.
|
||||
*
|
||||
* @param desc The string to parse.
|
||||
*/
|
||||
public CellFormatPart(String desc) {
|
||||
Matcher m = FORMAT_PAT.matcher(desc);
|
||||
if (!m.matches()) {
|
||||
throw new IllegalArgumentException("Unrecognized format: " + quote(
|
||||
desc));
|
||||
}
|
||||
color = getColor(m);
|
||||
condition = getCondition(m);
|
||||
format = getFormatter(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if this format part applies to the given value. If
|
||||
* the value is a number and this is part has a condition, returns
|
||||
* <tt>true</tt> only if the number passes the condition. Otherwise, this
|
||||
* allways return <tt>true</tt>.
|
||||
*
|
||||
* @param valueObject The value to evaluate.
|
||||
*
|
||||
* @return <tt>true</tt> if this format part applies to the given value.
|
||||
*/
|
||||
public boolean applies(Object valueObject) {
|
||||
if (condition == null || !(valueObject instanceof Number)) {
|
||||
if (valueObject == null)
|
||||
throw new NullPointerException("valueObject");
|
||||
return true;
|
||||
} else {
|
||||
Number num = (Number) valueObject;
|
||||
return condition.pass(num.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of the first group that is the same as the marker
|
||||
* string. The search starts with group 1.
|
||||
*
|
||||
* @param pat The pattern to use.
|
||||
* @param str The string to match against the pattern.
|
||||
* @param marker The marker value to find the group of.
|
||||
*
|
||||
* @return The matching group number.
|
||||
*
|
||||
* @throws IllegalArgumentException No group matches the marker.
|
||||
*/
|
||||
private static int findGroup(Pattern pat, String str, String marker) {
|
||||
Matcher m = pat.matcher(str);
|
||||
if (!m.find())
|
||||
throw new IllegalArgumentException(
|
||||
"Pattern \"" + pat.pattern() + "\" doesn't match \"" + str +
|
||||
"\"");
|
||||
for (int i = 1; i <= m.groupCount(); i++) {
|
||||
String grp = m.group(i);
|
||||
if (grp != null && grp.equals(marker))
|
||||
return i;
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"\"" + marker + "\" not found in \"" + pat.pattern() + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color specification from the matcher, or <tt>null</tt> if
|
||||
* there is none.
|
||||
*
|
||||
* @param m The matcher for the format part.
|
||||
*
|
||||
* @return The color specification or <tt>null</tt>.
|
||||
*/
|
||||
private static Color getColor(Matcher m) {
|
||||
String cdesc = m.group(COLOR_GROUP);
|
||||
if (cdesc == null || cdesc.length() == 0)
|
||||
return null;
|
||||
Color c = NAMED_COLORS.get(cdesc);
|
||||
if (c == null)
|
||||
logger.warning("Unknown color: " + quote(cdesc));
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the condition specification from the matcher, or <tt>null</tt> if
|
||||
* there is none.
|
||||
*
|
||||
* @param m The matcher for the format part.
|
||||
*
|
||||
* @return The condition specification or <tt>null</tt>.
|
||||
*/
|
||||
private CellFormatCondition getCondition(Matcher m) {
|
||||
String mdesc = m.group(CONDITION_OPERATOR_GROUP);
|
||||
if (mdesc == null || mdesc.length() == 0)
|
||||
return null;
|
||||
return CellFormatCondition.getInstance(m.group(
|
||||
CONDITION_OPERATOR_GROUP), m.group(CONDITION_VALUE_GROUP));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatter object implied by the format specification for the
|
||||
* format part.
|
||||
*
|
||||
* @param matcher The matcher for the format part.
|
||||
*
|
||||
* @return The formatter.
|
||||
*/
|
||||
private CellFormatter getFormatter(Matcher matcher) {
|
||||
String fdesc = matcher.group(SPECIFICATION_GROUP);
|
||||
CellFormatType type = formatType(fdesc);
|
||||
return type.formatter(fdesc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of format.
|
||||
*
|
||||
* @param fdesc The format specification
|
||||
*
|
||||
* @return The type of format.
|
||||
*/
|
||||
private CellFormatType formatType(String fdesc) {
|
||||
fdesc = fdesc.trim();
|
||||
if (fdesc.equals("") || fdesc.equalsIgnoreCase("General"))
|
||||
return CellFormatType.GENERAL;
|
||||
|
||||
Matcher m = SPECIFICATION_PAT.matcher(fdesc);
|
||||
boolean couldBeDate = false;
|
||||
boolean seenZero = false;
|
||||
while (m.find()) {
|
||||
String repl = m.group(0);
|
||||
if (repl.length() > 0) {
|
||||
switch (repl.charAt(0)) {
|
||||
case '@':
|
||||
return CellFormatType.TEXT;
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'y':
|
||||
case 'Y':
|
||||
return CellFormatType.DATE;
|
||||
case 'h':
|
||||
case 'H':
|
||||
case 'm':
|
||||
case 'M':
|
||||
case 's':
|
||||
case 'S':
|
||||
// These can be part of date, or elapsed
|
||||
couldBeDate = true;
|
||||
break;
|
||||
case '0':
|
||||
// This can be part of date, elapsed, or number
|
||||
seenZero = true;
|
||||
break;
|
||||
case '[':
|
||||
return CellFormatType.ELAPSED;
|
||||
case '#':
|
||||
case '?':
|
||||
return CellFormatType.NUMBER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing definitive was found, so we figure out it deductively
|
||||
if (couldBeDate)
|
||||
return CellFormatType.DATE;
|
||||
if (seenZero)
|
||||
return CellFormatType.NUMBER;
|
||||
return CellFormatType.TEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version of the original string that has any special characters
|
||||
* quoted (or escaped) as appropriate for the cell format type. The format
|
||||
* type object is queried to see what is special.
|
||||
*
|
||||
* @param repl The original string.
|
||||
* @param type The format type representation object.
|
||||
*
|
||||
* @return A version of the string with any special characters replaced.
|
||||
*
|
||||
* @see CellFormatType#isSpecial(char)
|
||||
*/
|
||||
static String quoteSpecial(String repl, CellFormatType type) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < repl.length(); i++) {
|
||||
char ch = repl.charAt(i);
|
||||
if (ch == '\'' && type.isSpecial('\'')) {
|
||||
sb.append('\u0000');
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean special = type.isSpecial(ch);
|
||||
if (special)
|
||||
sb.append("'");
|
||||
sb.append(ch);
|
||||
if (special)
|
||||
sb.append("'");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this format part to the given value. This returns a {@link
|
||||
* CellFormatResult} object with the results.
|
||||
*
|
||||
* @param value The value to apply this format part to.
|
||||
*
|
||||
* @return A {@link CellFormatResult} object containing the results of
|
||||
* applying the format to the value.
|
||||
*/
|
||||
public CellFormatResult apply(Object value) {
|
||||
boolean applies = applies(value);
|
||||
String text;
|
||||
Color textColor;
|
||||
if (applies) {
|
||||
text = format.format(value);
|
||||
textColor = color;
|
||||
} else {
|
||||
text = format.simpleFormat(value);
|
||||
textColor = null;
|
||||
}
|
||||
return new CellFormatResult(applies, text, textColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this format part to the given value, applying the result to the
|
||||
* given label.
|
||||
*
|
||||
* @param label The label
|
||||
* @param value The value to apply this format part to.
|
||||
*
|
||||
* @return <tt>true</tt> if the
|
||||
*/
|
||||
public CellFormatResult apply(JLabel label, Object value) {
|
||||
CellFormatResult result = apply(value);
|
||||
label.setText(result.text);
|
||||
if (result.textColor != null) {
|
||||
label.setForeground(result.textColor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static StringBuffer parseFormat(String fdesc, CellFormatType type,
|
||||
PartHandler partHandler) {
|
||||
|
||||
// Quoting is very awkward. In the Java classes, quoting is done
|
||||
// between ' chars, with '' meaning a single ' char. The problem is that
|
||||
// in Excel, it is legal to have two adjacent escaped strings. For
|
||||
// example, consider the Excel format "\a\b#". The naive (and easy)
|
||||
// translation into Java DecimalFormat is "'a''b'#". For the number 17,
|
||||
// in Excel you would get "ab17", but in Java it would be "a'b17" -- the
|
||||
// '' is in the middle of the quoted string in Java. So the trick we
|
||||
// use is this: When we encounter a ' char in the Excel format, we
|
||||
// output a \u0000 char into the string. Now we know that any '' in the
|
||||
// output is the result of two adjacent escaped strings. So after the
|
||||
// main loop, we have to do two passes: One to eliminate any ''
|
||||
// sequences, to make "'a''b'" become "'ab'", and another to replace any
|
||||
// \u0000 with '' to mean a quote char. Oy.
|
||||
//
|
||||
// For formats that don't use "'" we don't do any of this
|
||||
Matcher m = SPECIFICATION_PAT.matcher(fdesc);
|
||||
StringBuffer fmt = new StringBuffer();
|
||||
while (m.find()) {
|
||||
String part = group(m, 0);
|
||||
if (part.length() > 0) {
|
||||
String repl = partHandler.handlePart(m, part, type, fmt);
|
||||
if (repl == null) {
|
||||
switch (part.charAt(0)) {
|
||||
case '\"':
|
||||
repl = quoteSpecial(part.substring(1,
|
||||
part.length() - 1), type);
|
||||
break;
|
||||
case '\\':
|
||||
repl = quoteSpecial(part.substring(1), type);
|
||||
break;
|
||||
case '_':
|
||||
repl = " ";
|
||||
break;
|
||||
case '*': //!! We don't do this for real, we just put in 3 of them
|
||||
repl = expandChar(part);
|
||||
break;
|
||||
default:
|
||||
repl = part;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m.appendReplacement(fmt, Matcher.quoteReplacement(repl));
|
||||
}
|
||||
}
|
||||
m.appendTail(fmt);
|
||||
|
||||
if (type.isSpecial('\'')) {
|
||||
// Now the next pass for quoted characters: Remove '' chars, making "'a''b'" into "'ab'"
|
||||
int pos = 0;
|
||||
while ((pos = fmt.indexOf("''", pos)) >= 0) {
|
||||
fmt.delete(pos, pos + 2);
|
||||
}
|
||||
|
||||
// Now the final pass for quoted chars: Replace any \u0000 with ''
|
||||
pos = 0;
|
||||
while ((pos = fmt.indexOf("\u0000", pos)) >= 0) {
|
||||
fmt.replace(pos, pos + 1, "''");
|
||||
}
|
||||
}
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands a character. This is only partly done, because we don't have the
|
||||
* correct info. In Excel, this would be expanded to fill the rest of the
|
||||
* cell, but we don't know, in general, what the "rest of the cell" is.
|
||||
*
|
||||
* @param part The character to be repeated is the second character in this
|
||||
* string.
|
||||
*
|
||||
* @return The character repeated three times.
|
||||
*/
|
||||
static String expandChar(String part) {
|
||||
String repl;
|
||||
char ch = part.charAt(1);
|
||||
repl = "" + ch + ch + ch;
|
||||
return repl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string from the group, or <tt>""</tt> if the group is
|
||||
* <tt>null</tt>.
|
||||
*
|
||||
* @param m The matcher.
|
||||
* @param g The group number.
|
||||
*
|
||||
* @return The group or <tt>""</tt>.
|
||||
*/
|
||||
public static String group(Matcher m, int g) {
|
||||
String str = m.group(g);
|
||||
return (str == null ? "" : str);
|
||||
}
|
||||
}
|
58
src/java/org/apache/poi/ss/format/CellFormatResult.java
Normal file
58
src/java/org/apache/poi/ss/format/CellFormatResult.java
Normal file
@ -0,0 +1,58 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* This object contains the result of applying a cell format or cell format part
|
||||
* to a value.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
* @see CellFormatPart#apply(Object)
|
||||
* @see CellFormat#apply(Object)
|
||||
*/
|
||||
public class CellFormatResult {
|
||||
/**
|
||||
* This is <tt>true</tt> if no condition was given that applied to the
|
||||
* value, or if the condition is satisfied. If a condition is relevant, and
|
||||
* when applied the value fails the test, this is <tt>false</tt>.
|
||||
*/
|
||||
public final boolean applies;
|
||||
|
||||
/** The resulting text. This will never be <tt>null</tt>. */
|
||||
public final String text;
|
||||
|
||||
/**
|
||||
* The color the format sets, or <tt>null</tt> if the format sets no color.
|
||||
* This will always be <tt>null</tt> if {@link #applies} is <tt>false</tt>.
|
||||
*/
|
||||
public final Color textColor;
|
||||
|
||||
/**
|
||||
* Creates a new format result object.
|
||||
*
|
||||
* @param applies The value for {@link #applies}.
|
||||
* @param text The value for {@link #text}.
|
||||
* @param textColor The value for {@link #textColor}.
|
||||
*/
|
||||
public CellFormatResult(boolean applies, String text, Color textColor) {
|
||||
this.applies = applies;
|
||||
this.text = text;
|
||||
this.textColor = (applies ? textColor : null);
|
||||
}
|
||||
}
|
74
src/java/org/apache/poi/ss/format/CellFormatType.java
Normal file
74
src/java/org/apache/poi/ss/format/CellFormatType.java
Normal file
@ -0,0 +1,74 @@
|
||||
package org.apache.poi.ss.format;
|
||||
|
||||
/**
|
||||
* The different kinds of formats that the formatter understands.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public enum CellFormatType {
|
||||
|
||||
/** The general (default) format; also used for <tt>"General"</tt>. */
|
||||
GENERAL {
|
||||
CellFormatter formatter(String pattern) {
|
||||
return new CellGeneralFormatter();
|
||||
}
|
||||
boolean isSpecial(char ch) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
/** A numeric format. */
|
||||
NUMBER {
|
||||
boolean isSpecial(char ch) {
|
||||
return false;
|
||||
}
|
||||
CellFormatter formatter(String pattern) {
|
||||
return new CellNumberFormatter(pattern);
|
||||
}
|
||||
},
|
||||
/** A date format. */
|
||||
DATE {
|
||||
boolean isSpecial(char ch) {
|
||||
return ch == '\'' || (ch <= '\u007f' && Character.isLetter(ch));
|
||||
}
|
||||
CellFormatter formatter(String pattern) {
|
||||
return new CellDateFormatter(pattern);
|
||||
}
|
||||
},
|
||||
/** An elapsed time format. */
|
||||
ELAPSED {
|
||||
boolean isSpecial(char ch) {
|
||||
return false;
|
||||
}
|
||||
CellFormatter formatter(String pattern) {
|
||||
return new CellElapsedFormatter(pattern);
|
||||
}
|
||||
},
|
||||
/** A text format. */
|
||||
TEXT {
|
||||
boolean isSpecial(char ch) {
|
||||
return false;
|
||||
}
|
||||
CellFormatter formatter(String pattern) {
|
||||
return new CellTextFormatter(pattern);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if the format is special and needs to be quoted.
|
||||
*
|
||||
* @param ch The character to test.
|
||||
*
|
||||
* @return <tt>true</tt> if the format is special and needs to be quoted.
|
||||
*/
|
||||
abstract boolean isSpecial(char ch);
|
||||
|
||||
/**
|
||||
* Returns a new formatter of the appropriate type, for the given pattern.
|
||||
* The pattern must be appropriate for the type.
|
||||
*
|
||||
* @param pattern The pattern to use.
|
||||
*
|
||||
* @return A new formatter of the appropriate type, for the given pattern.
|
||||
*/
|
||||
abstract CellFormatter formatter(String pattern);
|
||||
}
|
102
src/java/org/apache/poi/ss/format/CellFormatter.java
Normal file
102
src/java/org/apache/poi/ss/format/CellFormatter.java
Normal file
@ -0,0 +1,102 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This is the abstract supertype for the various cell formatters.
|
||||
*
|
||||
* @@author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public abstract class CellFormatter {
|
||||
/** The original specified format. */
|
||||
protected final String format;
|
||||
|
||||
/**
|
||||
* This is the locale used to get a consistent format result from which to
|
||||
* work.
|
||||
*/
|
||||
public static final Locale LOCALE = Locale.US;
|
||||
|
||||
/**
|
||||
* Creates a new formatter object, storing the format in {@link #format}.
|
||||
*
|
||||
* @param format The format.
|
||||
*/
|
||||
public CellFormatter(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
/** The logger to use in the formatting code. */
|
||||
static final Logger logger = Logger.getLogger(
|
||||
CellFormatter.class.getName());
|
||||
|
||||
/**
|
||||
* Format a value according the format string.
|
||||
*
|
||||
* @param toAppendTo The buffer to append to.
|
||||
* @param value The value to format.
|
||||
*/
|
||||
public abstract void formatValue(StringBuffer toAppendTo, Object value);
|
||||
|
||||
/**
|
||||
* Format a value according to the type, in the most basic way.
|
||||
*
|
||||
* @param toAppendTo The buffer to append to.
|
||||
* @param value The value to format.
|
||||
*/
|
||||
public abstract void simpleValue(StringBuffer toAppendTo, Object value);
|
||||
|
||||
/**
|
||||
* Formats the value, returning the resulting string.
|
||||
*
|
||||
* @param value The value to format.
|
||||
*
|
||||
* @return The value, formatted.
|
||||
*/
|
||||
public String format(Object value) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
formatValue(sb, value);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the value in the most basic way, returning the resulting string.
|
||||
*
|
||||
* @param value The value to format.
|
||||
*
|
||||
* @return The value, formatted.
|
||||
*/
|
||||
public String simpleFormat(Object value) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
simpleValue(sb, value);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input string, surrounded by quotes.
|
||||
*
|
||||
* @param str The string to quote.
|
||||
*
|
||||
* @return The input string, surrounded by quotes.
|
||||
*/
|
||||
static String quote(String str) {
|
||||
return '"' + str + '"';
|
||||
}
|
||||
}
|
84
src/java/org/apache/poi/ss/format/CellGeneralFormatter.java
Normal file
84
src/java/org/apache/poi/ss/format/CellGeneralFormatter.java
Normal file
@ -0,0 +1,84 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import java.util.Formatter;
|
||||
|
||||
/**
|
||||
* A formatter for the default "General" cell format.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class CellGeneralFormatter extends CellFormatter {
|
||||
/** Creates a new general formatter. */
|
||||
public CellGeneralFormatter() {
|
||||
super("General");
|
||||
}
|
||||
|
||||
/**
|
||||
* The general style is not quite the same as any other, or any combination
|
||||
* of others.
|
||||
*
|
||||
* @param toAppendTo The buffer to append to.
|
||||
* @param value The value to format.
|
||||
*/
|
||||
public void formatValue(StringBuffer toAppendTo, Object value) {
|
||||
if (value instanceof Number) {
|
||||
double val = ((Number) value).doubleValue();
|
||||
if (val == 0) {
|
||||
toAppendTo.append('0');
|
||||
return;
|
||||
}
|
||||
|
||||
String fmt;
|
||||
double exp = Math.log10(Math.abs(val));
|
||||
boolean stripZeros = true;
|
||||
if (exp > 10 || exp < -9)
|
||||
fmt = "%1.5E";
|
||||
else if ((long) val != val)
|
||||
fmt = "%1.9f";
|
||||
else {
|
||||
fmt = "%1.0f";
|
||||
stripZeros = false;
|
||||
}
|
||||
|
||||
Formatter formatter = new Formatter(toAppendTo);
|
||||
formatter.format(LOCALE, fmt, value);
|
||||
if (stripZeros) {
|
||||
// strip off trailing zeros
|
||||
int removeFrom;
|
||||
if (fmt.endsWith("E"))
|
||||
removeFrom = toAppendTo.lastIndexOf("E") - 1;
|
||||
else
|
||||
removeFrom = toAppendTo.length() - 1;
|
||||
while (toAppendTo.charAt(removeFrom) == '0') {
|
||||
toAppendTo.deleteCharAt(removeFrom--);
|
||||
}
|
||||
if (toAppendTo.charAt(removeFrom) == '.') {
|
||||
toAppendTo.deleteCharAt(removeFrom--);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toAppendTo.append(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/** Equivalent to {@link #formatValue(StringBuffer,Object)}. {@inheritDoc}. */
|
||||
public void simpleValue(StringBuffer toAppendTo, Object value) {
|
||||
formatValue(toAppendTo, value);
|
||||
}
|
||||
}
|
1085
src/java/org/apache/poi/ss/format/CellNumberFormatter.java
Normal file
1085
src/java/org/apache/poi/ss/format/CellNumberFormatter.java
Normal file
File diff suppressed because it is too large
Load Diff
79
src/java/org/apache/poi/ss/format/CellTextFormatter.java
Normal file
79
src/java/org/apache/poi/ss/format/CellTextFormatter.java
Normal file
@ -0,0 +1,79 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import org.apache.poi.ss.format.CellFormatPart.PartHandler;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* This class implements printing out text.
|
||||
*
|
||||
* @author Ken Arnold, Industrious Media LLC
|
||||
*/
|
||||
public class CellTextFormatter extends CellFormatter {
|
||||
private final int[] textPos;
|
||||
private final String desc;
|
||||
|
||||
static final CellFormatter SIMPLE_TEXT = new CellTextFormatter("@");
|
||||
|
||||
public CellTextFormatter(String format) {
|
||||
super(format);
|
||||
|
||||
final int[] numPlaces = new int[1];
|
||||
|
||||
desc = CellFormatPart.parseFormat(format, CellFormatType.TEXT,
|
||||
new PartHandler() {
|
||||
public String handlePart(Matcher m, String part,
|
||||
CellFormatType type, StringBuffer desc) {
|
||||
if (part.equals("@")) {
|
||||
numPlaces[0]++;
|
||||
return "\u0000";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).toString();
|
||||
|
||||
// Remember the "@" positions in last-to-first order (to make insertion easier)
|
||||
textPos = new int[numPlaces[0]];
|
||||
int pos = desc.length() - 1;
|
||||
for (int i = 0; i < textPos.length; i++) {
|
||||
textPos[i] = desc.lastIndexOf("\u0000", pos);
|
||||
pos = textPos[i] - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void formatValue(StringBuffer toAppendTo, Object obj) {
|
||||
int start = toAppendTo.length();
|
||||
String text = obj.toString();
|
||||
toAppendTo.append(desc);
|
||||
for (int i = 0; i < textPos.length; i++) {
|
||||
int pos = start + textPos[i];
|
||||
toAppendTo.replace(pos, pos + 1, text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p/>
|
||||
* For text, this is just printing the text.
|
||||
*/
|
||||
public void simpleValue(StringBuffer toAppendTo, Object value) {
|
||||
SIMPLE_TEXT.formatValue(toAppendTo, value);
|
||||
}
|
||||
}
|
3
src/java/org/apache/poi/ss/format/package.html
Normal file
3
src/java/org/apache/poi/ss/format/package.html
Normal file
@ -0,0 +1,3 @@
|
||||
<body>
|
||||
This package contains classes that implement cell formatting
|
||||
</body>
|
@ -0,0 +1,126 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.xssf.XSSFITestDataProvider;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** Test the individual CellFormatPart types. */
|
||||
public class TestCellFormatPart extends CellFormatTestBase {
|
||||
private static final Pattern NUMBER_EXTRACT_FMT = Pattern.compile(
|
||||
"([-+]?[0-9]+)(\\.[0-9]+)?.*(?:(e).*?([+-]?[0-9]+))",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public TestCellFormatPart() {
|
||||
super(XSSFITestDataProvider.instance);
|
||||
}
|
||||
|
||||
public void testGeneralFormat() throws Exception {
|
||||
runFormatTests("GeneralFormatTests.xlsx", new CellValue() {
|
||||
public Object getValue(Cell cell) {
|
||||
int type = CellFormat.ultimateType(cell);
|
||||
if (type == Cell.CELL_TYPE_BOOLEAN)
|
||||
return cell.getBooleanCellValue() ? "TRUE" : "FALSE";
|
||||
else if (type == Cell.CELL_TYPE_NUMERIC)
|
||||
return cell.getNumericCellValue();
|
||||
else
|
||||
return cell.getStringCellValue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testNumberFormat() throws Exception {
|
||||
runFormatTests("NumberFormatTests.xlsx", new CellValue() {
|
||||
public Object getValue(Cell cell) {
|
||||
return cell.getNumericCellValue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testNumberApproxFormat() throws Exception {
|
||||
runFormatTests("NumberFormatApproxTests.xlsx", new CellValue() {
|
||||
public Object getValue(Cell cell) {
|
||||
return cell.getNumericCellValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
void equivalent(String expected, String actual,
|
||||
CellFormatPart format) {
|
||||
double expectedVal = extractNumber(expected);
|
||||
double actualVal = extractNumber(actual);
|
||||
// equal within 1%
|
||||
double delta = expectedVal / 100;
|
||||
assertEquals("format \"" + format + "\"," + expected + " ~= " +
|
||||
actual, expectedVal, actualVal, delta);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testDateFormat() throws Exception {
|
||||
runFormatTests("DateFormatTests.xlsx", new CellValue() {
|
||||
public Object getValue(Cell cell) {
|
||||
return cell.getDateCellValue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testElapsedFormat() throws Exception {
|
||||
runFormatTests("ElapsedFormatTests.xlsx", new CellValue() {
|
||||
public Object getValue(Cell cell) {
|
||||
return cell.getNumericCellValue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testTextFormat() throws Exception {
|
||||
runFormatTests("TextFormatTests.xlsx", new CellValue() {
|
||||
public Object getValue(Cell cell) {
|
||||
if (CellFormat.ultimateType(cell) == Cell.CELL_TYPE_BOOLEAN)
|
||||
return cell.getBooleanCellValue() ? "TRUE" : "FALSE";
|
||||
else
|
||||
return cell.getStringCellValue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testConditions() throws Exception {
|
||||
runFormatTests("FormatConditionTests.xlsx", new CellValue() {
|
||||
Object getValue(Cell cell) {
|
||||
return cell.getNumericCellValue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private double extractNumber(String str) {
|
||||
Matcher m = NUMBER_EXTRACT_FMT.matcher(str);
|
||||
if (!m.find())
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot find numer in \"" + str + "\"");
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
// The groups in the pattern are the parts of the number
|
||||
for (int i = 1; i <= m.groupCount(); i++) {
|
||||
String part = m.group(i);
|
||||
if (part != null)
|
||||
sb.append(part);
|
||||
}
|
||||
return Double.valueOf(sb.toString());
|
||||
}
|
||||
}
|
293
src/testcases/org/apache/poi/ss/format/CellFormatTestBase.java
Normal file
293
src/testcases/org/apache/poi/ss/format/CellFormatTestBase.java
Normal file
@ -0,0 +1,293 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.ss.ITestDataProvider;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
import static java.awt.Color.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class is a base class for spreadsheet-based tests, such as are used for
|
||||
* cell formatting. This reads tests from the spreadsheet, as well as reading
|
||||
* flags that can be used to paramterize these tests.
|
||||
* <p/>
|
||||
* Each test has four parts: The expected result (column A), the format string
|
||||
* (column B), the value to format (column C), and a comma-separated list of
|
||||
* categores that this test falls in. Normally all tests are run, but if the
|
||||
* flag "Categories" is not empty, only tests that have at least one category
|
||||
* listed in "Categories" are run.
|
||||
*/
|
||||
@SuppressWarnings(
|
||||
{"JUnitTestCaseWithNoTests", "JUnitTestClassNamingConvention"})
|
||||
public class CellFormatTestBase extends TestCase {
|
||||
private final ITestDataProvider _testDataProvider;
|
||||
|
||||
protected Workbook workbook;
|
||||
|
||||
private String testFile;
|
||||
private Map<String, String> testFlags;
|
||||
private boolean tryAllColors;
|
||||
private JLabel label;
|
||||
|
||||
private static final String[] COLOR_NAMES =
|
||||
{"Black", "Red", "Green", "Blue", "Yellow", "Cyan", "Magenta",
|
||||
"White"};
|
||||
private static final Color[] COLORS =
|
||||
{BLACK, RED, GREEN, BLUE, YELLOW, CYAN, MAGENTA, WHITE};
|
||||
|
||||
public static final Color TEST_COLOR = ORANGE.darker();
|
||||
|
||||
protected CellFormatTestBase(ITestDataProvider testDataProvider) {
|
||||
_testDataProvider = testDataProvider;
|
||||
}
|
||||
|
||||
abstract static class CellValue {
|
||||
abstract Object getValue(Cell cell);
|
||||
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
Color getColor(Cell cell) {
|
||||
return TEST_COLOR;
|
||||
}
|
||||
|
||||
void equivalent(String expected, String actual, CellFormatPart format) {
|
||||
assertEquals("format \"" + format + "\"", '"' + expected + '"',
|
||||
'"' + actual + '"');
|
||||
}
|
||||
}
|
||||
|
||||
protected void runFormatTests(String workbookName, CellValue valueGetter)
|
||||
throws IOException {
|
||||
|
||||
openWorkbook(workbookName);
|
||||
|
||||
readFlags(workbook);
|
||||
|
||||
Set<String> runCategories = new TreeSet<String>(
|
||||
String.CASE_INSENSITIVE_ORDER);
|
||||
String runCategoryList = flagString("Categories", "");
|
||||
if (runCategoryList != null) {
|
||||
runCategories.addAll(Arrays.asList(runCategoryList.split(
|
||||
"\\s*,\\s*")));
|
||||
runCategories.remove(""); // this can be found and means nothing
|
||||
}
|
||||
|
||||
Sheet sheet = workbook.getSheet("Tests");
|
||||
int end = sheet.getLastRowNum();
|
||||
// Skip the header row, therefore "+ 1"
|
||||
for (int r = sheet.getFirstRowNum() + 1; r <= end; r++) {
|
||||
Row row = sheet.getRow(r);
|
||||
if (row == null)
|
||||
continue;
|
||||
int cellnum = 0;
|
||||
String expectedText = row.getCell(cellnum).getStringCellValue();
|
||||
String format = row.getCell(1).getStringCellValue();
|
||||
String testCategoryList = row.getCell(3).getStringCellValue();
|
||||
boolean byCategory = runByCategory(runCategories, testCategoryList);
|
||||
if ((!expectedText.isEmpty() || !format.isEmpty()) && byCategory) {
|
||||
Cell cell = row.getCell(2);
|
||||
tryFormat(r, expectedText, format, valueGetter, cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a given workbook.
|
||||
*
|
||||
* @param workbookName The workbook name. This is presumed to live in the
|
||||
* "spreadsheets" directory under the directory named in
|
||||
* the Java property "POI.testdata.path".
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void openWorkbook(String workbookName)
|
||||
throws IOException {
|
||||
workbook = _testDataProvider.openSampleWorkbook(workbookName);
|
||||
workbook.setMissingCellPolicy(Row.CREATE_NULL_AS_BLANK);
|
||||
testFile = workbookName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the flags from the workbook. Flags are on the sheet named "Flags",
|
||||
* and consist of names in column A and values in column B. These are put
|
||||
* into a map that can be queried later.
|
||||
*
|
||||
* @param wb The workbook to look in.
|
||||
*/
|
||||
private void readFlags(Workbook wb) {
|
||||
Sheet flagSheet = wb.getSheet("Flags");
|
||||
testFlags = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
if (flagSheet != null) {
|
||||
int end = flagSheet.getLastRowNum();
|
||||
// Skip the header row, therefore "+ 1"
|
||||
for (int r = flagSheet.getFirstRowNum() + 1; r <= end; r++) {
|
||||
Row row = flagSheet.getRow(r);
|
||||
if (row == null)
|
||||
continue;
|
||||
String flagName = row.getCell(0).getStringCellValue();
|
||||
String flagValue = row.getCell(1).getStringCellValue();
|
||||
if (flagName.length() > 0) {
|
||||
testFlags.put(flagName, flagValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tryAllColors = flagBoolean("AllColors", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if any of the categories for this run are contained
|
||||
* in the test's listed categories.
|
||||
*
|
||||
* @param categories The categories of tests to be run. If this is
|
||||
* empty, then all tests will be run.
|
||||
* @param testCategories The categories that this test is in. This is a
|
||||
* comma-separated list. If <em>any</em> tests in
|
||||
* this list are in <tt>categories</tt>, the test will
|
||||
* be run.
|
||||
*
|
||||
* @return <tt>true</tt> if the test should be run.
|
||||
*/
|
||||
private boolean runByCategory(Set<String> categories,
|
||||
String testCategories) {
|
||||
|
||||
if (categories.isEmpty())
|
||||
return true;
|
||||
// If there are specified categories, find out if this has one of them
|
||||
for (String category : testCategories.split("\\s*,\\s*")) {
|
||||
if (categories.contains(category)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void tryFormat(int row, String expectedText, String desc,
|
||||
CellValue getter, Cell cell) {
|
||||
|
||||
Object value = getter.getValue(cell);
|
||||
Color testColor = getter.getColor(cell);
|
||||
if (testColor == null)
|
||||
testColor = TEST_COLOR;
|
||||
|
||||
if (label == null)
|
||||
label = new JLabel();
|
||||
label.setForeground(testColor);
|
||||
label.setText("xyzzy");
|
||||
|
||||
System.out.printf("Row %d: \"%s\" -> \"%s\": expected \"%s\"", row + 1,
|
||||
String.valueOf(value), desc, expectedText);
|
||||
System.out.flush();
|
||||
String actualText = tryColor(desc, null, getter, value, expectedText,
|
||||
testColor);
|
||||
System.out.printf(", actual \"%s\")%n", actualText);
|
||||
System.out.flush();
|
||||
|
||||
if (tryAllColors && testColor != TEST_COLOR) {
|
||||
for (int i = 0; i < COLOR_NAMES.length; i++) {
|
||||
String cname = COLOR_NAMES[i];
|
||||
tryColor(desc, cname, getter, value, expectedText, COLORS[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String tryColor(String desc, String cname, CellValue getter,
|
||||
Object value, String expectedText, Color expectedColor) {
|
||||
|
||||
if (cname != null)
|
||||
desc = "[" + cname + "]" + desc;
|
||||
Color origColor = label.getForeground();
|
||||
CellFormatPart format = new CellFormatPart(desc);
|
||||
if (!format.apply(label, value).applies) {
|
||||
// If this doesn't apply, no color change is expected
|
||||
expectedColor = origColor;
|
||||
}
|
||||
|
||||
String actualText = label.getText();
|
||||
Color actualColor = label.getForeground();
|
||||
getter.equivalent(expectedText, actualText, format);
|
||||
assertEquals(cname == null ? "no color" : "color " + cname,
|
||||
expectedColor, actualColor);
|
||||
return actualText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the given flag. The flag has the value of
|
||||
* <tt>true</tt> if the text value is <tt>"true"</tt>, <tt>"yes"</tt>, or
|
||||
* <tt>"on"</tt> (ignoring case).
|
||||
*
|
||||
* @param flagName The name of the flag to fetch.
|
||||
* @param expected The value for the flag that is expected when the tests
|
||||
* are run for a full test. If the current value is not the
|
||||
* expected one, you will get a warning in the test output.
|
||||
* This is so that you do not accidentally leave a flag set
|
||||
* to a value that prevents running some tests, thereby
|
||||
* letting you accidentally release code that is not fully
|
||||
* tested.
|
||||
*
|
||||
* @return The value for the flag.
|
||||
*/
|
||||
protected boolean flagBoolean(String flagName, boolean expected) {
|
||||
String value = testFlags.get(flagName);
|
||||
boolean isSet;
|
||||
if (value == null)
|
||||
isSet = false;
|
||||
else {
|
||||
isSet = value.equalsIgnoreCase("true") || value.equalsIgnoreCase(
|
||||
"yes") || value.equalsIgnoreCase("on");
|
||||
}
|
||||
warnIfUnexpected(flagName, expected, isSet);
|
||||
return isSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the given flag.
|
||||
*
|
||||
* @param flagName The name of the flag to fetch.
|
||||
* @param expected The value for the flag that is expected when the tests
|
||||
* are run for a full test. If the current value is not the
|
||||
* expected one, you will get a warning in the test output.
|
||||
* This is so that you do not accidentally leave a flag set
|
||||
* to a value that prevents running some tests, thereby
|
||||
* letting you accidentally release code that is not fully
|
||||
* tested.
|
||||
*
|
||||
* @return The value for the flag.
|
||||
*/
|
||||
protected String flagString(String flagName, String expected) {
|
||||
String value = testFlags.get(flagName);
|
||||
if (value == null)
|
||||
value = "";
|
||||
warnIfUnexpected(flagName, expected, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private void warnIfUnexpected(String flagName, Object expected,
|
||||
Object actual) {
|
||||
if (!actual.equals(expected)) {
|
||||
System.err.println(
|
||||
"WARNING: " + testFile + ": " + "Flag " + flagName +
|
||||
" = \"" + actual + "\" [not \"" + expected + "\"]");
|
||||
}
|
||||
}
|
||||
}
|
32
src/testcases/org/apache/poi/ss/format/TestCellFormat.java
Normal file
32
src/testcases/org/apache/poi/ss/format/TestCellFormat.java
Normal file
@ -0,0 +1,32 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import org.apache.poi.ss.format.CellFormat;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class TestCellFormat extends TestCase {
|
||||
public void testSome() {
|
||||
JLabel l = new JLabel();
|
||||
CellFormat fmt = CellFormat.getInstance(
|
||||
"\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)");
|
||||
fmt.apply(l, 1.1);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.apache.poi.ss.format;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.poi.ss.format.CellFormatCondition;
|
||||
|
||||
public class TestCellFormatCondition extends TestCase {
|
||||
public void testSVConditions() {
|
||||
CellFormatCondition lt = CellFormatCondition.getInstance("<", "1.5");
|
||||
assertTrue(lt.pass(1.4));
|
||||
assertFalse(lt.pass(1.5));
|
||||
assertFalse(lt.pass(1.6));
|
||||
|
||||
CellFormatCondition le = CellFormatCondition.getInstance("<=", "1.5");
|
||||
assertTrue(le.pass(1.4));
|
||||
assertTrue(le.pass(1.5));
|
||||
assertFalse(le.pass(1.6));
|
||||
|
||||
CellFormatCondition gt = CellFormatCondition.getInstance(">", "1.5");
|
||||
assertFalse(gt.pass(1.4));
|
||||
assertFalse(gt.pass(1.5));
|
||||
assertTrue(gt.pass(1.6));
|
||||
|
||||
CellFormatCondition ge = CellFormatCondition.getInstance(">=", "1.5");
|
||||
assertFalse(ge.pass(1.4));
|
||||
assertTrue(ge.pass(1.5));
|
||||
assertTrue(ge.pass(1.6));
|
||||
|
||||
CellFormatCondition eqs = CellFormatCondition.getInstance("=", "1.5");
|
||||
assertFalse(eqs.pass(1.4));
|
||||
assertTrue(eqs.pass(1.5));
|
||||
assertFalse(eqs.pass(1.6));
|
||||
|
||||
CellFormatCondition eql = CellFormatCondition.getInstance("==", "1.5");
|
||||
assertFalse(eql.pass(1.4));
|
||||
assertTrue(eql.pass(1.5));
|
||||
assertFalse(eql.pass(1.6));
|
||||
|
||||
CellFormatCondition neo = CellFormatCondition.getInstance("<>", "1.5");
|
||||
assertTrue(neo.pass(1.4));
|
||||
assertFalse(neo.pass(1.5));
|
||||
assertTrue(neo.pass(1.6));
|
||||
|
||||
CellFormatCondition nen = CellFormatCondition.getInstance("!=", "1.5");
|
||||
assertTrue(nen.pass(1.4));
|
||||
assertFalse(nen.pass(1.5));
|
||||
assertTrue(nen.pass(1.6));
|
||||
}
|
||||
}
|
BIN
test-data/spreadsheet/DateFormatTests.xlsx
Normal file
BIN
test-data/spreadsheet/DateFormatTests.xlsx
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/ElapsedFormatTests.xlsx
Normal file
BIN
test-data/spreadsheet/ElapsedFormatTests.xlsx
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/FormatChoiceTests.xls
Normal file
BIN
test-data/spreadsheet/FormatChoiceTests.xls
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/FormatChoiceTests.xlsx
Normal file
BIN
test-data/spreadsheet/FormatChoiceTests.xlsx
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/FormatConditionTests.xlsx
Normal file
BIN
test-data/spreadsheet/FormatConditionTests.xlsx
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/GeneralFormatTests.xlsx
Normal file
BIN
test-data/spreadsheet/GeneralFormatTests.xlsx
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/NumberFormatApproxTests.xlsx
Normal file
BIN
test-data/spreadsheet/NumberFormatApproxTests.xlsx
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/NumberFormatTests.xlsx
Normal file
BIN
test-data/spreadsheet/NumberFormatTests.xlsx
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/TextFormatTests.xlsx
Normal file
BIN
test-data/spreadsheet/TextFormatTests.xlsx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user