<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">
+++ /dev/null
-
-/* ====================================================================
- 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.contrib.view;
-
-import java.awt.*;
-
-import javax.swing.border.AbstractBorder;
-
-import org.apache.poi.hssf.usermodel.HSSFCellStyle;
-
-/**
- * This is an attempt to implement Excel style borders for the SheetViewer.
- * Mostly just overrides stuff so the javadoc won't appear here but will
- * appear in the generated stuff.
- *
- * @author Andrew C. Oliver (acoliver at apache dot org)
- * @author Jason Height
- */
-public class SVBorder extends AbstractBorder {
- private Color northColor = null;
- private Color eastColor = null;
- private Color southColor = null;
- private Color westColor = null;
- private int northBorderType = HSSFCellStyle.BORDER_NONE;
- private int eastBorderType =HSSFCellStyle.BORDER_NONE;
- private int southBorderType = HSSFCellStyle.BORDER_NONE;
- private int westBorderType = HSSFCellStyle.BORDER_NONE;
- private boolean northBorder=false;
- private boolean eastBorder=false;
- private boolean southBorder=false;
- private boolean westBorder=false;
- private boolean selected = false;
-
- public void setBorder(Color northColor, Color eastColor,
- Color southColor, Color westColor,
- int northBorderType, int eastBorderType,
- int southBorderType, int westBorderType,
- boolean selected) {
- this.eastColor = eastColor;
- this.southColor = southColor;
- this.westColor = westColor;
- this.northBorderType = northBorderType;
- this.eastBorderType = eastBorderType;
- this.southBorderType = southBorderType;
- this.westBorderType = westBorderType;
- this.northBorder=northBorderType != HSSFCellStyle.BORDER_NONE;
- this.eastBorder=eastBorderType != HSSFCellStyle.BORDER_NONE;
- this.southBorder=southBorderType != HSSFCellStyle.BORDER_NONE;
- this.westBorder=westBorderType != HSSFCellStyle.BORDER_NONE;
- this.selected = selected;
- }
-
- public void paintBorder(Component c, Graphics g, int x, int y, int width,
- int height) {
- Color oldColor = g.getColor();
-
-
- paintSelectedBorder(g, x, y, width, height);
- paintNormalBorders(g, x, y, width, height);
- paintDottedBorders(g, x, y, width, height);
- paintDashedBorders(g, x, y, width, height);
- paintDoubleBorders(g, x, y, width, height);
- paintDashDotDotBorders(g, x, y, width, height);
-
-
- g.setColor(oldColor);
- }
-
- /**
- * Called by paintBorder to paint the border of a selected cell.
- * The paramaters are the Graphics object, location and dimensions of the
- * cell.
- */
- private void paintSelectedBorder(Graphics g, int x, int y, int width,
- int height) {
- if (selected) {
- //Need to setup thickness of 2
- g.setColor(Color.black);
- //paint the border
- g.drawRect(x,y,width-1,height-1);
-
- //paint the filled rectangle at the bottom left hand position
- g.fillRect(x+width-5, y+height-5, 5, 5);
- }
- }
-
-
- /**
- * Called by paintBorder to paint the various versions of normal line
- * borders for a cell.
- */
- private void paintNormalBorders(Graphics g, int x, int y, int width,
- int height) {
-
- if (northBorder &&
- ((northBorderType == HSSFCellStyle.BORDER_THIN) ||
- (northBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
- (northBorderType == HSSFCellStyle.BORDER_THICK)
- )
- ) {
-
- int thickness = getThickness(northBorderType);
-
- g.setColor(northColor);
-
- for (int k=0; k < thickness; k++) {
- g.drawLine(x,y+k,width,y+k);
- }
- }
-
- if (eastBorder &&
- ((eastBorderType == HSSFCellStyle.BORDER_THIN) ||
- (eastBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
- (eastBorderType == HSSFCellStyle.BORDER_THICK)
- )
- ) {
-
- int thickness = getThickness(eastBorderType);
-
- g.setColor(eastColor);
-
- for (int k=0; k < thickness; k++) {
- g.drawLine(width-k,y,width-k,height);
- }
- }
-
- if (southBorder &&
- ((southBorderType == HSSFCellStyle.BORDER_THIN) ||
- (southBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
- (southBorderType == HSSFCellStyle.BORDER_THICK)
- )
- ) {
-
- int thickness = getThickness(southBorderType);
-
- g.setColor(southColor);
- for (int k=0; k < thickness; k++) {
- g.drawLine(x,height - k,width,height - k);
- }
- }
-
- if (westBorder &&
- ((westBorderType == HSSFCellStyle.BORDER_THIN) ||
- (westBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
- (westBorderType == HSSFCellStyle.BORDER_THICK)
- )
- ) {
-
- int thickness = getThickness(westBorderType);
-
- g.setColor(westColor);
-
- for (int k=0; k < thickness; k++) {
- g.drawLine(x+k,y,x+k,height);
- }
- }
- }
-
- /**
- * Called by paintBorder to paint the dotted line
- * borders for a cell.
- */
- private void paintDottedBorders(Graphics g, int x, int y, int width,
- int height) {
- if (northBorder &&
- northBorderType == HSSFCellStyle.BORDER_DOTTED) {
- int thickness = getThickness(northBorderType);
-
- g.setColor(northColor);
-
- for (int k=0; k < thickness; k++) {
- for (int xc = x; xc < width; xc=xc+2) {
- g.drawLine(xc,y+k,xc,y+k);
- }
- }
- }
-
- if (eastBorder &&
- eastBorderType == HSSFCellStyle.BORDER_DOTTED
- ) {
-
- int thickness = getThickness(eastBorderType);
- thickness++; //need for dotted borders to show up east
-
- g.setColor(eastColor);
-
- for (int k=0; k < thickness; k++) {
- for (int yc=y;yc < height; yc=yc+2) {
- g.drawLine(width-k,yc,width-k,yc);
- }
- }
- }
-
- if (southBorder &&
- southBorderType == HSSFCellStyle.BORDER_DOTTED
- ) {
-
- int thickness = getThickness(southBorderType);
- thickness++;
- g.setColor(southColor);
- for (int k=0; k < thickness; k++) {
- for (int xc = x; xc < width; xc=xc+2) {
- g.drawLine(xc,height-k,xc,height-k);
- }
- }
- }
-
- if (westBorder &&
- westBorderType == HSSFCellStyle.BORDER_DOTTED
- ) {
-
- int thickness = getThickness(westBorderType);
-// thickness++;
-
- g.setColor(westColor);
-
- for (int k=0; k < thickness; k++) {
- for (int yc=y;yc < height; yc=yc+2) {
- g.drawLine(x+k,yc,x+k,yc);
- }
- }
- }
- }
-
- /**
- * Called by paintBorder to paint the various versions of dotted line
- * borders for a cell.
- */
- private void paintDashedBorders(Graphics g, int x, int y, int width,
- int height) {
- if (northBorder &&
- ((northBorderType == HSSFCellStyle.BORDER_DASHED) ||
- (northBorderType == HSSFCellStyle.BORDER_HAIR))
- ) {
- int thickness = getThickness(northBorderType);
-
- int dashlength = 1;
-
- if (northBorderType == HSSFCellStyle.BORDER_DASHED)
- dashlength = 2;
-
- g.setColor(northColor);
-
- for (int k=0; k < thickness; k++) {
- for (int xc = x; xc < width; xc=xc+5) {
- g.drawLine(xc,y+k,xc+dashlength,y+k);
- }
- }
- }
-
- if (eastBorder &&
- ((eastBorderType == HSSFCellStyle.BORDER_DASHED) ||
- (eastBorderType == HSSFCellStyle.BORDER_HAIR))
- ) {
-
- int thickness = getThickness(eastBorderType);
- thickness++; //need for dotted borders to show up east
-
-
- int dashlength = 1;
-
- if (eastBorderType == HSSFCellStyle.BORDER_DASHED)
- dashlength = 2;
-
- g.setColor(eastColor);
-
- for (int k=0; k < thickness; k++) {
- for (int yc=y;yc < height; yc=yc+5) {
- g.drawLine(width-k,yc,width-k,yc+dashlength);
- }
- }
- }
-
- if (southBorder &&
- ((southBorderType == HSSFCellStyle.BORDER_DASHED) ||
- (southBorderType == HSSFCellStyle.BORDER_HAIR))
- ) {
-
- int thickness = getThickness(southBorderType);
- thickness++;
-
- int dashlength = 1;
-
- if (southBorderType == HSSFCellStyle.BORDER_DASHED)
- dashlength = 2;
-
- g.setColor(southColor);
- for (int k=0; k < thickness; k++) {
- for (int xc = x; xc < width; xc=xc+5) {
- g.drawLine(xc,height-k,xc+dashlength,height-k);
- }
- }
- }
-
- if (westBorder &&
- ((westBorderType == HSSFCellStyle.BORDER_DASHED) ||
- (westBorderType == HSSFCellStyle.BORDER_HAIR))
- ) {
-
- int thickness = getThickness(westBorderType);
-// thickness++;
-
- int dashlength = 1;
-
- if (westBorderType == HSSFCellStyle.BORDER_DASHED)
- dashlength = 2;
-
- g.setColor(westColor);
-
- for (int k=0; k < thickness; k++) {
- for (int yc=y;yc < height; yc=yc+5) {
- g.drawLine(x+k,yc,x+k,yc+dashlength);
- }
- }
- }
- }
-
- /**
- * Called by paintBorder to paint the double line
- * borders for a cell.
- */
- private void paintDoubleBorders(Graphics g, int x, int y, int width,
- int height) {
- if (northBorder &&
- northBorderType == HSSFCellStyle.BORDER_DOUBLE) {
-
- g.setColor(northColor);
-
- int leftx=x;
- int rightx=width;
-
- // if there are borders on the west or east then
- // the second line shouldn't cross them
- if (westBorder)
- leftx = x+3;
-
- if (eastBorder)
- rightx = width-3;
-
- g.drawLine(x,y,width,y);
- g.drawLine(leftx,y+2,rightx,y+2);
- }
-
- if (eastBorder &&
- eastBorderType == HSSFCellStyle.BORDER_DOUBLE
- ) {
-
- int thickness = getThickness(eastBorderType);
- thickness++; //need for dotted borders to show up east
-
- g.setColor(eastColor);
-
- int topy=y;
- int bottomy=height;
-
- if (northBorder)
- topy=y+3;
-
- if (southBorder)
- bottomy=height-3;
-
- g.drawLine(width-1,y,width-1,height);
- g.drawLine(width-3,topy,width-3,bottomy);
- }
-
- if (southBorder &&
- southBorderType == HSSFCellStyle.BORDER_DOUBLE
- ) {
-
- g.setColor(southColor);
-
- int leftx=y;
- int rightx=width;
-
- if (westBorder)
- leftx=x+3;
-
- if (eastBorder)
- rightx=width-3;
-
-
- g.drawLine(x,height - 1,width,height - 1);
- g.drawLine(leftx,height - 3,rightx,height - 3);
- }
-
- if (westBorder &&
- westBorderType == HSSFCellStyle.BORDER_DOUBLE
- ) {
-
- int thickness = getThickness(westBorderType);
-// thickness++;
-
- g.setColor(westColor);
-
- int topy=y;
- int bottomy=height-3;
-
- if (northBorder)
- topy=y+2;
-
- if (southBorder)
- bottomy=height-3;
-
- g.drawLine(x,y,x,height);
- g.drawLine(x+2,topy,x+2,bottomy);
- }
- }
-
- /**
- * Called by paintBorder to paint the various versions of dash dot dot line
- * borders for a cell.
- */
- private void paintDashDotDotBorders(Graphics g, int x, int y, int width,
- int height) {
- if (northBorder &&
- ((northBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
- (northBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
- ) {
- int thickness = getThickness(northBorderType);
-
- g.setColor(northColor);
- for (int l=x; l < width;) {
- l=l+drawDashDotDot(g, l, y, thickness, true, true);
- }
-
- }
-
- if (eastBorder &&
- ((eastBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
- (eastBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
- ) {
-
- int thickness = getThickness(eastBorderType);
-
- g.setColor(eastColor);
-
- for (int l=y;l < height;) {
- //System.err.println("drawing east");
- l=l+drawDashDotDot(g,width-1,l,thickness,false,false);
- }
- }
-
- if (southBorder &&
- ((southBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
- (southBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
- ) {
-
- int thickness = getThickness(southBorderType);
-
- g.setColor(southColor);
-
- for (int l=x; l < width;) {
- //System.err.println("drawing south");
- l=l+drawDashDotDot(g, l, height-1, thickness, true, false);
- }
- }
-
- if (westBorder &&
- ((westBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
- (westBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
- ) {
-
- int thickness = getThickness(westBorderType);
-
- g.setColor(westColor);
-
- for (int l=y;l < height;) {
- //System.err.println("drawing west");
- l=l+drawDashDotDot(g,x,l,thickness,false,true);
- }
-
- }
- }
-
- /**
- * Draws one dash dot dot horizontally or vertically with thickness drawn
- * incrementally to either the right or left.
- *
- * @param g graphics object for drawing with
- * @param x the x origin of the line
- * @param y the y origin of the line
- * @param thickness the thickness of the line
- * @param horizontal or vertical (true for horizontal)
- * @param right/bottom or left/top thickness (true for right or top),
- * if true then the x or y origin will be incremented to provide
- * thickness, if false, they'll be decremented. For vertical
- * borders, x is incremented or decremented, for horizontal its y.
- * Just set to true for north and west, and false for east and
- * south.
- * @returns length - returns the length of the line.
- */
- private int drawDashDotDot(Graphics g,int x, int y, int thickness,
- boolean horizontal,
- boolean rightBottom) {
-
- for (int t=0; t < thickness; t++) {
- if (!rightBottom) {
- t = 0 - t; //add negative thickness so we go the other way
- //then we'll decrement instead of increment.
- }
- if (horizontal) {
- g.drawLine(x,y+t,x+5,y+t);
- g.drawLine(x+8,y+t,x+10,y+t);
- g.drawLine(x+13,y+t,x+15,y+t);
- } else {
- g.drawLine(x+t,y,x+t,y+5);
- g.drawLine(x+t,y+8,x+t,y+10);
- g.drawLine(x+t,y+13,x+t,y+15);
- }
- }
- return 18;
- }
-
- /**
- * @returns the line thickness for a border based on border type
- */
- private int getThickness(int thickness) {
- int retval=1;
- switch (thickness) {
- case HSSFCellStyle.BORDER_THIN:
- retval=2;
- break;
- case HSSFCellStyle.BORDER_MEDIUM:
- retval=3;
- break;
- case HSSFCellStyle.BORDER_THICK:
- retval=4;
- break;
- case HSSFCellStyle.BORDER_DASHED:
- retval=1;
- break;
- case HSSFCellStyle.BORDER_DASH_DOT_DOT:
- retval=1;
- break;
- case HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT:
- retval=3;
- break;
- case HSSFCellStyle.BORDER_HAIR:
- retval=1;
- break;
- default:
- retval=1;
- }
- return retval;
- }
-
-
-}
+++ /dev/null
-
-/* ====================================================================
- 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.contrib.view;
-
-import java.text.*;
-
-/**
- * This class is used to format cells into their fractional format.
- *
- * I cant be 100% sure that the same fractional value will be displayed as in
- * excel but then again it is a lossy formating mode anyway
- *
- * @author Jason Height
- * @since 15 July 2002
- */
-public class SVFractionalFormat extends Format {
- private short ONE_DIGIT = 1;
- private short TWO_DIGIT = 2;
- private short THREE_DIGIT = 3;
- private short UNITS = 4;
- private int units = 1;
- private short mode = -1;
-
- /** Constructs a new FractionalFormatter
- *
- * The formatStr defines how the number will be formatted
- * # ?/? Up to one digit
- * # ??/?? Up to two digits
- * # ???/??? Up to three digits
- * # ?/2 In halves
- * # ?/4 In quarters
- * # ?/8 In eighths
- * # ?/16 In sixteenths
- * # ?/10 In tenths
- * # ?/100 In hundredths
- */
- public SVFractionalFormat(String formatStr) {
- if ("# ?/?".equals(formatStr))
- mode = ONE_DIGIT;
- else if ("# ??/??".equals(formatStr))
- mode = TWO_DIGIT;
- else if ("# ???/???".equals(formatStr))
- mode = THREE_DIGIT;
- else if ("# ?/2".equals(formatStr)) {
- mode = UNITS;
- units = 2;
- } else if ("# ?/4".equals(formatStr)) {
- mode = UNITS;
- units = 4;
- } else if ("# ?/8".equals(formatStr)) {
- mode = UNITS;
- units = 8;
- } else if ("# ?/16".equals(formatStr)) {
- mode = UNITS;
- units = 16;
- } else if ("# ?/10".equals(formatStr)) {
- mode = UNITS;
- units = 10;
- } else if ("# ?/100".equals(formatStr)) {
- mode = UNITS;
- units = 100;
- }
- }
-
- /**
- * Returns a fractional string representation of a double to a maximum denominator size
- *
- * This code has been translated to java from the following web page.
- * http://www.codeproject.com/cpp/fraction.asp
- * Originally coded in c++ By Dean Wyant dwyant@mindspring.com
- * The code on the web page is freely available.
- *
- * @param f Description of the Parameter
- * @param MaxDen Description of the Parameter
- * @return Description of the Return Value
- */
- private String format(final double f, final int MaxDen) {
- long Whole = (long)f;
- int sign = 1;
- if (f < 0) {
- sign = -1;
- }
- double Precision = 0.00001;
- double AllowedError = Precision;
- double d = Math.abs(f);
- d -= Whole;
- double Frac = d;
- double Diff = Frac;
- long Num = 1;
- long Den = 0;
- long A = 0;
- long B = 0;
- long i = 0;
- if (Frac > Precision) {
- while (true) {
- d = 1.0 / d;
- i = (long) (d + Precision);
- d -= i;
- if (A > 0) {
- Num = i * Num + B;
- }
- Den = (long) (Num / Frac + 0.5);
- Diff = Math.abs((double) Num / Den - Frac);
- if (Den > MaxDen) {
- if (A > 0) {
- Num = A;
- Den = (long) (Num / Frac + 0.5);
- Diff = Math.abs((double) Num / Den - Frac);
- } else {
- Den = MaxDen;
- Num = 1;
- Diff = Math.abs((double) Num / Den - Frac);
- if (Diff > Frac) {
- Num = 0;
- Den = 1;
- // Keeps final check below from adding 1 and keeps Den from being 0
- Diff = Frac;
- }
- }
- break;
- }
- if ((Diff <= AllowedError) || (d < Precision)) {
- break;
- }
- Precision = AllowedError / Diff;
- // This calcualtion of Precision does not always provide results within
- // Allowed Error. It compensates for loss of significant digits that occurs.
- // It helps to round the inprecise reciprocal values to i.
- B = A;
- A = Num;
- }
- }
- if (Num == Den) {
- Whole++;
- Num = 0;
- Den = 0;
- } else if (Den == 0) {
- Num = 0;
- }
- if (sign < 0) {
- if (Whole == 0) {
- Num = -Num;
- } else {
- Whole = -Whole;
- }
- }
- return new StringBuffer().append(Whole).append(" ").append(Num).append("/").append(Den).toString();
- }
-
- /** This method formats the double in the units specified.
- * The usints could be any number but in this current implementation it is
- * halves (2), quaters (4), eigths (8) etc
- */
- private String formatUnit(double f, int units) {
- long Whole = (long)f;
- f -= Whole;
- long Num = Math.round(f * units);
-
- return new StringBuffer().append(Whole).append(" ").append(Num).append("/").append(units).toString();
- }
-
- public final String format(double val) {
- if (mode == ONE_DIGIT) {
- return format(val, 9);
- } else if (mode == TWO_DIGIT) {
- return format(val, 99);
- } else if (mode == THREE_DIGIT) {
- return format(val, 999);
- } else if (mode == UNITS) {
- return formatUnit(val , units);
- }
- throw new RuntimeException("Unexpected Case");
- }
-
- public StringBuffer format(Object obj,
- StringBuffer toAppendTo,
- FieldPosition pos) {
- if (obj instanceof Number) {
- toAppendTo.append(format(((Number)obj).doubleValue()));
- return toAppendTo;
- }
- throw new IllegalArgumentException("Can only handle Numbers");
- }
-
- public Object parseObject(String source,
- ParsePosition status) {
- //JMH TBD
- return null;
- }
-
- public Object parseObject(String source)
- throws ParseException {
- //JMH TBD
- return null;
- }
-
- public Object clone() {
- //JMH TBD
- return null;
- }
-
-
-}
+++ /dev/null
-
-/* ====================================================================
- 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.contrib.view;
-
-import java.awt.*;
-import javax.swing.*;
-import javax.swing.table.*;
-
-import org.apache.poi.hssf.usermodel.*;
-
-/**
- * This class presents the row header to the table.
- *
- *
- * @author Jason Height
- */
-public class SVRowHeader extends JList {
- /** This model simply returns an integer number up to the number of rows
- * that are present in the sheet.
- *
- */
- private class SVRowHeaderModel extends AbstractListModel {
- private HSSFSheet sheet;
-
- public SVRowHeaderModel(HSSFSheet sheet) {
- this.sheet = sheet;
- }
-
- public int getSize() {
- return sheet.getLastRowNum() + 1;
- }
- public Object getElementAt(int index) {
- return Integer.toString(index+1);
- }
- }
-
- /** Renderes the row number*/
- private class RowHeaderRenderer extends JLabel implements ListCellRenderer {
- private HSSFSheet sheet;
- private int extraHeight;
-
- RowHeaderRenderer(HSSFSheet sheet, JTable table, int extraHeight) {
- this.sheet = sheet;
- this.extraHeight = extraHeight;
- JTableHeader header = table.getTableHeader();
- setOpaque(true);
- setBorder(UIManager.getBorder("TableHeader.cellBorder"));
- setHorizontalAlignment(CENTER);
- setForeground(header.getForeground());
- setBackground(header.getBackground());
- setFont(header.getFont());
- }
-
- public Component getListCellRendererComponent( JList list,
- Object value, int index, boolean isSelected, boolean cellHasFocus) {
- Dimension d = getPreferredSize();
- HSSFRow row = sheet.getRow(index);
- int rowHeight;
- if(row == null) {
- rowHeight = (int)sheet.getDefaultRowHeightInPoints();
- } else {
- rowHeight = (int)row.getHeightInPoints();
- }
- d.height = rowHeight+extraHeight;
- setPreferredSize(d);
- setText((value == null) ? "" : value.toString());
- return this;
- }
- }
-
- public SVRowHeader(HSSFSheet sheet, JTable table, int extraHeight) {
- ListModel lm = new SVRowHeaderModel(sheet);
- this.setModel(lm);
-
- setFixedCellWidth(50);
- setCellRenderer(new RowHeaderRenderer(sheet, table, extraHeight));
- }
-}
+++ /dev/null
-
-/* ====================================================================
- 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.contrib.view;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-
-import javax.swing.*;
-import javax.swing.table.*;
-
-import org.apache.poi.hssf.usermodel.*;
-import org.apache.poi.hssf.util.HSSFColor;
-
-/**
- * Sheet Viewer Table Cell Editor -- not commented via javadoc as it
- * nearly completely consists of overridden methods.
- *
- * @author Jason Height
- */
-public class SVTableCellEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {
- private static final Color black = getAWTColor(new HSSFColor.BLACK());
- private static final Color white = getAWTColor(new HSSFColor.WHITE());
- private Hashtable colors = HSSFColor.getIndexHash();
-
-
- private HSSFWorkbook wb;
- private JTextField editor;
-
- private HSSFCell editorValue;
-
-
- public SVTableCellEditor(HSSFWorkbook wb) {
- this.wb = wb;
- this.editor = new JTextField();
- }
-
-
- /**
- * Gets the cellEditable attribute of the SVTableCellEditor object
- *
- * @return The cellEditable value
- */
- public boolean isCellEditable(java.util.EventObject e) {
- if (e instanceof MouseEvent) {
- return ((MouseEvent) e).getClickCount() >= 2;
- }
- return false;
- }
-
-
- public boolean shouldSelectCell(EventObject anEvent) {
- return true;
- }
-
-
- public boolean startCellEditing(EventObject anEvent) {
- System.out.println("Start Cell Editing");
- return true;
- }
-
-
- public boolean stopCellEditing() {
- System.out.println("Stop Cell Editing");
- fireEditingStopped();
- return true;
- }
-
-
- public void cancelCellEditing() {
- System.out.println("Cancel Cell Editing");
- fireEditingCanceled();
- }
-
-
- public void actionPerformed(ActionEvent e) {
- System.out.println("Action performed");
- stopCellEditing();
- }
-
-
- /**
- * Gets the cellEditorValue attribute of the SVTableCellEditor object
- *
- * @return The cellEditorValue value
- */
- public Object getCellEditorValue() {
- System.out.println("GetCellEditorValue");
- //JMH Look at when this method is called. Should it return a HSSFCell?
- return editor.getText();
- }
-
-
- /**
- * Gets the tableCellEditorComponent attribute of the SVTableCellEditor object
- *
- * @return The tableCellEditorComponent value
- */
- public Component getTableCellEditorComponent(JTable table, Object value,
- boolean isSelected,
- int row,
- int column) {
- System.out.println("GetTableCellEditorComponent");
- HSSFCell cell = (HSSFCell) value;
- if (cell != null) {
- HSSFCellStyle style = cell.getCellStyle();
- HSSFFont f = wb.getFontAt(style.getFontIndex());
- boolean isbold = f.getBoldweight() > HSSFFont.BOLDWEIGHT_NORMAL;
- boolean isitalics = f.getItalic();
-
- int fontstyle = Font.PLAIN;
-
- if (isbold) fontstyle = Font.BOLD;
- if (isitalics) fontstyle = fontstyle | Font.ITALIC;
-
- int fontheight = f.getFontHeightInPoints();
- if (fontheight == 9) fontheight = 10; //fix for stupid ol Windows
-
- Font font = new Font(f.getFontName(),fontstyle,fontheight);
- editor.setFont(font);
-
- if (style.getFillPattern() == HSSFCellStyle.SOLID_FOREGROUND) {
- editor.setBackground(getAWTColor(style.getFillForegroundColor(), white));
- } else editor.setBackground(white);
-
- editor.setForeground(getAWTColor(f.getColor(), black));
-
-
- //Set the value that is rendered for the cell
- switch (cell.getCellType()) {
- case HSSFCell.CELL_TYPE_BLANK:
- editor.setText("");
- break;
- case HSSFCell.CELL_TYPE_BOOLEAN:
- if (cell.getBooleanCellValue()) {
- editor.setText("true");
- } else {
- editor.setText("false");
- }
- break;
- case HSSFCell.CELL_TYPE_NUMERIC:
- editor.setText(Double.toString(cell.getNumericCellValue()));
- break;
- case HSSFCell.CELL_TYPE_STRING:
- editor.setText(cell.getRichStringCellValue().getString());
- break;
- case HSSFCell.CELL_TYPE_FORMULA:
- default:
- editor.setText("?");
- }
- switch (style.getAlignment()) {
- case HSSFCellStyle.ALIGN_LEFT:
- case HSSFCellStyle.ALIGN_JUSTIFY:
- case HSSFCellStyle.ALIGN_FILL:
- editor.setHorizontalAlignment(SwingConstants.LEFT);
- break;
- case HSSFCellStyle.ALIGN_CENTER:
- case HSSFCellStyle.ALIGN_CENTER_SELECTION:
- editor.setHorizontalAlignment(SwingConstants.CENTER);
- break;
- case HSSFCellStyle.ALIGN_GENERAL:
- case HSSFCellStyle.ALIGN_RIGHT:
- editor.setHorizontalAlignment(SwingConstants.RIGHT);
- break;
- default:
- editor.setHorizontalAlignment(SwingConstants.LEFT);
- break;
- }
-
- }
- return editor;
- }
-
- /** This method retrieves the AWT Color representation from the colour hash table
- *
- */
- private final Color getAWTColor(int index, Color deflt) {
- HSSFColor clr = (HSSFColor)colors.get(Integer.valueOf(index));
- if (clr == null) return deflt;
- return getAWTColor(clr);
- }
-
- private static final Color getAWTColor(HSSFColor clr) {
- short[] rgb = clr.getTriplet();
- return new Color(rgb[0],rgb[1],rgb[2]);
- }
-}
+++ /dev/null
-
-/* ====================================================================
- 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.contrib.view;
-
-import javax.swing.*;
-import javax.swing.table.TableCellRenderer;
-import javax.swing.border.*;
-
-import java.awt.Component;
-import java.awt.Color;
-import java.awt.Rectangle;
-
-import java.io.Serializable;
-import java.text.*;
-
-import org.apache.poi.hssf.usermodel.*;
-
-
-/**
- * Sheet Viewer Table Cell Render -- not commented via javadoc as it
- * nearly completely consists of overridden methods.
- *
- * @author Andrew C. Oliver
- */
-public class SVTableCellRenderer extends JLabel
- implements TableCellRenderer, Serializable
-{
- protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
- protected SVBorder cellBorder = new SVBorder();
-
-
- private HSSFWorkbook wb = null;
-
- /** This class holds the references to the predefined cell formats.
- */
- private class CellFormatter {
- private Format[] textFormatter;
-
- private DecimalFormat generalNumberFormat = new DecimalFormat("0");
-
- public CellFormatter() {
- textFormatter = new Format[0x31];
-
- textFormatter[0x01] = new DecimalFormat("0");
- textFormatter[0x02] = new DecimalFormat("0.00");
- textFormatter[0x03] = new DecimalFormat("#,##0");
- textFormatter[0x04] = new DecimalFormat("#,##0.00");
- textFormatter[0x05] = new DecimalFormat("$#,##0;$#,##0");
- textFormatter[0x06] = new DecimalFormat("$#,##0;$#,##0");
- textFormatter[0x07] = new DecimalFormat("$#,##0.00;$#,##0.00");
- textFormatter[0x08] = new DecimalFormat("$#,##0.00;$#,##0.00");
- textFormatter[0x09] = new DecimalFormat("0%");
- textFormatter[0x0A] = new DecimalFormat("0.00%");
- textFormatter[0x0B] = new DecimalFormat("0.00E0");
- textFormatter[0x0C] = new SVFractionalFormat("# ?/?");
- textFormatter[0x0D] = new SVFractionalFormat("# ??/??");
- textFormatter[0x0E] = new SimpleDateFormat("M/d/yy");
- textFormatter[0x0F] = new SimpleDateFormat("d-MMM-yy");
- textFormatter[0x10] = new SimpleDateFormat("d-MMM");
- textFormatter[0x11] = new SimpleDateFormat("MMM-yy");
- textFormatter[0x12] = new SimpleDateFormat("h:mm a");
- textFormatter[0x13] = new SimpleDateFormat("h:mm:ss a");
- textFormatter[0x14] = new SimpleDateFormat("h:mm");
- textFormatter[0x15] = new SimpleDateFormat("h:mm:ss");
- textFormatter[0x16] = new SimpleDateFormat("M/d/yy h:mm");
- // 0x17 - 0x24 reserved for international and undocumented 0x25, "(#,##0_);(#,##0)"
- //start at 0x26
- //jmh need to do colour
- //"(#,##0_);[Red](#,##0)"
- textFormatter[0x26] = new DecimalFormat("#,##0;#,##0");
- //jmh need to do colour
- //(#,##0.00_);(#,##0.00)
- textFormatter[0x27] = new DecimalFormat("#,##0.00;#,##0.00");
- textFormatter[0x28] = new DecimalFormat("#,##0.00;#,##0.00");
-//?? textFormatter[0x29] = new DecimalFormat("_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)");
-//?? textFormatter[0x2A] = new DecimalFormat("_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)");
-//?? textFormatter[0x2B] = new DecimalFormat("_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)");
-//?? textFormatter[0x2C] = new DecimalFormat("_($*#,##0.00_);_($*(#,##0.00);_($*\"-\"??_);_(@_)");
- textFormatter[0x2D] = new SimpleDateFormat("mm:ss");
-//?? textFormatter[0x2E] = new SimpleDateFormat("[h]:mm:ss");
- textFormatter[0x2F] = new SimpleDateFormat("mm:ss.0");
- textFormatter[0x30] = new DecimalFormat("##0.0E0");
- }
-
- public String format(short index, Object value) {
- if (index == 0)
- return value.toString();
- if (textFormatter[index] == null)
- throw new RuntimeException("Sorry. I cant handle the format code :"+Integer.toHexString(index));
- return textFormatter[index].format(value);
- }
-
- public String format(short index, double value) {
- if ( index <= 0 )
- return generalNumberFormat.format(value);
- if (textFormatter[index] == null)
- throw new RuntimeException("Sorry. I cant handle the format code :"+Integer.toHexString(index));
- if (textFormatter[index] instanceof DecimalFormat) {
- return ((DecimalFormat)textFormatter[index]).format(value);
- }
- if (textFormatter[index] instanceof SVFractionalFormat) {
- return ((SVFractionalFormat)textFormatter[index]).format(value);
- }
- throw new RuntimeException("Sorry. I cant handle a non decimal formatter for a decimal value :"+Integer.toHexString(index));
- }
-
- public boolean useRedColor(short index, double value) {
- return (((index == 0x06)||(index == 0x08)||(index == 0x26) || (index == 0x27)) && (value < 0));
- }
- }
-
- private final CellFormatter cellFormatter = new CellFormatter();
-
- public SVTableCellRenderer(HSSFWorkbook wb) {
- super();
- setOpaque(true);
- setBorder(noFocusBorder);
- this.wb = wb;
- }
-
- public Component getTableCellRendererComponent(JTable table, Object value,
- boolean isSelected, boolean hasFocus, int row, int column) {
- boolean isBorderSet = false;
-
- //If the JTables default cell renderer has been setup correctly the
- //value will be the HSSFCell that we are trying to render
- HSSFCell c = (HSSFCell)value;
-
- if (c != null) {
- HSSFCellStyle s = c.getCellStyle();
- HSSFFont f = wb.getFontAt(s.getFontIndex());
- setFont(SVTableUtils.makeFont(f));
-
- if (s.getFillPattern() == HSSFCellStyle.SOLID_FOREGROUND) {
- setBackground(SVTableUtils.getAWTColor(s.getFillForegroundColor(), SVTableUtils.white));
- } else setBackground(SVTableUtils.white);
-
- setForeground(SVTableUtils.getAWTColor(f.getColor(), SVTableUtils.black));
-
- cellBorder.setBorder(SVTableUtils.getAWTColor(s.getTopBorderColor(), SVTableUtils.black),
- SVTableUtils.getAWTColor(s.getRightBorderColor(), SVTableUtils.black),
- SVTableUtils.getAWTColor(s.getBottomBorderColor(), SVTableUtils.black),
- SVTableUtils.getAWTColor(s.getLeftBorderColor(), SVTableUtils.black),
- s.getBorderTop(), s.getBorderRight(),
- s.getBorderBottom(), s.getBorderLeft(),
- hasFocus);
- setBorder(cellBorder);
- isBorderSet=true;
-
- //Set the value that is rendered for the cell
- switch (c.getCellType()) {
- case HSSFCell.CELL_TYPE_BLANK:
- setValue("");
- break;
- case HSSFCell.CELL_TYPE_BOOLEAN:
- if (c.getBooleanCellValue()) {
- setValue("true");
- } else {
- setValue("false");
- }
- break;
- case HSSFCell.CELL_TYPE_NUMERIC:
- short format = s.getDataFormat();
- double numericValue = c.getNumericCellValue();
- if (cellFormatter.useRedColor(format, numericValue))
- setForeground(Color.red);
- else setForeground(null);
- setValue(cellFormatter.format(format, c.getNumericCellValue()));
- break;
- case HSSFCell.CELL_TYPE_STRING:
- setValue(c.getRichStringCellValue().getString());
- break;
- case HSSFCell.CELL_TYPE_FORMULA:
- default:
- setValue("?");
- }
- //Set the text alignment of the cell
- switch (s.getAlignment()) {
- case HSSFCellStyle.ALIGN_LEFT:
- case HSSFCellStyle.ALIGN_JUSTIFY:
- case HSSFCellStyle.ALIGN_FILL:
- setHorizontalAlignment(SwingConstants.LEFT);
- break;
- case HSSFCellStyle.ALIGN_CENTER:
- case HSSFCellStyle.ALIGN_CENTER_SELECTION:
- setHorizontalAlignment(SwingConstants.CENTER);
- break;
- case HSSFCellStyle.ALIGN_GENERAL:
- case HSSFCellStyle.ALIGN_RIGHT:
- setHorizontalAlignment(SwingConstants.RIGHT);
- break;
- default:
- setHorizontalAlignment(SwingConstants.LEFT);
- break;
- }
- } else {
- setValue("");
- setBackground(SVTableUtils.white);
- }
-
-
- if (hasFocus) {
- if (!isBorderSet) {
- //This is the border to paint when there is no border
- //and the cell has focus
- cellBorder.setBorder(SVTableUtils.black,
- SVTableUtils.black,
- SVTableUtils.black,
- SVTableUtils.black,
- HSSFCellStyle.BORDER_NONE,
- HSSFCellStyle.BORDER_NONE,
- HSSFCellStyle.BORDER_NONE,
- HSSFCellStyle.BORDER_NONE,
- isSelected);
- setBorder(cellBorder);
- }
- if (table.isCellEditable(row, column)) {
- setForeground( UIManager.getColor("Table.focusCellForeground") );
- setBackground( UIManager.getColor("Table.focusCellBackground") );
- }
- } else if (!isBorderSet) {
- setBorder(noFocusBorder);
- }
-
- // ---- begin optimization to avoid painting background ----
- Color back = getBackground();
- boolean colorMatch = (back != null) && ( back.equals(table.getBackground()) ) && table.isOpaque();
- setOpaque(!colorMatch);
- // ---- end optimization to aviod painting background ----
- return this;
- }
-
- public void validate() {}
-
- public void revalidate() {}
-
- public void repaint(long tm, int x, int y, int width, int height) {}
-
- public void repaint(Rectangle r) { }
-
- protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
- // Strings get interned...
- if (propertyName=="text") {
- super.firePropertyChange(propertyName, oldValue, newValue);
- }
- }
-
- public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { }
-
- /**
- * Sets the string to either the value or "" if the value is null.
- *
- */
- protected void setValue(Object value) {
- setText((value == null) ? "" : value.toString());
- }
-}
+++ /dev/null
-
-/* ====================================================================
- 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.contrib.view;
-
-import java.util.Iterator;
-import javax.swing.table.*;
-
-import org.apache.poi.hssf.usermodel.HSSFRow;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFCell;
-
-/**
- * Sheet Viewer Table Model - The model for the Sheet Viewer just overrides things.
- * @author Andrew C. Oliver
- */
-
-public class SVTableModel extends AbstractTableModel {
- private HSSFSheet st = null;
- int maxcol = 0;
-
- public SVTableModel(HSSFSheet st, int maxcol) {
- this.st = st;
- this.maxcol=maxcol;
- }
-
- public SVTableModel(HSSFSheet st) {
- this.st = st;
- Iterator i = st.rowIterator();
-
- while (i.hasNext()) {
- HSSFRow row = (HSSFRow)i.next();
- if (maxcol < (row.getLastCellNum()+1)) {
- this.maxcol = row.getLastCellNum();
- }
- }
- }
-
-
- public int getColumnCount() {
- return this.maxcol+1;
- }
- public Object getValueAt(int row, int col) {
- HSSFRow r = st.getRow(row);
- HSSFCell c = null;
- if (r != null) {
- c = r.getCell(col);
- }
- return c;
- }
- public int getRowCount() {
- return st.getLastRowNum() + 1;
- }
-
- public Class getColumnClass(int c) {
- return HSSFCell.class;
- }
-
- public boolean isCellEditable(int rowIndex, int columnIndex) {
- return true;
- }
-
- public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
- if (aValue != null)
- System.out.println("SVTableModel.setValueAt. value type = "+aValue.getClass().getName());
- else System.out.println("SVTableModel.setValueAt. value type = null");
- }
-
-
-}
+++ /dev/null
-
-/* ====================================================================
- 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.contrib.view;
-
-import java.util.*;
-import java.awt.*;
-import javax.swing.border.*;
-
-import org.apache.poi.hssf.usermodel.*;
-import org.apache.poi.hssf.util.*;
-
-/**
- * SVTableCell Editor and Renderer helper functions.
- *
- * @author Jason Height
- */
-public class SVTableUtils {
- private final static Hashtable colors = HSSFColor.getIndexHash();
- /** Description of the Field */
- public final static Color black = getAWTColor(new HSSFColor.BLACK());
- /** Description of the Field */
- public final static Color white = getAWTColor(new HSSFColor.WHITE());
- /** Description of the Field */
- public static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
-
-
- /**
- * Creates a new font for a specific cell style
- */
- public static Font makeFont(HSSFFont font) {
- boolean isbold = font.getBoldweight() > HSSFFont.BOLDWEIGHT_NORMAL;
- boolean isitalics = font.getItalic();
- int fontstyle = Font.PLAIN;
- if (isbold) {
- fontstyle = Font.BOLD;
- }
- if (isitalics) {
- fontstyle = fontstyle | Font.ITALIC;
- }
-
- int fontheight = font.getFontHeightInPoints();
- if (fontheight == 9) {
- //fix for stupid ol Windows
- fontheight = 10;
- }
-
- return new Font(font.getFontName(), fontstyle, fontheight);
- }
-
-
- /**
- * This method retrieves the AWT Color representation from the colour hash table
- *
- * @param index Description of the Parameter
- * @param deflt Description of the Parameter
- * @return The aWTColor value
- */
- public final static Color getAWTColor(int index, Color deflt) {
- HSSFColor clr = (HSSFColor) colors.get(Integer.valueOf(index));
- if (clr == null) {
- return deflt;
- }
- return getAWTColor(clr);
- }
-
-
- /**
- * Gets the aWTColor attribute of the SVTableUtils class
- *
- * @param clr Description of the Parameter
- * @return The aWTColor value
- */
- public final static Color getAWTColor(HSSFColor clr) {
- short[] rgb = clr.getTriplet();
- return new Color(rgb[0], rgb[1], rgb[2]);
- }
-}
+++ /dev/null
-
-/* ====================================================================
- 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.contrib.view;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.net.*;
-import java.io.*;
-import javax.swing.*;
-
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-
-/**
- * Sheet Viewer - Views XLS files via HSSF. Can be used as an applet with
- * filename="" or as a applications (pass the filename as the first parameter).
- * Or you can pass it a URL in a "url" parameter when run as an applet or just
- * that first parameter must start with http:// and it will guess its a url. I
- * only tested it as an applet though, so it probably won't work...you fix it.
- *
- * @author Andrew C. Oliver
- * @author Jason Height
- */
-public class SViewer extends JApplet {
- private SViewerPanel panel;
- boolean isStandalone = false;
- String filename = null;
-
- /**Get a parameter value*/
- public String getParameter(String key, String def) {
- return isStandalone ? System.getProperty(key, def) :
- (getParameter(key) != null ? getParameter(key) : def);
- }
-
- /**Construct the applet*/
- public SViewer() {
- }
-
- /**Initialize the applet*/
- public void init() {
- try {
- jbInit();
- }
- catch(Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- }
-
- /**Component initialization*/
- private void jbInit() throws Exception {
- InputStream i = null;
- boolean isurl = false;
- if (filename == null) filename = getParameter("filename");
-
- if (filename == null || filename.substring(0,7).equals("http://")) {
- isurl = true;
- if (filename == null) filename = getParameter("url");
- i = getXLSFromURL(filename);
- }
-
- HSSFWorkbook wb = null;
- if (isurl) {
- wb = constructWorkbook(i);
- } else {
- wb = constructWorkbook(filename);
- }
- panel = new SViewerPanel(wb, false);
- getContentPane().setLayout(new BorderLayout());
- getContentPane().add(panel, BorderLayout.CENTER);
- }
-
- private HSSFWorkbook constructWorkbook(String filename) throws FileNotFoundException, IOException {
- HSSFWorkbook wb = null;
- FileInputStream in = new FileInputStream(filename);
- wb = new HSSFWorkbook(in);
- in.close();
- return wb;
- }
-
- private HSSFWorkbook constructWorkbook(InputStream in) throws IOException {
- HSSFWorkbook wb = null;
-
- wb = new HSSFWorkbook(in);
- in.close();
- return wb;
- }
-
- /**Start the applet*/
- public void start() {
- }
- /**Stop the applet*/
- public void stop() {
- }
- /**Destroy the applet*/
- public void destroy() {
- }
- /**Get Applet information*/
- public String getAppletInfo() {
- return "Applet Information";
- }
- /**Get parameter info*/
- public String[][] getParameterInfo() {
- return null;
- }
-
- /**
- * opens a url and returns an inputstream
- *
- */
- private InputStream getXLSFromURL(String urlstring) throws MalformedURLException, IOException {
- URL url = new URL(urlstring);
- URLConnection uc = url.openConnection();
- String field = uc.getHeaderField(0);
- for (int i=0;field != null; i++) {
- System.out.println(field);
- field = uc.getHeaderField(i);
- }
- BufferedInputStream is = new BufferedInputStream(uc.getInputStream());
- return is;
- }
-
-
- /**Main method*/
- public static void main(String[] args) {
- if(args.length < 1) {
- throw new IllegalArgumentException("A filename to view must be supplied as the first argument, but none was given");
- }
-
- SViewer applet = new SViewer();
- applet.isStandalone = true;
- applet.filename = args[0];
- Frame frame;
- frame = new Frame() {
- protected void processWindowEvent(WindowEvent e) {
- super.processWindowEvent(e);
- if (e.getID() == WindowEvent.WINDOW_CLOSING) {
- System.exit(0);
- }
- }
- public synchronized void setTitle(String title) {
- super.setTitle(title);
- enableEvents(AWTEvent.WINDOW_EVENT_MASK);
- }
- };
- frame.setTitle("Applet Frame");
- frame.add(applet, BorderLayout.CENTER);
- applet.init();
- applet.start();
- frame.setSize(400,320);
- Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
- frame.setLocation((d.width - frame.getSize().width) / 2, (d.height - frame.getSize().height) / 2);
- frame.setVisible(true);
- }
-}
+++ /dev/null
-/* ====================================================================
- 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.contrib.view;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.io.*;
-import javax.swing.*;
-import javax.swing.table.*;
-
-import org.apache.poi.hssf.usermodel.*;
-
-/**
- * This class presents the sheets to the user.
- *
- *
- * @author Andrew C. Oliver
- * @author Jason Height
- */
-public class SViewerPanel extends JPanel {
- /** This field is the magic number to convert from a Character width to a
- * java pixel width.
- *
- * 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.
- *
- * This number may only work for the normal style font size of Arial size 10.
- *
- */
- private static final int magicCharFactor = 7;
- /** Reference to the wookbook that is being displayed*/
- /* package */ HSSFWorkbook wb;
- /** Reference to the tabs component*/
- /* package */ JTabbedPane sheetPane;
- /** Reference to the cell renderer that is used to render all cells*/
- private SVTableCellRenderer cellRenderer;
- /** Reference to the cell editor that is used to edit all cells.
- * Only constructed if editing is allowed
- */
- private SVTableCellEditor cellEditor;
- /** Flag indicating if editing is allowed. Otherwise the viewer is in
- * view only mode.
- */
- private boolean allowEdits;
-
- /**Construct the representation of the workbook*/
- public SViewerPanel(HSSFWorkbook wb, boolean allowEdits) {
- this.wb = wb;
- this.allowEdits = allowEdits;
-
- initialiseGui();
- }
-
- private void initialiseGui() {
- cellRenderer = new SVTableCellRenderer(this.wb);
- if (allowEdits)
- cellEditor = new SVTableCellEditor(this.wb);
-
- //Initialise the Panel
- sheetPane = new JTabbedPane(JTabbedPane.BOTTOM);
-
- if (allowEdits)
- sheetPane.addMouseListener(createTabListener());
- int sheetCount = wb.getNumberOfSheets();
- for (int i=0; i<sheetCount;i++) {
- String sheetName = wb.getSheetName(i);
- //Add the new sheet to the tabbed pane
- sheetPane.addTab(sheetName, makeSheetView(wb.getSheetAt(i)));
- }
- setLayout(new BorderLayout());
- add(sheetPane, BorderLayout.CENTER);
- }
-
- protected JComponent makeSheetView(HSSFSheet sheet) {
- JTable sheetView = new JTable(new SVTableModel(sheet));
- sheetView.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
- sheetView.setDefaultRenderer(HSSFCell.class, cellRenderer);
- if (allowEdits)
- sheetView.setDefaultEditor(HSSFCell.class, cellEditor);
- JTableHeader header = sheetView.getTableHeader();
- //Dont allow column reordering
- header.setReorderingAllowed(false);
- //Only allow column resizing if editing is allowed
- header.setResizingAllowed(allowEdits);
-
- //Set the columns the correct size
- TableColumnModel columns = sheetView.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);
- }
-
- //Set the rows to the correct size
- int rows = sheet.getPhysicalNumberOfRows();
- Insets insets = cellRenderer.getInsets();
- //Need to include the insets in the calculation of the row height to use.
- int extraHeight = insets.bottom+insets.top;
- for (int i=0; i< rows; i++) {
- HSSFRow row = sheet.getRow(i);
- if (row == null) {
- sheetView.setRowHeight(i, (int)sheet.getDefaultRowHeightInPoints()+extraHeight);
- } else {
- sheetView.setRowHeight(i, (int)row.getHeightInPoints()+extraHeight);
- }
- }
-
- //Add the row header to the sheet
- SVRowHeader rowHeader = new SVRowHeader(sheet, sheetView, extraHeight);
- JScrollPane scroll = new JScrollPane( sheetView );
- scroll.setRowHeaderView(rowHeader);
- return scroll;
- }
-
- public void paint(Graphics g) {
- //JMH I am only overriding this to get a picture of the time taken to paint
- long start = System.currentTimeMillis();
- super.paint(g);
- long elapsed = System.currentTimeMillis()-start;
- System.out.println("Paint time = "+elapsed);
- }
-
- protected MouseListener createTabListener() {
- return new TabListener();
- }
-
- /** This class defines the default MouseListener that listens to
- * mouse events in the tabbed pane
- *
- * The default is to popup a menu when the event occurs over a tab
- */
- private class TabListener implements MouseListener {
- public JPopupMenu popup;
- public TabListener() {
- popup = new JPopupMenu("Sheet");
- popup.add(createInsertSheetAction());
- popup.add(createDeleteSheetAction());
- popup.add(createRenameSheetAction());
- }
-
- protected Action createInsertSheetAction() {
- return new InsertSheetAction();
- }
-
- protected Action createDeleteSheetAction() {
- return new DeleteSheetAction();
- }
-
- protected Action createRenameSheetAction() {
- return new RenameSheetAction();
- }
-
-
- /** This method will display the popup if the mouseevent is a popup event
- * and the event occurred over a tab
- */
- protected void checkPopup(MouseEvent e) {
- if (e.isPopupTrigger()) {
- int tab = sheetPane.getUI().tabForCoordinate(sheetPane, e.getX(), e.getY());
- if (tab != -1) {
- popup.show(sheetPane, e.getX(), e.getY());
- }
- }
- }
-
- public void mouseClicked(MouseEvent e) {
- checkPopup(e);
- }
-
- public void mousePressed(MouseEvent e) {
- checkPopup(e);
- }
-
- public void mouseReleased(MouseEvent e) {
- checkPopup(e);
- }
-
- public void mouseEntered(MouseEvent e) {}
- public void mouseExited(MouseEvent e) {}
- }
-
- /** This class defines the action that is performed when the sheet is renamed*/
- private class RenameSheetAction extends AbstractAction {
- public RenameSheetAction() {
- super("Rename");
- }
-
- public void actionPerformed(ActionEvent e) {
- int tabIndex = sheetPane.getSelectedIndex();
- if (tabIndex != -1) {
- String newSheetName = JOptionPane.showInputDialog(sheetPane, "Enter a new Sheetname", "Rename Sheet", JOptionPane.QUESTION_MESSAGE);
- if (newSheetName != null) {
- wb.setSheetName(tabIndex, newSheetName);
- sheetPane.setTitleAt(tabIndex, newSheetName);
- }
- }
- }
- }
-
- /** This class defines the action that is performed when a sheet is inserted*/
- private class InsertSheetAction extends AbstractAction {
- public InsertSheetAction() {
- super("Insert");
- }
-
- public void actionPerformed(ActionEvent e) {
- //Create a new sheet then search for the sheet and make sure that the
- //sheetPane shows it.
- HSSFSheet newSheet = wb.createSheet();
- for (int i=0; i<wb.getNumberOfSheets();i++) {
- HSSFSheet sheet = wb.getSheetAt(i);
- if (newSheet == sheet) {
- sheetPane.insertTab(wb.getSheetName(i), null, makeSheetView(sheet), null, i);
- }
- }
- }
- }
-
- /** This class defines the action that is performed when the sheet is deleted*/
- private class DeleteSheetAction extends AbstractAction {
- public DeleteSheetAction() {
- super("Delete");
- }
-
- public void actionPerformed(ActionEvent e) {
- int tabIndex = sheetPane.getSelectedIndex();
- if (tabIndex != -1) {
- if (JOptionPane.showConfirmDialog(sheetPane, "Are you sure that you want to delete the selected sheet", "Delete Sheet?", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
- wb.removeSheetAt(tabIndex);
- sheetPane.remove(tabIndex);
- }
- }
- }
- }
-
- public boolean isEditable() {
- return allowEdits;
- }
-
- /**Main method*/
- public static void main(String[] args) {
- if(args.length < 1) {
- throw new IllegalArgumentException("A filename to view must be supplied as the first argument, but none was given");
- }
- try {
- FileInputStream in = new FileInputStream(args[0]);
- HSSFWorkbook wb = new HSSFWorkbook(in);
- in.close();
-
- SViewerPanel p = new SViewerPanel(wb, true);
- JFrame frame;
- frame = new JFrame() {
- protected void processWindowEvent(WindowEvent e) {
- super.processWindowEvent(e);
- if (e.getID() == WindowEvent.WINDOW_CLOSING) {
- System.exit(0);
- }
- }
- public synchronized void setTitle(String title) {
- super.setTitle(title);
- enableEvents(AWTEvent.WINDOW_EVENT_MASK);
- }
- };
- frame.setTitle("Viewer Frame");
- frame.getContentPane().add(p, BorderLayout.CENTER);
- frame.setSize(800,640);
- Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
- frame.setLocation((d.width - frame.getSize().width) / 2, (d.height - frame.getSize().height) / 2);
- frame.setVisible(true);
- } catch (IOException ex) {
- ex.printStackTrace();
- System.exit(1);
- }
- }
-}
<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>
--- /dev/null
+
+/* ====================================================================
+ 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 java.awt.*;
+
+import javax.swing.border.AbstractBorder;
+
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+
+/**
+ * This is an attempt to implement Excel style borders for the SheetViewer.
+ * Mostly just overrides stuff so the javadoc won't appear here but will
+ * appear in the generated stuff.
+ *
+ * @author Andrew C. Oliver (acoliver at apache dot org)
+ * @author Jason Height
+ */
+public class SVBorder extends AbstractBorder {
+ private Color northColor = null;
+ private Color eastColor = null;
+ private Color southColor = null;
+ private Color westColor = null;
+ private int northBorderType = HSSFCellStyle.BORDER_NONE;
+ private int eastBorderType =HSSFCellStyle.BORDER_NONE;
+ private int southBorderType = HSSFCellStyle.BORDER_NONE;
+ private int westBorderType = HSSFCellStyle.BORDER_NONE;
+ private boolean northBorder=false;
+ private boolean eastBorder=false;
+ private boolean southBorder=false;
+ private boolean westBorder=false;
+ private boolean selected = false;
+
+ public void setBorder(Color northColor, Color eastColor,
+ Color southColor, Color westColor,
+ int northBorderType, int eastBorderType,
+ int southBorderType, int westBorderType,
+ boolean selected) {
+ this.eastColor = eastColor;
+ this.southColor = southColor;
+ this.westColor = westColor;
+ this.northBorderType = northBorderType;
+ this.eastBorderType = eastBorderType;
+ this.southBorderType = southBorderType;
+ this.westBorderType = westBorderType;
+ this.northBorder=northBorderType != HSSFCellStyle.BORDER_NONE;
+ this.eastBorder=eastBorderType != HSSFCellStyle.BORDER_NONE;
+ this.southBorder=southBorderType != HSSFCellStyle.BORDER_NONE;
+ this.westBorder=westBorderType != HSSFCellStyle.BORDER_NONE;
+ this.selected = selected;
+ }
+
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height) {
+ Color oldColor = g.getColor();
+
+
+ paintSelectedBorder(g, x, y, width, height);
+ paintNormalBorders(g, x, y, width, height);
+ paintDottedBorders(g, x, y, width, height);
+ paintDashedBorders(g, x, y, width, height);
+ paintDoubleBorders(g, x, y, width, height);
+ paintDashDotDotBorders(g, x, y, width, height);
+
+
+ g.setColor(oldColor);
+ }
+
+ /**
+ * Called by paintBorder to paint the border of a selected cell.
+ * The paramaters are the Graphics object, location and dimensions of the
+ * cell.
+ */
+ private void paintSelectedBorder(Graphics g, int x, int y, int width,
+ int height) {
+ if (selected) {
+ //Need to setup thickness of 2
+ g.setColor(Color.black);
+ //paint the border
+ g.drawRect(x,y,width-1,height-1);
+
+ //paint the filled rectangle at the bottom left hand position
+ g.fillRect(x+width-5, y+height-5, 5, 5);
+ }
+ }
+
+
+ /**
+ * Called by paintBorder to paint the various versions of normal line
+ * borders for a cell.
+ */
+ private void paintNormalBorders(Graphics g, int x, int y, int width,
+ int height) {
+
+ if (northBorder &&
+ ((northBorderType == HSSFCellStyle.BORDER_THIN) ||
+ (northBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
+ (northBorderType == HSSFCellStyle.BORDER_THICK)
+ )
+ ) {
+
+ int thickness = getThickness(northBorderType);
+
+ g.setColor(northColor);
+
+ for (int k=0; k < thickness; k++) {
+ g.drawLine(x,y+k,width,y+k);
+ }
+ }
+
+ if (eastBorder &&
+ ((eastBorderType == HSSFCellStyle.BORDER_THIN) ||
+ (eastBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
+ (eastBorderType == HSSFCellStyle.BORDER_THICK)
+ )
+ ) {
+
+ int thickness = getThickness(eastBorderType);
+
+ g.setColor(eastColor);
+
+ for (int k=0; k < thickness; k++) {
+ g.drawLine(width-k,y,width-k,height);
+ }
+ }
+
+ if (southBorder &&
+ ((southBorderType == HSSFCellStyle.BORDER_THIN) ||
+ (southBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
+ (southBorderType == HSSFCellStyle.BORDER_THICK)
+ )
+ ) {
+
+ int thickness = getThickness(southBorderType);
+
+ g.setColor(southColor);
+ for (int k=0; k < thickness; k++) {
+ g.drawLine(x,height - k,width,height - k);
+ }
+ }
+
+ if (westBorder &&
+ ((westBorderType == HSSFCellStyle.BORDER_THIN) ||
+ (westBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
+ (westBorderType == HSSFCellStyle.BORDER_THICK)
+ )
+ ) {
+
+ int thickness = getThickness(westBorderType);
+
+ g.setColor(westColor);
+
+ for (int k=0; k < thickness; k++) {
+ g.drawLine(x+k,y,x+k,height);
+ }
+ }
+ }
+
+ /**
+ * Called by paintBorder to paint the dotted line
+ * borders for a cell.
+ */
+ private void paintDottedBorders(Graphics g, int x, int y, int width,
+ int height) {
+ if (northBorder &&
+ northBorderType == HSSFCellStyle.BORDER_DOTTED) {
+ int thickness = getThickness(northBorderType);
+
+ g.setColor(northColor);
+
+ for (int k=0; k < thickness; k++) {
+ for (int xc = x; xc < width; xc=xc+2) {
+ g.drawLine(xc,y+k,xc,y+k);
+ }
+ }
+ }
+
+ if (eastBorder &&
+ eastBorderType == HSSFCellStyle.BORDER_DOTTED
+ ) {
+
+ int thickness = getThickness(eastBorderType);
+ thickness++; //need for dotted borders to show up east
+
+ g.setColor(eastColor);
+
+ for (int k=0; k < thickness; k++) {
+ for (int yc=y;yc < height; yc=yc+2) {
+ g.drawLine(width-k,yc,width-k,yc);
+ }
+ }
+ }
+
+ if (southBorder &&
+ southBorderType == HSSFCellStyle.BORDER_DOTTED
+ ) {
+
+ int thickness = getThickness(southBorderType);
+ thickness++;
+ g.setColor(southColor);
+ for (int k=0; k < thickness; k++) {
+ for (int xc = x; xc < width; xc=xc+2) {
+ g.drawLine(xc,height-k,xc,height-k);
+ }
+ }
+ }
+
+ if (westBorder &&
+ westBorderType == HSSFCellStyle.BORDER_DOTTED
+ ) {
+
+ int thickness = getThickness(westBorderType);
+// thickness++;
+
+ g.setColor(westColor);
+
+ for (int k=0; k < thickness; k++) {
+ for (int yc=y;yc < height; yc=yc+2) {
+ g.drawLine(x+k,yc,x+k,yc);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called by paintBorder to paint the various versions of dotted line
+ * borders for a cell.
+ */
+ private void paintDashedBorders(Graphics g, int x, int y, int width,
+ int height) {
+ if (northBorder &&
+ ((northBorderType == HSSFCellStyle.BORDER_DASHED) ||
+ (northBorderType == HSSFCellStyle.BORDER_HAIR))
+ ) {
+ int thickness = getThickness(northBorderType);
+
+ int dashlength = 1;
+
+ if (northBorderType == HSSFCellStyle.BORDER_DASHED)
+ dashlength = 2;
+
+ g.setColor(northColor);
+
+ for (int k=0; k < thickness; k++) {
+ for (int xc = x; xc < width; xc=xc+5) {
+ g.drawLine(xc,y+k,xc+dashlength,y+k);
+ }
+ }
+ }
+
+ if (eastBorder &&
+ ((eastBorderType == HSSFCellStyle.BORDER_DASHED) ||
+ (eastBorderType == HSSFCellStyle.BORDER_HAIR))
+ ) {
+
+ int thickness = getThickness(eastBorderType);
+ thickness++; //need for dotted borders to show up east
+
+
+ int dashlength = 1;
+
+ if (eastBorderType == HSSFCellStyle.BORDER_DASHED)
+ dashlength = 2;
+
+ g.setColor(eastColor);
+
+ for (int k=0; k < thickness; k++) {
+ for (int yc=y;yc < height; yc=yc+5) {
+ g.drawLine(width-k,yc,width-k,yc+dashlength);
+ }
+ }
+ }
+
+ if (southBorder &&
+ ((southBorderType == HSSFCellStyle.BORDER_DASHED) ||
+ (southBorderType == HSSFCellStyle.BORDER_HAIR))
+ ) {
+
+ int thickness = getThickness(southBorderType);
+ thickness++;
+
+ int dashlength = 1;
+
+ if (southBorderType == HSSFCellStyle.BORDER_DASHED)
+ dashlength = 2;
+
+ g.setColor(southColor);
+ for (int k=0; k < thickness; k++) {
+ for (int xc = x; xc < width; xc=xc+5) {
+ g.drawLine(xc,height-k,xc+dashlength,height-k);
+ }
+ }
+ }
+
+ if (westBorder &&
+ ((westBorderType == HSSFCellStyle.BORDER_DASHED) ||
+ (westBorderType == HSSFCellStyle.BORDER_HAIR))
+ ) {
+
+ int thickness = getThickness(westBorderType);
+// thickness++;
+
+ int dashlength = 1;
+
+ if (westBorderType == HSSFCellStyle.BORDER_DASHED)
+ dashlength = 2;
+
+ g.setColor(westColor);
+
+ for (int k=0; k < thickness; k++) {
+ for (int yc=y;yc < height; yc=yc+5) {
+ g.drawLine(x+k,yc,x+k,yc+dashlength);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called by paintBorder to paint the double line
+ * borders for a cell.
+ */
+ private void paintDoubleBorders(Graphics g, int x, int y, int width,
+ int height) {
+ if (northBorder &&
+ northBorderType == HSSFCellStyle.BORDER_DOUBLE) {
+
+ g.setColor(northColor);
+
+ int leftx=x;
+ int rightx=width;
+
+ // if there are borders on the west or east then
+ // the second line shouldn't cross them
+ if (westBorder)
+ leftx = x+3;
+
+ if (eastBorder)
+ rightx = width-3;
+
+ g.drawLine(x,y,width,y);
+ g.drawLine(leftx,y+2,rightx,y+2);
+ }
+
+ if (eastBorder &&
+ eastBorderType == HSSFCellStyle.BORDER_DOUBLE
+ ) {
+
+ int thickness = getThickness(eastBorderType);
+ thickness++; //need for dotted borders to show up east
+
+ g.setColor(eastColor);
+
+ int topy=y;
+ int bottomy=height;
+
+ if (northBorder)
+ topy=y+3;
+
+ if (southBorder)
+ bottomy=height-3;
+
+ g.drawLine(width-1,y,width-1,height);
+ g.drawLine(width-3,topy,width-3,bottomy);
+ }
+
+ if (southBorder &&
+ southBorderType == HSSFCellStyle.BORDER_DOUBLE
+ ) {
+
+ g.setColor(southColor);
+
+ int leftx=y;
+ int rightx=width;
+
+ if (westBorder)
+ leftx=x+3;
+
+ if (eastBorder)
+ rightx=width-3;
+
+
+ g.drawLine(x,height - 1,width,height - 1);
+ g.drawLine(leftx,height - 3,rightx,height - 3);
+ }
+
+ if (westBorder &&
+ westBorderType == HSSFCellStyle.BORDER_DOUBLE
+ ) {
+
+ int thickness = getThickness(westBorderType);
+// thickness++;
+
+ g.setColor(westColor);
+
+ int topy=y;
+ int bottomy=height-3;
+
+ if (northBorder)
+ topy=y+2;
+
+ if (southBorder)
+ bottomy=height-3;
+
+ g.drawLine(x,y,x,height);
+ g.drawLine(x+2,topy,x+2,bottomy);
+ }
+ }
+
+ /**
+ * Called by paintBorder to paint the various versions of dash dot dot line
+ * borders for a cell.
+ */
+ private void paintDashDotDotBorders(Graphics g, int x, int y, int width,
+ int height) {
+ if (northBorder &&
+ ((northBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
+ (northBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
+ ) {
+ int thickness = getThickness(northBorderType);
+
+ g.setColor(northColor);
+ for (int l=x; l < width;) {
+ l=l+drawDashDotDot(g, l, y, thickness, true, true);
+ }
+
+ }
+
+ if (eastBorder &&
+ ((eastBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
+ (eastBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
+ ) {
+
+ int thickness = getThickness(eastBorderType);
+
+ g.setColor(eastColor);
+
+ for (int l=y;l < height;) {
+ //System.err.println("drawing east");
+ l=l+drawDashDotDot(g,width-1,l,thickness,false,false);
+ }
+ }
+
+ if (southBorder &&
+ ((southBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
+ (southBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
+ ) {
+
+ int thickness = getThickness(southBorderType);
+
+ g.setColor(southColor);
+
+ for (int l=x; l < width;) {
+ //System.err.println("drawing south");
+ l=l+drawDashDotDot(g, l, height-1, thickness, true, false);
+ }
+ }
+
+ if (westBorder &&
+ ((westBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
+ (westBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
+ ) {
+
+ int thickness = getThickness(westBorderType);
+
+ g.setColor(westColor);
+
+ for (int l=y;l < height;) {
+ //System.err.println("drawing west");
+ l=l+drawDashDotDot(g,x,l,thickness,false,true);
+ }
+
+ }
+ }
+
+ /**
+ * Draws one dash dot dot horizontally or vertically with thickness drawn
+ * incrementally to either the right or left.
+ *
+ * @param g graphics object for drawing with
+ * @param x the x origin of the line
+ * @param y the y origin of the line
+ * @param thickness the thickness of the line
+ * @param horizontal or vertical (true for horizontal)
+ * @param right/bottom or left/top thickness (true for right or top),
+ * if true then the x or y origin will be incremented to provide
+ * thickness, if false, they'll be decremented. For vertical
+ * borders, x is incremented or decremented, for horizontal its y.
+ * Just set to true for north and west, and false for east and
+ * south.
+ * @returns length - returns the length of the line.
+ */
+ private int drawDashDotDot(Graphics g,int x, int y, int thickness,
+ boolean horizontal,
+ boolean rightBottom) {
+
+ for (int t=0; t < thickness; t++) {
+ if (!rightBottom) {
+ t = 0 - t; //add negative thickness so we go the other way
+ //then we'll decrement instead of increment.
+ }
+ if (horizontal) {
+ g.drawLine(x,y+t,x+5,y+t);
+ g.drawLine(x+8,y+t,x+10,y+t);
+ g.drawLine(x+13,y+t,x+15,y+t);
+ } else {
+ g.drawLine(x+t,y,x+t,y+5);
+ g.drawLine(x+t,y+8,x+t,y+10);
+ g.drawLine(x+t,y+13,x+t,y+15);
+ }
+ }
+ return 18;
+ }
+
+ /**
+ * @returns the line thickness for a border based on border type
+ */
+ private int getThickness(int thickness) {
+ int retval=1;
+ switch (thickness) {
+ case HSSFCellStyle.BORDER_THIN:
+ retval=2;
+ break;
+ case HSSFCellStyle.BORDER_MEDIUM:
+ retval=3;
+ break;
+ case HSSFCellStyle.BORDER_THICK:
+ retval=4;
+ break;
+ case HSSFCellStyle.BORDER_DASHED:
+ retval=1;
+ break;
+ case HSSFCellStyle.BORDER_DASH_DOT_DOT:
+ retval=1;
+ break;
+ case HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT:
+ retval=3;
+ break;
+ case HSSFCellStyle.BORDER_HAIR:
+ retval=1;
+ break;
+ default:
+ retval=1;
+ }
+ return retval;
+ }
+
+
+}
--- /dev/null
+
+/* ====================================================================
+ 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 java.text.*;
+
+/**
+ * This class is used to format cells into their fractional format.
+ *
+ * I cant be 100% sure that the same fractional value will be displayed as in
+ * excel but then again it is a lossy formating mode anyway
+ *
+ * @author Jason Height
+ * @since 15 July 2002
+ */
+public class SVFractionalFormat extends Format {
+ private short ONE_DIGIT = 1;
+ private short TWO_DIGIT = 2;
+ private short THREE_DIGIT = 3;
+ private short UNITS = 4;
+ private int units = 1;
+ private short mode = -1;
+
+ /** Constructs a new FractionalFormatter
+ *
+ * The formatStr defines how the number will be formatted
+ * # ?/? Up to one digit
+ * # ??/?? Up to two digits
+ * # ???/??? Up to three digits
+ * # ?/2 In halves
+ * # ?/4 In quarters
+ * # ?/8 In eighths
+ * # ?/16 In sixteenths
+ * # ?/10 In tenths
+ * # ?/100 In hundredths
+ */
+ public SVFractionalFormat(String formatStr) {
+ if ("# ?/?".equals(formatStr))
+ mode = ONE_DIGIT;
+ else if ("# ??/??".equals(formatStr))
+ mode = TWO_DIGIT;
+ else if ("# ???/???".equals(formatStr))
+ mode = THREE_DIGIT;
+ else if ("# ?/2".equals(formatStr)) {
+ mode = UNITS;
+ units = 2;
+ } else if ("# ?/4".equals(formatStr)) {
+ mode = UNITS;
+ units = 4;
+ } else if ("# ?/8".equals(formatStr)) {
+ mode = UNITS;
+ units = 8;
+ } else if ("# ?/16".equals(formatStr)) {
+ mode = UNITS;
+ units = 16;
+ } else if ("# ?/10".equals(formatStr)) {
+ mode = UNITS;
+ units = 10;
+ } else if ("# ?/100".equals(formatStr)) {
+ mode = UNITS;
+ units = 100;
+ }
+ }
+
+ /**
+ * Returns a fractional string representation of a double to a maximum denominator size
+ *
+ * This code has been translated to java from the following web page.
+ * http://www.codeproject.com/cpp/fraction.asp
+ * Originally coded in c++ By Dean Wyant dwyant@mindspring.com
+ * The code on the web page is freely available.
+ *
+ * @param f Description of the Parameter
+ * @param MaxDen Description of the Parameter
+ * @return Description of the Return Value
+ */
+ private String format(final double f, final int MaxDen) {
+ long Whole = (long)f;
+ int sign = 1;
+ if (f < 0) {
+ sign = -1;
+ }
+ double Precision = 0.00001;
+ double AllowedError = Precision;
+ double d = Math.abs(f);
+ d -= Whole;
+ double Frac = d;
+ double Diff = Frac;
+ long Num = 1;
+ long Den = 0;
+ long A = 0;
+ long B = 0;
+ long i = 0;
+ if (Frac > Precision) {
+ while (true) {
+ d = 1.0 / d;
+ i = (long) (d + Precision);
+ d -= i;
+ if (A > 0) {
+ Num = i * Num + B;
+ }
+ Den = (long) (Num / Frac + 0.5);
+ Diff = Math.abs((double) Num / Den - Frac);
+ if (Den > MaxDen) {
+ if (A > 0) {
+ Num = A;
+ Den = (long) (Num / Frac + 0.5);
+ Diff = Math.abs((double) Num / Den - Frac);
+ } else {
+ Den = MaxDen;
+ Num = 1;
+ Diff = Math.abs((double) Num / Den - Frac);
+ if (Diff > Frac) {
+ Num = 0;
+ Den = 1;
+ // Keeps final check below from adding 1 and keeps Den from being 0
+ Diff = Frac;
+ }
+ }
+ break;
+ }
+ if ((Diff <= AllowedError) || (d < Precision)) {
+ break;
+ }
+ Precision = AllowedError / Diff;
+ // This calcualtion of Precision does not always provide results within
+ // Allowed Error. It compensates for loss of significant digits that occurs.
+ // It helps to round the inprecise reciprocal values to i.
+ B = A;
+ A = Num;
+ }
+ }
+ if (Num == Den) {
+ Whole++;
+ Num = 0;
+ Den = 0;
+ } else if (Den == 0) {
+ Num = 0;
+ }
+ if (sign < 0) {
+ if (Whole == 0) {
+ Num = -Num;
+ } else {
+ Whole = -Whole;
+ }
+ }
+ return new StringBuffer().append(Whole).append(" ").append(Num).append("/").append(Den).toString();
+ }
+
+ /** This method formats the double in the units specified.
+ * The usints could be any number but in this current implementation it is
+ * halves (2), quaters (4), eigths (8) etc
+ */
+ private String formatUnit(double f, int units) {
+ long Whole = (long)f;
+ f -= Whole;
+ long Num = Math.round(f * units);
+
+ return new StringBuffer().append(Whole).append(" ").append(Num).append("/").append(units).toString();
+ }
+
+ public final String format(double val) {
+ if (mode == ONE_DIGIT) {
+ return format(val, 9);
+ } else if (mode == TWO_DIGIT) {
+ return format(val, 99);
+ } else if (mode == THREE_DIGIT) {
+ return format(val, 999);
+ } else if (mode == UNITS) {
+ return formatUnit(val , units);
+ }
+ throw new RuntimeException("Unexpected Case");
+ }
+
+ public StringBuffer format(Object obj,
+ StringBuffer toAppendTo,
+ FieldPosition pos) {
+ if (obj instanceof Number) {
+ toAppendTo.append(format(((Number)obj).doubleValue()));
+ return toAppendTo;
+ }
+ throw new IllegalArgumentException("Can only handle Numbers");
+ }
+
+ public Object parseObject(String source,
+ ParsePosition status) {
+ //JMH TBD
+ return null;
+ }
+
+ public Object parseObject(String source)
+ throws ParseException {
+ //JMH TBD
+ return null;
+ }
+
+ public Object clone() {
+ //JMH TBD
+ return null;
+ }
+
+
+}
--- /dev/null
+
+/* ====================================================================
+ 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 java.awt.*;
+import javax.swing.*;
+import javax.swing.table.*;
+
+import org.apache.poi.hssf.usermodel.*;
+
+/**
+ * This class presents the row header to the table.
+ *
+ *
+ * @author Jason Height
+ */
+public class SVRowHeader extends JList {
+ /** This model simply returns an integer number up to the number of rows
+ * that are present in the sheet.
+ *
+ */
+ private class SVRowHeaderModel extends AbstractListModel {
+ private HSSFSheet sheet;
+
+ public SVRowHeaderModel(HSSFSheet sheet) {
+ this.sheet = sheet;
+ }
+
+ public int getSize() {
+ return sheet.getLastRowNum() + 1;
+ }
+ public Object getElementAt(int index) {
+ return Integer.toString(index+1);
+ }
+ }
+
+ /** Renderes the row number*/
+ private class RowHeaderRenderer extends JLabel implements ListCellRenderer {
+ private HSSFSheet sheet;
+ private int extraHeight;
+
+ RowHeaderRenderer(HSSFSheet sheet, JTable table, int extraHeight) {
+ this.sheet = sheet;
+ this.extraHeight = extraHeight;
+ JTableHeader header = table.getTableHeader();
+ setOpaque(true);
+ setBorder(UIManager.getBorder("TableHeader.cellBorder"));
+ setHorizontalAlignment(CENTER);
+ setForeground(header.getForeground());
+ setBackground(header.getBackground());
+ setFont(header.getFont());
+ }
+
+ public Component getListCellRendererComponent( JList list,
+ Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ Dimension d = getPreferredSize();
+ HSSFRow row = sheet.getRow(index);
+ int rowHeight;
+ if(row == null) {
+ rowHeight = (int)sheet.getDefaultRowHeightInPoints();
+ } else {
+ rowHeight = (int)row.getHeightInPoints();
+ }
+ d.height = rowHeight+extraHeight;
+ setPreferredSize(d);
+ setText((value == null) ? "" : value.toString());
+ return this;
+ }
+ }
+
+ public SVRowHeader(HSSFSheet sheet, JTable table, int extraHeight) {
+ ListModel lm = new SVRowHeaderModel(sheet);
+ this.setModel(lm);
+
+ setFixedCellWidth(50);
+ setCellRenderer(new RowHeaderRenderer(sheet, table, extraHeight));
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+
+/* ====================================================================
+ 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 java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+
+import javax.swing.*;
+import javax.swing.table.*;
+
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.hssf.util.HSSFColor;
+
+/**
+ * Sheet Viewer Table Cell Editor -- not commented via javadoc as it
+ * nearly completely consists of overridden methods.
+ *
+ * @author Jason Height
+ */
+public class SVTableCellEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {
+ private static final Color black = getAWTColor(new HSSFColor.BLACK());
+ private static final Color white = getAWTColor(new HSSFColor.WHITE());
+ private Hashtable colors = HSSFColor.getIndexHash();
+
+
+ private HSSFWorkbook wb;
+ private JTextField editor;
+
+ private HSSFCell editorValue;
+
+
+ public SVTableCellEditor(HSSFWorkbook wb) {
+ this.wb = wb;
+ this.editor = new JTextField();
+ }
+
+
+ /**
+ * Gets the cellEditable attribute of the SVTableCellEditor object
+ *
+ * @return The cellEditable value
+ */
+ public boolean isCellEditable(java.util.EventObject e) {
+ if (e instanceof MouseEvent) {
+ return ((MouseEvent) e).getClickCount() >= 2;
+ }
+ return false;
+ }
+
+
+ public boolean shouldSelectCell(EventObject anEvent) {
+ return true;
+ }
+
+
+ public boolean startCellEditing(EventObject anEvent) {
+ System.out.println("Start Cell Editing");
+ return true;
+ }
+
+
+ public boolean stopCellEditing() {
+ System.out.println("Stop Cell Editing");
+ fireEditingStopped();
+ return true;
+ }
+
+
+ public void cancelCellEditing() {
+ System.out.println("Cancel Cell Editing");
+ fireEditingCanceled();
+ }
+
+
+ public void actionPerformed(ActionEvent e) {
+ System.out.println("Action performed");
+ stopCellEditing();
+ }
+
+
+ /**
+ * Gets the cellEditorValue attribute of the SVTableCellEditor object
+ *
+ * @return The cellEditorValue value
+ */
+ public Object getCellEditorValue() {
+ System.out.println("GetCellEditorValue");
+ //JMH Look at when this method is called. Should it return a HSSFCell?
+ return editor.getText();
+ }
+
+
+ /**
+ * Gets the tableCellEditorComponent attribute of the SVTableCellEditor object
+ *
+ * @return The tableCellEditorComponent value
+ */
+ public Component getTableCellEditorComponent(JTable table, Object value,
+ boolean isSelected,
+ int row,
+ int column) {
+ System.out.println("GetTableCellEditorComponent");
+ HSSFCell cell = (HSSFCell) value;
+ if (cell != null) {
+ HSSFCellStyle style = cell.getCellStyle();
+ HSSFFont f = wb.getFontAt(style.getFontIndex());
+ boolean isbold = f.getBoldweight() > HSSFFont.BOLDWEIGHT_NORMAL;
+ boolean isitalics = f.getItalic();
+
+ int fontstyle = Font.PLAIN;
+
+ if (isbold) fontstyle = Font.BOLD;
+ if (isitalics) fontstyle = fontstyle | Font.ITALIC;
+
+ int fontheight = f.getFontHeightInPoints();
+ if (fontheight == 9) fontheight = 10; //fix for stupid ol Windows
+
+ Font font = new Font(f.getFontName(),fontstyle,fontheight);
+ editor.setFont(font);
+
+ if (style.getFillPattern() == HSSFCellStyle.SOLID_FOREGROUND) {
+ editor.setBackground(getAWTColor(style.getFillForegroundColor(), white));
+ } else editor.setBackground(white);
+
+ editor.setForeground(getAWTColor(f.getColor(), black));
+
+
+ //Set the value that is rendered for the cell
+ switch (cell.getCellType()) {
+ case HSSFCell.CELL_TYPE_BLANK:
+ editor.setText("");
+ break;
+ case HSSFCell.CELL_TYPE_BOOLEAN:
+ if (cell.getBooleanCellValue()) {
+ editor.setText("true");
+ } else {
+ editor.setText("false");
+ }
+ break;
+ case HSSFCell.CELL_TYPE_NUMERIC:
+ editor.setText(Double.toString(cell.getNumericCellValue()));
+ break;
+ case HSSFCell.CELL_TYPE_STRING:
+ editor.setText(cell.getRichStringCellValue().getString());
+ break;
+ case HSSFCell.CELL_TYPE_FORMULA:
+ default:
+ editor.setText("?");
+ }
+ switch (style.getAlignment()) {
+ case HSSFCellStyle.ALIGN_LEFT:
+ case HSSFCellStyle.ALIGN_JUSTIFY:
+ case HSSFCellStyle.ALIGN_FILL:
+ editor.setHorizontalAlignment(SwingConstants.LEFT);
+ break;
+ case HSSFCellStyle.ALIGN_CENTER:
+ case HSSFCellStyle.ALIGN_CENTER_SELECTION:
+ editor.setHorizontalAlignment(SwingConstants.CENTER);
+ break;
+ case HSSFCellStyle.ALIGN_GENERAL:
+ case HSSFCellStyle.ALIGN_RIGHT:
+ editor.setHorizontalAlignment(SwingConstants.RIGHT);
+ break;
+ default:
+ editor.setHorizontalAlignment(SwingConstants.LEFT);
+ break;
+ }
+
+ }
+ return editor;
+ }
+
+ /** This method retrieves the AWT Color representation from the colour hash table
+ *
+ */
+ private final Color getAWTColor(int index, Color deflt) {
+ HSSFColor clr = (HSSFColor)colors.get(Integer.valueOf(index));
+ if (clr == null) return deflt;
+ return getAWTColor(clr);
+ }
+
+ private static final Color getAWTColor(HSSFColor clr) {
+ short[] rgb = clr.getTriplet();
+ return new Color(rgb[0],rgb[1],rgb[2]);
+ }
+}
--- /dev/null
+
+/* ====================================================================
+ 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 javax.swing.*;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.border.*;
+
+import java.awt.Component;
+import java.awt.Color;
+import java.awt.Rectangle;
+
+import java.io.Serializable;
+import java.text.*;
+
+import org.apache.poi.hssf.usermodel.*;
+
+
+/**
+ * Sheet Viewer Table Cell Render -- not commented via javadoc as it
+ * nearly completely consists of overridden methods.
+ *
+ * @author Andrew C. Oliver
+ */
+public class SVTableCellRenderer extends JLabel
+ implements TableCellRenderer, Serializable
+{
+ protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+ protected SVBorder cellBorder = new SVBorder();
+
+
+ private HSSFWorkbook wb = null;
+
+ /** This class holds the references to the predefined cell formats.
+ */
+ private class CellFormatter {
+ private Format[] textFormatter;
+
+ private DecimalFormat generalNumberFormat = new DecimalFormat("0");
+
+ public CellFormatter() {
+ textFormatter = new Format[0x31];
+
+ textFormatter[0x01] = new DecimalFormat("0");
+ textFormatter[0x02] = new DecimalFormat("0.00");
+ textFormatter[0x03] = new DecimalFormat("#,##0");
+ textFormatter[0x04] = new DecimalFormat("#,##0.00");
+ textFormatter[0x05] = new DecimalFormat("$#,##0;$#,##0");
+ textFormatter[0x06] = new DecimalFormat("$#,##0;$#,##0");
+ textFormatter[0x07] = new DecimalFormat("$#,##0.00;$#,##0.00");
+ textFormatter[0x08] = new DecimalFormat("$#,##0.00;$#,##0.00");
+ textFormatter[0x09] = new DecimalFormat("0%");
+ textFormatter[0x0A] = new DecimalFormat("0.00%");
+ textFormatter[0x0B] = new DecimalFormat("0.00E0");
+ textFormatter[0x0C] = new SVFractionalFormat("# ?/?");
+ textFormatter[0x0D] = new SVFractionalFormat("# ??/??");
+ textFormatter[0x0E] = new SimpleDateFormat("M/d/yy");
+ textFormatter[0x0F] = new SimpleDateFormat("d-MMM-yy");
+ textFormatter[0x10] = new SimpleDateFormat("d-MMM");
+ textFormatter[0x11] = new SimpleDateFormat("MMM-yy");
+ textFormatter[0x12] = new SimpleDateFormat("h:mm a");
+ textFormatter[0x13] = new SimpleDateFormat("h:mm:ss a");
+ textFormatter[0x14] = new SimpleDateFormat("h:mm");
+ textFormatter[0x15] = new SimpleDateFormat("h:mm:ss");
+ textFormatter[0x16] = new SimpleDateFormat("M/d/yy h:mm");
+ // 0x17 - 0x24 reserved for international and undocumented 0x25, "(#,##0_);(#,##0)"
+ //start at 0x26
+ //jmh need to do colour
+ //"(#,##0_);[Red](#,##0)"
+ textFormatter[0x26] = new DecimalFormat("#,##0;#,##0");
+ //jmh need to do colour
+ //(#,##0.00_);(#,##0.00)
+ textFormatter[0x27] = new DecimalFormat("#,##0.00;#,##0.00");
+ textFormatter[0x28] = new DecimalFormat("#,##0.00;#,##0.00");
+//?? textFormatter[0x29] = new DecimalFormat("_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)");
+//?? textFormatter[0x2A] = new DecimalFormat("_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)");
+//?? textFormatter[0x2B] = new DecimalFormat("_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)");
+//?? textFormatter[0x2C] = new DecimalFormat("_($*#,##0.00_);_($*(#,##0.00);_($*\"-\"??_);_(@_)");
+ textFormatter[0x2D] = new SimpleDateFormat("mm:ss");
+//?? textFormatter[0x2E] = new SimpleDateFormat("[h]:mm:ss");
+ textFormatter[0x2F] = new SimpleDateFormat("mm:ss.0");
+ textFormatter[0x30] = new DecimalFormat("##0.0E0");
+ }
+
+ public String format(short index, Object value) {
+ if (index == 0)
+ return value.toString();
+ if (textFormatter[index] == null)
+ throw new RuntimeException("Sorry. I cant handle the format code :"+Integer.toHexString(index));
+ return textFormatter[index].format(value);
+ }
+
+ public String format(short index, double value) {
+ if ( index <= 0 )
+ return generalNumberFormat.format(value);
+ if (textFormatter[index] == null)
+ throw new RuntimeException("Sorry. I cant handle the format code :"+Integer.toHexString(index));
+ if (textFormatter[index] instanceof DecimalFormat) {
+ return ((DecimalFormat)textFormatter[index]).format(value);
+ }
+ if (textFormatter[index] instanceof SVFractionalFormat) {
+ return ((SVFractionalFormat)textFormatter[index]).format(value);
+ }
+ throw new RuntimeException("Sorry. I cant handle a non decimal formatter for a decimal value :"+Integer.toHexString(index));
+ }
+
+ public boolean useRedColor(short index, double value) {
+ return (((index == 0x06)||(index == 0x08)||(index == 0x26) || (index == 0x27)) && (value < 0));
+ }
+ }
+
+ private final CellFormatter cellFormatter = new CellFormatter();
+
+ public SVTableCellRenderer(HSSFWorkbook wb) {
+ super();
+ setOpaque(true);
+ setBorder(noFocusBorder);
+ this.wb = wb;
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected, boolean hasFocus, int row, int column) {
+ boolean isBorderSet = false;
+
+ //If the JTables default cell renderer has been setup correctly the
+ //value will be the HSSFCell that we are trying to render
+ HSSFCell c = (HSSFCell)value;
+
+ if (c != null) {
+ HSSFCellStyle s = c.getCellStyle();
+ HSSFFont f = wb.getFontAt(s.getFontIndex());
+ setFont(SVTableUtils.makeFont(f));
+
+ if (s.getFillPattern() == HSSFCellStyle.SOLID_FOREGROUND) {
+ setBackground(SVTableUtils.getAWTColor(s.getFillForegroundColor(), SVTableUtils.white));
+ } else setBackground(SVTableUtils.white);
+
+ setForeground(SVTableUtils.getAWTColor(f.getColor(), SVTableUtils.black));
+
+ cellBorder.setBorder(SVTableUtils.getAWTColor(s.getTopBorderColor(), SVTableUtils.black),
+ SVTableUtils.getAWTColor(s.getRightBorderColor(), SVTableUtils.black),
+ SVTableUtils.getAWTColor(s.getBottomBorderColor(), SVTableUtils.black),
+ SVTableUtils.getAWTColor(s.getLeftBorderColor(), SVTableUtils.black),
+ s.getBorderTop(), s.getBorderRight(),
+ s.getBorderBottom(), s.getBorderLeft(),
+ hasFocus);
+ setBorder(cellBorder);
+ isBorderSet=true;
+
+ //Set the value that is rendered for the cell
+ switch (c.getCellType()) {
+ case HSSFCell.CELL_TYPE_BLANK:
+ setValue("");
+ break;
+ case HSSFCell.CELL_TYPE_BOOLEAN:
+ if (c.getBooleanCellValue()) {
+ setValue("true");
+ } else {
+ setValue("false");
+ }
+ break;
+ case HSSFCell.CELL_TYPE_NUMERIC:
+ short format = s.getDataFormat();
+ double numericValue = c.getNumericCellValue();
+ if (cellFormatter.useRedColor(format, numericValue))
+ setForeground(Color.red);
+ else setForeground(null);
+ setValue(cellFormatter.format(format, c.getNumericCellValue()));
+ break;
+ case HSSFCell.CELL_TYPE_STRING:
+ setValue(c.getRichStringCellValue().getString());
+ break;
+ case HSSFCell.CELL_TYPE_FORMULA:
+ default:
+ setValue("?");
+ }
+ //Set the text alignment of the cell
+ switch (s.getAlignment()) {
+ case HSSFCellStyle.ALIGN_LEFT:
+ case HSSFCellStyle.ALIGN_JUSTIFY:
+ case HSSFCellStyle.ALIGN_FILL:
+ setHorizontalAlignment(SwingConstants.LEFT);
+ break;
+ case HSSFCellStyle.ALIGN_CENTER:
+ case HSSFCellStyle.ALIGN_CENTER_SELECTION:
+ setHorizontalAlignment(SwingConstants.CENTER);
+ break;
+ case HSSFCellStyle.ALIGN_GENERAL:
+ case HSSFCellStyle.ALIGN_RIGHT:
+ setHorizontalAlignment(SwingConstants.RIGHT);
+ break;
+ default:
+ setHorizontalAlignment(SwingConstants.LEFT);
+ break;
+ }
+ } else {
+ setValue("");
+ setBackground(SVTableUtils.white);
+ }
+
+
+ if (hasFocus) {
+ if (!isBorderSet) {
+ //This is the border to paint when there is no border
+ //and the cell has focus
+ cellBorder.setBorder(SVTableUtils.black,
+ SVTableUtils.black,
+ SVTableUtils.black,
+ SVTableUtils.black,
+ HSSFCellStyle.BORDER_NONE,
+ HSSFCellStyle.BORDER_NONE,
+ HSSFCellStyle.BORDER_NONE,
+ HSSFCellStyle.BORDER_NONE,
+ isSelected);
+ setBorder(cellBorder);
+ }
+ if (table.isCellEditable(row, column)) {
+ setForeground( UIManager.getColor("Table.focusCellForeground") );
+ setBackground( UIManager.getColor("Table.focusCellBackground") );
+ }
+ } else if (!isBorderSet) {
+ setBorder(noFocusBorder);
+ }
+
+ // ---- begin optimization to avoid painting background ----
+ Color back = getBackground();
+ boolean colorMatch = (back != null) && ( back.equals(table.getBackground()) ) && table.isOpaque();
+ setOpaque(!colorMatch);
+ // ---- end optimization to aviod painting background ----
+ return this;
+ }
+
+ public void validate() {}
+
+ public void revalidate() {}
+
+ public void repaint(long tm, int x, int y, int width, int height) {}
+
+ public void repaint(Rectangle r) { }
+
+ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
+ // Strings get interned...
+ if (propertyName=="text") {
+ super.firePropertyChange(propertyName, oldValue, newValue);
+ }
+ }
+
+ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { }
+
+ /**
+ * Sets the string to either the value or "" if the value is null.
+ *
+ */
+ protected void setValue(Object value) {
+ setText((value == null) ? "" : value.toString());
+ }
+}
--- /dev/null
+
+/* ====================================================================
+ 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 java.util.Iterator;
+import javax.swing.table.*;
+
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+
+/**
+ * Sheet Viewer Table Model - The model for the Sheet Viewer just overrides things.
+ * @author Andrew C. Oliver
+ */
+
+public class SVTableModel extends AbstractTableModel {
+ private HSSFSheet st = null;
+ int maxcol = 0;
+
+ public SVTableModel(HSSFSheet st, int maxcol) {
+ this.st = st;
+ this.maxcol=maxcol;
+ }
+
+ public SVTableModel(HSSFSheet st) {
+ this.st = st;
+ Iterator i = st.rowIterator();
+
+ while (i.hasNext()) {
+ HSSFRow row = (HSSFRow)i.next();
+ if (maxcol < (row.getLastCellNum()+1)) {
+ this.maxcol = row.getLastCellNum();
+ }
+ }
+ }
+
+
+ public int getColumnCount() {
+ return this.maxcol+1;
+ }
+ public Object getValueAt(int row, int col) {
+ HSSFRow r = st.getRow(row);
+ HSSFCell c = null;
+ if (r != null) {
+ c = r.getCell(col);
+ }
+ return c;
+ }
+ public int getRowCount() {
+ return st.getLastRowNum() + 1;
+ }
+
+ public Class getColumnClass(int c) {
+ return HSSFCell.class;
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return true;
+ }
+
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ if (aValue != null)
+ System.out.println("SVTableModel.setValueAt. value type = "+aValue.getClass().getName());
+ else System.out.println("SVTableModel.setValueAt. value type = null");
+ }
+
+
+}
--- /dev/null
+
+/* ====================================================================
+ 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 java.util.*;
+import java.awt.*;
+import javax.swing.border.*;
+
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.hssf.util.*;
+
+/**
+ * SVTableCell Editor and Renderer helper functions.
+ *
+ * @author Jason Height
+ */
+public class SVTableUtils {
+ private final static Hashtable colors = HSSFColor.getIndexHash();
+ /** Description of the Field */
+ public final static Color black = getAWTColor(new HSSFColor.BLACK());
+ /** Description of the Field */
+ public final static Color white = getAWTColor(new HSSFColor.WHITE());
+ /** Description of the Field */
+ public static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+
+
+ /**
+ * Creates a new font for a specific cell style
+ */
+ public static Font makeFont(HSSFFont font) {
+ boolean isbold = font.getBoldweight() > HSSFFont.BOLDWEIGHT_NORMAL;
+ boolean isitalics = font.getItalic();
+ int fontstyle = Font.PLAIN;
+ if (isbold) {
+ fontstyle = Font.BOLD;
+ }
+ if (isitalics) {
+ fontstyle = fontstyle | Font.ITALIC;
+ }
+
+ int fontheight = font.getFontHeightInPoints();
+ if (fontheight == 9) {
+ //fix for stupid ol Windows
+ fontheight = 10;
+ }
+
+ return new Font(font.getFontName(), fontstyle, fontheight);
+ }
+
+
+ /**
+ * This method retrieves the AWT Color representation from the colour hash table
+ *
+ * @param index Description of the Parameter
+ * @param deflt Description of the Parameter
+ * @return The aWTColor value
+ */
+ public final static Color getAWTColor(int index, Color deflt) {
+ HSSFColor clr = (HSSFColor) colors.get(Integer.valueOf(index));
+ if (clr == null) {
+ return deflt;
+ }
+ return getAWTColor(clr);
+ }
+
+
+ /**
+ * Gets the aWTColor attribute of the SVTableUtils class
+ *
+ * @param clr Description of the Parameter
+ * @return The aWTColor value
+ */
+ public final static Color getAWTColor(HSSFColor clr) {
+ short[] rgb = clr.getTriplet();
+ return new Color(rgb[0], rgb[1], rgb[2]);
+ }
+}
--- /dev/null
+
+/* ====================================================================
+ 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 java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+import java.io.*;
+import javax.swing.*;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * Sheet Viewer - Views XLS files via HSSF. Can be used as an applet with
+ * filename="" or as a applications (pass the filename as the first parameter).
+ * Or you can pass it a URL in a "url" parameter when run as an applet or just
+ * that first parameter must start with http:// and it will guess its a url. I
+ * only tested it as an applet though, so it probably won't work...you fix it.
+ *
+ * @author Andrew C. Oliver
+ * @author Jason Height
+ */
+public class SViewer extends JApplet {
+ private SViewerPanel panel;
+ boolean isStandalone = false;
+ String filename = null;
+
+ /**Get a parameter value*/
+ public String getParameter(String key, String def) {
+ return isStandalone ? System.getProperty(key, def) :
+ (getParameter(key) != null ? getParameter(key) : def);
+ }
+
+ /**Construct the applet*/
+ public SViewer() {
+ }
+
+ /**Initialize the applet*/
+ public void init() {
+ try {
+ jbInit();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ /**Component initialization*/
+ private void jbInit() throws Exception {
+ InputStream i = null;
+ boolean isurl = false;
+ if (filename == null) filename = getParameter("filename");
+
+ if (filename == null || filename.substring(0,7).equals("http://")) {
+ isurl = true;
+ if (filename == null) filename = getParameter("url");
+ i = getXLSFromURL(filename);
+ }
+
+ HSSFWorkbook wb = null;
+ if (isurl) {
+ wb = constructWorkbook(i);
+ } else {
+ wb = constructWorkbook(filename);
+ }
+ panel = new SViewerPanel(wb, false);
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().add(panel, BorderLayout.CENTER);
+ }
+
+ private HSSFWorkbook constructWorkbook(String filename) throws FileNotFoundException, IOException {
+ HSSFWorkbook wb = null;
+ FileInputStream in = new FileInputStream(filename);
+ wb = new HSSFWorkbook(in);
+ in.close();
+ return wb;
+ }
+
+ private HSSFWorkbook constructWorkbook(InputStream in) throws IOException {
+ HSSFWorkbook wb = null;
+
+ wb = new HSSFWorkbook(in);
+ in.close();
+ return wb;
+ }
+
+ /**Start the applet*/
+ public void start() {
+ }
+ /**Stop the applet*/
+ public void stop() {
+ }
+ /**Destroy the applet*/
+ public void destroy() {
+ }
+ /**Get Applet information*/
+ public String getAppletInfo() {
+ return "Applet Information";
+ }
+ /**Get parameter info*/
+ public String[][] getParameterInfo() {
+ return null;
+ }
+
+ /**
+ * opens a url and returns an inputstream
+ *
+ */
+ private InputStream getXLSFromURL(String urlstring) throws MalformedURLException, IOException {
+ URL url = new URL(urlstring);
+ URLConnection uc = url.openConnection();
+ String field = uc.getHeaderField(0);
+ for (int i=0;field != null; i++) {
+ System.out.println(field);
+ field = uc.getHeaderField(i);
+ }
+ BufferedInputStream is = new BufferedInputStream(uc.getInputStream());
+ return is;
+ }
+
+
+ /**Main method*/
+ public static void main(String[] args) {
+ if(args.length < 1) {
+ throw new IllegalArgumentException("A filename to view must be supplied as the first argument, but none was given");
+ }
+
+ SViewer applet = new SViewer();
+ applet.isStandalone = true;
+ applet.filename = args[0];
+ Frame frame;
+ frame = new Frame() {
+ protected void processWindowEvent(WindowEvent e) {
+ super.processWindowEvent(e);
+ if (e.getID() == WindowEvent.WINDOW_CLOSING) {
+ System.exit(0);
+ }
+ }
+ public synchronized void setTitle(String title) {
+ super.setTitle(title);
+ enableEvents(AWTEvent.WINDOW_EVENT_MASK);
+ }
+ };
+ frame.setTitle("Applet Frame");
+ frame.add(applet, BorderLayout.CENTER);
+ applet.init();
+ applet.start();
+ frame.setSize(400,320);
+ Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
+ frame.setLocation((d.width - frame.getSize().width) / 2, (d.height - frame.getSize().height) / 2);
+ frame.setVisible(true);
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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 java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import javax.swing.*;
+import javax.swing.table.*;
+
+import org.apache.poi.hssf.usermodel.*;
+
+/**
+ * This class presents the sheets to the user.
+ *
+ *
+ * @author Andrew C. Oliver
+ * @author Jason Height
+ */
+public class SViewerPanel extends JPanel {
+ /** This field is the magic number to convert from a Character width to a
+ * java pixel width.
+ *
+ * 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.
+ *
+ * This number may only work for the normal style font size of Arial size 10.
+ *
+ */
+ private static final int magicCharFactor = 7;
+ /** Reference to the wookbook that is being displayed*/
+ /* package */ HSSFWorkbook wb;
+ /** Reference to the tabs component*/
+ /* package */ JTabbedPane sheetPane;
+ /** Reference to the cell renderer that is used to render all cells*/
+ private SVTableCellRenderer cellRenderer;
+ /** Reference to the cell editor that is used to edit all cells.
+ * Only constructed if editing is allowed
+ */
+ private SVTableCellEditor cellEditor;
+ /** Flag indicating if editing is allowed. Otherwise the viewer is in
+ * view only mode.
+ */
+ private boolean allowEdits;
+
+ /**Construct the representation of the workbook*/
+ public SViewerPanel(HSSFWorkbook wb, boolean allowEdits) {
+ this.wb = wb;
+ this.allowEdits = allowEdits;
+
+ initialiseGui();
+ }
+
+ private void initialiseGui() {
+ cellRenderer = new SVTableCellRenderer(this.wb);
+ if (allowEdits)
+ cellEditor = new SVTableCellEditor(this.wb);
+
+ //Initialise the Panel
+ sheetPane = new JTabbedPane(JTabbedPane.BOTTOM);
+
+ if (allowEdits)
+ sheetPane.addMouseListener(createTabListener());
+ int sheetCount = wb.getNumberOfSheets();
+ for (int i=0; i<sheetCount;i++) {
+ String sheetName = wb.getSheetName(i);
+ //Add the new sheet to the tabbed pane
+ sheetPane.addTab(sheetName, makeSheetView(wb.getSheetAt(i)));
+ }
+ setLayout(new BorderLayout());
+ add(sheetPane, BorderLayout.CENTER);
+ }
+
+ protected JComponent makeSheetView(HSSFSheet sheet) {
+ JTable sheetView = new JTable(new SVTableModel(sheet));
+ sheetView.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+ sheetView.setDefaultRenderer(HSSFCell.class, cellRenderer);
+ if (allowEdits)
+ sheetView.setDefaultEditor(HSSFCell.class, cellEditor);
+ JTableHeader header = sheetView.getTableHeader();
+ //Dont allow column reordering
+ header.setReorderingAllowed(false);
+ //Only allow column resizing if editing is allowed
+ header.setResizingAllowed(allowEdits);
+
+ //Set the columns the correct size
+ TableColumnModel columns = sheetView.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);
+ }
+
+ //Set the rows to the correct size
+ int rows = sheet.getPhysicalNumberOfRows();
+ Insets insets = cellRenderer.getInsets();
+ //Need to include the insets in the calculation of the row height to use.
+ int extraHeight = insets.bottom+insets.top;
+ for (int i=0; i< rows; i++) {
+ HSSFRow row = sheet.getRow(i);
+ if (row == null) {
+ sheetView.setRowHeight(i, (int)sheet.getDefaultRowHeightInPoints()+extraHeight);
+ } else {
+ sheetView.setRowHeight(i, (int)row.getHeightInPoints()+extraHeight);
+ }
+ }
+
+ //Add the row header to the sheet
+ SVRowHeader rowHeader = new SVRowHeader(sheet, sheetView, extraHeight);
+ JScrollPane scroll = new JScrollPane( sheetView );
+ scroll.setRowHeaderView(rowHeader);
+ return scroll;
+ }
+
+ public void paint(Graphics g) {
+ //JMH I am only overriding this to get a picture of the time taken to paint
+ long start = System.currentTimeMillis();
+ super.paint(g);
+ long elapsed = System.currentTimeMillis()-start;
+ System.out.println("Paint time = "+elapsed);
+ }
+
+ protected MouseListener createTabListener() {
+ return new TabListener();
+ }
+
+ /** This class defines the default MouseListener that listens to
+ * mouse events in the tabbed pane
+ *
+ * The default is to popup a menu when the event occurs over a tab
+ */
+ private class TabListener implements MouseListener {
+ public JPopupMenu popup;
+ public TabListener() {
+ popup = new JPopupMenu("Sheet");
+ popup.add(createInsertSheetAction());
+ popup.add(createDeleteSheetAction());
+ popup.add(createRenameSheetAction());
+ }
+
+ protected Action createInsertSheetAction() {
+ return new InsertSheetAction();
+ }
+
+ protected Action createDeleteSheetAction() {
+ return new DeleteSheetAction();
+ }
+
+ protected Action createRenameSheetAction() {
+ return new RenameSheetAction();
+ }
+
+
+ /** This method will display the popup if the mouseevent is a popup event
+ * and the event occurred over a tab
+ */
+ protected void checkPopup(MouseEvent e) {
+ if (e.isPopupTrigger()) {
+ int tab = sheetPane.getUI().tabForCoordinate(sheetPane, e.getX(), e.getY());
+ if (tab != -1) {
+ popup.show(sheetPane, e.getX(), e.getY());
+ }
+ }
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ checkPopup(e);
+ }
+
+ public void mousePressed(MouseEvent e) {
+ checkPopup(e);
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ checkPopup(e);
+ }
+
+ public void mouseEntered(MouseEvent e) {}
+ public void mouseExited(MouseEvent e) {}
+ }
+
+ /** This class defines the action that is performed when the sheet is renamed*/
+ private class RenameSheetAction extends AbstractAction {
+ public RenameSheetAction() {
+ super("Rename");
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ int tabIndex = sheetPane.getSelectedIndex();
+ if (tabIndex != -1) {
+ String newSheetName = JOptionPane.showInputDialog(sheetPane, "Enter a new Sheetname", "Rename Sheet", JOptionPane.QUESTION_MESSAGE);
+ if (newSheetName != null) {
+ wb.setSheetName(tabIndex, newSheetName);
+ sheetPane.setTitleAt(tabIndex, newSheetName);
+ }
+ }
+ }
+ }
+
+ /** This class defines the action that is performed when a sheet is inserted*/
+ private class InsertSheetAction extends AbstractAction {
+ public InsertSheetAction() {
+ super("Insert");
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ //Create a new sheet then search for the sheet and make sure that the
+ //sheetPane shows it.
+ HSSFSheet newSheet = wb.createSheet();
+ for (int i=0; i<wb.getNumberOfSheets();i++) {
+ HSSFSheet sheet = wb.getSheetAt(i);
+ if (newSheet == sheet) {
+ sheetPane.insertTab(wb.getSheetName(i), null, makeSheetView(sheet), null, i);
+ }
+ }
+ }
+ }
+
+ /** This class defines the action that is performed when the sheet is deleted*/
+ private class DeleteSheetAction extends AbstractAction {
+ public DeleteSheetAction() {
+ super("Delete");
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ int tabIndex = sheetPane.getSelectedIndex();
+ if (tabIndex != -1) {
+ if (JOptionPane.showConfirmDialog(sheetPane, "Are you sure that you want to delete the selected sheet", "Delete Sheet?", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
+ wb.removeSheetAt(tabIndex);
+ sheetPane.remove(tabIndex);
+ }
+ }
+ }
+ }
+
+ public boolean isEditable() {
+ return allowEdits;
+ }
+
+ /**Main method*/
+ public static void main(String[] args) {
+ if(args.length < 1) {
+ throw new IllegalArgumentException("A filename to view must be supplied as the first argument, but none was given");
+ }
+ try {
+ FileInputStream in = new FileInputStream(args[0]);
+ HSSFWorkbook wb = new HSSFWorkbook(in);
+ in.close();
+
+ SViewerPanel p = new SViewerPanel(wb, true);
+ JFrame frame;
+ frame = new JFrame() {
+ protected void processWindowEvent(WindowEvent e) {
+ super.processWindowEvent(e);
+ if (e.getID() == WindowEvent.WINDOW_CLOSING) {
+ System.exit(0);
+ }
+ }
+ public synchronized void setTitle(String title) {
+ super.setTitle(title);
+ enableEvents(AWTEvent.WINDOW_EVENT_MASK);
+ }
+ };
+ frame.setTitle("Viewer Frame");
+ frame.getContentPane().add(p, BorderLayout.CENTER);
+ frame.setSize(800,640);
+ Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
+ frame.setLocation((d.width - frame.getSize().width) / 2, (d.height - frame.getSize().height) / 2);
+ frame.setVisible(true);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+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();
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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();
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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;
+ }
+}
\ No newline at end of file
--- /dev/null
+This package contains some brushes that are used when drawing borders for Excel
+cells.
+
+@author Ken Arnold, Industrious Media LLC
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+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);
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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 "";
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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]);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+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.
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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();
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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);
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+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);
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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 + '"';
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Formatter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+
+/**
+ * This class implements printing out a value using a number format.
+ *
+ * @author Ken Arnold, Industrious Media LLC
+ */
+public class CellNumberFormatter extends CellFormatter {
+ private final String desc;
+ private String printfFmt;
+ private double scale;
+ private Special decimalPoint;
+ private Special slash;
+ private Special exponent;
+ private Special numerator;
+ private Special afterInteger;
+ private Special afterFractional;
+ private boolean integerCommas;
+ private final List<Special> specials;
+ private List<Special> integerSpecials;
+ private List<Special> fractionalSpecials;
+ private List<Special> numeratorSpecials;
+ private List<Special> denominatorSpecials;
+ private List<Special> exponentSpecials;
+ private List<Special> exponentDigitSpecials;
+ private int maxDenominator;
+ private String numeratorFmt;
+ private String denominatorFmt;
+ private boolean improperFraction;
+ private DecimalFormat decimalFmt;
+
+ static final CellFormatter SIMPLE_NUMBER = new CellFormatter("General") {
+ public void formatValue(StringBuffer toAppendTo, Object value) {
+ if (value == null)
+ return;
+ if (value instanceof Number) {
+ Number num = (Number) value;
+ if (num.doubleValue() % 1.0 == 0)
+ SIMPLE_INT.formatValue(toAppendTo, value);
+ else
+ SIMPLE_FLOAT.formatValue(toAppendTo, value);
+ } else {
+ CellTextFormatter.SIMPLE_TEXT.formatValue(toAppendTo, value);
+ }
+ }
+
+ public void simpleValue(StringBuffer toAppendTo, Object value) {
+ formatValue(toAppendTo, value);
+ }
+ };
+
+ private static final CellFormatter SIMPLE_INT = new CellNumberFormatter(
+ "#");
+ private static final CellFormatter SIMPLE_FLOAT = new CellNumberFormatter(
+ "#.#");
+
+ /**
+ * This class is used to mark where the special characters in the format
+ * are, as opposed to the other characters that are simply printed.
+ */
+ static class Special {
+ final char ch;
+ int pos;
+
+ Special(char ch, int pos) {
+ this.ch = ch;
+ this.pos = pos;
+ }
+
+ @Override
+ public String toString() {
+ return "'" + ch + "' @ " + pos;
+ }
+ }
+
+ /**
+ * This class represents a single modification to a result string. The way
+ * this works is complicated, but so is numeric formatting. In general, for
+ * most formats, we use a DecimalFormat object that will put the string out
+ * in a known format, usually with all possible leading and trailing zeros.
+ * We then walk through the result and the orginal format, and note any
+ * modifications that need to be made. Finally, we go through and apply
+ * them all, dealing with overlapping modifications.
+ */
+ static class StringMod implements Comparable<StringMod> {
+ final Special special;
+ final int op;
+ CharSequence toAdd;
+ Special end;
+ boolean startInclusive;
+ boolean endInclusive;
+
+ public static final int BEFORE = 1;
+ public static final int AFTER = 2;
+ public static final int REPLACE = 3;
+
+ private StringMod(Special special, CharSequence toAdd, int op) {
+ this.special = special;
+ this.toAdd = toAdd;
+ this.op = op;
+ }
+
+ public StringMod(Special start, boolean startInclusive, Special end,
+ boolean endInclusive, char toAdd) {
+ this(start, startInclusive, end, endInclusive);
+ this.toAdd = toAdd + "";
+ }
+
+ public StringMod(Special start, boolean startInclusive, Special end,
+ boolean endInclusive) {
+ special = start;
+ this.startInclusive = startInclusive;
+ this.end = end;
+ this.endInclusive = endInclusive;
+ op = REPLACE;
+ toAdd = "";
+ }
+
+ public int compareTo(StringMod that) {
+ int diff = special.pos - that.special.pos;
+ if (diff != 0)
+ return diff;
+ else
+ return op - that.op;
+ }
+
+ @Override
+ public boolean equals(Object that) {
+ try {
+ return compareTo((StringMod) that) == 0;
+ } catch (RuntimeException ignored) {
+ // NullPointerException or CastException
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return special.hashCode() + op;
+ }
+ }
+
+ private class NumPartHandler implements PartHandler {
+ private char insertSignForExponent;
+
+ public String handlePart(Matcher m, String part, CellFormatType type,
+ StringBuffer desc) {
+ int pos = desc.length();
+ char firstCh = part.charAt(0);
+ switch (firstCh) {
+ case 'e':
+ case 'E':
+ // See comment in writeScientific -- exponent handling is complex.
+ // (1) When parsing the format, remove the sign from after the 'e' and
+ // put it before the first digit of the exponent.
+ if (exponent == null && specials.size() > 0) {
+ specials.add(exponent = new Special('.', pos));
+ insertSignForExponent = part.charAt(1);
+ return part.substring(0, 1);
+ }
+ break;
+
+ case '0':
+ case '?':
+ case '#':
+ if (insertSignForExponent != '\0') {
+ specials.add(new Special(insertSignForExponent, pos));
+ desc.append(insertSignForExponent);
+ insertSignForExponent = '\0';
+ pos++;
+ }
+ for (int i = 0; i < part.length(); i++) {
+ char ch = part.charAt(i);
+ specials.add(new Special(ch, pos + i));
+ }
+ break;
+
+ case '.':
+ if (decimalPoint == null && specials.size() > 0)
+ specials.add(decimalPoint = new Special('.', pos));
+ break;
+
+ case '/':
+ //!! This assumes there is a numerator and a denominator, but these are actually optional
+ if (slash == null && specials.size() > 0) {
+ numerator = previousNumber();
+ // If the first number in the whole format is the numerator, the
+ // entire number should be printed as an improper fraction
+ if (numerator == firstDigit(specials))
+ improperFraction = true;
+ specials.add(slash = new Special('.', pos));
+ }
+ break;
+
+ case '%':
+ // don't need to remember because we don't need to do anything with these
+ scale *= 100;
+ break;
+
+ default:
+ return null;
+ }
+ return part;
+ }
+ }
+
+ /**
+ * Creates a new cell number formatter.
+ *
+ * @param format The format to parse.
+ */
+ public CellNumberFormatter(String format) {
+ super(format);
+
+ scale = 1;
+
+ specials = new LinkedList<Special>();
+
+ NumPartHandler partHandler = new NumPartHandler();
+ StringBuffer descBuf = CellFormatPart.parseFormat(format,
+ CellFormatType.NUMBER, partHandler);
+
+ // These are inconsistent settings, so ditch 'em
+ if ((decimalPoint != null || exponent != null) && slash != null) {
+ slash = null;
+ numerator = null;
+ }
+
+ interpretCommas(descBuf);
+
+ int precision;
+ int fractionPartWidth = 0;
+ if (decimalPoint == null) {
+ precision = 0;
+ } else {
+ precision = interpretPrecision();
+ fractionPartWidth = 1 + precision;
+ if (precision == 0) {
+ // This means the format has a ".", but that output should have no decimals after it.
+ // We just stop treating it specially
+ specials.remove(decimalPoint);
+ decimalPoint = null;
+ }
+ }
+
+ if (precision == 0)
+ fractionalSpecials = Collections.emptyList();
+ else
+ fractionalSpecials = specials.subList(specials.indexOf(
+ decimalPoint) + 1, fractionalEnd());
+ if (exponent == null)
+ exponentSpecials = Collections.emptyList();
+ else {
+ int exponentPos = specials.indexOf(exponent);
+ exponentSpecials = specialsFor(exponentPos, 2);
+ exponentDigitSpecials = specialsFor(exponentPos + 2);
+ }
+
+ if (slash == null) {
+ numeratorSpecials = Collections.emptyList();
+ denominatorSpecials = Collections.emptyList();
+ } else {
+ if (numerator == null)
+ numeratorSpecials = Collections.emptyList();
+ else
+ numeratorSpecials = specialsFor(specials.indexOf(numerator));
+
+ denominatorSpecials = specialsFor(specials.indexOf(slash) + 1);
+ if (denominatorSpecials.isEmpty()) {
+ // no denominator follows the slash, drop the fraction idea
+ numeratorSpecials = Collections.emptyList();
+ } else {
+ maxDenominator = maxValue(denominatorSpecials);
+ numeratorFmt = singleNumberFormat(numeratorSpecials);
+ denominatorFmt = singleNumberFormat(denominatorSpecials);
+ }
+ }
+
+ integerSpecials = specials.subList(0, integerEnd());
+
+ if (exponent == null) {
+ StringBuffer fmtBuf = new StringBuffer("%");
+
+ int integerPartWidth = calculateIntegerPartWidth();
+ int totalWidth = integerPartWidth + fractionPartWidth;
+
+ fmtBuf.append('0').append(totalWidth).append('.').append(precision);
+
+ fmtBuf.append("f");
+ printfFmt = fmtBuf.toString();
+ } else {
+ StringBuffer fmtBuf = new StringBuffer();
+ boolean first = true;
+ List<Special> specialList = integerSpecials;
+ if (integerSpecials.size() == 1) {
+ // If we don't do this, we get ".6e5" instead of "6e4"
+ fmtBuf.append("0");
+ first = false;
+ } else
+ for (Special s : specialList) {
+ if (isDigitFmt(s)) {
+ fmtBuf.append(first ? '#' : '0');
+ first = false;
+ }
+ }
+ if (fractionalSpecials.size() > 0) {
+ fmtBuf.append('.');
+ for (Special s : fractionalSpecials) {
+ if (isDigitFmt(s)) {
+ if (!first)
+ fmtBuf.append('0');
+ first = false;
+ }
+ }
+ }
+ fmtBuf.append('E');
+ placeZeros(fmtBuf, exponentSpecials.subList(2,
+ exponentSpecials.size()));
+ decimalFmt = new DecimalFormat(fmtBuf.toString());
+ }
+
+ if (exponent != null)
+ scale =
+ 1; // in "e" formats,% and trailing commas have no scaling effect
+
+ desc = descBuf.toString();
+ }
+
+ private static void placeZeros(StringBuffer sb, List<Special> specials) {
+ for (Special s : specials) {
+ if (isDigitFmt(s))
+ sb.append('0');
+ }
+ }
+
+ private static Special firstDigit(List<Special> specials) {
+ for (Special s : specials) {
+ if (isDigitFmt(s))
+ return s;
+ }
+ return null;
+ }
+
+ static StringMod insertMod(Special special, CharSequence toAdd, int where) {
+ return new StringMod(special, toAdd, where);
+ }
+
+ static StringMod deleteMod(Special start, boolean startInclusive,
+ Special end, boolean endInclusive) {
+
+ return new StringMod(start, startInclusive, end, endInclusive);
+ }
+
+ static StringMod replaceMod(Special start, boolean startInclusive,
+ Special end, boolean endInclusive, char withChar) {
+
+ return new StringMod(start, startInclusive, end, endInclusive,
+ withChar);
+ }
+
+ private static String singleNumberFormat(List<Special> numSpecials) {
+ return "%0" + numSpecials.size() + "d";
+ }
+
+ private static int maxValue(List<Special> s) {
+ return (int) Math.round(Math.pow(10, s.size()) - 1);
+ }
+
+ private List<Special> specialsFor(int pos, int takeFirst) {
+ if (pos >= specials.size())
+ return Collections.emptyList();
+ ListIterator<Special> it = specials.listIterator(pos + takeFirst);
+ Special last = it.next();
+ int end = pos + takeFirst;
+ while (it.hasNext()) {
+ Special s = it.next();
+ if (!isDigitFmt(s) || s.pos - last.pos > 1)
+ break;
+ end++;
+ last = s;
+ }
+ return specials.subList(pos, end + 1);
+ }
+
+ private List<Special> specialsFor(int pos) {
+ return specialsFor(pos, 0);
+ }
+
+ private static boolean isDigitFmt(Special s) {
+ return s.ch == '0' || s.ch == '?' || s.ch == '#';
+ }
+
+ private Special previousNumber() {
+ ListIterator<Special> it = specials.listIterator(specials.size());
+ while (it.hasPrevious()) {
+ Special s = it.previous();
+ if (isDigitFmt(s)) {
+ Special numStart = s;
+ Special last = s;
+ while (it.hasPrevious()) {
+ s = it.previous();
+ if (last.pos - s.pos > 1) // it has to be continuous digits
+ break;
+ if (isDigitFmt(s))
+ numStart = s;
+ else
+ break;
+ last = s;
+ }
+ return numStart;
+ }
+ }
+ return null;
+ }
+
+ private int calculateIntegerPartWidth() {
+ ListIterator<Special> it = specials.listIterator();
+ int digitCount = 0;
+ while (it.hasNext()) {
+ Special s = it.next();
+ //!! Handle fractions: The previous set of digits before that is the numerator, so we should stop short of that
+ if (s == afterInteger)
+ break;
+ else if (isDigitFmt(s))
+ digitCount++;
+ }
+ return digitCount;
+ }
+
+ private int interpretPrecision() {
+ if (decimalPoint == null) {
+ return -1;
+ } else {
+ int precision = 0;
+ ListIterator<Special> it = specials.listIterator(specials.indexOf(
+ decimalPoint));
+ if (it.hasNext())
+ it.next(); // skip over the decimal point itself
+ while (it.hasNext()) {
+ Special s = it.next();
+ if (isDigitFmt(s))
+ precision++;
+ else
+ break;
+ }
+ return precision;
+ }
+ }
+
+ private void interpretCommas(StringBuffer sb) {
+ // In the integer part, commas at the end are scaling commas; other commas mean to show thousand-grouping commas
+ ListIterator<Special> it = specials.listIterator(integerEnd());
+
+ boolean stillScaling = true;
+ integerCommas = false;
+ while (it.hasPrevious()) {
+ Special s = it.previous();
+ if (s.ch != ',') {
+ stillScaling = false;
+ } else {
+ if (stillScaling) {
+ scale /= 1000;
+ } else {
+ integerCommas = true;
+ }
+ }
+ }
+
+ if (decimalPoint != null) {
+ it = specials.listIterator(fractionalEnd());
+ while (it.hasPrevious()) {
+ Special s = it.previous();
+ if (s.ch != ',') {
+ break;
+ } else {
+ scale /= 1000;
+ }
+ }
+ }
+
+ // Now strip them out -- we only need their interpretation, not their presence
+ it = specials.listIterator();
+ int removed = 0;
+ while (it.hasNext()) {
+ Special s = it.next();
+ s.pos -= removed;
+ if (s.ch == ',') {
+ removed++;
+ it.remove();
+ sb.deleteCharAt(s.pos);
+ }
+ }
+ }
+
+ private int integerEnd() {
+ if (decimalPoint != null)
+ afterInteger = decimalPoint;
+ else if (exponent != null)
+ afterInteger = exponent;
+ else if (numerator != null)
+ afterInteger = numerator;
+ else
+ afterInteger = null;
+ return afterInteger == null ? specials.size() : specials.indexOf(
+ afterInteger);
+ }
+
+ private int fractionalEnd() {
+ int end;
+ if (exponent != null)
+ afterFractional = exponent;
+ else if (numerator != null)
+ afterInteger = numerator;
+ else
+ afterFractional = null;
+ end = afterFractional == null ? specials.size() : specials.indexOf(
+ afterFractional);
+ return end;
+ }
+
+ /** {@inheritDoc} */
+ public void formatValue(StringBuffer toAppendTo, Object valueObject) {
+ double value = ((Number) valueObject).doubleValue();
+ value *= scale;
+
+ // the '-' sign goes at the front, always, so we pick it out
+ boolean negative = value < 0;
+ if (negative)
+ value = -value;
+
+ // Split out the fractional part if we need to print a fraction
+ double fractional = 0;
+ if (slash != null) {
+ if (improperFraction) {
+ fractional = value;
+ value = 0;
+ } else {
+ fractional = value % 1.0;
+ //noinspection SillyAssignment
+ value = (long) value;
+ }
+ }
+
+ Set<StringMod> mods = new TreeSet<StringMod>();
+ StringBuffer output = new StringBuffer(desc);
+
+ if (exponent != null) {
+ writeScientific(value, output, mods);
+ } else if (improperFraction) {
+ writeFraction(value, null, fractional, output, mods);
+ } else {
+ StringBuffer result = new StringBuffer();
+ Formatter f = new Formatter(result);
+ f.format(LOCALE, printfFmt, value);
+
+ if (numerator == null) {
+ writeFractional(result, output);
+ writeInteger(result, output, integerSpecials, mods,
+ integerCommas);
+ } else {
+ writeFraction(value, result, fractional, output, mods);
+ }
+ }
+
+ // Now strip out any remaining '#'s and add any pending text ...
+ ListIterator<Special> it = specials.listIterator();
+ Iterator<StringMod> changes = mods.iterator();
+ StringMod nextChange = (changes.hasNext() ? changes.next() : null);
+ int adjust = 0;
+ BitSet deletedChars = new BitSet(); // records chars already deleted
+ while (it.hasNext()) {
+ Special s = it.next();
+ int adjustedPos = s.pos + adjust;
+ if (!deletedChars.get(s.pos) && output.charAt(adjustedPos) == '#') {
+ output.deleteCharAt(adjustedPos);
+ adjust--;
+ deletedChars.set(s.pos);
+ }
+ while (nextChange != null && s == nextChange.special) {
+ int lenBefore = output.length();
+ int modPos = s.pos + adjust;
+ int posTweak = 0;
+ switch (nextChange.op) {
+ case StringMod.AFTER:
+ // ignore adding a comma after a deleted char (which was a '#')
+ if (nextChange.toAdd.equals(",") && deletedChars.get(s.pos))
+ break;
+ posTweak = 1;
+ //noinspection fallthrough
+ case StringMod.BEFORE:
+ output.insert(modPos + posTweak, nextChange.toAdd);
+ break;
+
+ case StringMod.REPLACE:
+ int delPos =
+ s.pos; // delete starting pos in original coordinates
+ if (!nextChange.startInclusive) {
+ delPos++;
+ modPos++;
+ }
+
+ // Skip over anything already deleted
+ while (deletedChars.get(delPos)) {
+ delPos++;
+ modPos++;
+ }
+
+ int delEndPos =
+ nextChange.end.pos; // delete end point in original
+ if (nextChange.endInclusive)
+ delEndPos++;
+
+ int modEndPos =
+ delEndPos + adjust; // delete end point in current
+
+ if (modPos < modEndPos) {
+ if (nextChange.toAdd == "")
+ output.delete(modPos, modEndPos);
+ else {
+ char fillCh = nextChange.toAdd.charAt(0);
+ for (int i = modPos; i < modEndPos; i++)
+ output.setCharAt(i, fillCh);
+ }
+ deletedChars.set(delPos, delEndPos);
+ }
+ break;
+
+ default:
+ throw new IllegalStateException(
+ "Unknown op: " + nextChange.op);
+ }
+ adjust += output.length() - lenBefore;
+
+ if (changes.hasNext())
+ nextChange = changes.next();
+ else
+ nextChange = null;
+ }
+ }
+
+ // Finally, add it to the string
+ if (negative)
+ toAppendTo.append('-');
+ toAppendTo.append(output);
+ }
+
+ private void writeScientific(double value, StringBuffer output,
+ Set<StringMod> mods) {
+
+ StringBuffer result = new StringBuffer();
+ FieldPosition fractionPos = new FieldPosition(
+ DecimalFormat.FRACTION_FIELD);
+ decimalFmt.format(value, result, fractionPos);
+ writeInteger(result, output, integerSpecials, mods, integerCommas);
+ writeFractional(result, output);
+
+ /*
+ * Exponent sign handling is complex.
+ *
+ * In DecimalFormat, you never put the sign in the format, and the sign only
+ * comes out of the format if it is negative.
+ *
+ * In Excel, you always say whether to always show the sign ("e+") or only
+ * show negative signs ("e-").
+ *
+ * Also in Excel, where you put the sign in the format is NOT where it comes
+ * out in the result. In the format, the sign goes with the "e"; in the
+ * output it goes with the exponent value. That is, if you say "#e-|#" you
+ * get "1e|-5", not "1e-|5". This makes sense I suppose, but it complicates
+ * things.
+ *
+ * Finally, everything else in this formatting code assumes that the base of
+ * the result is the original format, and that starting from that situation,
+ * the indexes of the original special characters can be used to place the new
+ * characters. As just described, this is not true for the exponent's sign.
+ * <p/>
+ * So here is how we handle it:
+ *
+ * (1) When parsing the format, remove the sign from after the 'e' and put it
+ * before the first digit of the exponent (where it will be shown).
+ *
+ * (2) Determine the result's sign.
+ *
+ * (3) If it's missing, put the sign into the output to keep the result
+ * lined up with the output. (In the result, "after the 'e'" and "before the
+ * first digit" are the same because the result has no extra chars to be in
+ * the way.)
+ *
+ * (4) In the output, remove the sign if it should not be shown ("e-" was used
+ * and the sign is negative) or set it to the correct value.
+ */
+
+ // (2) Determine the result's sign.
+ int ePos = fractionPos.getEndIndex();
+ int signPos = ePos + 1;
+ char expSignRes = result.charAt(signPos);
+ if (expSignRes != '-') {
+ // not a sign, so it's a digit, and therefore a positive exponent
+ expSignRes = '+';
+ // (3) If it's missing, put the sign into the output to keep the result
+ // lined up with the output.
+ result.insert(signPos, '+');
+ }
+
+ // Now the result lines up like it is supposed to with the specials' indexes
+ ListIterator<Special> it = exponentSpecials.listIterator(1);
+ Special expSign = it.next();
+ char expSignFmt = expSign.ch;
+
+ // (4) In the output, remove the sign if it should not be shown or set it to
+ // the correct value.
+ if (expSignRes == '-' || expSignFmt == '+')
+ mods.add(replaceMod(expSign, true, expSign, true, expSignRes));
+ else
+ mods.add(deleteMod(expSign, true, expSign, true));
+
+ StringBuffer exponentNum = new StringBuffer(result.substring(
+ signPos + 1));
+ writeInteger(exponentNum, output, exponentDigitSpecials, mods, false);
+ }
+
+ private void writeFraction(double value, StringBuffer result,
+ double fractional, StringBuffer output, Set<StringMod> mods) {
+
+ // Figure out if we are to suppress either the integer or fractional part.
+ // With # the suppressed part is removed; with ? it is replaced with spaces.
+ if (!improperFraction) {
+ // If fractional part is zero, and numerator doesn't have '0', write out
+ // only the integer part and strip the rest.
+ if (fractional == 0 && !hasChar('0', numeratorSpecials)) {
+ writeInteger(result, output, integerSpecials, mods, false);
+
+ Special start = integerSpecials.get(integerSpecials.size() - 1);
+ Special end = denominatorSpecials.get(
+ denominatorSpecials.size() - 1);
+ if (hasChar('?', integerSpecials, numeratorSpecials,
+ denominatorSpecials)) {
+ //if any format has '?', then replace the fraction with spaces
+ mods.add(replaceMod(start, false, end, true, ' '));
+ } else {
+ // otherwise, remove the fraction
+ mods.add(deleteMod(start, false, end, true));
+ }
+
+ // That's all, just return
+ return;
+ } else {
+ // New we check to see if we should remove the integer part
+ boolean allZero = (value == 0 && fractional == 0);
+ boolean willShowFraction = fractional != 0 || hasChar('0',
+ numeratorSpecials);
+ boolean removeBecauseZero = allZero && (hasOnly('#',
+ integerSpecials) || !hasChar('0', numeratorSpecials));
+ boolean removeBecauseFraction =
+ !allZero && value == 0 && willShowFraction && !hasChar(
+ '0', integerSpecials);
+ if (removeBecauseZero || removeBecauseFraction) {
+ Special start = integerSpecials.get(
+ integerSpecials.size() - 1);
+ if (hasChar('?', integerSpecials, numeratorSpecials)) {
+ mods.add(replaceMod(start, true, numerator, false,
+ ' '));
+ } else {
+ mods.add(deleteMod(start, true, numerator, false));
+ }
+ } else {
+ // Not removing the integer part -- print it out
+ writeInteger(result, output, integerSpecials, mods, false);
+ }
+ }
+ }
+
+ // Calculate and print the actual fraction (improper or otherwise)
+ try {
+ int n;
+ int d;
+ // the "fractional % 1" captures integer values in improper fractions
+ if (fractional == 0 || (improperFraction && fractional % 1 == 0)) {
+ // 0 as a fraction is reported by excel as 0/1
+ n = (int) Math.round(fractional);
+ d = 1;
+ } else {
+ Fraction frac = new Fraction(fractional, maxDenominator);
+ n = frac.getNumerator();
+ d = frac.getDenominator();
+ }
+ if (improperFraction)
+ n += Math.round(value * d);
+ writeSingleInteger(numeratorFmt, n, output, numeratorSpecials,
+ mods);
+ writeSingleInteger(denominatorFmt, d, output, denominatorSpecials,
+ mods);
+ } catch (RuntimeException ignored) {
+ ignored.printStackTrace();
+ }
+ }
+
+ private static boolean hasChar(char ch, List<Special>... numSpecials) {
+ for (List<Special> specials : numSpecials) {
+ for (Special s : specials) {
+ if (s.ch == ch) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasOnly(char ch, List<Special>... numSpecials) {
+ for (List<Special> specials : numSpecials) {
+ for (Special s : specials) {
+ if (s.ch != ch) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private void writeSingleInteger(String fmt, int num, StringBuffer output,
+ List<Special> numSpecials, Set<StringMod> mods) {
+
+ StringBuffer sb = new StringBuffer();
+ Formatter formatter = new Formatter(sb);
+ formatter.format(LOCALE, fmt, num);
+ writeInteger(sb, output, numSpecials, mods, false);
+ }
+
+ private void writeInteger(StringBuffer result, StringBuffer output,
+ List<Special> numSpecials, Set<StringMod> mods,
+ boolean showCommas) {
+
+ int pos = result.indexOf(".") - 1;
+ if (pos < 0) {
+ if (exponent != null && numSpecials == integerSpecials)
+ pos = result.indexOf("E") - 1;
+ else
+ pos = result.length() - 1;
+ }
+
+ int strip;
+ for (strip = 0; strip < pos; strip++) {
+ char resultCh = result.charAt(strip);
+ if (resultCh != '0' && resultCh != ',')
+ break;
+ }
+
+ ListIterator<Special> it = numSpecials.listIterator(numSpecials.size());
+ boolean followWithComma = false;
+ Special lastOutputIntegerDigit = null;
+ int digit = 0;
+ while (it.hasPrevious()) {
+ char resultCh;
+ if (pos >= 0)
+ resultCh = result.charAt(pos);
+ else {
+ // If result is shorter than field, pretend there are leading zeros
+ resultCh = '0';
+ }
+ Special s = it.previous();
+ followWithComma = showCommas && digit > 0 && digit % 3 == 0;
+ boolean zeroStrip = false;
+ if (resultCh != '0' || s.ch == '0' || s.ch == '?' || pos >= strip) {
+ zeroStrip = s.ch == '?' && pos < strip;
+ output.setCharAt(s.pos, (zeroStrip ? ' ' : resultCh));
+ lastOutputIntegerDigit = s;
+ }
+ if (followWithComma) {
+ mods.add(insertMod(s, zeroStrip ? " " : ",", StringMod.AFTER));
+ followWithComma = false;
+ }
+ digit++;
+ --pos;
+ }
+ StringBuffer extraLeadingDigits = new StringBuffer();
+ if (pos >= 0) {
+ // We ran out of places to put digits before we ran out of digits; put this aside so we can add it later
+ ++pos; // pos was decremented at the end of the loop above when the iterator was at its end
+ extraLeadingDigits = new StringBuffer(result.substring(0, pos));
+ if (showCommas) {
+ while (pos > 0) {
+ if (digit > 0 && digit % 3 == 0)
+ extraLeadingDigits.insert(pos, ',');
+ digit++;
+ --pos;
+ }
+ }
+ mods.add(insertMod(lastOutputIntegerDigit, extraLeadingDigits,
+ StringMod.BEFORE));
+ }
+ }
+
+ private void writeFractional(StringBuffer result, StringBuffer output) {
+ int digit;
+ int strip;
+ ListIterator<Special> it;
+ if (fractionalSpecials.size() > 0) {
+ digit = result.indexOf(".") + 1;
+ if (exponent != null)
+ strip = result.indexOf("e") - 1;
+ else
+ strip = result.length() - 1;
+ while (strip > digit && result.charAt(strip) == '0')
+ strip--;
+ it = fractionalSpecials.listIterator();
+ while (it.hasNext()) {
+ Special s = it.next();
+ char resultCh = result.charAt(digit);
+ if (resultCh != '0' || s.ch == '0' || digit < strip)
+ output.setCharAt(s.pos, resultCh);
+ else if (s.ch == '?') {
+ // This is when we're in trailing zeros, and the format is '?'. We still strip out remaining '#'s later
+ output.setCharAt(s.pos, ' ');
+ }
+ digit++;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * For a number, this is <tt>"#"</tt> for integer values, and <tt>"#.#"</tt>
+ * for floating-point values.
+ */
+ public void simpleValue(StringBuffer toAppendTo, Object value) {
+ SIMPLE_NUMBER.formatValue(toAppendTo, value);
+ }
+
+ /**
+ * Based on org.apache.commons.math.fraction.Fraction from Apache Commons-Math.
+ * YK: The only reason of having this inner class is to avoid dependency on the Commons-Math jar.
+ */
+ private static class Fraction {
+ /** The denominator. */
+ private final int denominator;
+
+ /** The numerator. */
+ private final int numerator;
+
+ /**
+ * Create a fraction given the double value and either the maximum error
+ * allowed or the maximum number of denominator digits.
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within
+ * <code>epsilon</code> of <code>value</code>, in absolute terms.
+ * @param maxDenominator maximum denominator value allowed.
+ * @param maxIterations maximum number of convergents
+ * @throws RuntimeException if the continued fraction failed to
+ * converge.
+ */
+ private Fraction(double value, double epsilon, int maxDenominator, int maxIterations)
+ {
+ long overflow = Integer.MAX_VALUE;
+ double r0 = value;
+ long a0 = (long)Math.floor(r0);
+ if (a0 > overflow) {
+ throw new IllegalArgumentException("Overflow trying to convert "+value+" to fraction ("+a0+"/"+1l+")");
+ }
+
+ // check for (almost) integer arguments, which should not go
+ // to iterations.
+ if (Math.abs(a0 - value) < epsilon) {
+ this.numerator = (int) a0;
+ this.denominator = 1;
+ return;
+ }
+
+ long p0 = 1;
+ long q0 = 0;
+ long p1 = a0;
+ long q1 = 1;
+
+ long p2;
+ long q2;
+
+ int n = 0;
+ boolean stop = false;
+ do {
+ ++n;
+ double r1 = 1.0 / (r0 - a0);
+ long a1 = (long)Math.floor(r1);
+ p2 = (a1 * p1) + p0;
+ q2 = (a1 * q1) + q0;
+ if ((p2 > overflow) || (q2 > overflow)) {
+ throw new RuntimeException("Overflow trying to convert "+value+" to fraction ("+p2+"/"+q2+")");
+ }
+
+ double convergent = (double)p2 / (double)q2;
+ if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator) {
+ p0 = p1;
+ p1 = p2;
+ q0 = q1;
+ q1 = q2;
+ a0 = a1;
+ r0 = r1;
+ } else {
+ stop = true;
+ }
+ } while (!stop);
+
+ if (n >= maxIterations) {
+ throw new RuntimeException("Unable to convert "+value+" to fraction after "+maxIterations+" iterations");
+ }
+
+ if (q2 < maxDenominator) {
+ this.numerator = (int) p2;
+ this.denominator = (int) q2;
+ } else {
+ this.numerator = (int) p1;
+ this.denominator = (int) q1;
+ }
+
+ }
+
+ /**
+ * Create a fraction given the double value and maximum denominator.
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
+ * Continued Fraction</a> equations (11) and (22)-(26)</li>
+ * </ul>
+ * </p>
+ * @param value the double value to convert to a fraction.
+ * @param maxDenominator The maximum allowed value for denominator
+ * @throws RuntimeException if the continued fraction failed to
+ * converge
+ */
+ public Fraction(double value, int maxDenominator)
+ {
+ this(value, 0, maxDenominator, 100);
+ }
+
+ /**
+ * Access the denominator.
+ * @return the denominator.
+ */
+ public int getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * Access the numerator.
+ * @return the numerator.
+ */
+ public int getNumerator() {
+ return numerator;
+ }
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+<body>
+This package contains classes that implement cell formatting
+</body>
--- /dev/null
+/* ====================================================================
+ 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());
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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 + "\"]");
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+/* ====================================================================
+ 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));
+ }
+}
\ No newline at end of file