diff --git a/src/contrib/src/org/apache/poi/hssf/usermodel/contrib/HSSFCellUtil.java b/src/contrib/src/org/apache/poi/hssf/usermodel/contrib/HSSFCellUtil.java index 7c17614ec..5e2cf1e0f 100644 --- a/src/contrib/src/org/apache/poi/hssf/usermodel/contrib/HSSFCellUtil.java +++ b/src/contrib/src/org/apache/poi/hssf/usermodel/contrib/HSSFCellUtil.java @@ -55,10 +55,10 @@ public class HSSFCellUtil */ public static HSSFRow getRow( int rowCounter, HSSFSheet sheet ) { - HSSFRow row = sheet.getRow( (short) rowCounter ); + HSSFRow row = sheet.getRow( rowCounter ); if ( row == null ) { - row = sheet.createRow( (short) rowCounter ); + row = sheet.createRow( rowCounter ); } return row; @@ -66,7 +66,8 @@ public class HSSFCellUtil /** - * Get a specific cell from a row. If the cell doesn't exist, then create it. + * Get a specific cell from a row. If the cell doesn't exist, + * then create it. * *@param row The row that the cell is part of *@param column The column index that the cell is in. @@ -74,11 +75,11 @@ public class HSSFCellUtil */ public static HSSFCell getCell( HSSFRow row, int column ) { - HSSFCell cell = row.getCell( (short) column ); + HSSFCell cell = row.getCell( column ); if ( cell == null ) { - cell = row.createCell( (short) column ); + cell = row.createCell( (short)column ); } return cell; } @@ -98,7 +99,7 @@ public class HSSFCellUtil { HSSFCell cell = getCell( row, column ); - cell.setCellValue( value ); + cell.setCellValue(new HSSFRichTextString(value)); if ( style != null ) { cell.setCellStyle( style ); @@ -222,7 +223,7 @@ public class HSSFCellUtil public static HSSFCell translateUnicodeValues( HSSFCell cell ) { - String s = cell.getStringCellValue(); + String s = cell.getRichStringCellValue().getString(); boolean foundUnicode = false; for ( Iterator i = unicodeMappings.entrySet().iterator(); i.hasNext(); ) diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 150b41ff9..f6f5f8a62 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,17 @@ + 44306 - fixed reading/writing of AttrPtg(type=choose) and method toFormulaString() for CHOOSE formulas + 24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists + 40414 - fixed selected/active sheet after removing sheet from workbook + 44523 - fixed workbook sheet selection and focus + 45000 - Fixed NPE in ListLevel when numberText is null + 44985 - Properly update TextSpecInfoAtom when the parent text is changed + 41187 - fixed HSSFSheet to properly read xls files without ROW records + 44950 - fixed HSSFFormulaEvaluator.evaluateInCell() and Area3DEval.getValue() also added validation for number of elements in AreaEvals + 42570 - fixed LabelRecord to use empty string instead of null when the length is zero. + 42564 - fixed ArrayPtg to use ConstantValueParser. Fixed a few other ArrayPtg encoding issues. + Follow-on from 28754 - StringPtg.toFormulaString() should escape double quotes 44929 - Improved error handling in HSSFWorkbook when attempting to read a BIFF5 file 44675 - Parameter operand classes (function metadata) required to encode SUM() etc properly. Added parse validation for number of parameters 44921 - allow Ptg.writeBytes() to be called on relative ref Ptgs (RefN* and AreaN*) diff --git a/src/documentation/content/xdocs/hssf/quick-guide.xml b/src/documentation/content/xdocs/hssf/quick-guide.xml index 66da60489..28e237433 100644 --- a/src/documentation/content/xdocs/hssf/quick-guide.xml +++ b/src/documentation/content/xdocs/hssf/quick-guide.xml @@ -1225,7 +1225,19 @@ Examples: // Do something with this corner cell } } - + +

+ Note, when a cell is deleted, Excel does not delete the attached named range. + As result, workbook can contain named ranges that point to cells that no longer exist. + You should check the validity of a reference before constructing AreaReference +

+ + if(hssfName.isDeleted()){ + //named range points to a deleted cell. + } else { + AreaReference ref = new AreaReference(hssfName.getReference()); + } +
Cell Comments diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index e9059493b..2b5196f96 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,17 @@ + 44306 - fixed reading/writing of AttrPtg(type=choose) and method toFormulaString() for CHOOSE formulas + 24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists + 40414 - fixed selected/active sheet after removing sheet from workbook + 44523 - fixed workbook sheet selection and focus + 45000 - Fixed NPE in ListLevel when numberText is null + 44985 - Properly update TextSpecInfoAtom when the parent text is changed + 41187 - fixed HSSFSheet to properly read xls files without ROW records + 44950 - fixed HSSFFormulaEvaluator.evaluateInCell() and Area3DEval.getValue() also added validation for number of elements in AreaEvals + 42570 - fixed LabelRecord to use empty string instead of null when the length is zero. + 42564 - fixed ArrayPtg to use ConstantValueParser. Fixed a few other ArrayPtg encoding issues. + Follow-on from 28754 - StringPtg.toFormulaString() should escape double quotes 44929 - Improved error handling in HSSFWorkbook when attempting to read a BIFF5 file 44675 - Parameter operand classes (function metadata) required to encode SUM() etc properly. Added parse validation for number of parameters 44921 - allow Ptg.writeBytes() to be called on relative ref Ptgs (RefN* and AreaN*) diff --git a/src/documentation/release-guide.txt b/src/documentation/release-guide.txt index 56ebf3fa1..e1fbc94e6 100755 --- a/src/documentation/release-guide.txt +++ b/src/documentation/release-guide.txt @@ -37,14 +37,14 @@ where $TAG is the release tag, for example, REL_3_1_BETA1 3. Checkout the tagged version {code} cd tags -svn checkout https://svn.apache.org/repos/asf/poi/tags/TAG +svn checkout https://svn.apache.org/repos/asf/poi/tags/$TAG {code} 4. Merge (if required) {code} cd $TAG -$ svn merge https://svn.apache.org/repos/asf/poi/tags/TAG \ +$ svn merge https://svn.apache.org/repos/asf/poi/tags/$TAG \ https://svn.apache.org/repos/asf/poi/trunk {code} diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java index 8263d1b9b..cd4365191 100644 --- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java +++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java @@ -15,22 +15,20 @@ limitations under the License. ==================================================================== */ -/* - * BiffViewer.java - * - * Created on November 13, 2001, 9:23 AM - */ package org.apache.poi.hssf.dev; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; + import org.apache.poi.hssf.record.*; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.HexDump; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; - /** * Utillity for reading in BIFF8 records and displaying data from them. * @@ -38,38 +36,26 @@ import java.util.ArrayList; *@author Glen Stampoultzis (glens at apache.org) *@see #main */ - -public class BiffViewer { - String filename; +public final class BiffViewer { + private final File _inputFile; private boolean dump; + private final PrintStream _ps; - /** - * Creates new BiffViewer - * - *@param args - */ - - public BiffViewer(String[] args) { - if (args.length > 0) { - filename = args[0]; - } else { - System.out.println("BIFFVIEWER REQUIRES A FILENAME***"); - } + public BiffViewer(File inFile, PrintStream ps) { + _inputFile = inFile; + _ps = ps; } /** * Method run starts up BiffViewer... */ - public void run() { try { - POIFSFileSystem fs = - new POIFSFileSystem(new FileInputStream(filename)); - InputStream stream = - fs.createDocumentInputStream("Workbook"); - createRecords(stream, dump); + POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(_inputFile)); + InputStream stream = fs.createDocumentInputStream("Workbook"); + createRecords(stream, dump, _ps); } catch (Exception e) { e.printStackTrace(); } @@ -86,451 +72,306 @@ public class BiffViewer { * InputStream *@exception RecordFormatException on error processing the InputStream */ - - public static Record[] createRecords(InputStream in, boolean dump) + public static Record[] createRecords(InputStream in, boolean dump, PrintStream ps) throws RecordFormatException { ArrayList records = new ArrayList(); RecordDetails activeRecord = null; - try { - BiffviewRecordInputStream recStream = new BiffviewRecordInputStream(in); - while (recStream.hasNextRecord()) { + BiffviewRecordInputStream recStream = new BiffviewRecordInputStream(in); + while (recStream.hasNextRecord()) { recStream.nextRecord(); if (recStream.getSid() != 0) { - Record record = createRecord (recStream); + Record record = createRecord (recStream); if (record.getSid() != ContinueRecord.sid) { records.add(record); if (activeRecord != null) - activeRecord.dump(); - activeRecord = new RecordDetails(recStream.getSid(), recStream.getLength(), (int)recStream.getPos(), record); + activeRecord.dump(ps); + activeRecord = new RecordDetails(recStream.getSid(), recStream.getLength(), (int)recStream.getPos(), record); } if (dump) { - recStream.dumpBytes(); - } + recStream.dumpBytes(ps); } - } - activeRecord.dump(); - } catch (IOException e) { - throw new RecordFormatException("Error reading bytes", e); + } + } + if (activeRecord != null) { + activeRecord.dump(ps); } Record[] retval = new Record[records.size()]; - - retval = (Record[]) records.toArray(retval); + records.toArray(retval); return retval; } - private static void dumpNormal(Record record, int startloc, short rectype, short recsize) - { - //System.out.println("Offset 0x" + Integer.toHexString(startloc) + " (" + startloc + ")"); - System.out.println( "recordid = 0x" + Integer.toHexString( rectype ) + ", size = " + recsize ); - System.out.println( record.toString() ); - - } /** - * Essentially a duplicate of RecordFactory. Kept seperate as not to screw + * Essentially a duplicate of RecordFactory. Kept separate as not to screw * up non-debug operations. * */ private static Record createRecord( RecordInputStream in ) { - Record retval = null; - switch ( in.getSid() ) { - case ChartRecord.sid: - retval = new ChartRecord( in ); - break; + return new ChartRecord( in ); case ChartFormatRecord.sid: - retval = new ChartFormatRecord( in ); - break; + return new ChartFormatRecord( in ); case SeriesRecord.sid: - retval = new SeriesRecord( in ); - break; + return new SeriesRecord( in ); case BeginRecord.sid: - retval = new BeginRecord( in ); - break; + return new BeginRecord( in ); case EndRecord.sid: - retval = new EndRecord( in ); - break; + return new EndRecord( in ); case BOFRecord.sid: - retval = new BOFRecord( in ); - break; + return new BOFRecord( in ); case InterfaceHdrRecord.sid: - retval = new InterfaceHdrRecord( in ); - break; + return new InterfaceHdrRecord( in ); case MMSRecord.sid: - retval = new MMSRecord( in ); - break; + return new MMSRecord( in ); case InterfaceEndRecord.sid: - retval = new InterfaceEndRecord( in ); - break; + return new InterfaceEndRecord( in ); case WriteAccessRecord.sid: - retval = new WriteAccessRecord( in ); - break; + return new WriteAccessRecord( in ); case CodepageRecord.sid: - retval = new CodepageRecord( in ); - break; + return new CodepageRecord( in ); case DSFRecord.sid: - retval = new DSFRecord( in ); - break; + return new DSFRecord( in ); case TabIdRecord.sid: - retval = new TabIdRecord( in ); - break; + return new TabIdRecord( in ); case FnGroupCountRecord.sid: - retval = new FnGroupCountRecord( in ); - break; + return new FnGroupCountRecord( in ); case WindowProtectRecord.sid: - retval = new WindowProtectRecord( in ); - break; + return new WindowProtectRecord( in ); case ProtectRecord.sid: - retval = new ProtectRecord( in ); - break; + return new ProtectRecord( in ); case PasswordRecord.sid: - retval = new PasswordRecord( in ); - break; + return new PasswordRecord( in ); case ProtectionRev4Record.sid: - retval = new ProtectionRev4Record( in ); - break; + return new ProtectionRev4Record( in ); case PasswordRev4Record.sid: - retval = new PasswordRev4Record( in ); - break; + return new PasswordRev4Record( in ); case WindowOneRecord.sid: - retval = new WindowOneRecord( in ); - break; + return new WindowOneRecord( in ); case BackupRecord.sid: - retval = new BackupRecord( in ); - break; + return new BackupRecord( in ); case HideObjRecord.sid: - retval = new HideObjRecord( in ); - break; + return new HideObjRecord( in ); case DateWindow1904Record.sid: - retval = new DateWindow1904Record( in ); - break; + return new DateWindow1904Record( in ); case PrecisionRecord.sid: - retval = new PrecisionRecord( in ); - break; + return new PrecisionRecord( in ); case RefreshAllRecord.sid: - retval = new RefreshAllRecord( in ); - break; + return new RefreshAllRecord( in ); case BookBoolRecord.sid: - retval = new BookBoolRecord( in ); - break; + return new BookBoolRecord( in ); case FontRecord.sid: - retval = new FontRecord( in ); - break; + return new FontRecord( in ); case FormatRecord.sid: - retval = new FormatRecord( in ); - break; + return new FormatRecord( in ); case ExtendedFormatRecord.sid: - retval = new ExtendedFormatRecord( in ); - break; + return new ExtendedFormatRecord( in ); case StyleRecord.sid: - retval = new StyleRecord( in ); - break; + return new StyleRecord( in ); case UseSelFSRecord.sid: - retval = new UseSelFSRecord( in ); - break; + return new UseSelFSRecord( in ); case BoundSheetRecord.sid: - retval = new BoundSheetRecord( in ); - break; + return new BoundSheetRecord( in ); case CountryRecord.sid: - retval = new CountryRecord( in ); - break; + return new CountryRecord( in ); case SSTRecord.sid: - retval = new SSTRecord( in ); - break; + return new SSTRecord( in ); case ExtSSTRecord.sid: - retval = new ExtSSTRecord( in ); - break; + return new ExtSSTRecord( in ); case EOFRecord.sid: - retval = new EOFRecord( in ); - break; + return new EOFRecord( in ); case IndexRecord.sid: - retval = new IndexRecord( in ); - break; + return new IndexRecord( in ); case CalcModeRecord.sid: - retval = new CalcModeRecord( in ); - break; + return new CalcModeRecord( in ); case CalcCountRecord.sid: - retval = new CalcCountRecord( in ); - break; + return new CalcCountRecord( in ); case RefModeRecord.sid: - retval = new RefModeRecord( in ); - break; + return new RefModeRecord( in ); case IterationRecord.sid: - retval = new IterationRecord( in ); - break; + return new IterationRecord( in ); case DeltaRecord.sid: - retval = new DeltaRecord( in ); - break; + return new DeltaRecord( in ); case SaveRecalcRecord.sid: - retval = new SaveRecalcRecord( in ); - break; + return new SaveRecalcRecord( in ); case PrintHeadersRecord.sid: - retval = new PrintHeadersRecord( in ); - break; + return new PrintHeadersRecord( in ); case PrintGridlinesRecord.sid: - retval = new PrintGridlinesRecord( in ); - break; + return new PrintGridlinesRecord( in ); case GridsetRecord.sid: - retval = new GridsetRecord( in ); - break; + return new GridsetRecord( in ); case DrawingGroupRecord.sid: - retval = new DrawingGroupRecord( in ); - break; + return new DrawingGroupRecord( in ); case DrawingRecordForBiffViewer.sid: - retval = new DrawingRecordForBiffViewer( in ); - break; + return new DrawingRecordForBiffViewer( in ); case DrawingSelectionRecord.sid: - retval = new DrawingSelectionRecord( in ); - break; + return new DrawingSelectionRecord( in ); case GutsRecord.sid: - retval = new GutsRecord( in ); - break; + return new GutsRecord( in ); case DefaultRowHeightRecord.sid: - retval = new DefaultRowHeightRecord( in ); - break; + return new DefaultRowHeightRecord( in ); case WSBoolRecord.sid: - retval = new WSBoolRecord( in ); - break; + return new WSBoolRecord( in ); case HeaderRecord.sid: - retval = new HeaderRecord( in ); - break; + return new HeaderRecord( in ); case FooterRecord.sid: - retval = new FooterRecord( in ); - break; + return new FooterRecord( in ); case HCenterRecord.sid: - retval = new HCenterRecord( in ); - break; + return new HCenterRecord( in ); case VCenterRecord.sid: - retval = new VCenterRecord( in ); - break; + return new VCenterRecord( in ); case PrintSetupRecord.sid: - retval = new PrintSetupRecord( in ); - break; + return new PrintSetupRecord( in ); case DefaultColWidthRecord.sid: - retval = new DefaultColWidthRecord( in ); - break; + return new DefaultColWidthRecord( in ); case DimensionsRecord.sid: - retval = new DimensionsRecord( in ); - break; + return new DimensionsRecord( in ); case RowRecord.sid: - retval = new RowRecord( in ); - break; + return new RowRecord( in ); case LabelSSTRecord.sid: - retval = new LabelSSTRecord( in ); - break; + return new LabelSSTRecord( in ); case RKRecord.sid: - retval = new RKRecord( in ); - break; + return new RKRecord( in ); case NumberRecord.sid: - retval = new NumberRecord( in ); - break; + return new NumberRecord( in ); case DBCellRecord.sid: - retval = new DBCellRecord( in ); - break; + return new DBCellRecord( in ); case WindowTwoRecord.sid: - retval = new WindowTwoRecord( in ); - break; + return new WindowTwoRecord( in ); case SelectionRecord.sid: - retval = new SelectionRecord( in ); - break; + return new SelectionRecord( in ); case ContinueRecord.sid: - retval = new ContinueRecord( in ); - break; + return new ContinueRecord( in ); case LabelRecord.sid: - retval = new LabelRecord( in ); - break; + return new LabelRecord( in ); case MulRKRecord.sid: - retval = new MulRKRecord( in ); - break; + return new MulRKRecord( in ); case MulBlankRecord.sid: - retval = new MulBlankRecord( in ); - break; + return new MulBlankRecord( in ); case BlankRecord.sid: - retval = new BlankRecord( in ); - break; + return new BlankRecord( in ); case BoolErrRecord.sid: - retval = new BoolErrRecord( in ); - break; + return new BoolErrRecord( in ); case ColumnInfoRecord.sid: - retval = new ColumnInfoRecord( in ); - break; + return new ColumnInfoRecord( in ); case MergeCellsRecord.sid: - retval = new MergeCellsRecord( in ); - break; + return new MergeCellsRecord( in ); case AreaRecord.sid: - retval = new AreaRecord( in ); - break; + return new AreaRecord( in ); case DataFormatRecord.sid: - retval = new DataFormatRecord( in ); - break; + return new DataFormatRecord( in ); case BarRecord.sid: - retval = new BarRecord( in ); - break; + return new BarRecord( in ); case DatRecord.sid: - retval = new DatRecord( in ); - break; + return new DatRecord( in ); case PlotGrowthRecord.sid: - retval = new PlotGrowthRecord( in ); - break; + return new PlotGrowthRecord( in ); case UnitsRecord.sid: - retval = new UnitsRecord( in ); - break; + return new UnitsRecord( in ); case FrameRecord.sid: - retval = new FrameRecord( in ); - break; + return new FrameRecord( in ); case ValueRangeRecord.sid: - retval = new ValueRangeRecord( in ); - break; + return new ValueRangeRecord( in ); case SeriesListRecord.sid: - retval = new SeriesListRecord( in ); - break; + return new SeriesListRecord( in ); case FontBasisRecord.sid: - retval = new FontBasisRecord( in ); - break; + return new FontBasisRecord( in ); case FontIndexRecord.sid: - retval = new FontIndexRecord( in ); - break; + return new FontIndexRecord( in ); case LineFormatRecord.sid: - retval = new LineFormatRecord( in ); - break; + return new LineFormatRecord( in ); case AreaFormatRecord.sid: - retval = new AreaFormatRecord( in ); - break; + return new AreaFormatRecord( in ); case LinkedDataRecord.sid: - retval = new LinkedDataRecord( in ); - break; + return new LinkedDataRecord( in ); case FormulaRecord.sid: - retval = new FormulaRecord( in ); - break; + return new FormulaRecord( in ); case SheetPropertiesRecord.sid: - retval = new SheetPropertiesRecord( in ); - break; + return new SheetPropertiesRecord( in ); case DefaultDataLabelTextPropertiesRecord.sid: - retval = new DefaultDataLabelTextPropertiesRecord( in ); - break; + return new DefaultDataLabelTextPropertiesRecord( in ); case TextRecord.sid: - retval = new TextRecord( in ); - break; + return new TextRecord( in ); case AxisParentRecord.sid: - retval = new AxisParentRecord( in ); - break; + return new AxisParentRecord( in ); case AxisLineFormatRecord.sid: - retval = new AxisLineFormatRecord( in ); - break; + return new AxisLineFormatRecord( in ); case SupBookRecord.sid: - retval = new SupBookRecord( in ); - break; + return new SupBookRecord( in ); case ExternSheetRecord.sid: - retval = new ExternSheetRecord( in ); - break; + return new ExternSheetRecord( in ); case SCLRecord.sid: - retval = new SCLRecord( in ); - break; + return new SCLRecord( in ); case SeriesToChartGroupRecord.sid: - retval = new SeriesToChartGroupRecord( in ); - break; + return new SeriesToChartGroupRecord( in ); case AxisUsedRecord.sid: - retval = new AxisUsedRecord( in ); - break; + return new AxisUsedRecord( in ); case AxisRecord.sid: - retval = new AxisRecord( in ); - break; + return new AxisRecord( in ); case CategorySeriesAxisRecord.sid: - retval = new CategorySeriesAxisRecord( in ); - break; + return new CategorySeriesAxisRecord( in ); case AxisOptionsRecord.sid: - retval = new AxisOptionsRecord( in ); - break; + return new AxisOptionsRecord( in ); case TickRecord.sid: - retval = new TickRecord( in ); - break; + return new TickRecord( in ); case SeriesTextRecord.sid: - retval = new SeriesTextRecord( in ); - break; + return new SeriesTextRecord( in ); case ObjectLinkRecord.sid: - retval = new ObjectLinkRecord( in ); - break; + return new ObjectLinkRecord( in ); case PlotAreaRecord.sid: - retval = new PlotAreaRecord( in ); - break; + return new PlotAreaRecord( in ); case SeriesIndexRecord.sid: - retval = new SeriesIndexRecord( in ); - break; + return new SeriesIndexRecord( in ); case LegendRecord.sid: - retval = new LegendRecord( in ); - break; + return new LegendRecord( in ); case LeftMarginRecord.sid: - retval = new LeftMarginRecord( in ); - break; + return new LeftMarginRecord( in ); case RightMarginRecord.sid: - retval = new RightMarginRecord( in ); - break; + return new RightMarginRecord( in ); case TopMarginRecord.sid: - retval = new TopMarginRecord( in ); - break; + return new TopMarginRecord( in ); case BottomMarginRecord.sid: - retval = new BottomMarginRecord( in ); - break; + return new BottomMarginRecord( in ); case PaletteRecord.sid: - retval = new PaletteRecord( in ); - break; + return new PaletteRecord( in ); case StringRecord.sid: - retval = new StringRecord( in ); - break; + return new StringRecord( in ); case NameRecord.sid: - retval = new NameRecord( in ); - break; + return new NameRecord( in ); case PaneRecord.sid: - retval = new PaneRecord( in ); - break; + return new PaneRecord( in ); case SharedFormulaRecord.sid: - retval = new SharedFormulaRecord( in); - break; + return new SharedFormulaRecord( in); case ObjRecord.sid: - retval = new ObjRecord( in); - break; + return new ObjRecord( in); case TextObjectRecord.sid: - retval = new TextObjectRecord( in); - break; + return new TextObjectRecord( in); case HorizontalPageBreakRecord.sid: - retval = new HorizontalPageBreakRecord( in); - break; + return new HorizontalPageBreakRecord( in); case VerticalPageBreakRecord.sid: - retval = new VerticalPageBreakRecord( in); - break; + return new VerticalPageBreakRecord( in); case WriteProtectRecord.sid: - retval = new WriteProtectRecord( in); - break; + return new WriteProtectRecord( in); case FilePassRecord.sid: - retval = new FilePassRecord(in); - break; + return new FilePassRecord(in); case NoteRecord.sid: - retval = new NoteRecord( in ); - break; + return new NoteRecord( in ); case FileSharingRecord.sid: - retval = new FileSharingRecord( in ); - break; + return new FileSharingRecord( in ); case HyperlinkRecord.sid: - retval = new HyperlinkRecord( in ); - break; - default: - retval = new UnknownRecord( in ); + return new HyperlinkRecord( in ); } - return retval; + return new UnknownRecord( in ); } /** * Method setDump - hex dump out data or not. - * - *@param dump */ - public void setDump(boolean dump) { this.dump = dump; } @@ -552,33 +393,44 @@ public class BiffViewer { * */ public static void main(String[] args) { + + System.setProperty("poi.deserialize.escher", "true"); + + if (args.length == 0) { + System.out.println( "Biff viewer needs a filename" ); + return; + } + try { - System.setProperty("poi.deserialize.escher", "true"); - - if (args.length == 0) - { - System.out.println( "Biff viewer needs a filename" ); + String inFileName = args[0]; + File inputFile = new File(inFileName); + if(!inputFile.exists()) { + throw new RuntimeException("specified inputFile '" + inFileName + "' does not exist"); } - else - { - BiffViewer viewer = new BiffViewer(args); - if ((args.length > 1) && args[1].equals("on")) { - viewer.setDump(true); - } - if ((args.length > 1) && args[1].equals("bfd")) { - POIFSFileSystem fs = - new POIFSFileSystem(new FileInputStream(args[0])); - InputStream stream = - fs.createDocumentInputStream("Workbook"); - int size = stream.available(); - byte[] data = new byte[size]; - - stream.read(data); - HexDump.dump(data, 0, System.out, 0); - } else { - viewer.run(); - } + PrintStream ps; + if (false) { // set to true to output to file + OutputStream os = new FileOutputStream(inFileName + ".out"); + ps = new PrintStream(os); + } else { + ps = System.out; } + BiffViewer viewer = new BiffViewer(inputFile, ps); + + if (args.length > 1 && args[1].equals("on")) { + viewer.setDump(true); + } + if (args.length > 1 && args[1].equals("bfd")) { + POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(inputFile)); + InputStream stream = fs.createDocumentInputStream("Workbook"); + int size = stream.available(); + byte[] data = new byte[size]; + + stream.read(data); + HexDump.dump(data, 0, System.out, 0); + } else { + viewer.run(); + } + ps.close(); } catch (Exception e) { e.printStackTrace(); } @@ -587,7 +439,7 @@ public class BiffViewer { /** * This record supports dumping of completed continue records. */ - static class RecordDetails + private static final class RecordDetails { short rectype, recsize; int startloc; @@ -616,18 +468,19 @@ public class BiffViewer { return record; } - public void dump() throws IOException - { - dumpNormal(record, startloc, rectype, recsize); + public void dump(PrintStream ps) { + ps.println("Offset 0x" + Integer.toHexString(startloc) + " (" + startloc + ")"); + ps.println( "recordid = 0x" + Integer.toHexString( rectype ) + ", size = " + recsize ); + ps.println( record.toString() ); } } - static class BiffviewRecordInputStream extends RecordInputStream { + private static final class BiffviewRecordInputStream extends RecordInputStream { public BiffviewRecordInputStream(InputStream in) { super(in); } - public void dumpBytes() { - HexDump.dump(this.data, 0, this.currentLength); + public void dumpBytes(PrintStream ps) { + ps.println(HexDump.dump(this.data, 0, this.currentLength)); } } diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java index f16ab4512..2819786ef 100644 --- a/src/java/org/apache/poi/hssf/model/FormulaParser.java +++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java @@ -1000,7 +1000,7 @@ end; if (ptg instanceof AttrPtg) { AttrPtg attrPtg = ((AttrPtg) ptg); - if (attrPtg.isOptimizedIf()) { + if (attrPtg.isOptimizedIf() || attrPtg.isOptimizedChoose() || attrPtg.isGoto()) { continue; } if (attrPtg.isSpace()) { @@ -1014,6 +1014,9 @@ end; // similar to tAttrSpace - RPN is violated continue; } + if (!attrPtg.isSum()) { + throw new RuntimeException("Unexpected tAttr: " + attrPtg.toString()); + } } final OperationPtg o = (OperationPtg) ptg; diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java index 2af25e748..45090e85c 100644 --- a/src/java/org/apache/poi/hssf/model/Sheet.java +++ b/src/java/org/apache/poi/hssf/model/Sheet.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -16,7 +15,6 @@ limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.model; import org.apache.poi.hssf.record.*; @@ -57,9 +55,7 @@ import java.util.List; // normally I don't do this, buy we literally mean ALL * @see org.apache.poi.hssf.usermodel.HSSFSheet * @version 1.0-pre */ - -public class Sheet implements Model -{ +public final class Sheet implements Model { public static final short LeftMargin = 0; public static final short RightMargin = 1; public static final short TopMargin = 2; @@ -97,9 +93,9 @@ public class Sheet implements Model protected ObjectProtectRecord objprotect = null; protected ScenarioProtectRecord scenprotect = null; protected PasswordRecord password = null; - protected List condFormatting = new ArrayList();; + protected List condFormatting = new ArrayList(); - /** Add an UncalcedRecord if not true indicating formulas have not been calculated */ + /** Add an UncalcedRecord if not true indicating formulas have not been calculated */ protected boolean uncalced = false; public static final byte PANE_LOWER_RIGHT = (byte)0; @@ -108,7 +104,7 @@ public class Sheet implements Model public static final byte PANE_UPPER_LEFT = (byte)3; /** - * Creates new Sheet with no intialization --useless at this point + * Creates new Sheet with no initialization --useless at this point * @see #createSheet(List,int,int) */ public Sheet() @@ -166,7 +162,7 @@ public class Sheet implements Model } } else if (rec.getSid() == UncalcedRecord.sid) { - retval.uncalced = true; + retval.uncalced = true; } else if (rec.getSid() == DimensionsRecord.sid) { @@ -188,14 +184,14 @@ public class Sheet implements Model } else if ( rec.getSid() == CFHeaderRecord.sid ) { - CFRecordsAggregate cfAgg = CFRecordsAggregate.createCFAggregate(recs, k); - retval.condFormatting.add(cfAgg); - rec = cfAgg; + CFRecordsAggregate cfAgg = CFRecordsAggregate.createCFAggregate(recs, k); + retval.condFormatting.add(cfAgg); + rec = cfAgg; } else if ( rec.getSid() == CFRuleRecord.sid ) { - // Skip it since it is processed by CFRecordsAggregate - rec = null; + // Skip it since it is processed by CFRecordsAggregate + rec = null; } else if (rec.getSid() == ColumnInfoRecord.sid) { @@ -244,7 +240,7 @@ public class Sheet implements Model if ( isfirstrow ) { retval.rows = new RowRecordsAggregate(); - rec = retval.rows; + rec = retval.rows; isfirstrow = false; } retval.rows.insertRow(row); @@ -256,7 +252,7 @@ public class Sheet implements Model else if ( rec.getSid() == GridsetRecord.sid ) { retval.gridset = (GridsetRecord) rec; - } + } else if ( rec.getSid() == HeaderRecord.sid && bofEofNestingLevel == 1) { retval.header = (HeaderRecord) rec; @@ -301,32 +297,32 @@ public class Sheet implements Model { rec = null; } - - else if ( rec.getSid() == ProtectRecord.sid ) - { - retval.protect = (ProtectRecord) rec; - } - else if ( rec.getSid() == ObjectProtectRecord.sid ) - { - retval.objprotect = (ObjectProtectRecord) rec; - } - else if ( rec.getSid() == ScenarioProtectRecord.sid ) - { - retval.scenprotect = (ScenarioProtectRecord) rec; - } - else if ( rec.getSid() == PasswordRecord.sid ) - { - retval.password = (PasswordRecord) rec; - } - else if (rec.getSid() == PageBreakRecord.HORIZONTAL_SID) - { - retval.rowBreaks = (PageBreakRecord)rec; - } - else if (rec.getSid() == PageBreakRecord.VERTICAL_SID) - { - retval.colBreaks = (PageBreakRecord)rec; - } - + + else if ( rec.getSid() == ProtectRecord.sid ) + { + retval.protect = (ProtectRecord) rec; + } + else if ( rec.getSid() == ObjectProtectRecord.sid ) + { + retval.objprotect = (ObjectProtectRecord) rec; + } + else if ( rec.getSid() == ScenarioProtectRecord.sid ) + { + retval.scenprotect = (ScenarioProtectRecord) rec; + } + else if ( rec.getSid() == PasswordRecord.sid ) + { + retval.password = (PasswordRecord) rec; + } + else if (rec.getSid() == PageBreakRecord.HORIZONTAL_SID) + { + retval.rowBreaks = (PageBreakRecord)rec; + } + else if (rec.getSid() == PageBreakRecord.VERTICAL_SID) + { + retval.colBreaks = (PageBreakRecord)rec; + } + if (rec != null) { records.add(rec); @@ -351,9 +347,9 @@ public class Sheet implements Model /** * Clones the low level records of this sheet and returns the new sheet instance. * This method is implemented by adding methods for deep cloning to all records that - * can be added to a sheet. The Record object does not implement cloneable. + * can be added to a sheet. The Record object does not implement cloneable. * When adding a new record, implement a public clone method if and only if the record - * belongs to a sheet. + * belongs to a sheet. */ public Sheet cloneSheet() { @@ -374,7 +370,7 @@ public class Sheet implements Model ValueRecordsAggregate vrAgg = (ValueRecordsAggregate)rec; for (Iterator cellIter = vrAgg.getIterator();cellIter.hasNext();) { Record valRec = (Record)cellIter.next(); - + if (valRec instanceof FormulaRecordAggregate) { FormulaRecordAggregate fmAgg = (FormulaRecordAggregate)valRec; Record fmAggRec = fmAgg.getFormulaRecord(); @@ -459,9 +455,9 @@ public class Sheet implements Model records.add(retval.rowBreaks); retval.colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID); records.add(retval.colBreaks); - + retval.header = (HeaderRecord) retval.createHeader(); - records.add( retval.header ); + records.add( retval.header ); retval.footer = (FooterRecord) retval.createFooter(); records.add( retval.footer ); records.add( retval.createHCenter() ); @@ -479,11 +475,11 @@ public class Sheet implements Model retval.dimsloc = records.size()-1; records.add(retval.windowTwo = retval.createWindowTwo()); retval.setLoc(records.size() - 1); - retval.selection = + retval.selection = (SelectionRecord) retval.createSelection(); records.add(retval.selection); - retval.protect = (ProtectRecord) retval.createProtect(); - records.add(retval.protect); + retval.protect = (ProtectRecord) retval.createProtect(); + records.add(retval.protect); records.add(retval.createEOF()); @@ -511,26 +507,25 @@ public class Sheet implements Model } } - //public int addMergedRegion(short rowFrom, short colFrom, short rowTo, - public int addMergedRegion(int rowFrom, short colFrom, int rowTo, - short colTo) - { - // Validate input - if(rowTo < rowFrom) { - throw new IllegalArgumentException("The row to ("+rowTo+") must be >= the row from ("+rowFrom+")"); - } - if(colTo < colFrom) { - throw new IllegalArgumentException("The col to ("+colTo+") must be >= the col from ("+colFrom+")"); - } - + public int addMergedRegion(int rowFrom, short colFrom, int rowTo, short colTo) { + // Validate input + if (rowTo < rowFrom) { + throw new IllegalArgumentException("The 'to' row (" + rowTo + + ") must not be less than the 'from' row (" + rowFrom + ")"); + } + if (colTo < colFrom) { + throw new IllegalArgumentException("The 'to' col (" + colTo + + ") must not be less than the 'from' col (" + colFrom + ")"); + } + if (merged == null || merged.getNumAreas() == 1027) { merged = ( MergeCellsRecord ) createMergedCells(); - mergedRecords.add(merged); + mergedRecords.add(merged); records.add(records.size() - 1, merged); } merged.addArea(rowFrom, colFrom, rowTo, colTo); - return numMergedRegions++; + return numMergedRegions++; } public void removeMergedRegion(int index) @@ -538,59 +533,10 @@ public class Sheet implements Model //safety checks if (index >= numMergedRegions || mergedRecords.size() == 0) return; - + int pos = 0; int startNumRegions = 0; - - //optimisation for current record - if (numMergedRegions - index < merged.getNumAreas()) - { - pos = mergedRecords.size() - 1; - startNumRegions = numMergedRegions - merged.getNumAreas(); - } - else - { - for (int n = 0; n < mergedRecords.size(); n++) - { - MergeCellsRecord record = (MergeCellsRecord) mergedRecords.get(n); - if (startNumRegions + record.getNumAreas() > index) - { - pos = n; - break; - } - startNumRegions += record.getNumAreas(); - } - } - MergeCellsRecord rec = (MergeCellsRecord) mergedRecords.get(pos); - rec.removeAreaAt(index - startNumRegions); - numMergedRegions--; - if (rec.getNumAreas() == 0) - { - mergedRecords.remove(pos); - //get rid of the record from the sheet - records.remove(merged); - if (merged == rec) { - //pull up the LAST record for operations when we finally - //support continue records for mergedRegions - if (mergedRecords.size() > 0) { - merged = (MergeCellsRecord) mergedRecords.get(mergedRecords.size() - 1); - } else { - merged = null; - } - } - } - } - - public MergeCellsRecord.MergedRegion getMergedRegionAt(int index) - { - //safety checks - if (index >= numMergedRegions || mergedRecords.size() == 0) - return null; - - int pos = 0; - int startNumRegions = 0; - //optimisation for current record if (numMergedRegions - index < merged.getNumAreas()) { @@ -607,7 +553,56 @@ public class Sheet implements Model pos = n; break; } - startNumRegions += record.getNumAreas(); + startNumRegions += record.getNumAreas(); + } + } + + MergeCellsRecord rec = (MergeCellsRecord) mergedRecords.get(pos); + rec.removeAreaAt(index - startNumRegions); + numMergedRegions--; + if (rec.getNumAreas() == 0) + { + mergedRecords.remove(pos); + //get rid of the record from the sheet + records.remove(merged); + if (merged == rec) { + //pull up the LAST record for operations when we finally + //support continue records for mergedRegions + if (mergedRecords.size() > 0) { + merged = (MergeCellsRecord) mergedRecords.get(mergedRecords.size() - 1); + } else { + merged = null; + } + } + } + } + + public MergeCellsRecord.MergedRegion getMergedRegionAt(int index) + { + //safety checks + if (index >= numMergedRegions || mergedRecords.size() == 0) + return null; + + int pos = 0; + int startNumRegions = 0; + + //optimisation for current record + if (numMergedRegions - index < merged.getNumAreas()) + { + pos = mergedRecords.size() - 1; + startNumRegions = numMergedRegions - merged.getNumAreas(); + } + else + { + for (int n = 0; n < mergedRecords.size(); n++) + { + MergeCellsRecord record = (MergeCellsRecord) mergedRecords.get(n); + if (startNumRegions + record.getNumAreas() > index) + { + pos = n; + break; + } + startNumRegions += record.getNumAreas(); } } return ((MergeCellsRecord) mergedRecords.get(pos)).getAreaAt(index - startNumRegions); @@ -620,62 +615,62 @@ public class Sheet implements Model // Find correct position to add new CF record private int findConditionalFormattingPosition() { - // This is default. - // If the algorithm does not find the right position, - // this one will be used (this is a position before EOF record) - int index = records.size()-2; - - for( int i=index; i>=0; i-- ) - { - Record rec = (Record)records.get(i); - short sid = rec.getSid(); - - // CFRecordsAggregate records already exist, just add to the end - if (rec instanceof CFRecordsAggregate) { return i+1; } - - if( sid == (short)0x00ef ) { return i+1; }// PHONETICPR - if( sid == (short)0x015f ) { return i+1; }// LABELRANGES - if( sid == MergeCellsRecord.sid ) { return i+1; } - if( sid == (short)0x0099 ) { return i+1; }// STANDARDWIDTH - if( sid == SelectionRecord.sid ) { return i+1; } - if( sid == PaneRecord.sid ) { return i+1; } - if( sid == SCLRecord.sid ) { return i+1; } - if( sid == WindowTwoRecord.sid ) { return i+1; } - } - - return index; + // This is default. + // If the algorithm does not find the right position, + // this one will be used (this is a position before EOF record) + int index = records.size()-2; + + for( int i=index; i>=0; i-- ) + { + Record rec = (Record)records.get(i); + short sid = rec.getSid(); + + // CFRecordsAggregate records already exist, just add to the end + if (rec instanceof CFRecordsAggregate) { return i+1; } + + if( sid == (short)0x00ef ) { return i+1; }// PHONETICPR + if( sid == (short)0x015f ) { return i+1; }// LABELRANGES + if( sid == MergeCellsRecord.sid ) { return i+1; } + if( sid == (short)0x0099 ) { return i+1; }// STANDARDWIDTH + if( sid == SelectionRecord.sid ) { return i+1; } + if( sid == PaneRecord.sid ) { return i+1; } + if( sid == SCLRecord.sid ) { return i+1; } + if( sid == WindowTwoRecord.sid ) { return i+1; } + } + + return index; } public int addConditionalFormatting(CFRecordsAggregate cfAggregate) { - int index = findConditionalFormattingPosition(); - records.add(index, cfAggregate); - condFormatting.add(cfAggregate); - return condFormatting.size()-1; + int index = findConditionalFormattingPosition(); + records.add(index, cfAggregate); + condFormatting.add(cfAggregate); + return condFormatting.size()-1; } public void removeConditionalFormatting(int index) { if (index >= 0 && index <= condFormatting.size()-1 ) { - CFRecordsAggregate cfAggregate = getCFRecordsAggregateAt(index); - records.remove(cfAggregate); - condFormatting.remove(index); + CFRecordsAggregate cfAggregate = getCFRecordsAggregateAt(index); + records.remove(cfAggregate); + condFormatting.remove(index); } } - + public CFRecordsAggregate getCFRecordsAggregateAt(int index) { if (index >= 0 && index <= condFormatting.size()-1 ) { - return (CFRecordsAggregate) condFormatting.get(index); + return (CFRecordsAggregate) condFormatting.get(index); } return null; } - + public int getNumConditionalFormattings() { - return condFormatting.size(); + return condFormatting.size(); } /** @@ -711,8 +706,6 @@ public class Sheet implements Model * * @see org.apache.poi.hssf.record.DimensionsRecord */ - - //public void setDimensions(short firstrow, short firstcol, short lastrow, public void setDimensions(int firstrow, short firstcol, int lastrow, short lastcol) { @@ -735,7 +728,7 @@ public class Sheet implements Model /** * set the locator for where we should look for the next value record. The - * algorythm will actually start here and find the correct location so you + * algorithm will actually start here and find the correct location so you * can set this to 0 and watch performance go down the tubes but it will work. * After a value is set this is automatically advanced. Its also set by the * create method. So you probably shouldn't mess with this unless you have @@ -813,13 +806,13 @@ public class Sheet implements Model for (int k = 0; k < records.size(); k++) { Record record = (( Record ) records.get(k)); - + // Don't write out UncalcedRecord entries, as // we handle those specially just below if (record instanceof UncalcedRecord) { - continue; + continue; } - + // Once the rows have been found in the list of records, start // writing out the blocked row information. This includes the DBCell references if (record instanceof RowRecordsAggregate) { @@ -829,13 +822,13 @@ public class Sheet implements Model } else { pos += record.serialize(pos, data ); // rec.length; } - + // If the BOF record was just serialized then add the IndexRecord if (record.getSid() == BOFRecord.sid) { // Add an optional UncalcedRecord if (uncalced) { - UncalcedRecord rec = new UncalcedRecord(); - pos += rec.serialize(pos, data); + UncalcedRecord rec = new UncalcedRecord(); + pos += rec.serialize(pos, data); } //Can there be more than one BOF for a sheet? If not then we can //remove this guard. So be safe it is left here. @@ -871,7 +864,7 @@ public class Sheet implements Model log.log(POILogger.DEBUG, "Sheet.serialize returning "); return pos-offset; } - + private int serializeIndexRecord(final int BOFRecordIndex, final int offset, byte[] data) { IndexRecord index = new IndexRecord(); index.setFirstRow(rows.getFirstRowNum()); @@ -906,7 +899,7 @@ public class Sheet implements Model } return index.serialize(offset, data); } - + /** * Create a row record. (does not add it to the records contained in this sheet) @@ -930,8 +923,6 @@ public class Sheet implements Model * @return LabelSSTRecord newly created containing your SST Index, row,col. * @see org.apache.poi.hssf.record.SSTRecord */ - - //public LabelSSTRecord createLabelSST(short row, short col, int index) public LabelSSTRecord createLabelSST(int row, short col, int index) { log.logFormatted(POILogger.DEBUG, "create labelsst row,col,index %,%,%", @@ -957,8 +948,6 @@ public class Sheet implements Model * * @return NumberRecord for that row, col containing that value as added to the sheet */ - - //public NumberRecord createNumber(short row, short col, double value) public NumberRecord createNumber(int row, short col, double value) { log.logFormatted(POILogger.DEBUG, "create number row,col,value %,%,%", @@ -968,7 +957,6 @@ public class Sheet implements Model }); NumberRecord rec = new NumberRecord(); - //rec.setRow(( short ) row); rec.setRow(row); rec.setColumn(col); rec.setValue(value); @@ -982,18 +970,14 @@ public class Sheet implements Model * @param row - the row the BlankRecord is a member of * @param col - the column the BlankRecord is a member of */ - - //public BlankRecord createBlank(short row, short col) public BlankRecord createBlank(int row, short col) { - //log.logFormatted(POILogger.DEBUG, "create blank row,col %,%", new short[] log.logFormatted(POILogger.DEBUG, "create blank row,col %,%", new int[] { row, col }); BlankRecord rec = new BlankRecord(); - //rec.setRow(( short ) row); rec.setRow(row); rec.setColumn(col); rec.setXFIndex(( short ) 0x0f); @@ -1009,12 +993,9 @@ public class Sheet implements Model * @param formula - a String representing the formula. To be parsed to PTGs * @return bogus/useless formula record */ - - //public FormulaRecord createFormula(short row, short col, String formula) public FormulaRecord createFormula(int row, short col, String formula) { log.logFormatted(POILogger.DEBUG, "create formula row,col,formula %,%,%", - //new short[] new int[] { row, col @@ -1052,8 +1033,6 @@ public class Sheet implements Model * @param row the row to add the cell value to * @param col the cell value record itself. */ - - //public void addValueRecord(short row, CellValueRecordInterface col) public void addValueRecord(int row, CellValueRecordInterface col) { checkCells(); @@ -1075,29 +1054,6 @@ public class Sheet implements Model d.setFirstCol(col.getColumn()); } cells.insertCell(col); - - /* - * for (int k = loc; k < records.size(); k++) - * { - * Record rec = ( Record ) records.get(k); - * - * if (rec.getSid() == RowRecord.sid) - * { - * RowRecord rowrec = ( RowRecord ) rec; - * - * if (rowrec.getRowNumber() == col.getRow()) - * { - * records.add(k + 1, col); - * loc = k; - * if (rowrec.getLastCol() <= col.getColumn()) - * { - * rowrec.setLastCol((( short ) (col.getColumn() + 1))); - * } - * break; - * } - * } - * } - */ } /** @@ -1109,8 +1065,6 @@ public class Sheet implements Model * @param col - a record supporting the CellValueRecordInterface. * @see org.apache.poi.hssf.record.CellValueRecordInterface */ - - //public void removeValueRecord(short row, CellValueRecordInterface col) public void removeValueRecord(int row, CellValueRecordInterface col) { checkCells(); @@ -1118,27 +1072,6 @@ public class Sheet implements Model new int[]{row, dimsloc} ); loc = dimsloc; cells.removeCell(col); - - /* - * for (int k = loc; k < records.size(); k++) - * { - * Record rec = ( Record ) records.get(k); - * - * // checkDimsLoc(rec,k); - * if (rec.isValue()) - * { - * CellValueRecordInterface cell = - * ( CellValueRecordInterface ) rec; - * - * if ((cell.getRow() == col.getRow()) - * && (cell.getColumn() == col.getColumn())) - * { - * records.remove(k); - * break; - * } - * } - * } - */ } /** @@ -1160,26 +1093,10 @@ public class Sheet implements Model //The ValueRecordsAggregate use a tree map underneath. //The tree Map uses the CellValueRecordInterface as both the //key and the value, if we dont do a remove, then - //the previous instance of the key is retained, effectively using + //the previous instance of the key is retained, effectively using //double the memory cells.removeCell(newval); cells.insertCell(newval); - - /* - * CellValueRecordInterface oldval = getNextValueRecord(); - * - * while (oldval != null) - * { - * if (oldval.isEqual(newval)) - * { - * records.set(( short ) (getLoc() - 1), newval); - * return; - * } - * oldval = getNextValueRecord(); - * } - * addValueRecord(newval.getRow(), newval); - * setLoc(dimsloc); - */ } /** @@ -1218,41 +1135,6 @@ public class Sheet implements Model rows.insertRow(row); - /* - * for (int k = loc; k < records.size(); k++) - * { - * Record rec = ( Record ) records.get(k); - * - * if (rec.getSid() == IndexRecord.sid) - * { - * index = ( IndexRecord ) rec; - * } - * if (rec.getSid() == RowRecord.sid) - * { - * RowRecord rowrec = ( RowRecord ) rec; - * - * if (rowrec.getRowNumber() > row.getRowNumber()) - * { - * records.add(k, row); - * loc = k; - * break; - * } - * } - * if (rec.getSid() == WindowTwoRecord.sid) - * { - * records.add(k, row); - * loc = k; - * break; - * } - * } - * if (index != null) - * { - * if (index.getLastRowAdd1() <= row.getRowNumber()) - * { - * index.setLastRowAdd1(row.getRowNumber() + 1); - * } - * } - */ if (log.check( POILogger.DEBUG )) log.log(POILogger.DEBUG, "exit addRow"); } @@ -1268,33 +1150,9 @@ public class Sheet implements Model public void removeRow(RowRecord row) { checkRows(); - // IndexRecord index = null; setLoc(getDimsLoc()); rows.removeRow(row); - - /* - * for (int k = loc; k < records.size(); k++) - * { - * Record rec = ( Record ) records.get(k); - * - * // checkDimsLoc(rec,k); - * if (rec.getSid() == RowRecord.sid) - * { - * RowRecord rowrec = ( RowRecord ) rec; - * - * if (rowrec.getRowNumber() == row.getRowNumber()) - * { - * records.remove(k); - * break; - * } - * } - * if (rec.getSid() == WindowTwoRecord.sid) - * { - * break; - * } - * } - */ } /** @@ -1325,66 +1183,8 @@ public class Sheet implements Model return null; } return ( CellValueRecordInterface ) valueRecIterator.next(); - - /* - * if (this.getLoc() < records.size()) - * { - * for (int k = getLoc(); k < records.size(); k++) - * { - * Record rec = ( Record ) records.get(k); - * - * this.setLoc(k + 1); - * if (rec instanceof CellValueRecordInterface) - * { - * return ( CellValueRecordInterface ) rec; - * } - * } - * } - * return null; - */ } - /** - * get the NEXT RowRecord or CellValueRecord(from LOC). The first record that - * is a Row record or CellValueRecord(starting at LOC) will be returned. - *

- * This method is "loc" sensitive. Meaning you need to set LOC to where you - * want it to start searching. If you don't know do this: setLoc(getDimsLoc). - * When adding several rows you can just start at the last one by leaving loc - * at what this sets it to. For this method, set loc to dimsloc to start with. - * subsequent calls will return rows in (physical) sequence or NULL when you get to the end. - * - * @return RowRecord representing the next row record or CellValueRecordInterface - * representing the next cellvalue or NULL if there are no more - * @see #setLoc(int) - * - */ - -/* public Record getNextRowOrValue() - { - POILogger.DEBUG((new StringBuffer("getNextRow loc= ")).append(loc) - .toString()); - if (this.getLoc() < records.size()) - { - for (int k = this.getLoc(); k < records.size(); k++) - { - Record rec = ( Record ) records.get(k); - - this.setLoc(k + 1); - if (rec.getSid() == RowRecord.sid) - { - return rec; - } - else if (rec.isValue()) - { - return rec; - } - } - } - return null; - } - */ - /** * get the NEXT RowRecord (from LOC). The first record that is a Row record * (starting at LOC) will be returned. @@ -1413,20 +1213,6 @@ public class Sheet implements Model return null; } return ( RowRecord ) rowRecIterator.next(); - -/* if (this.getLoc() < records.size()) - { - for (int k = this.getLoc(); k < records.size(); k++) - { - Record rec = ( Record ) records.get(k); - - this.setLoc(k + 1); - if (rec.getSid() == RowRecord.sid) - { - return ( RowRecord ) rec; - } - } - }*/ } /** @@ -1445,34 +1231,10 @@ public class Sheet implements Model * @see #setLoc(int) * */ - - //public RowRecord getRow(short rownum) - public RowRecord getRow(int rownum) - { + public RowRecord getRow(int rownum) { if (log.check( POILogger.DEBUG )) log.log(POILogger.DEBUG, "getNextRow loc= " + loc); return rows.getRow(rownum); - - /* - * if (this.getLoc() < records.size()) - * { - * for (int k = this.getLoc(); k < records.size(); k++) - * { - * Record rec = ( Record ) records.get(k); - * - * this.setLoc(k + 1); - * if (rec.getSid() == RowRecord.sid) - * { - * if ((( RowRecord ) rec).getRowNumber() == rownum) - * { - * return ( RowRecord ) rec; - * } - * } - * } - * } - */ - - // return null; } /** @@ -1489,7 +1251,6 @@ public class Sheet implements Model retval.setVersion(( short ) 0x600); retval.setType(( short ) 0x010); - // retval.setBuild((short)0x10d3); retval.setBuild(( short ) 0x0dbb); retval.setBuildYear(( short ) 1996); retval.setHistoryBitMask(0xc1); @@ -1807,7 +1568,7 @@ public class Sheet implements Model * @see org.apache.poi.hssf.record.ColumnInfoRecord * @return record containing a ColumnInfoRecord */ - + // TODO change return type to ColumnInfoRecord protected Record createColInfo() { return ColumnInfoRecordsAggregate.createColInfo(); @@ -1830,12 +1591,12 @@ public class Sheet implements Model public boolean isGridsPrinted() { - if (gridset == null) { - gridset = (GridsetRecord)createGridset(); - //Insert the newlycreated Gridset record at the end of the record (just before the EOF) - int loc = findFirstRecordLocBySid(EOFRecord.sid); - records.add(loc, gridset); - } + if (gridset == null) { + gridset = (GridsetRecord)createGridset(); + //Insert the newlycreated Gridset record at the end of the record (just before the EOF) + int loc = findFirstRecordLocBySid(EOFRecord.sid); + records.add(loc, gridset); + } return !gridset.getGridset(); } @@ -1918,16 +1679,16 @@ public class Sheet implements Model } return retval; } - + /** - * get the index to the ExtendedFormatRecord "associated" with - * the column at specified 0-based index. (In this case, an - * ExtendedFormatRecord index is actually associated with a + * get the index to the ExtendedFormatRecord "associated" with + * the column at specified 0-based index. (In this case, an + * ExtendedFormatRecord index is actually associated with a * ColumnInfoRecord which spans 1 or more columns) *
* Returns the index to the default ExtendedFormatRecord (0xF) * if no ColumnInfoRecord exists that includes the column - * index specified. + * index specified. * @param column * @return index of ExtendedFormatRecord associated with * ColumnInfoRecord that includes the column index or the @@ -2116,38 +1877,38 @@ public class Sheet implements Model retval.setNumRefs(( short ) 0x0); return retval; } - - public short getTopRow() + + public short getTopRow() { - return (windowTwo==null) ? (short) 0 : windowTwo.getTopRow(); + return (windowTwo==null) ? (short) 0 : windowTwo.getTopRow(); } - - public void setTopRow(short topRow) + + public void setTopRow(short topRow) { - if (windowTwo!=null) - { - windowTwo.setTopRow(topRow); - } + if (windowTwo!=null) + { + windowTwo.setTopRow(topRow); + } } - + /** * Sets the left column to show in desktop window pane. * @param leftCol the left column to show in desktop window pane */ public void setLeftCol(short leftCol){ - if (windowTwo!=null) - { - windowTwo.setLeftCol(leftCol); - } + if (windowTwo!=null) + { + windowTwo.setLeftCol(leftCol); + } } - - public short getLeftCol() + + public short getLeftCol() { - return (windowTwo==null) ? (short) 0 : windowTwo.getLeftCol(); + return (windowTwo==null) ? (short) 0 : windowTwo.getLeftCol(); } - - - + + + /** * Returns the active row * @@ -2162,7 +1923,7 @@ public class Sheet implements Model } return selection.getActiveCellRow(); } - + /** * Sets the active row * @@ -2177,7 +1938,7 @@ public class Sheet implements Model selection.setActiveCellRow(row); } } - + /** * Returns the active column * @@ -2192,7 +1953,7 @@ public class Sheet implements Model } return selection.getActiveCellCol(); } - + /** * Sets the active column * @@ -2278,9 +2039,9 @@ public class Sheet implements Model } // Add space for UncalcedRecord if (uncalced) { - retval += UncalcedRecord.getStaticRecordSize(); + retval += UncalcedRecord.getStaticRecordSize(); } - + return retval; } @@ -2367,16 +2128,19 @@ public class Sheet implements Model */ public HeaderRecord getHeader () { - return header; + return header; } + public WindowTwoRecord getWindowTwo() { + return windowTwo; + } /** * Sets the HeaderRecord. * @param newHeader The new HeaderRecord for the sheet. */ public void setHeader (HeaderRecord newHeader) { - header = newHeader; + header = newHeader; } /** @@ -2385,7 +2149,7 @@ public class Sheet implements Model */ public FooterRecord getFooter () { - return footer; + return footer; } /** @@ -2394,7 +2158,7 @@ public class Sheet implements Model */ public void setFooter (FooterRecord newFooter) { - footer = newFooter; + footer = newFooter; } /** @@ -2403,7 +2167,7 @@ public class Sheet implements Model */ public PrintSetupRecord getPrintSetup () { - return printSetup; + return printSetup; } /** @@ -2412,7 +2176,7 @@ public class Sheet implements Model */ public void setPrintSetup (PrintSetupRecord newPrintSetup) { - printSetup = newPrintSetup; + printSetup = newPrintSetup; } /** @@ -2421,7 +2185,7 @@ public class Sheet implements Model */ public PrintGridlinesRecord getPrintGridlines () { - return printGridlines; + return printGridlines; } /** @@ -2430,7 +2194,7 @@ public class Sheet implements Model */ public void setPrintGridlines (PrintGridlinesRecord newPrintGridlines) { - printGridlines = newPrintGridlines; + printGridlines = newPrintGridlines; } /** @@ -2447,23 +2211,23 @@ public class Sheet implements Model * @return the size of the margin */ public double getMargin(short margin) { - if (getMargins()[margin] != null) - return margins[margin].getMargin(); - else { - switch ( margin ) - { - case LeftMargin: - return .75; - case RightMargin: - return .75; - case TopMargin: - return 1.0; - case BottomMargin: - return 1.0; - default : - throw new RuntimeException( "Unknown margin constant: " + margin ); - } - } + if (getMargins()[margin] != null) + return margins[margin].getMargin(); + else { + switch ( margin ) + { + case LeftMargin: + return .75; + case RightMargin: + return .75; + case TopMargin: + return 1.0; + case BottomMargin: + return 1.0; + default : + throw new RuntimeException( "Unknown margin constant: " + margin ); + } + } } /** @@ -2472,32 +2236,32 @@ public class Sheet implements Model * @param size the size of the margin */ public void setMargin(short margin, double size) { - Margin m = getMargins()[margin]; - if (m == null) { - switch ( margin ) - { - case LeftMargin: - m = new LeftMarginRecord(); - records.add( getDimsLoc() + 1, m ); - break; - case RightMargin: - m = new RightMarginRecord(); - records.add( getDimsLoc() + 1, m ); - break; - case TopMargin: - m = new TopMarginRecord(); - records.add( getDimsLoc() + 1, m ); - break; - case BottomMargin: - m = new BottomMarginRecord(); - records.add( getDimsLoc() + 1, m ); - break; - default : - throw new RuntimeException( "Unknown margin constant: " + margin ); - } - margins[margin] = m; - } - m.setMargin( size ); + Margin m = getMargins()[margin]; + if (m == null) { + switch ( margin ) + { + case LeftMargin: + m = new LeftMarginRecord(); + records.add( getDimsLoc() + 1, m ); + break; + case RightMargin: + m = new RightMarginRecord(); + records.add( getDimsLoc() + 1, m ); + break; + case TopMargin: + m = new TopMarginRecord(); + records.add( getDimsLoc() + 1, m ); + break; + case BottomMargin: + m = new BottomMarginRecord(); + records.add( getDimsLoc() + 1, m ); + break; + default : + throw new RuntimeException( "Unknown margin constant: " + margin ); + } + margins[margin] = m; + } + m.setMargin( size ); } public int getEofLoc() @@ -2514,10 +2278,10 @@ public class Sheet implements Model */ public void createFreezePane(int colSplit, int rowSplit, int topRow, int leftmostColumn ) { - int paneLoc = findFirstRecordLocBySid(PaneRecord.sid); - if (paneLoc != -1) - records.remove(paneLoc); - + int paneLoc = findFirstRecordLocBySid(PaneRecord.sid); + if (paneLoc != -1) + records.remove(paneLoc); + int loc = findFirstRecordLocBySid(WindowTwoRecord.sid); PaneRecord pane = new PaneRecord(); pane.setX((short)colSplit); @@ -2563,10 +2327,10 @@ public class Sheet implements Model */ public void createSplitPane(int xSplitPos, int ySplitPos, int topRow, int leftmostColumn, int activePane ) { - int paneLoc = findFirstRecordLocBySid(PaneRecord.sid); - if (paneLoc != -1) - records.remove(paneLoc); - + int paneLoc = findFirstRecordLocBySid(PaneRecord.sid); + if (paneLoc != -1) + records.remove(paneLoc); + int loc = findFirstRecordLocBySid(WindowTwoRecord.sid); PaneRecord r = new PaneRecord(); r.setX((short)xSplitPos); @@ -2583,7 +2347,7 @@ public class Sheet implements Model sel.setPane(PANE_LOWER_RIGHT); } - + /** * Returns the information regarding the currently configured pane (split or freeze). * @return null if no pane configured, or the pane information. @@ -2592,9 +2356,9 @@ public class Sheet implements Model PaneRecord rec = (PaneRecord)findFirstRecordBySid(PaneRecord.sid); if (rec == null) return null; - + return new PaneInformation(rec.getX(), rec.getY(), rec.getTopRow(), - rec.getLeftColumn(), (byte)rec.getActivePane(), windowTwo.getFreezePanes()); + rec.getLeftColumn(), (byte)rec.getActivePane(), windowTwo.getFreezePanes()); } public SelectionRecord getSelection() @@ -2660,12 +2424,12 @@ public class Sheet implements Model */ public ProtectRecord getProtect() { - if (protect == null) { - protect = (ProtectRecord)createProtect(); - //Insert the newlycreated protect record at the end of the record (just before the EOF) - int loc = findFirstRecordLocBySid(EOFRecord.sid); - records.add(loc, protect); - } + if (protect == null) { + protect = (ProtectRecord)createProtect(); + //Insert the newlycreated protect record at the end of the record (just before the EOF) + int loc = findFirstRecordLocBySid(EOFRecord.sid); + records.add(loc, protect); + } return protect; } @@ -2674,12 +2438,12 @@ public class Sheet implements Model */ public PasswordRecord getPassword() { - if (password == null) { - password = createPassword(); - //Insert the newly created password record at the end of the record (just before the EOF) - int loc = findFirstRecordLocBySid(EOFRecord.sid); - records.add(loc, password); - } + if (password == null) { + password = createPassword(); + //Insert the newly created password record at the end of the record (just before the EOF) + int loc = findFirstRecordLocBySid(EOFRecord.sid); + records.add(loc, password); + } return password; } @@ -2714,7 +2478,7 @@ public class Sheet implements Model * @return whether gridlines are displayed */ public boolean isDisplayGridlines() { - return windowTwo.getDisplayGridlines(); + return windowTwo.getDisplayGridlines(); } /** @@ -2730,7 +2494,7 @@ public class Sheet implements Model * @return whether formulas are displayed */ public boolean isDisplayFormulas() { - return windowTwo.getDisplayFormulas(); + return windowTwo.getDisplayFormulas(); } /** @@ -2746,24 +2510,24 @@ public class Sheet implements Model * @return whether RowColHeadings are displayed */ public boolean isDisplayRowColHeadings() { - return windowTwo.getDisplayRowColHeadings(); + return windowTwo.getDisplayRowColHeadings(); } - + /** - * @return whether an uncalced record must be inserted or not at generation - */ - public boolean getUncalced() { - return uncalced; - } - /** - * @param uncalced whether an uncalced record must be inserted or not at generation - */ - public void setUncalced(boolean uncalced) { - this.uncalced = uncalced; - } + * @return whether an uncalced record must be inserted or not at generation + */ + public boolean getUncalced() { + return uncalced; + } + /** + * @param uncalced whether an uncalced record must be inserted or not at generation + */ + public void setUncalced(boolean uncalced) { + this.uncalced = uncalced; + } - /** + /** * Returns the array of margins. If not created, will create. * * @return the array of marings. @@ -2771,7 +2535,7 @@ public class Sheet implements Model protected Margin[] getMargins() { if (margins == null) margins = new Margin[4]; - return margins; + return margins; } /** @@ -2789,11 +2553,11 @@ public class Sheet implements Model boolean noDrawingRecordsFound = (loc == -1); if (noDrawingRecordsFound) { - if(!createIfMissing) { - // None found, and not allowed to add in - return -1; - } - + if(!createIfMissing) { + // None found, and not allowed to add in + return -1; + } + EscherAggregate aggregate = new EscherAggregate( drawingManager ); loc = findFirstRecordLocBySid(EscherAggregate.sid); if (loc == -1) @@ -2847,43 +2611,43 @@ public class Sheet implements Model * @param breaks The page record to be shifted * @param start Starting "main" value to shift breaks * @param stop Ending "main" value to shift breaks - * @param count number of units (rows/columns) to shift by + * @param count number of units (rows/columns) to shift by */ public void shiftBreaks(PageBreakRecord breaks, short start, short stop, int count) { - - if(rowBreaks == null) - return; - Iterator iterator = breaks.getBreaksIterator(); - List shiftedBreak = new ArrayList(); - while(iterator.hasNext()) - { - PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); - short breakLocation = breakItem.main; - boolean inStart = (breakLocation >= start); - boolean inEnd = (breakLocation <= stop); - if(inStart && inEnd) - shiftedBreak.add(breakItem); - } - - iterator = shiftedBreak.iterator(); - while (iterator.hasNext()) { - PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); - breaks.removeBreak(breakItem.main); - breaks.addBreak((short)(breakItem.main+count), breakItem.subFrom, breakItem.subTo); - } + + if(rowBreaks == null) + return; + Iterator iterator = breaks.getBreaksIterator(); + List shiftedBreak = new ArrayList(); + while(iterator.hasNext()) + { + PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); + short breakLocation = breakItem.main; + boolean inStart = (breakLocation >= start); + boolean inEnd = (breakLocation <= stop); + if(inStart && inEnd) + shiftedBreak.add(breakItem); + } + + iterator = shiftedBreak.iterator(); + while (iterator.hasNext()) { + PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); + breaks.removeBreak(breakItem.main); + breaks.addBreak((short)(breakItem.main+count), breakItem.subFrom, breakItem.subTo); + } } - + /** * Sets a page break at the indicated row * @param row */ - public void setRowBreak(int row, short fromCol, short toCol) { - if (rowBreaks == null) { + public void setRowBreak(int row, short fromCol, short toCol) { + if (rowBreaks == null) { int loc = findFirstRecordLocBySid(WindowTwoRecord.sid); rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID); records.add(loc, rowBreaks); - } - rowBreaks.addBreak((short)row, fromCol, toCol); + } + rowBreaks.addBreak((short)row, fromCol, toCol); } /** @@ -2891,9 +2655,9 @@ public class Sheet implements Model * @param row */ public void removeRowBreak(int row) { - if (rowBreaks == null) - throw new IllegalArgumentException("Sheet does not define any row breaks"); - rowBreaks.removeBreak((short)row); + if (rowBreaks == null) + throw new IllegalArgumentException("Sheet does not define any row breaks"); + rowBreaks.removeBreak((short)row); } /** @@ -2902,7 +2666,7 @@ public class Sheet implements Model * @return true if the specified row has a page break */ public boolean isRowBroken(int row) { - return (rowBreaks == null) ? false : rowBreaks.getBreak((short)row) != null; + return (rowBreaks == null) ? false : rowBreaks.getBreak((short)row) != null; } /** @@ -2910,12 +2674,12 @@ public class Sheet implements Model * */ public void setColumnBreak(short column, short fromRow, short toRow) { - if (colBreaks == null) { + if (colBreaks == null) { int loc = findFirstRecordLocBySid(WindowTwoRecord.sid); colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID); records.add(loc, colBreaks); - } - colBreaks.addBreak(column, fromRow, toRow); + } + colBreaks.addBreak(column, fromRow, toRow); } /** @@ -2923,10 +2687,10 @@ public class Sheet implements Model * */ public void removeColumnBreak(short column) { - if (colBreaks == null) - throw new IllegalArgumentException("Sheet does not define any column breaks"); - - colBreaks.removeBreak(column); + if (colBreaks == null) + throw new IllegalArgumentException("Sheet does not define any column breaks"); + + colBreaks.removeBreak(column); } /** @@ -2935,9 +2699,9 @@ public class Sheet implements Model * @return true if the specified column has a page break */ public boolean isColumnBroken(short column) { - return (colBreaks == null) ? false : colBreaks.getBreak(column) != null; + return (colBreaks == null) ? false : colBreaks.getBreak(column) != null; } - + /** * Shifts the horizontal page breaks for the indicated count * @param startingRow @@ -2945,7 +2709,7 @@ public class Sheet implements Model * @param count */ public void shiftRowBreaks(int startingRow, int endingRow, int count) { - shiftBreaks(rowBreaks, (short)startingRow, (short)endingRow, (short)count); + shiftBreaks(rowBreaks, (short)startingRow, (short)endingRow, (short)count); } /** @@ -2955,39 +2719,39 @@ public class Sheet implements Model * @param count */ public void shiftColumnBreaks(short startingCol, short endingCol, short count) { - shiftBreaks(colBreaks, startingCol, endingCol, count); + shiftBreaks(colBreaks, startingCol, endingCol, count); } - + /** * Returns all the row page breaks * @return all the row page breaks */ public Iterator getRowBreaks() { - return rowBreaks.getBreaksIterator(); + return rowBreaks.getBreaksIterator(); } - + /** * Returns the number of row page breaks * @return the number of row page breaks */ public int getNumRowBreaks(){ - return (rowBreaks == null) ? 0 : (int)rowBreaks.getNumBreaks(); + return (rowBreaks == null) ? 0 : (int)rowBreaks.getNumBreaks(); } - + /** * Returns all the column page breaks * @return all the column page breaks */ public Iterator getColumnBreaks(){ - return colBreaks.getBreaksIterator(); + return colBreaks.getBreaksIterator(); } - + /** * Returns the number of column page breaks * @return the number of column page breaks */ public int getNumColumnBreaks(){ - return (colBreaks == null) ? 0 : (int)colBreaks.getNumBreaks(); + return (colBreaks == null) ? 0 : (int)colBreaks.getNumBreaks(); } public void setColumnGroupCollapsed( short columnNumber, boolean collapsed ) @@ -3030,213 +2794,40 @@ public class Sheet implements Model records.add(protIdx+2,srec); scenprotect = srec; } - } + } /** - * unprotect objects in the sheet (will not protect them, but any set to false are + * unprotect objects in the sheet (will not protect them, but any set to false are * unprotected. * @param sheet is unprotected (false = unprotect) * @param objects are unprotected (false = unprotect) * @param scenarios are unprotected (false = unprotect) */ public void unprotectSheet( boolean sheet, boolean objects, boolean scenarios ) { - int protIdx = -1; + if (!sheet) { ProtectRecord prec = getProtect(); prec.setProtect(sheet); PasswordRecord pass = getPassword(); pass.setPassword((short)00); - } + } if(objprotect != null && !objects) { objprotect.setProtect(false); } if(scenprotect != null && !scenarios) { scenprotect.setProtect(false); } - } + } /** * @return {sheet is protected, objects are proteced, scenarios are protected} */ public boolean[] isProtected() { - return new boolean[] { (protect != null && protect.getProtect()), + return new boolean[] { (protect != null && protect.getProtect()), (objprotect != null && objprotect.getProtect()), (scenprotect != null && scenprotect.getProtect())}; } - -// private void collapseColumn( short columnNumber ) -// { -// int idx = findColumnIdx( columnNumber, 0 ); -// if (idx == -1) -// return; -// -// // Find the start of the group. -// ColumnInfoRecord columnInfo = (ColumnInfoRecord) columnSizes.get( findStartOfColumnOutlineGroup( idx ) ); -// -// // Hide all the columns until the end of the group -// columnInfo = writeHidden( columnInfo, idx, true ); -// -// // Write collapse field -// setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, Boolean.TRUE); -// } -// private void expandColumn( short columnNumber ) -// { -// int idx = findColumnIdx( columnNumber, 0 ); -// if (idx == -1) -// return; -// -// // If it is already exapanded do nothing. -// if (!isColumnGroupCollapsed(idx)) -// return; -// -// // Find the start of the group. -// int startIdx = findStartOfColumnOutlineGroup( idx ); -// ColumnInfoRecord columnInfo = getColInfo( startIdx ); -// -// // Find the end of the group. -// int endIdx = findEndOfColumnOutlineGroup( idx ); -// ColumnInfoRecord endColumnInfo = getColInfo( endIdx ); -// -// // expand: -// // colapsed bit must be unset -// // hidden bit gets unset _if_ surrounding groups are expanded you can determine -// // this by looking at the hidden bit of the enclosing group. You will have -// // to look at the start and the end of the current group to determine which -// // is the enclosing group -// // hidden bit only is altered for this outline level. ie. don't uncollapse contained groups -// if (!isColumnGroupHiddenByParent( idx )) -// { -// for (int i = startIdx; i <= endIdx; i++) -// { -// if (columnInfo.getOutlineLevel() == getColInfo(i).getOutlineLevel()) -// getColInfo(i).setHidden( false ); -// } -// } -// -// // Write collapse field -// setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, Boolean.FALSE); -// } - -// private boolean isColumnGroupCollapsed( int idx ) -// { -// int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx ); -// if (endOfOutlineGroupIdx >= columnSizes.size()) -// return false; -// if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn()) -// return false; -// else -// return getColInfo(endOfOutlineGroupIdx+1).getCollapsed(); -// } - -// private boolean isColumnGroupHiddenByParent( int idx ) -// { -// // Look out outline details of end -// int endLevel; -// boolean endHidden; -// int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx ); -// if (endOfOutlineGroupIdx >= columnSizes.size()) -// { -// endLevel = 0; -// endHidden = false; -// } -// else if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn()) -// { -// endLevel = 0; -// endHidden = false; -// } -// else -// { -// endLevel = getColInfo( endOfOutlineGroupIdx + 1).getOutlineLevel(); -// endHidden = getColInfo( endOfOutlineGroupIdx + 1).getHidden(); -// } -// -// // Look out outline details of start -// int startLevel; -// boolean startHidden; -// int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup( idx ); -// if (startOfOutlineGroupIdx <= 0) -// { -// startLevel = 0; -// startHidden = false; -// } -// else if (getColInfo(startOfOutlineGroupIdx).getFirstColumn() - 1 != getColInfo(startOfOutlineGroupIdx - 1).getLastColumn()) -// { -// startLevel = 0; -// startHidden = false; -// } -// else -// { -// startLevel = getColInfo( startOfOutlineGroupIdx - 1).getOutlineLevel(); -// startHidden = getColInfo( startOfOutlineGroupIdx - 1 ).getHidden(); -// } -// -// if (endLevel > startLevel) -// { -// return endHidden; -// } -// else -// { -// return startHidden; -// } -// } - -// private ColumnInfoRecord getColInfo(int idx) -// { -// return columns.getColInfo( idx ); -// } - -// private int findStartOfColumnOutlineGroup(int idx) -// { -// // Find the start of the group. -// ColumnInfoRecord columnInfo = (ColumnInfoRecord) columnSizes.get( idx ); -// int level = columnInfo.getOutlineLevel(); -// while (idx != 0) -// { -// ColumnInfoRecord prevColumnInfo = (ColumnInfoRecord) columnSizes.get( idx - 1 ); -// if (columnInfo.getFirstColumn() - 1 == prevColumnInfo.getLastColumn()) -// { -// if (prevColumnInfo.getOutlineLevel() < level) -// { -// break; -// } -// idx--; -// columnInfo = prevColumnInfo; -// } -// else -// { -// break; -// } -// } -// -// return idx; -// } - -// private int findEndOfColumnOutlineGroup(int idx) -// { -// // Find the end of the group. -// ColumnInfoRecord columnInfo = (ColumnInfoRecord) columnSizes.get( idx ); -// int level = columnInfo.getOutlineLevel(); -// while (idx < columnSizes.size() - 1) -// { -// ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) columnSizes.get( idx + 1 ); -// if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn()) -// { -// if (nextColumnInfo.getOutlineLevel() < level) -// { -// break; -// } -// idx++; -// columnInfo = nextColumnInfo; -// } -// else -// { -// break; -// } -// } -// -// return idx; -// } public void groupRowRange(int fromRow, int toRow, boolean indent) { @@ -3272,8 +2863,8 @@ public class Sheet implements Model // Grab the guts record, adding if needed GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid ); if(guts == null) { - guts = new GutsRecord(); - records.add(guts); + guts = new GutsRecord(); + records.add(guts); } // Set the levels onto it guts.setRowLevelMax( (short) ( maxLevel + 1 ) ); @@ -3291,126 +2882,4 @@ public class Sheet implements Model rows.expandRow( row ); } } - - -// private void collapseRow( int rowNumber ) -// { -// -// // Find the start of the group. -// int startRow = rows.findStartOfRowOutlineGroup( rowNumber ); -// RowRecord rowRecord = (RowRecord) rows.getRow( startRow ); -// -// // Hide all the columns until the end of the group -// int lastRow = rows.writeHidden( rowRecord, startRow, true ); -// -// // Write collapse field -// if (getRow(lastRow + 1) != null) -// { -// getRow(lastRow + 1).setColapsed( true ); -// } -// else -// { -// RowRecord row = createRow( lastRow + 1); -// row.setColapsed( true ); -// rows.insertRow( row ); -// } -// } - -// private int findStartOfRowOutlineGroup(int row) -// { -// // Find the start of the group. -// RowRecord rowRecord = rows.getRow( row ); -// int level = rowRecord.getOutlineLevel(); -// int currentRow = row; -// while (rows.getRow( currentRow ) != null) -// { -// rowRecord = rows.getRow( currentRow ); -// if (rowRecord.getOutlineLevel() < level) -// return currentRow + 1; -// currentRow--; -// } -// -// return currentRow + 1; -// } - -// private int writeHidden( RowRecord rowRecord, int row, boolean hidden ) -// { -// int level = rowRecord.getOutlineLevel(); -// while (rowRecord != null && rows.getRow(row).getOutlineLevel() >= level) -// { -// rowRecord.setZeroHeight( hidden ); -// row++; -// rowRecord = rows.getRow( row ); -// } -// return row - 1; -// } - -// private int findEndOfRowOutlineGroup( int row ) -// { -// int level = getRow( row ).getOutlineLevel(); -// int currentRow; -// for (currentRow = row; currentRow < rows.getLastRowNum(); currentRow++) -// { -// if (getRow(currentRow) == null || getRow(currentRow).getOutlineLevel() < level) -// { -// break; -// } -// } -// -// return currentRow-1; -// } - -// private boolean isRowGroupCollapsed( int row ) -// { -// int collapseRow = rows.findEndOfRowOutlineGroup( row ) + 1; -// -// if (getRow(collapseRow) == null) -// return false; -// else -// return getRow( collapseRow ).getColapsed(); -// } - - -// private boolean isRowGroupHiddenByParent( int row ) -// { -// // Look out outline details of end -// int endLevel; -// boolean endHidden; -// int endOfOutlineGroupIdx = rows.findEndOfRowOutlineGroup( row ); -// if (getRow( endOfOutlineGroupIdx + 1 ) == null) -// { -// endLevel = 0; -// endHidden = false; -// } -// else -// { -// endLevel = getRow( endOfOutlineGroupIdx + 1).getOutlineLevel(); -// endHidden = getRow( endOfOutlineGroupIdx + 1).getZeroHeight(); -// } -// -// // Look out outline details of start -// int startLevel; -// boolean startHidden; -// int startOfOutlineGroupIdx = rows.findStartOfRowOutlineGroup( row ); -// if (startOfOutlineGroupIdx - 1 < 0 || getRow(startOfOutlineGroupIdx - 1) == null) -// { -// startLevel = 0; -// startHidden = false; -// } -// else -// { -// startLevel = getRow( startOfOutlineGroupIdx - 1).getOutlineLevel(); -// startHidden = getRow( startOfOutlineGroupIdx - 1 ).getZeroHeight(); -// } -// -// if (endLevel > startLevel) -// { -// return endHidden; -// } -// else -// { -// return startHidden; -// } -// } - } diff --git a/src/java/org/apache/poi/hssf/record/LabelRecord.java b/src/java/org/apache/poi/hssf/record/LabelRecord.java index 2dc2d940f..c3fd5fb3d 100644 --- a/src/java/org/apache/poi/hssf/record/LabelRecord.java +++ b/src/java/org/apache/poi/hssf/record/LabelRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,13 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - -/* - * LabelRecord.java - * - * Created on November 11, 2001, 12:51 PM - */ package org.apache.poi.hssf.record; /** @@ -33,14 +26,10 @@ package org.apache.poi.hssf.record; * @version 2.0-pre * @see org.apache.poi.hssf.record.LabelSSTRecord */ - -public class LabelRecord - extends Record - implements CellValueRecordInterface -{ +public final class LabelRecord extends Record implements CellValueRecordInterface { public final static short sid = 0x204; - //private short field_1_row; - private int field_1_row; + + private int field_1_row; private short field_2_column; private short field_3_xf_index; private short field_4_string_len; @@ -85,35 +74,30 @@ public class LabelRecord protected void fillFields(RecordInputStream in) { - //field_1_row = LittleEndian.getShort(data, 0 + offset); field_1_row = in.readUShort(); field_2_column = in.readShort(); field_3_xf_index = in.readShort(); field_4_string_len = in.readShort(); field_5_unicode_flag = in.readByte(); if (field_4_string_len > 0) { - if (isUnCompressedUnicode()) { - field_6_value = in.readUnicodeLEString(field_4_string_len); - } else { - field_6_value = in.readCompressedUnicode(field_4_string_len); + if (isUnCompressedUnicode()) { + field_6_value = in.readUnicodeLEString(field_4_string_len); + } else { + field_6_value = in.readCompressedUnicode(field_4_string_len); + } + } else { + field_6_value = ""; } - } else field_6_value = null; } -/* READ ONLY ACCESS... THIS IS FOR COMPATIBILITY ONLY...USE LABELSST! - public void setRow(short row) { - field_1_row = row; - } - - public void setColumn(short col) { - field_2_column = col; - } - - public void setXFIndex(short index) { - field_3_xf_index = index; - } - */ - //public short getRow() +/* + * READ ONLY ACCESS... THIS IS FOR COMPATIBILITY ONLY...USE LABELSST! public + * void setRow(short row) { field_1_row = row; } + * + * public void setColumn(short col) { field_2_column = col; } + * + * public void setXFIndex(short index) { field_3_xf_index = index; } + */ public int getRow() { return field_1_row; diff --git a/src/java/org/apache/poi/hssf/record/PaneRecord.java b/src/java/org/apache/poi/hssf/record/PaneRecord.java index 6f13ad092..f02e41165 100644 --- a/src/java/org/apache/poi/hssf/record/PaneRecord.java +++ b/src/java/org/apache/poi/hssf/record/PaneRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,13 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record; - -import org.apache.poi.util.*; +import org.apache.poi.util.HexDump; +import org.apache.poi.util.LittleEndian; /** * Describes the frozen and unfozen panes. @@ -30,9 +28,7 @@ import org.apache.poi.util.*; * @author Glen Stampoultzis (glens at apache.org) */ -public class PaneRecord - extends Record -{ +public final class PaneRecord extends Record { public final static short sid = 0x41; private short field_1_x; private short field_2_y; @@ -42,7 +38,10 @@ public class PaneRecord public final static short ACTIVE_PANE_LOWER_RIGHT = 0; public final static short ACTIVE_PANE_UPPER_RIGHT = 1; public final static short ACTIVE_PANE_LOWER_LEFT = 2; + // TODO - remove obsolete field (it was deprecated May-2008 v3.1) + /** @deprecated use ACTIVE_PANE_UPPER_LEFT */ public final static short ACTIVE_PANE_UPER_LEFT = 3; + public final static short ACTIVE_PANE_UPPER_LEFT = 3; public PaneRecord() @@ -82,7 +81,6 @@ public class PaneRecord field_3_topRow = in.readShort(); field_4_leftColumn = in.readShort(); field_5_activePane = in.readShort(); - } public String toString() @@ -229,7 +227,7 @@ public class PaneRecord * ACTIVE_PANE_LOWER_RIGHT * ACTIVE_PANE_UPPER_RIGHT * ACTIVE_PANE_LOWER_LEFT - * ACTIVE_PANE_UPER_LEFT + * ACTIVE_PANE_UPPER_LEFT */ public short getActivePane() { @@ -244,16 +242,10 @@ public class PaneRecord * ACTIVE_PANE_LOWER_RIGHT * ACTIVE_PANE_UPPER_RIGHT * ACTIVE_PANE_LOWER_LEFT - * ACTIVE_PANE_UPER_LEFT + * ACTIVE_PANE_UPPER_LEFT */ public void setActivePane(short field_5_activePane) { this.field_5_activePane = field_5_activePane; } - - -} // END OF CLASS - - - - +} diff --git a/src/java/org/apache/poi/hssf/record/RowRecord.java b/src/java/org/apache/poi/hssf/record/RowRecord.java index a2815d287..cbfc0ec59 100644 --- a/src/java/org/apache/poi/hssf/record/RowRecord.java +++ b/src/java/org/apache/poi/hssf/record/RowRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record; @@ -31,20 +29,18 @@ import org.apache.poi.util.LittleEndian; * @author Jason Height (jheight at chariot dot net dot au) * @version 2.0-pre */ - -public class RowRecord - extends Record - implements Comparable -{ - public final static short sid = 0x208; +public final class RowRecord extends Record implements Comparable { + public final static short sid = 0x208; - /** The maximum row number that excel can handle (zero bazed) ie 65536 rows is + private static final int OPTION_BITS_ALWAYS_SET = 0x0100; + private static final int DEFAULT_HEIGHT_BIT = 0x8000; + + /** The maximum row number that excel can handle (zero based) ie 65536 rows is * max number of rows. */ public final static int MAX_ROW_NUMBER = 65535; - //private short field_1_row_number; - private int field_1_row_number; + private int field_1_row_number; private short field_2_first_col; private short field_3_last_col; // plus 1 private short field_4_height; @@ -52,7 +48,8 @@ public class RowRecord // for generated sheets. private short field_6_reserved; - private short field_7_option_flags; + /** 16 bit options flags */ + private int field_7_option_flags; private static final BitField outlineLevel = BitFieldFactory.getInstance(0x07); // bit 3 reserved @@ -62,8 +59,17 @@ public class RowRecord private static final BitField formatted = BitFieldFactory.getInstance(0x80); private short field_8_xf_index; // only if isFormatted - public RowRecord() - { + public RowRecord(int rowNumber) { + field_1_row_number = rowNumber; + field_2_first_col = -1; + field_3_last_col = -1; + field_4_height = (short)DEFAULT_HEIGHT_BIT; + field_4_height = (short)DEFAULT_HEIGHT_BIT; + field_5_optimize = ( short ) 0; + field_6_reserved = ( short ) 0; + field_7_option_flags = OPTION_BITS_ALWAYS_SET; // seems necessary for outlining + + field_8_xf_index = ( short ) 0xf; } /** @@ -86,7 +92,6 @@ public class RowRecord protected void fillFields(RecordInputStream in) { - //field_1_row_number = LittleEndian.getShort(data, 0 + offset); field_1_row_number = in.readUShort(); field_2_first_col = in.readShort(); field_3_last_col = in.readShort(); @@ -156,7 +161,7 @@ public class RowRecord public void setOptionFlags(short options) { - field_7_option_flags = options; + field_7_option_flags = options | OPTION_BITS_ALWAYS_SET; } // option bitfields @@ -169,20 +174,18 @@ public class RowRecord public void setOutlineLevel(short ol) { - field_7_option_flags = - outlineLevel.setShortValue(field_7_option_flags, ol); + field_7_option_flags = outlineLevel.setValue(field_7_option_flags, ol); } /** - * set whether or not to colapse this row - * @param c - colapse or not + * set whether or not to collapse this row + * @param c - collapse or not * @see #setOptionFlags(short) */ public void setColapsed(boolean c) { - field_7_option_flags = colapsed.setShortBoolean(field_7_option_flags, - c); + field_7_option_flags = colapsed.setBoolean(field_7_option_flags, c); } /** @@ -193,8 +196,7 @@ public class RowRecord public void setZeroHeight(boolean z) { - field_7_option_flags = - zeroHeight.setShortBoolean(field_7_option_flags, z); + field_7_option_flags = zeroHeight.setBoolean(field_7_option_flags, z); } /** @@ -205,8 +207,7 @@ public class RowRecord public void setBadFontHeight(boolean f) { - field_7_option_flags = - badFontHeight.setShortBoolean(field_7_option_flags, f); + field_7_option_flags = badFontHeight.setBoolean(field_7_option_flags, f); } /** @@ -217,8 +218,7 @@ public class RowRecord public void setFormatted(boolean f) { - field_7_option_flags = formatted.setShortBoolean(field_7_option_flags, - f); + field_7_option_flags = formatted.setBoolean(field_7_option_flags, f); } // end bitfields @@ -293,7 +293,7 @@ public class RowRecord public short getOptionFlags() { - return field_7_option_flags; + return (short)field_7_option_flags; } // option bitfields @@ -306,7 +306,7 @@ public class RowRecord public short getOutlineLevel() { - return outlineLevel.getShortValue(field_7_option_flags); + return (short)outlineLevel.getValue(field_7_option_flags); } /** @@ -410,7 +410,6 @@ public class RowRecord { LittleEndian.putShort(data, 0 + offset, sid); LittleEndian.putShort(data, 2 + offset, ( short ) 16); - //LittleEndian.putShort(data, 4 + offset, getRowNumber()); LittleEndian.putShort(data, 4 + offset, ( short ) getRowNumber()); LittleEndian.putShort(data, 6 + offset, getFirstCol() == -1 ? (short)0 : getFirstCol()); LittleEndian.putShort(data, 8 + offset, getLastCol() == -1 ? (short)0 : getLastCol()); @@ -419,7 +418,6 @@ public class RowRecord LittleEndian.putShort(data, 14 + offset, field_6_reserved); LittleEndian.putShort(data, 16 + offset, getOptionFlags()); -// LittleEndian.putShort(data,18,getOutlineLevel()); LittleEndian.putShort(data, 18 + offset, getXFIndex()); return getRecordSize(); } @@ -469,8 +467,7 @@ public class RowRecord } public Object clone() { - RowRecord rec = new RowRecord(); - rec.field_1_row_number = field_1_row_number; + RowRecord rec = new RowRecord(field_1_row_number); rec.field_2_first_col = field_2_first_col; rec.field_3_last_col = field_3_last_col; rec.field_4_height = field_4_height; diff --git a/src/java/org/apache/poi/hssf/record/WindowOneRecord.java b/src/java/org/apache/poi/hssf/record/WindowOneRecord.java index f101cb59a..4c836f2ee 100644 --- a/src/java/org/apache/poi/hssf/record/WindowOneRecord.java +++ b/src/java/org/apache/poi/hssf/record/WindowOneRecord.java @@ -57,8 +57,8 @@ public class WindowOneRecord BitFieldFactory.getInstance(0x20); // display tabs at the bottom // all the rest are "reserved" - private short field_6_selected_tab; - private short field_7_displayed_tab; + private int field_6_active_sheet; + private int field_7_first_visible_tab; private short field_8_num_selected_tabs; private short field_9_tab_width_ratio; @@ -91,8 +91,8 @@ public class WindowOneRecord field_3_width = in.readShort(); field_4_height = in.readShort(); field_5_options = in.readShort(); - field_6_selected_tab = in.readShort(); - field_7_displayed_tab = in.readShort(); + field_6_active_sheet = in.readShort(); + field_7_first_visible_tab = in.readShort(); field_8_num_selected_tabs = in.readShort(); field_9_tab_width_ratio = in.readShort(); } @@ -202,24 +202,33 @@ public class WindowOneRecord // end bitfields + public void setActiveSheetIndex(int index) { + field_6_active_sheet = index; + } /** - * set the selected tab number - * @param s tab number + * deprecated May 2008 + * @deprecated - Misleading name - use setActiveSheetIndex() */ - public void setSelectedTab(short s) { - field_6_selected_tab = s; + setActiveSheetIndex(s); } /** - * set the displayed tab number - * @param t tab number + * Sets the first visible sheet in the worksheet tab-bar. This method does not + * hide, select or focus sheets. It just sets the scroll position in the tab-bar. + * @param t the sheet index of the tab that will become the first in the tab-bar */ + public void setFirstVisibleTab(int t) { + field_7_first_visible_tab = t; + } - public void setDisplayedTab(short t) - { - field_7_displayed_tab = t; + /** + * deprecated May 2008 + * @deprecated - Misleading name - use setFirstVisibleTab() + */ + public void setDisplayedTab(short t) { + setFirstVisibleTab(t); } /** @@ -347,24 +356,36 @@ public class WindowOneRecord // end options bitfields + /** - * get the selected tab number - * @return Tab number + * @return the index of the currently displayed sheet + */ + public int getActiveSheetIndex() { + return field_6_active_sheet; + } + /** + * deprecated May 2008 + * @deprecated - Misleading name - use getActiveSheetIndex() */ - public short getSelectedTab() { - return field_6_selected_tab; + return (short) getActiveSheetIndex(); } /** - * get the displayed tab number - * @return Tab number + * @return the first visible sheet in the worksheet tab-bar. + * I.E. the scroll position of the tab-bar. + */ + public int getFirstVisibleTab() { + return field_7_first_visible_tab; + } + /** + * deprecated May 2008 + * @deprecated - Misleading name - use getFirstVisibleTab() */ - public short getDisplayedTab() { - return field_7_displayed_tab; + return (short) getFirstVisibleTab(); } /** @@ -412,10 +433,10 @@ public class WindowOneRecord .append(getDisplayVerticalScrollbar()).append("\n"); buffer.append(" .tabs = ").append(getDisplayTabs()) .append("\n"); - buffer.append(" .selectedtab = ") - .append(Integer.toHexString(getSelectedTab())).append("\n"); - buffer.append(" .displayedtab = ") - .append(Integer.toHexString(getDisplayedTab())).append("\n"); + buffer.append(" .activeSheet = ") + .append(Integer.toHexString(getActiveSheetIndex())).append("\n"); + buffer.append(" .firstVisibleTab = ") + .append(Integer.toHexString(getFirstVisibleTab())).append("\n"); buffer.append(" .numselectedtabs = ") .append(Integer.toHexString(getNumSelectedTabs())).append("\n"); buffer.append(" .tabwidthratio = ") @@ -434,8 +455,8 @@ public class WindowOneRecord LittleEndian.putShort(data, 8 + offset, getWidth()); LittleEndian.putShort(data, 10 + offset, getHeight()); LittleEndian.putShort(data, 12 + offset, getOptions()); - LittleEndian.putShort(data, 14 + offset, getSelectedTab()); - LittleEndian.putShort(data, 16 + offset, getDisplayedTab()); + LittleEndian.putUShort(data, 14 + offset, getActiveSheetIndex()); + LittleEndian.putUShort(data, 16 + offset, getFirstVisibleTab()); LittleEndian.putShort(data, 18 + offset, getNumSelectedTabs()); LittleEndian.putShort(data, 20 + offset, getTabWidthRatio()); return getRecordSize(); diff --git a/src/java/org/apache/poi/hssf/record/WindowTwoRecord.java b/src/java/org/apache/poi/hssf/record/WindowTwoRecord.java index f6b51996d..e2bb6d560 100644 --- a/src/java/org/apache/poi/hssf/record/WindowTwoRecord.java +++ b/src/java/org/apache/poi/hssf/record/WindowTwoRecord.java @@ -54,7 +54,7 @@ public class WindowTwoRecord private BitField displayGuts = BitFieldFactory.getInstance(0x80); private BitField freezePanesNoSplit = BitFieldFactory.getInstance(0x100); private BitField selected = BitFieldFactory.getInstance(0x200); - private BitField paged = BitFieldFactory.getInstance(0x400); + private BitField active = BitFieldFactory.getInstance(0x400); private BitField savedInPageBreakPreview = BitFieldFactory.getInstance(0x800); // 4-7 reserved @@ -222,12 +222,16 @@ public class WindowTwoRecord * is the sheet currently displayed in the window * @param p displayed or not */ - - public void setPaged(boolean p) - { - field_1_options = paged.setShortBoolean(field_1_options, p); + public void setActive(boolean p) { + field_1_options = active.setShortBoolean(field_1_options, p); + } + /** + * deprecated May 2008 + * @deprecated use setActive() + */ + public void setPaged(boolean p) { + setActive(p); } - /** * was the sheet saved in page break view * @param p pagebreaksaved or not @@ -416,9 +420,15 @@ public class WindowTwoRecord * @return displayed or not */ - public boolean getPaged() - { - return paged.isSet(field_1_options); + public boolean isActive() { + return active.isSet(field_1_options); + } + /** + * deprecated May 2008 + * @deprecated use isActive() + */ + public boolean getPaged() { + return isActive(); } /** @@ -520,7 +530,7 @@ public class WindowTwoRecord .append(getFreezePanesNoSplit()).append("\n"); buffer.append(" .selected = ").append(getSelected()) .append("\n"); - buffer.append(" .paged = ").append(getPaged()) + buffer.append(" .active = ").append(isActive()) .append("\n"); buffer.append(" .svdinpgbrkpv= ") .append(getSavedInPageBreakPreview()).append("\n"); diff --git a/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java index bed1f0748..65af632d3 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java @@ -35,19 +35,17 @@ import java.util.TreeMap; * @author Jason Height (jheight at chariot dot net dot au) */ -public class RowRecordsAggregate - extends Record -{ - int firstrow = -1; - int lastrow = -1; - Map records = null; - int size = 0; +public final class RowRecordsAggregate extends Record { + private int firstrow = -1; + private int lastrow = -1; + private Map records = null; // TODO - use a proper key in this map + private int size = 0; /** Creates a new instance of ValueRecordsAggregate */ public RowRecordsAggregate() { - records = new TreeMap(); + records = new TreeMap(); } public void insertRow(RowRecord row) @@ -74,15 +72,13 @@ public class RowRecordsAggregate records.remove(row); } - public RowRecord getRow(int rownum) - { - // Row must be between 0 and 65535 - if(rownum < 0 || rownum > 65535) { - throw new IllegalArgumentException("The row number must be between 0 and 65535"); - } + public RowRecord getRow(int rownum) { + // Row must be between 0 and 65535 + if(rownum < 0 || rownum > 65535) { + throw new IllegalArgumentException("The row number must be between 0 and 65535"); + } - RowRecord row = new RowRecord(); - row.setRowNumber(rownum); + RowRecord row = new RowRecord(rownum); return ( RowRecord ) records.get(row); } @@ -333,7 +329,7 @@ public class RowRecordsAggregate // Find the start of the group. int startRow = findStartOfRowOutlineGroup( rowNumber ); - RowRecord rowRecord = (RowRecord) getRow( startRow ); + RowRecord rowRecord = getRow( startRow ); // Hide all the columns until the end of the group int lastRow = writeHidden( rowRecord, startRow, true ); @@ -358,17 +354,8 @@ public class RowRecordsAggregate * @return RowRecord created for the passed in row number * @see org.apache.poi.hssf.record.RowRecord */ - public static RowRecord createRow(int row) - { - RowRecord rowrec = new RowRecord(); - - //rowrec.setRowNumber(( short ) row); - rowrec.setRowNumber(row); - rowrec.setHeight(( short ) 0xff); - rowrec.setOptimize(( short ) 0x0); - rowrec.setOptionFlags(( short ) 0x100); // seems necessary for outlining - rowrec.setXFIndex(( short ) 0xf); - return rowrec; + public static RowRecord createRow(int rowNumber) { + return new RowRecord(rowNumber); } public boolean isRowGroupCollapsed( int row ) @@ -399,12 +386,12 @@ public class RowRecordsAggregate int endIdx = findEndOfRowOutlineGroup( idx ); // expand: - // colapsed bit must be unset + // collapsed bit must be unset // hidden bit gets unset _if_ surrounding groups are expanded you can determine // this by looking at the hidden bit of the enclosing group. You will have // to look at the start and the end of the current group to determine which // is the enclosing group - // hidden bit only is altered for this outline level. ie. don't uncollapse contained groups + // hidden bit only is altered for this outline level. ie. don't un-collapse contained groups if ( !isRowGroupHiddenByParent( idx ) ) { for ( int i = startIdx; i <= endIdx; i++ ) diff --git a/src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java b/src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java index 7d44b008f..12f26bfde 100755 --- a/src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java +++ b/src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java @@ -24,9 +24,8 @@ import org.apache.poi.util.LittleEndian; /** * To support Constant Values (2.5.7) as required by the CRN record. - * This class should probably also be used for two dimensional arrays which are encoded by + * This class is also used for two dimensional arrays which are encoded by * EXTERNALNAME (5.39) records and Array tokens.

- * TODO - code in ArrayPtg should be merged with this code. It currently supports only 2 of the constant types * * @author Josh Micich */ diff --git a/src/java/org/apache/poi/hssf/record/constant/ErrorConstant.java b/src/java/org/apache/poi/hssf/record/constant/ErrorConstant.java index 2fc79a948..3421dd4a8 100644 --- a/src/java/org/apache/poi/hssf/record/constant/ErrorConstant.java +++ b/src/java/org/apache/poi/hssf/record/constant/ErrorConstant.java @@ -47,18 +47,31 @@ public class ErrorConstant { public int getErrorCode() { return _errorCode; } + public String getText() { + if(HSSFErrorConstants.isValidCode(_errorCode)) { + return HSSFErrorConstants.getText(_errorCode); + } + return "unknown error code (" + _errorCode + ")"; + } public static ErrorConstant valueOf(int errorCode) { switch (errorCode) { - case HSSFErrorConstants.ERROR_NULL: return NULL; - case HSSFErrorConstants.ERROR_DIV_0: return DIV_0; - case HSSFErrorConstants.ERROR_VALUE: return VALUE; - case HSSFErrorConstants.ERROR_REF: return REF; - case HSSFErrorConstants.ERROR_NAME: return NAME; - case HSSFErrorConstants.ERROR_NUM: return NUM; - case HSSFErrorConstants.ERROR_NA: return NA; + case HSSFErrorConstants.ERROR_NULL: return NULL; + case HSSFErrorConstants.ERROR_DIV_0: return DIV_0; + case HSSFErrorConstants.ERROR_VALUE: return VALUE; + case HSSFErrorConstants.ERROR_REF: return REF; + case HSSFErrorConstants.ERROR_NAME: return NAME; + case HSSFErrorConstants.ERROR_NUM: return NUM; + case HSSFErrorConstants.ERROR_NA: return NA; } System.err.println("Warning - unexpected error code (" + errorCode + ")"); return new ErrorConstant(errorCode); } + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(getText()); + sb.append("]"); + return sb.toString(); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java index 01942be55..251009b28 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java @@ -17,22 +17,17 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.util.BitField; -import org.apache.poi.util.BitFieldFactory; -import org.apache.poi.util.StringUtil; - -import org.apache.poi.hssf.util.CellReference; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.hssf.record.RecordFormatException; import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.hssf.record.SSTRecord; import org.apache.poi.hssf.record.UnicodeString; +import org.apache.poi.hssf.record.constant.ConstantValueParser; +import org.apache.poi.hssf.record.constant.ErrorConstant; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.util.LittleEndian; /** * ArrayPtg - handles arrays * - * The ArrayPtg is a little wierd, the size of the Ptg when parsing initially only + * The ArrayPtg is a little weird, the size of the Ptg when parsing initially only * includes the Ptg sid and the reserved bytes. The next Ptg in the expression then follows. * It is only after the "size" of all the Ptgs is met, that the ArrayPtg data is actually * held after this. So Ptg.createParsedExpression keeps track of the number of @@ -40,209 +35,160 @@ import org.apache.poi.hssf.record.UnicodeString; * * @author Jason Height (jheight at chariot dot net dot au) */ +public class ArrayPtg extends Ptg { + public static final byte sid = 0x20; -public class ArrayPtg extends Ptg -{ - public final static byte sid = 0x20; - protected byte field_1_reserved; - protected byte field_2_reserved; - protected byte field_3_reserved; - protected byte field_4_reserved; - protected byte field_5_reserved; - protected byte field_6_reserved; - protected byte field_7_reserved; - - - protected short token_1_columns; - protected short token_2_rows; - protected Object[][] token_3_arrayValues; + private static final int RESERVED_FIELD_LEN = 7; + // TODO - fix up field visibility and subclasses + protected byte[] field_1_reserved; + // data from these fields comes after the Ptg data of all tokens in current formula + protected short token_1_columns; + protected short token_2_rows; + protected Object[] token_3_arrayValues; - protected ArrayPtg() { - //Required for clone methods - } + protected ArrayPtg() { + //Required for clone methods + } - public ArrayPtg(RecordInputStream in) - { - field_1_reserved = in.readByte(); - field_2_reserved = in.readByte(); - field_3_reserved = in.readByte(); - field_4_reserved = in.readByte(); - field_5_reserved = in.readByte(); - field_6_reserved = in.readByte(); - field_7_reserved = in.readByte(); - } - - /** - * Read in the actual token (array) values. This occurs - * AFTER the last Ptg in the expression. - * See page 304-305 of Excel97-2007BinaryFileFormat(xls)Specification.pdf - */ - public void readTokenValues(RecordInputStream in) { - token_1_columns = (short)(0x00ff & in.readByte()); - token_2_rows = in.readShort(); - - //The token_1_columns and token_2_rows do not follow the documentation. - //The number of physical rows and columns is actually +1 of these values. - //Which is not explicitly documented. - token_1_columns++; - token_2_rows++; - - token_3_arrayValues = new Object[token_1_columns][token_2_rows]; - - for (int x=0;x= token_1_columns) { + throw new IllegalArgumentException("Specified colIx (" + colIx + + ") is outside the allowed range (0.." + (token_1_columns-1) + ")"); + } + if(rowIx < 0 || rowIx >= token_2_rows) { + throw new IllegalArgumentException("Specified rowIx (" + rowIx + + ") is outside the allowed range (0.." + (token_2_rows-1) + ")"); + } + return rowIx * token_1_columns + colIx; + } - buffer.append("columns = ").append(getColumnCount()).append("\n"); - buffer.append("rows = ").append(getRowCount()).append("\n"); - for (int x=0;x 0) { + b.append(";"); + } + for (int y=0;y 0) { + b.append(","); + } + Object o = token_3_arrayValues[getValueIndex(x, y)]; + b.append(getConstantText(o)); + } + } + b.append("}"); + return b.toString(); + } + + private static String getConstantText(Object o) { - public String toFormulaString(HSSFWorkbook book) - { - StringBuffer b = new StringBuffer(); - b.append("{"); - for (int x=0;x255) { - throw new IllegalArgumentException("String literals in formulas cant be bigger than 255 characters ASCII"); + if (value.length() > 255) { + throw new IllegalArgumentException( + "String literals in formulas can't be bigger than 255 characters ASCII"); } - this.field_2_options=0; - field_2_options = (byte)this.fHighByte.setBoolean(field_2_options, StringUtil.hasMultibyte(value)); - this.field_3_string=value; - this.field_1_length=value.length(); //for the moment, we support only ASCII strings in formulas we create + field_2_options = 0; + field_2_options = (byte) fHighByte.setBoolean(field_2_options, StringUtil + .hasMultibyte(value)); + field_3_string = value; + field_1_length = value.length(); // for the moment, we support only ASCII strings in formulas we create } - /* - public void setValue(String value) - { - field_1_value = value; - }*/ - - - public String getValue() - { + public String getValue() { return field_3_string; } - public void writeBytes(byte [] array, int offset) - { - array[ offset + 0 ] = sid; - array[ offset + 1 ] = (byte)field_1_length; - array[ offset + 2 ] = field_2_options; + public void writeBytes(byte[] array, int offset) { + array[offset + 0] = sid; + array[offset + 1] = (byte) field_1_length; + array[offset + 2] = field_2_options; if (fHighByte.isSet(field_2_options)) { - StringUtil.putUnicodeLE(getValue(),array,offset+3); - }else { - StringUtil.putCompressedUnicode(getValue(),array,offset+3); - } - } - - public int getSize() - { - if (fHighByte.isSet(field_2_options)) { - return 2*field_1_length+3; + StringUtil.putUnicodeLE(getValue(), array, offset + 3); } else { - return field_1_length+3; + StringUtil.putCompressedUnicode(getValue(), array, offset + 3); } } - public String toFormulaString(HSSFWorkbook book) - { - return "\""+getValue()+"\""; + public int getSize() { + if (fHighByte.isSet(field_2_options)) { + return 2 * field_1_length + 3; + } else { + return field_1_length + 3; + } } + + public String toFormulaString(HSSFWorkbook book) { + String value = field_3_string; + int len = value.length(); + StringBuffer sb = new StringBuffer(len + 4); + sb.append(FORMULA_DELIMITER); + + for (int i = 0; i < len; i++) { + char c = value.charAt(i); + if (c == FORMULA_DELIMITER) { + sb.append(FORMULA_DELIMITER); + } + sb.append(c); + } + + sb.append(FORMULA_DELIMITER); + return sb.toString(); + } + public byte getDefaultOperandClass() { - return Ptg.CLASS_VALUE; - } + return Ptg.CLASS_VALUE; + } - public Object clone() { - StringPtg ptg = new StringPtg(); - ptg.field_1_length = field_1_length; - ptg.field_2_options=field_2_options; - ptg.field_3_string=field_3_string; - return ptg; - } + public Object clone() { + StringPtg ptg = new StringPtg(); + ptg.field_1_length = field_1_length; + ptg.field_2_options = field_2_options; + ptg.field_3_string = field_3_string; + return ptg; + } + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(field_3_string); + sb.append("]"); + return sb.toString(); + } } - diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java index 4b9a64c1c..5ae98b39f 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java @@ -22,79 +22,11 @@ import org.apache.poi.hssf.record.formula.Ptg; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * */ -public final class Area2DEval implements AreaEval { -// TODO -refactor with Area3DEval - private final AreaPtg _delegate; +public final class Area2DEval extends AreaEvalBase { - private final ValueEval[] _values; - - public Area2DEval(Ptg ptg, ValueEval[] values) { - if(ptg == null) { - throw new IllegalArgumentException("ptg must not be null"); - } - if(values == null) { - throw new IllegalArgumentException("values must not be null"); - } - for(int i=values.length-1; i>=0; i--) { - if(values[i] == null) { - throw new IllegalArgumentException("value array elements must not be null"); - } - } - // TODO - check size of array vs size of AreaPtg - _delegate = (AreaPtg) ptg; - _values = values; - } - - public int getFirstColumn() { - return _delegate.getFirstColumn(); - } - - public int getFirstRow() { - return _delegate.getFirstRow(); - } - - public int getLastColumn() { - return _delegate.getLastColumn(); - } - - public int getLastRow() { - return _delegate.getLastRow(); - } - - public ValueEval[] getValues() { - return _values; - } - - public ValueEval getValueAt(int row, int col) { - ValueEval retval; - int index = ((row-getFirstRow())*(getLastColumn()-getFirstColumn()+1))+(col-getFirstColumn()); - if (index <0 || index >= _values.length) - retval = ErrorEval.VALUE_INVALID; - else - retval = _values[index]; - return retval; - } - - public boolean contains(int row, int col) { - return (getFirstRow() <= row) && (getLastRow() >= row) - && (getFirstColumn() <= col) && (getLastColumn() >= col); - } - - public boolean containsRow(int row) { - return (getFirstRow() <= row) && (getLastRow() >= row); - } - - public boolean containsColumn(short col) { - return (getFirstColumn() <= col) && (getLastColumn() >= col); - } - - public boolean isColumn() { - return _delegate.getFirstColumn() == _delegate.getLastColumn(); - } - - public boolean isRow() { - return _delegate.getFirstRow() == _delegate.getLastRow(); - } -} + public Area2DEval(Ptg ptg, ValueEval[] values) { + super((AreaPtg) ptg, values); + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java index 2f539142d..89209e21b 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java @@ -22,84 +22,18 @@ import org.apache.poi.hssf.record.formula.Ptg; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * */ -public final class Area3DEval implements AreaEval { - // TODO -refactor with Area3DEval - private final Area3DPtg _delegate; +public final class Area3DEval extends AreaEvalBase { - private final ValueEval[] _values; + private final int _externSheetIndex; - public Area3DEval(Ptg ptg, ValueEval[] values) { - if(ptg == null) { - throw new IllegalArgumentException("ptg must not be null"); - } - if(values == null) { - throw new IllegalArgumentException("values must not be null"); - } - for(int i=values.length-1; i>=0; i--) { - if(values[i] == null) { - throw new IllegalArgumentException("value array elements must not be null"); - } - } - // TODO - check size of array vs size of AreaPtg - _values = values; - _delegate = (Area3DPtg) ptg; - } + public Area3DEval(Ptg ptg, ValueEval[] values) { + super((Area3DPtg) ptg, values); + _externSheetIndex = ((Area3DPtg) ptg).getExternSheetIndex(); + } - public int getFirstColumn() { - return _delegate.getFirstColumn(); - } - - public int getFirstRow() { - return _delegate.getFirstRow(); - } - - public int getLastColumn() { - return (short) _delegate.getLastColumn(); - } - - public int getLastRow() { - return _delegate.getLastRow(); - } - - public ValueEval[] getValues() { - return _values; - } - - public ValueEval getValueAt(int row, int col) { - ValueEval retval; - int index = (row-getFirstRow())*(col-getFirstColumn()); - if (index <0 || index >= _values.length) - retval = ErrorEval.VALUE_INVALID; - else - retval = _values[index]; - return retval; - } - - public boolean contains(int row, int col) { - return (getFirstRow() <= row) && (getLastRow() >= row) - && (getFirstColumn() <= col) && (getLastColumn() >= col); - } - - public boolean containsRow(int row) { - return (getFirstRow() <= row) && (getLastRow() >= row); - } - - public boolean containsColumn(short col) { - return (getFirstColumn() <= col) && (getLastColumn() >= col); - } - - - public boolean isColumn() { - return _delegate.getFirstColumn() == _delegate.getLastColumn(); - } - - public boolean isRow() { - return _delegate.getFirstRow() == _delegate.getLastRow(); - } - - public int getExternSheetIndex() { - return _delegate.getExternSheetIndex(); - } + public int getExternSheetIndex() { + return _externSheetIndex; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java new file mode 100644 index 000000000..9436ae0aa --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java @@ -0,0 +1,122 @@ +/* + * 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.record.formula.eval; + +import org.apache.poi.hssf.record.formula.AreaI; + +/** + * @author Josh Micich + */ +abstract class AreaEvalBase implements AreaEval { + + private final int _firstColumn; + private final int _firstRow; + private final int _lastColumn; + private final int _lastRow; + private final ValueEval[] _values; + private final int _nColumns; + private final int _nRows; + + protected AreaEvalBase(AreaI ptg, ValueEval[] values) { + if (values == null) { + throw new IllegalArgumentException("values must not be null"); + } + _firstRow = ptg.getFirstRow(); + _firstColumn = ptg.getFirstColumn(); + _lastRow = ptg.getLastRow(); + _lastColumn = ptg.getLastColumn(); + + _nColumns = _lastColumn - _firstColumn + 1; + _nRows = _lastRow - _firstRow + 1; + + int expectedItemCount = _nRows * _nColumns; + if ((values.length != expectedItemCount)) { + // Note - this math may need alteration when POI starts to support full column or full row refs + throw new IllegalArgumentException("Array size should be (" + expectedItemCount + + ") but was (" + values.length + ")"); + } + + + + for (int i = values.length - 1; i >= 0; i--) { + if (values[i] == null) { + throw new IllegalArgumentException("value array elements must not be null"); + } + } + _values = values; + } + + public final int getFirstColumn() { + return _firstColumn; + } + + public final int getFirstRow() { + return _firstRow; + } + + public final int getLastColumn() { + return _lastColumn; + } + + public final int getLastRow() { + return _lastRow; + } + + public final ValueEval[] getValues() { + // TODO - clone() - but some junits rely on not cloning at the moment + return _values; + } + + public final ValueEval getValueAt(int row, int col) { + int rowOffsetIx = row - _firstRow; + int colOffsetIx = col - _firstColumn; + + if(rowOffsetIx < 0 || rowOffsetIx >= _nRows) { + throw new IllegalArgumentException("Specified row index (" + row + + ") is outside the allowed range (" + _firstRow + ".." + _lastRow + ")"); + } + if(colOffsetIx < 0 || colOffsetIx >= _nColumns) { + throw new IllegalArgumentException("Specified column index (" + col + + ") is outside the allowed range (" + _firstColumn + ".." + col + ")"); + } + + int index = rowOffsetIx * _nColumns + colOffsetIx; + return _values[index]; + } + + public final boolean contains(int row, int col) { + return _firstRow <= row && _lastRow >= row + && _firstColumn <= col && _lastColumn >= col; + } + + public final boolean containsRow(int row) { + return (_firstRow <= row) && (_lastRow >= row); + } + + public final boolean containsColumn(short col) { + return (_firstColumn <= col) && (_lastColumn >= col); + } + + public final boolean isColumn() { + return _firstColumn == _lastColumn; + } + + public final boolean isRow() { + return _firstRow == _lastRow; + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java index 2cdc540e6..5ce732719 100644 --- a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java @@ -21,6 +21,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -36,15 +37,14 @@ import org.apache.poi.hssf.record.formula.Ptg; final class FunctionMetadataReader { private static final String METADATA_FILE_NAME = "functionMetadata.txt"; + + /** plain ASCII text metadata file uses three dots for ellipsis */ + private static final String ELLIPSIS = "..."; private static final Pattern TAB_DELIM_PATTERN = Pattern.compile("\t"); private static final Pattern SPACE_DELIM_PATTERN = Pattern.compile(" "); private static final byte[] EMPTY_BYTE_ARRAY = { }; - // special characters from the ooo document - private static final int CHAR_ELLIPSIS_8230 = 8230; - private static final int CHAR_NDASH_8211 = 8211; - private static final String[] DIGIT_ENDING_FUNCTION_NAMES = { // Digits at the end of a function might be due to a left-over footnote marker. // except in these cases @@ -58,7 +58,12 @@ final class FunctionMetadataReader { throw new RuntimeException("resource '" + METADATA_FILE_NAME + "' not found"); } - BufferedReader br = new BufferedReader(new InputStreamReader(is)); + BufferedReader br; + try { + br = new BufferedReader(new InputStreamReader(is,"UTF-8")); + } catch(UnsupportedEncodingException e) { + throw new RuntimeException(e); + } FunctionDataBuilder fdb = new FunctionDataBuilder(400); try { @@ -123,7 +128,9 @@ final class FunctionMetadataReader { } String[] array = SPACE_DELIM_PATTERN.split(codes); int nItems = array.length; - if(array[nItems-1].charAt(0) == CHAR_ELLIPSIS_8230) { + if(ELLIPSIS.equals(array[nItems-1])) { + // final ellipsis is optional, and ignored + // (all unspecified params are assumed to be the same as the last) nItems --; } byte[] result = new byte[nItems]; @@ -137,7 +144,6 @@ final class FunctionMetadataReader { if(codes.length() == 1) { switch (codes.charAt(0)) { case '-': - case CHAR_NDASH_8211: // this is what the ooo doc has return true; } } @@ -153,7 +159,7 @@ final class FunctionMetadataReader { case 'R': return Ptg.CLASS_REF; case 'A': return Ptg.CLASS_ARRAY; } - throw new IllegalArgumentException("Unexpected operand type code '" + code + "'"); + throw new IllegalArgumentException("Unexpected operand type code '" + code + "' (" + (int)code.charAt(0) + ")"); } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java index 81a338256..7d2a53d20 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -817,8 +817,7 @@ public class HSSFCell int row=record.getRow(); short col=record.getColumn(); short styleIndex=record.getXFIndex(); - if ((cellType != CELL_TYPE_ERROR) && (cellType != CELL_TYPE_FORMULA)) - { + if (cellType != CELL_TYPE_ERROR) { setCellType(CELL_TYPE_ERROR, false, row, col, styleIndex); } (( BoolErrRecord ) record).setValue(value); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java index 0a9c3dfa8..cf4f83b31 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java @@ -232,8 +232,7 @@ public class HSSFFormulaEvaluator { cell.setCellValue(cv.getBooleanValue()); break; case HSSFCell.CELL_TYPE_ERROR: - cell.setCellType(HSSFCell.CELL_TYPE_ERROR); - cell.setCellValue(cv.getErrorValue()); + cell.setCellErrorValue(cv.getErrorValue()); break; case HSSFCell.CELL_TYPE_NUMERIC: cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java index 91b3fcd0f..e24ca23ea 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java @@ -139,4 +139,13 @@ public class HSSFName { } + /** + * Tests if this name points to a cell that no longer exists + * + * @return true if the name refers to a deleted cell, false otherwise + */ + public boolean isDeleted(){ + String ref = getReference(); + return "#REF!".endsWith(ref); + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java index 0444843d2..def79f6d3 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java @@ -21,7 +21,6 @@ import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.poi.hssf.model.Sheet; -import org.apache.poi.hssf.model.Workbook; import org.apache.poi.hssf.record.CellValueRecordInterface; import org.apache.poi.hssf.record.RowRecord; @@ -37,11 +36,9 @@ public final class HSSFRow implements Comparable { // used for collections public final static int INITIAL_CAPACITY = 5; - //private short rowNum; + private int rowNum; private HSSFCell[] cells=new HSSFCell[INITIAL_CAPACITY]; -// private short firstcell = -1; -// private short lastcell = -1; /** * reference to low level representation @@ -61,7 +58,8 @@ public final class HSSFRow implements Comparable { private Sheet sheet; - protected HSSFRow() + // TODO - ditch this constructor + HSSFRow() { } @@ -73,18 +71,12 @@ public final class HSSFRow implements Comparable { * @param rowNum the row number of this row (0 based) * @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(int) */ - - //protected HSSFRow(Workbook book, Sheet sheet, short rowNum) - protected HSSFRow(HSSFWorkbook book, Sheet sheet, int rowNum) + HSSFRow(HSSFWorkbook book, Sheet sheet, int rowNum) { this.rowNum = rowNum; this.book = book; this.sheet = sheet; - row = new RowRecord(); - row.setOptionFlags( (short)0x100 ); // seems necessary for outlining to work. - row.setHeight((short) 0xff); - row.setLastCol((short) -1); - row.setFirstCol((short) -1); + row = new RowRecord(rowNum); setRowNum(rowNum); } @@ -98,8 +90,7 @@ public final class HSSFRow implements Comparable { * @param record the low level api object this row should represent * @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(int) */ - - protected HSSFRow(HSSFWorkbook book, Sheet sheet, RowRecord record) + HSSFRow(HSSFWorkbook book, Sheet sheet, RowRecord record) { this.book = book; this.sheet = sheet; @@ -200,12 +191,11 @@ public final class HSSFRow implements Comparable { * @param rowNum the row number (0-based) * @throws IndexOutOfBoundsException if the row number is not within the range 0-65535. */ - - //public void setRowNum(short rowNum) - public void setRowNum(int rowNum) - { - if ((rowNum < 0) || (rowNum > RowRecord.MAX_ROW_NUMBER)) - throw new IndexOutOfBoundsException("Row number must be between 0 and "+RowRecord.MAX_ROW_NUMBER+", was <"+rowNum+">"); + public void setRowNum(int rowNum) { + if ((rowNum < 0) || (rowNum > RowRecord.MAX_ROW_NUMBER)) { + throw new IllegalArgumentException("Invalid row number (" + rowNum + + ") outside allowable range (0.." + RowRecord.MAX_ROW_NUMBER + ")"); + } this.rowNum = rowNum; if (row != null) { @@ -217,8 +207,6 @@ public final class HSSFRow implements Comparable { * get row number this row represents * @return the row number (0 based) */ - - //public short getRowNum() public int getRowNum() { return rowNum; @@ -479,8 +467,10 @@ public final class HSSFRow implements Comparable { } /** - * @return cell iterator of the physically defined cells. Note element 4 may - * actually be row cell depending on how many are defined! + * @return cell iterator of the physically defined cells. + * Note that the 4th element might well not be cell 4, as the iterator + * will not return un-defined (null) cells. + * Call getCellNum() on the returned cells to know which cell they are. */ public Iterator cellIterator() { diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index b56122688..ced871980 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -136,6 +136,7 @@ public final class HSSFSheet { { int sloc = sheet.getLoc(); RowRecord row = sheet.getNextRow(); + boolean rowRecordsAlreadyPresent = row!=null; while (row != null) { @@ -160,6 +161,18 @@ public final class HSSFSheet { if ( ( lastrow == null ) || ( lastrow.getRowNum() != cval.getRow() ) ) { hrow = getRow( cval.getRow() ); + if (hrow == null) { + // Some tools (like Perl module Spreadsheet::WriteExcel - bug 41187) skip the RowRecords + // Excel, OpenOffice.org and GoogleDocs are all OK with this, so POI should be too. + if (rowRecordsAlreadyPresent) { + // if at least one row record is present, all should be present. + throw new RuntimeException("Unexpected missing row when some rows already present"); + } + // create the row record on the fly now. + RowRecord rowRec = new RowRecord(cval.getRow()); + sheet.addRow(rowRec); + hrow = createRowFromRecord(rowRec); + } } if ( hrow != null ) { @@ -699,6 +712,7 @@ public final class HSSFSheet { /** * @return an iterator of the PHYSICAL rows. Meaning the 3rd element may not * be the third row if say for instance the second row is undefined. + * Call getRowNum() on each row if you care which one it is. */ public Iterator rowIterator() { @@ -963,13 +977,34 @@ public final class HSSFSheet { return new HSSFFooter( getSheet().getFooter() ); } + /** + * Note - this is not the same as whether the sheet is focused (isActive) + * @return true if this sheet is currently selected + */ + public boolean isSelected() { + return getSheet().getWindowTwo().getSelected(); + } /** * Sets whether sheet is selected. * @param sel Whether to select the sheet or deselect the sheet. */ public void setSelected( boolean sel ) { - getSheet().setSelected( sel ); + getSheet().getWindowTwo().setSelected(sel); + } + /** + * @return true if this sheet is currently focused + */ + public boolean isActive() { + return getSheet().getWindowTwo().isActive(); + } + /** + * Sets whether sheet is selected. + * @param sel Whether to select the sheet or deselect the sheet. + */ + public void setActive(boolean sel ) + { + getSheet().getWindowTwo().setActive(sel); } /** @@ -1671,6 +1706,23 @@ public final class HSSFSheet { * @param column the column index */ public void autoSizeColumn(short column) { + autoSizeColumn(column, false); + } + + /** + * Adjusts the column width to fit the contents. + * + * This process can be relatively slow on large sheets, so this should + * normally only be called once per column, at the end of your + * processing. + * + * You can specify whether the content of merged cells should be considered or ignored. + * Default is to ignore merged cells. + * + * @param column the column index + * @param useMergedCells whether to use the contents of merged cells when calculating the width of the column + */ + public void autoSizeColumn(short column, boolean useMergedCells) { AttributedString str; TextLayout layout; /** @@ -1679,13 +1731,13 @@ public final class HSSFSheet { * '0' looks to be a good choice. */ char defaultChar = '0'; - + /** * This is the multiple that the font height is scaled by when determining the * boundary of rotated text. */ double fontHeightMultiple = 2.0; - + FontRenderContext frc = new FontRenderContext(null, true, true); HSSFWorkbook wb = new HSSFWorkbook(book); @@ -1697,21 +1749,27 @@ public final class HSSFSheet { int defaultCharWidth = (int)layout.getAdvance(); double width = -1; + rows: for (Iterator it = rowIterator(); it.hasNext();) { HSSFRow row = (HSSFRow) it.next(); HSSFCell cell = row.getCell(column); - boolean isCellInMergedRegion = false; - for (int i = 0 ; i < getNumMergedRegions() && ! isCellInMergedRegion; i++) { - isCellInMergedRegion = getMergedRegionAt(i).contains(row.getRowNum(), column); - } + if (cell == null) continue; - if (cell == null | isCellInMergedRegion) continue; + int colspan = 1; + for (int i = 0 ; i < getNumMergedRegions(); i++) { + if (getMergedRegionAt(i).contains(row.getRowNum(), column)) { + if (!useMergedCells) { + // If we're not using merged cells, skip this one and move on to the next. + continue rows; + } + cell = row.getCell(getMergedRegionAt(i).getColumnFrom()); + colspan = 1+ getMergedRegionAt(i).getColumnTo() - getMergedRegionAt(i).getColumnFrom(); + } + } HSSFCellStyle style = cell.getCellStyle(); HSSFFont font = wb.getFontAt(style.getFontIndex()); - //the number of spaces to indent the text in the cell - int indention = style.getIndention(); if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING) { HSSFRichTextString rt = cell.getRichStringCellValue(); @@ -1744,9 +1802,9 @@ public final class HSSFSheet { trans.concatenate( AffineTransform.getScaleInstance(1, fontHeightMultiple) ); - width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth + indention); + width = Math.max(width, ((layout.getOutline(trans).getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); } else { - width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth + indention); + width = Math.max(width, ((layout.getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); } } } else { @@ -1789,19 +1847,19 @@ public final class HSSFSheet { trans.concatenate( AffineTransform.getScaleInstance(1, fontHeightMultiple) ); - width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth + indention); + width = Math.max(width, ((layout.getOutline(trans).getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); } else { - width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth + indention); + width = Math.max(width, ((layout.getBounds().getWidth() / colspan) / defaultCharWidth) + cell.getCellStyle().getIndention()); } } } - if (width != -1) { - if (width > Short.MAX_VALUE) { //calculated width can be greater that Short.MAX_VALUE! - width = Short.MAX_VALUE; - } - sheet.setColumnWidth(column, (short) (width * 256)); + } + if (width != -1) { + if (width > Short.MAX_VALUE) { //width can be bigger that Short.MAX_VALUE! + width = Short.MAX_VALUE; } + sheet.setColumnWidth(column, (short) (width * 256)); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 78fe26013..896ef19c1 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -140,7 +140,7 @@ public class HSSFWorkbook extends POIDocument protected HSSFWorkbook( Workbook book ) { - super(null, null); + super(null, null); workbook = book; sheets = new ArrayList( INITIAL_CAPACITY ); names = new ArrayList( INITIAL_CAPACITY ); @@ -365,16 +365,66 @@ public class HSSFWorkbook extends POIDocument workbook.setSheetOrder(sheetname, pos); } + private void validateSheetIndex(int index) { + int lastSheetIx = sheets.size() - 1; + if (index < 0 || index > lastSheetIx) { + throw new IllegalArgumentException("Sheet index (" + + index +") is out of range (0.." + lastSheetIx + ")"); + } + } + /** - * sets the tab whose data is actually seen when the sheet is opened. - * This may be different from the "selected sheet" since excel seems to - * allow you to show the data of one sheet when another is seen "selected" - * in the tabs (at the bottom). - * @see org.apache.poi.hssf.usermodel.HSSFSheet#setSelected(boolean) - * @param index + * Selects a single sheet. This may be different to + * the 'active' sheet (which is the sheet with focus). + */ + public void setSelectedTab(int index) { + + validateSheetIndex(index); + int nSheets = sheets.size(); + for (int i=0; i + * + * Care must be taken if the removed sheet is the currently active or only selected sheet in + * the workbook. There are a few situations when Excel must have a selection and/or active + * sheet. (For example when printing - see Bug 40414).
+ * + * This method makes sure that if the removed sheet was active, another sheet will become + * active in its place. Furthermore, if the removed sheet was the only selected sheet, another + * sheet will become selected. The newly active/selected sheet will have the same index, or + * one less if the removed sheet was the last in the workbook. + * * @param index of the sheet (0-based) */ + public void removeSheetAt(int index) { + validateSheetIndex(index); + boolean wasActive = getSheetAt(index).isActive(); + boolean wasSelected = getSheetAt(index).isSelected(); - public void removeSheetAt(int index) - { sheets.remove(index); workbook.removeSheet(index); + + // set the remaining active/selected sheet + int nSheets = sheets.size(); + if (nSheets < 1) { + // nothing more to do if there are no sheets left + return; + } + // the index of the closest remaining sheet to the one just deleted + int newSheetIndex = index; + if (newSheetIndex >= nSheets) { + newSheetIndex = nSheets-1; + } + if (wasActive) { + setActiveSheet(newSheetIndex); + } + + if (wasSelected) { + boolean someOtherSheetIsStillSelected = false; + for (int i =0; i < nSheets; i++) { + if (getSheetAt(i).isSelected()) { + someOtherSheetIsStillSelected = true; + break; + } + } + if (!someOtherSheetIsStillSelected) { + setSelectedTab(newSheetIndex); + } + } } /** @@ -834,8 +942,7 @@ public class HSSFWorkbook extends POIDocument HSSFPrintSetup printSetup = sheet.getPrintSetup(); printSetup.setValidSettings(false); - WindowTwoRecord w2 = (WindowTwoRecord) sheet.getSheet().findFirstRecordBySid(WindowTwoRecord.sid); - w2.setPaged(true); + sheet.setActive(true); } private NameRecord findExistingRowColHeaderNameRecord( int sheetIndex ) diff --git a/src/java/org/apache/poi/poifs/storage/BlockListImpl.java b/src/java/org/apache/poi/poifs/storage/BlockListImpl.java index 07f690c6b..7e44fda3f 100644 --- a/src/java/org/apache/poi/poifs/storage/BlockListImpl.java +++ b/src/java/org/apache/poi/poifs/storage/BlockListImpl.java @@ -102,7 +102,8 @@ class BlockListImpl catch (ArrayIndexOutOfBoundsException ignored) { throw new IOException("Cannot remove block[ " + index - + " ]; out of range"); + + " ]; out of range[ 0 - " + + (_blocks.length-1) + " ]"); } return result; } diff --git a/src/java/org/apache/poi/poifs/storage/RawDataBlock.java b/src/java/org/apache/poi/poifs/storage/RawDataBlock.java index 472fd8b8b..b4630a78b 100644 --- a/src/java/org/apache/poi/poifs/storage/RawDataBlock.java +++ b/src/java/org/apache/poi/poifs/storage/RawDataBlock.java @@ -81,8 +81,9 @@ public class RawDataBlock log.log(POILogger.ERROR, "Unable to read entire block; " + count + type + " read before EOF; expected " - + blockSize + " bytes. Your document" - + " has probably been truncated!" + + blockSize + " bytes. Your document " + + "was either written by software that " + + "ignores the spec, or has been truncated!" ); } else { diff --git a/src/records/definitions/pane_record.xml b/src/records/definitions/pane_record.xml index c6f93b675..436443113 100644 --- a/src/records/definitions/pane_record.xml +++ b/src/records/definitions/pane_record.xml @@ -31,7 +31,7 @@ - + diff --git a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt index 60d2f1fe8..475131e1c 100644 --- a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt +++ b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt @@ -29,7 +29,7 @@ 7 MAX 1 30 V R 8 ROW 0 1 V R 9 COLUMN 0 1 V R -10 NA 0 0 V – +10 NA 0 0 V - 11 NPV 2 30 V V R 12 STDEV 1 30 V R 13 DOLLAR 1 2 V V V @@ -38,7 +38,7 @@ 16 COS 1 1 V V 17 TAN 1 1 V V 18 ARCTAN 1 1 V V -19 PI 0 0 V – +19 PI 0 0 V - 20 SQRT 1 1 V V 21 EXP 1 1 V V 22 LN 1 1 V V @@ -53,8 +53,8 @@ 31 MID 3 3 V V V V 32 LEN 1 1 V V 33 VALUE 1 1 V V -34 TRUE 0 0 V – -35 FALSE 0 0 V – +34 TRUE 0 0 V - +35 FALSE 0 0 V - 36 AND 1 30 V R 37 OR 1 30 V R 38 NOT 1 1 V V @@ -80,7 +80,7 @@ 60 RATE 3 6 V V V V V V V 61 MIRR 3 3 V R V V 62 IRR 1 2 V R V -63 RAND 0 0 V – x +63 RAND 0 0 V - x 64 MATCH 2 3 V V R R 65 DATE 3 3 V V V V 66 TIME 3 3 V V V V @@ -91,7 +91,7 @@ 71 HOUR 1 1 V V 72 MINUTE 1 1 V V 73 SECOND 1 1 V V -74 NOW 0 0 V – x +74 NOW 0 0 V - x 75 AREAS 1 1 V R 76 ROWS 1 1 V R 77 COLUMNS 1 1 V R @@ -170,10 +170,10 @@ 215 JIS 1 1 V V x 219 ADDRESS 2 5 V V V V V V 220 DAYS360 2 2 V V V x -221 TODAY 0 0 V – x +221 TODAY 0 0 V - x 222 VDB 5 7 V V V V V V V V -227 MEDIAN 1 30 V R … -228 SUMPRODUCT 1 30 V A … +227 MEDIAN 1 30 V R ... +228 SUMPRODUCT 1 30 V A ... 229 SINH 1 1 V V 230 COSH 1 1 V V 231 TANH 1 1 V V @@ -188,7 +188,7 @@ 247 DB 4 5 V V V V V V 252 FREQUENCY 2 2 A R R 261 ERROR.TYPE 1 1 V V -269 AVEDEV 1 30 V R … +269 AVEDEV 1 30 V R ... 270 BETADIST 3 5 V V V V V V 271 GAMMALN 1 1 V V 272 BETAINV 3 5 V V V V V V @@ -237,12 +237,12 @@ 315 SLOPE 2 2 V A A 316 TTEST 4 4 V A A V V 317 PROB 3 4 V A A V V -318 DEVSQ 1 30 V R … -319 GEOMEAN 1 30 V R … -320 HARMEAN 1 30 V R … -321 SUMSQ 0 30 V R … -322 KURT 1 30 V R … -323 SKEW 1 30 V R … +318 DEVSQ 1 30 V R ... +319 GEOMEAN 1 30 V R ... +320 HARMEAN 1 30 V R ... +321 SUMSQ 0 30 V R ... +322 KURT 1 30 V R ... +323 SKEW 1 30 V R ... 324 ZTEST 2 3 V R V V 325 LARGE 2 2 V R V 326 SMALL 2 2 V R V @@ -274,10 +274,10 @@ 358 GETPIVOTDATA 2 30 359 HYPERLINK 1 2 V V V 360 PHONETIC 1 1 V R -361 AVERAGEA 1 30 V R … -362 MAXA 1 30 V R … -363 MINA 1 30 V R … -364 STDEVPA 1 30 V R … -365 VARPA 1 30 V R … -366 STDEVA 1 30 V R … -367 VARA 1 30 V R … +361 AVERAGEA 1 30 V R ... +362 MAXA 1 30 V R ... +363 MINA 1 30 V R ... +364 STDEVPA 1 30 V R ... +365 VARPA 1 30 V R ... +366 STDEVA 1 30 V R ... +367 VARA 1 30 V R ... diff --git a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt index 590b455c4..6902027de 100644 --- a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt +++ b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt @@ -31,7 +31,7 @@ 7 MAX 1 30 V R 8 ROW 0 1 V R 9 COLUMN 0 1 V R -10 NA 0 0 V – +10 NA 0 0 V - 11 NPV 2 30 V V R 12 STDEV 1 30 V R 13 DOLLAR 1 2 V V V @@ -40,7 +40,7 @@ 16 COS 1 1 V V 17 TAN 1 1 V V 18 ATAN 1 1 V V -19 PI 0 0 V – +19 PI 0 0 V - 20 SQRT 1 1 V V 21 EXP 1 1 V V 22 LN 1 1 V V @@ -55,8 +55,8 @@ 31 MID 3 3 V V V V 32 LEN 1 1 V V 33 VALUE 1 1 V V -34 TRUE 0 0 V – -35 FALSE 0 0 V – +34 TRUE 0 0 V - +35 FALSE 0 0 V - 36 AND 1 30 V R 37 OR 1 30 V R 38 NOT 1 1 V V @@ -82,7 +82,7 @@ 60 RATE 3 6 V V V V V V V 61 MIRR 3 3 V R V V 62 IRR 1 2 V R V -63 RAND 0 0 V – x +63 RAND 0 0 V - x 64 MATCH 2 3 V V R R 65 DATE 3 3 V V V V 66 TIME 3 3 V V V V @@ -93,7 +93,7 @@ 71 HOUR 1 1 V V 72 MINUTE 1 1 V V 73 SECOND 1 1 V V -74 NOW 0 0 V – x +74 NOW 0 0 V - x 75 AREAS 1 1 V R 76 ROWS 1 1 V R 77 COLUMNS 1 1 V R @@ -172,10 +172,10 @@ 215 JIS 1 1 V V x 219 ADDRESS 2 5 V V V V V V 220 DAYS360 2 2 V V V x -221 TODAY 0 0 V – x +221 TODAY 0 0 V - x 222 VDB 5 7 V V V V V V V V -227 MEDIAN 1 30 V R … -228 SUMPRODUCT 1 30 V A … +227 MEDIAN 1 30 V R ... +228 SUMPRODUCT 1 30 V A ... 229 SINH 1 1 V V 230 COSH 1 1 V V 231 TANH 1 1 V V @@ -192,7 +192,7 @@ 247 DB 4 5 V V V V V V 252 FREQUENCY 2 2 A R R 261 ERROR.TYPE 1 1 V V -269 AVEDEV 1 30 V R … +269 AVEDEV 1 30 V R ... 270 BETADIST 3 5 V V V V V V 271 GAMMALN 1 1 V V 272 BETAINV 3 5 V V V V V V @@ -241,12 +241,12 @@ 315 SLOPE 2 2 V A A 316 TTEST 4 4 V A A V V 317 PROB 3 4 V A A V V -318 DEVSQ 1 30 V R … -319 GEOMEAN 1 30 V R … -320 HARMEAN 1 30 V R … -321 SUMSQ 0 30 V R … -322 KURT 1 30 V R … -323 SKEW 1 30 V R … +318 DEVSQ 1 30 V R ... +319 GEOMEAN 1 30 V R ... +320 HARMEAN 1 30 V R ... +321 SUMSQ 0 30 V R ... +322 KURT 1 30 V R ... +323 SKEW 1 30 V R ... 324 ZTEST 2 3 V R V V 325 LARGE 2 2 V R V 326 SMALL 2 2 V R V @@ -278,10 +278,10 @@ 358 GETPIVOTDATA 2 30 359 HYPERLINK 1 2 V V V 360 PHONETIC 1 1 V R -361 AVERAGEA 1 30 V R … -362 MAXA 1 30 V R … -363 MINA 1 30 V R … -364 STDEVPA 1 30 V R … -365 VARPA 1 30 V R … -366 STDEVA 1 30 V R … -367 VARA 1 30 V R … +361 AVERAGEA 1 30 V R ... +362 MAXA 1 30 V R ... +363 MINA 1 30 V R ... +364 STDEVPA 1 30 V R ... +365 VARPA 1 30 V R ... +366 STDEVA 1 30 V R ... +367 VARA 1 30 V R ... diff --git a/src/scratchpad/examples/src/org/apache/poi/hslf/examples/DataExtraction.java b/src/scratchpad/examples/src/org/apache/poi/hslf/examples/DataExtraction.java index 05bf82472..611466c92 100755 --- a/src/scratchpad/examples/src/org/apache/poi/hslf/examples/DataExtraction.java +++ b/src/scratchpad/examples/src/org/apache/poi/hslf/examples/DataExtraction.java @@ -69,16 +69,9 @@ public class DataExtraction { String name = ole.getInstanceName(); if ("Worksheet".equals(name)) { - //save xls on disk - FileOutputStream out = new FileOutputStream(name + "-("+(j)+").xls"); - InputStream dis = data.getData(); - byte[] chunk = new byte[2048]; - int count; - while ((count = dis.read(chunk)) >= 0) { - out.write(chunk,0,count); - } - is.close(); - out.close(); + //read xls + HSSFWorkbook wb = new HSSFWorkbook(data.getData()); + } else if ("Document".equals(name)) { HWPFDocument doc = new HWPFDocument(data.getData()); //read the word document @@ -93,7 +86,15 @@ public class DataExtraction { doc.write(out); out.close(); } else { - System.err.println("Processing " + name); + FileOutputStream out = new FileOutputStream(ole.getProgID() + "-"+(j+1)+".dat"); + InputStream dis = data.getData(); + byte[] chunk = new byte[2048]; + int count; + while ((count = dis.read(chunk)) >= 0) { + out.write(chunk,0,count); + } + is.close(); + out.close(); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java index 4abc8c1c0..4bef9033d 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java @@ -76,6 +76,7 @@ public class ShapeFactory { case ShapeTypes.TextBox: shape = new TextBox(spContainer, parent); break; + case ShapeTypes.HostControl: case ShapeTypes.PictureFrame: { EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID); EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.BLIP__PICTUREID); diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java index 5dac65025..4030ddc0c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java @@ -45,7 +45,6 @@ public class TextRun protected TextBytesAtom _byteAtom; protected TextCharsAtom _charAtom; protected StyleTextPropAtom _styleAtom; - protected TextSpecInfoAtom _specAtom; protected boolean _isUnicode; protected RichTextRun[] _rtRuns; private SlideShow slideShow; @@ -319,9 +318,9 @@ public class TextRun * touch the stylings. */ private void storeText(String s) { - // Remove a single trailing \n, as there is an implicit one at the + // Remove a single trailing \r, as there is an implicit one at the // end of every record - if(s.endsWith("\n")) { + if(s.endsWith("\r")) { s = s.substring(0, s.length()-1); } @@ -361,6 +360,18 @@ public class TextRun _isUnicode = true; } } + /** + * If TextSpecInfoAtom is present, we must update the text size in it, + * otherwise the ppt will be corrupted + */ + if(_records != null) for (int i = 0; i < _records.length; i++) { + if(_records[i] instanceof TextSpecInfoAtom){ + TextSpecInfoAtom specAtom = (TextSpecInfoAtom)_records[i]; + if((s.length() + 1) != specAtom.getCharactersCovered()){ + specAtom.reset(s.length() + 1); + } + } + } } /** @@ -446,7 +457,7 @@ public class TextRun * as the the first character has. * If you care about styling, do setText on a RichTextRun instead */ - public synchronized void setText(String s) { + public synchronized void setRawText(String s) { // Save the new text to the atoms storeText(s); RichTextRun fst = _rtRuns[0]; @@ -474,20 +485,18 @@ public class TextRun _rtRuns[0] = new RichTextRun(this,0,s.length()); } - /** - * If TextSpecInfoAtom is present, we must update the text size, - * otherwise the ppt will be corrupted - */ - if(_records != null) for (int i = 0; i < _records.length; i++) { - if(_records[i] instanceof TextSpecInfoAtom){ - TextSpecInfoAtom specAtom = (TextSpecInfoAtom)_records[i]; - specAtom.setTextSize(s.length()); - } - - } } - /** + /** + * Changes the text. + * Converts '\r' into '\n' + */ + public synchronized void setText(String s) { + String text = normalize(s); + setRawText(text); + } + + /** * Ensure a StyleTextPropAtom is present for this run, * by adding if required. Normally for internal TextRun use. */ @@ -666,4 +675,12 @@ public class TextRun return null; } + + /** + * Returns a new string with line breaks converted into internal ppt representation + */ + public String normalize(String s){ + String ns = s.replaceAll("\\r?\\n", "\r"); + return ns; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExControl.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExControl.java new file mode 100755 index 000000000..e6e7da893 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExControl.java @@ -0,0 +1,95 @@ +/* ==================================================================== + 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.hslf.record; + +import java.io.OutputStream; +import java.io.IOException; + +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogger; + +/** + * Container for OLE Control object. It contains: + *

+ * 1. ExControlAtom (4091) + * 2. ExOleObjAtom (4035) + * 3. CString (4026), Instance MenuName (1) used for menus and the Links dialog box. + * 4. CString (4026), Instance ProgID (2) that stores the OLE Programmatic Identifier. + * A ProgID is a string that uniquely identifies a given object. + * 5. CString (4026), Instance ClipboardName (3) that appears in the paste special dialog. + * 6. MetaFile( 4033), optional + *

+ * + * + * @author Yegor kozlov + */ +public class ExControl extends ExEmbed { + + // Links to our more interesting children + private ExControlAtom ctrlAtom; + + /** + * Set things up, and find our more interesting children + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected ExControl(byte[] source, int start, int len) { + super(source, start, len); + } + + /** + * Create a new ExEmbed, with blank fields + */ + public ExControl() { + super(); + + _children[0] = ctrlAtom = new ExControlAtom(); + } + + /** + * Gets the {@link ExControlAtom}. + * + * @return the {@link ExControlAtom}. + */ + public ExControlAtom getExControlAtom() + { + return ctrlAtom; + } + + /** + * Returns the type (held as a little endian in bytes 3 and 4) + * that this class handles. + * + * @return the record type. + */ + public long getRecordType() { + return RecordTypes.ExControl.typeID; + } + + protected RecordAtom getEmbedAtom(Record[] children){ + RecordAtom atom = null; + if(_children[0] instanceof ExControlAtom) { + atom = (ExControlAtom)_children[0]; + } else { + logger.log(POILogger.ERROR, "First child record wasn't a ExControlAtom, was of type " + _children[0].getRecordType()); + } + return atom; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java new file mode 100755 index 000000000..ae99d0a23 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java @@ -0,0 +1,96 @@ +/* ==================================================================== + 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.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.util.LittleEndian; + +/** + * Contains a long integer, slideID, which stores the unique slide identifier of the slide + * where this control resides. + * + * @author Yegor Kozlov + */ +public class ExControlAtom extends RecordAtom { + + + /** + * Record header. + */ + private byte[] _header; + + /** + * slideId. + */ + private int _id; + + /** + * Constructs a brand new embedded object atom record. + */ + protected ExControlAtom() { + _header = new byte[8]; + + LittleEndian.putShort(_header, 2, (short) getRecordType()); + LittleEndian.putInt(_header, 4, 4); + + } + + /** + * Constructs the ExControlAtom record from its source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected ExControlAtom(byte[] source, int start, int len) { + // Get the header. + _header = new byte[8]; + System.arraycopy(source, start, _header, 0, 8); + + _id = LittleEndian.getInt(source, start + 8); + } + + public int getSlideId() { + return _id; + } + + /** + * Gets the record type. + * @return the record type. + */ + public long getRecordType() { + return RecordTypes.ExControlAtom.typeID; + } + + /** + * Write the contents of the record back, so it can be written + * to disk + * + * @param out the output stream to write to. + * @throws java.io.IOException if an error occurs. + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + byte[] data = new byte[4]; + LittleEndian.putInt(data, _id); + out.write(data); + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java index 9e58e8ae1..501712e9d 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java @@ -36,7 +36,7 @@ public class ExEmbed extends RecordContainer { private byte[] _header; // Links to our more interesting children - private ExEmbedAtom embedAtom; + private RecordAtom embedAtom; private ExOleObjAtom oleObjAtom; private CString menuName; private CString progId; @@ -91,11 +91,7 @@ public class ExEmbed extends RecordContainer { private void findInterestingChildren() { // First child should be the ExHyperlinkAtom - if(_children[0] instanceof ExEmbedAtom) { - embedAtom = (ExEmbedAtom)_children[0]; - } else { - logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType()); - } + embedAtom = getEmbedAtom(_children); // Second child should be the ExOleObjAtom if (_children[1] instanceof ExOleObjAtom) { @@ -115,6 +111,16 @@ public class ExEmbed extends RecordContainer { } } + protected RecordAtom getEmbedAtom(Record[] children){ + RecordAtom atom = null; + if(_children[0] instanceof ExEmbedAtom) { + atom = (ExEmbedAtom)_children[0]; + } else { + logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType()); + } + return atom; + } + /** * Gets the {@link ExEmbedAtom}. * @@ -122,7 +128,7 @@ public class ExEmbed extends RecordContainer { */ public ExEmbedAtom getExEmbedAtom() { - return embedAtom; + return (ExEmbedAtom)embedAtom; } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java index 197497fd0..c7408b0fe 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java @@ -17,11 +17,9 @@ package org.apache.poi.hslf.record; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.util.zip.InflaterInputStream; +import java.util.zip.DeflaterOutputStream; import org.apache.poi.util.LittleEndian; @@ -92,6 +90,25 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord { return new InflaterInputStream(compressedStream); } + /** + * Sets the embedded data. + * + * @param data the embedded data. + */ + public void setData(byte[] data) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + //first four bytes is the length of the raw data + byte[] b = new byte[4]; + LittleEndian.putInt(b, data.length); + out.write(b); + + DeflaterOutputStream def = new DeflaterOutputStream(out); + def.write(data, 0, data.length); + def.finish(); + _data = out.toByteArray(); + LittleEndian.putInt(_header, 4, _data.length); + } + /** * Gets the record type. * diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java index cedc99ce0..a6f00da12 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java @@ -119,7 +119,7 @@ public class RecordTypes { public static final Type RecolorInfoAtom = new Type(4071,null); public static final Type ExQuickTimeMovie = new Type(4074,null); public static final Type ExQuickTimeMovieData = new Type(4075,null); - public static final Type ExControl = new Type(4078,null); + public static final Type ExControl = new Type(4078,ExControl.class); public static final Type SlideListWithText = new Type(4080,SlideListWithText.class); public static final Type InteractiveInfo = new Type(4082,InteractiveInfo.class); public static final Type InteractiveInfoAtom = new Type(4083,InteractiveInfoAtom.class); diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoAtom.java index a8915f049..85cdd1e15 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoAtom.java @@ -20,6 +20,7 @@ import org.apache.poi.util.LittleEndian; import java.io.OutputStream; import java.io.IOException; +import java.util.ArrayList; /** * The special info runs contained in this text. @@ -82,4 +83,118 @@ public class TextSpecInfoAtom extends RecordAtom { public void setTextSize(int size){ LittleEndian.putInt(_data, 0, size); } + + /** + * Reset the content to one info run with the default values + * @param size the site of parent text + */ + public void reset(int size){ + _data = new byte[10]; + // 01 00 00 00 + LittleEndian.putInt(_data, 0, size); + // 01 00 00 00 + LittleEndian.putInt(_data, 4, 1); //mask + // 00 00 + LittleEndian.putShort(_data, 8, (short)0); //langId + + // Update the size (header bytes 5-8) + LittleEndian.putInt(_header, 4, _data.length); + } + + /** + * Get the number of characters covered by this records + * + * @return the number of characters covered by this records + */ + public int getCharactersCovered(){ + int covered = 0; + TextSpecInfoRun[] runs = getTextSpecInfoRuns(); + for (int i = 0; i < runs.length; i++) covered += runs[i].len; + return covered; + } + + public TextSpecInfoRun[] getTextSpecInfoRuns(){ + ArrayList lst = new ArrayList(); + int pos = 0; + int[] bits = {1, 0, 2}; + while(pos < _data.length) { + TextSpecInfoRun run = new TextSpecInfoRun(); + run.len = LittleEndian.getInt(_data, pos); pos += 4; + run.mask = LittleEndian.getInt(_data, pos); pos += 4; + for (int i = 0; i < bits.length; i++) { + if((run.mask & 1 << bits[i]) != 0){ + switch (bits[i]){ + case 0: + run.spellInfo = LittleEndian.getShort(_data, pos); pos += 2; + break; + case 1: + run.langId = LittleEndian.getShort(_data, pos); pos += 2; + break; + case 2: + run.altLangId = LittleEndian.getShort(_data, pos); pos += 2; + break; + } + } + } + lst.add(run); + } + return (TextSpecInfoRun[])lst.toArray(new TextSpecInfoRun[lst.size()]); + + } + + public static class TextSpecInfoRun { + //Length of special info run. + protected int len; + + //Special info mask of this run; + protected int mask; + + // info fields as indicated by the mask. + // -1 means the bit is not set + protected short spellInfo = -1; + protected short langId = -1; + protected short altLangId = -1; + + /** + * Spelling status of this text. See Spell Info table below. + * + *

Spell Info Types:

+ *
  • 0 Unchecked + *
  • 1 Previously incorrect, needs rechecking + *
  • 2 Correct + *
  • 3 Incorrect + * + * @return Spelling status of this text + */ + public short getSpellInfo(){ + return spellInfo; + } + + /** + * Windows LANGID for this text. + * + * @return Windows LANGID for this text. + */ + public short getLangId(){ + return spellInfo; + } + + /** + * Alternate Windows LANGID of this text; + * must be a valid non-East Asian LANGID if the text has an East Asian language, + * otherwise may be an East Asian LANGID or language neutral (zero). + * + * @return Alternate Windows LANGID of this text + */ + public short getAltLangId(){ + return altLangId; + } + + /** + * @return Length of special info run. + */ + public int length(){ + return len; + } + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/ObjectData.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/ObjectData.java index 957e78868..bbef87a96 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/ObjectData.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/ObjectData.java @@ -17,6 +17,7 @@ package org.apache.poi.hslf.usermodel; import java.io.InputStream; +import java.io.IOException; import org.apache.poi.hslf.record.ExOleObjStg; @@ -49,6 +50,15 @@ public class ObjectData { return storage.getData(); } + /** + * Sets the embedded data. + * + * @param data the embedded data. + */ + public void setData(byte[] data) throws IOException { + storage.setData(data); + } + /** * Return the record that contains the object data. * diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java index 7458df7e6..6bc203b65 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java @@ -162,10 +162,18 @@ public class RichTextRun { * Change the text */ public void setText(String text) { - length = text.length(); - parentRun.changeTextInRichTextRun(this,text); + String s = parentRun.normalize(text); + setRawText(s); } + /** + * Change the text + */ + public void setRawText(String text) { + length = text.length(); + parentRun.changeTextInRichTextRun(this,text); + } + /** * Tells the RichTextRun its new position in the parent TextRun * @param startAt diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/ListLevel.java b/src/scratchpad/src/org/apache/poi/hwpf/model/ListLevel.java index 0adfe617f..f6b4d8594 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/ListLevel.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/ListLevel.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -16,22 +15,20 @@ limitations under the License. ==================================================================== */ - package org.apache.poi.hwpf.model; +import java.util.Arrays; + import org.apache.poi.util.BitField; import org.apache.poi.util.LittleEndian; -import org.apache.poi.hwpf.usermodel.CharacterProperties; -import org.apache.poi.hwpf.usermodel.ParagraphProperties; - -import org.apache.poi.hwpf.sprm.ParagraphSprmCompressor; -import org.apache.poi.hwpf.sprm.CharacterSprmCompressor; - -import java.util.Arrays; - -public class ListLevel +/** + * + */ +public final class ListLevel { + private static final int RGBXCH_NUMS_SIZE = 9; + private int _iStartAt; private byte _nfc; private byte _info; @@ -70,7 +67,7 @@ public class ListLevel _grpprlPapx = new byte[0]; _grpprlChpx = new byte[0]; _numberText = new char[0]; - _rgbxchNums = new byte[9]; + _rgbxchNums = new byte[RGBXCH_NUMS_SIZE]; if (numbered) { @@ -90,12 +87,11 @@ public class ListLevel _nfc = buf[offset++]; _info = buf[offset++]; - _rgbxchNums = new byte[9]; - for (int x = 0; x < 9; x++) - { - _rgbxchNums[x] = buf[offset++]; - } - _ixchFollow = buf[offset++]; + _rgbxchNums = new byte[RGBXCH_NUMS_SIZE]; + System.arraycopy(buf, offset, _rgbxchNums, 0, RGBXCH_NUMS_SIZE); + offset += RGBXCH_NUMS_SIZE; + + _ixchFollow = buf[offset++]; _dxaSpace = LittleEndian.getInt(buf, offset); offset += LittleEndian.INT_SIZE; _dxaIndent = LittleEndian.getInt(buf, offset); @@ -207,8 +203,8 @@ public class ListLevel offset += LittleEndian.INT_SIZE; buf[offset++] = _nfc; buf[offset++] = _info; - System.arraycopy(_rgbxchNums, 0, buf, offset, _rgbxchNums.length); - offset += _rgbxchNums.length; + System.arraycopy(_rgbxchNums, 0, buf, offset, RGBXCH_NUMS_SIZE); + offset += RGBXCH_NUMS_SIZE; buf[offset++] = _ixchFollow; LittleEndian.putInt(buf, offset, _dxaSpace); offset += LittleEndian.INT_SIZE; @@ -225,23 +221,33 @@ public class ListLevel System.arraycopy(_grpprlPapx, 0, buf, offset, _cbGrpprlPapx); offset += _cbGrpprlPapx; - LittleEndian.putShort(buf, offset, (short)_numberText.length); - offset += LittleEndian.SHORT_SIZE; - for (int x = 0; x < _numberText.length; x++) - { - LittleEndian.putShort(buf, offset, (short)_numberText[x]); + if (_numberText == null) { + // TODO - write junit to test this flow + LittleEndian.putUShort(buf, offset, 0); + } else { + LittleEndian.putUShort(buf, offset, _numberText.length); offset += LittleEndian.SHORT_SIZE; + for (int x = 0; x < _numberText.length; x++) + { + LittleEndian.putUShort(buf, offset, _numberText[x]); + offset += LittleEndian.SHORT_SIZE; + } } return buf; } public int getSizeInBytes() { - if (_numberText!=null) - { - return 28 + _cbGrpprlChpx + _cbGrpprlPapx + (_numberText.length * LittleEndian.SHORT_SIZE) + 2; - } else { - return 28 + _cbGrpprlChpx + _cbGrpprlPapx + 2; - } + int result = + 6 // int byte byte + + RGBXCH_NUMS_SIZE + + 13 // byte int int byte byte short + + _cbGrpprlChpx + + _cbGrpprlPapx + + 2; // numberText length + if (_numberText != null) { + result += _numberText.length * LittleEndian.SHORT_SIZE; + } + return result; } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextSpecInfoAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextSpecInfoAtom.java new file mode 100755 index 000000000..5079628ae --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextSpecInfoAtom.java @@ -0,0 +1,95 @@ + +/* ==================================================================== + 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.hslf.record; + +import org.apache.poi.hslf.HSLFSlideShow; +import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp; +import org.apache.poi.hslf.model.textproperties.TextProp; +import org.apache.poi.hslf.model.textproperties.TextPropCollection; +import org.apache.poi.hslf.record.StyleTextPropAtom.*; +import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.util.HexDump; + +import junit.framework.TestCase; +import java.io.ByteArrayOutputStream; +import java.util.LinkedList; +import java.util.Arrays; + +/** + * Tests TextSpecInfoAtom + * + * @author Yegor Kozlov + */ +public class TestTextSpecInfoAtom extends TestCase { + + //from a real file + private byte[] data_1 = new byte[] { + 0x00, 0x00, (byte)0xAA, 0x0F, 0x2C, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + + public void testRead() throws Exception { + TextSpecInfoAtom spec = new TextSpecInfoAtom(data_1, 0, data_1.length); + TextSpecInfoAtom.TextSpecInfoRun[] run = spec.getTextSpecInfoRuns(); + assertEquals(5, run.length); + + assertEquals(10, run[0].length()); + assertEquals(1, run[1].length()); + assertEquals(70, run[2].length()); + assertEquals(9, run[3].length()); + assertEquals(32, run[4].length()); + + } + + public void testWrite() throws Exception { + TextSpecInfoAtom spec = new TextSpecInfoAtom(data_1, 0, data_1.length); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + spec.writeOut(out); + + byte[] result = out.toByteArray(); + assertTrue(Arrays.equals(result, data_1)); + } + + public void testReset() throws Exception { + TextSpecInfoAtom spec = new TextSpecInfoAtom(data_1, 0, data_1.length); + spec.reset(32); //length of the parent text + + TextSpecInfoAtom.TextSpecInfoRun[] run = spec.getTextSpecInfoRuns(); + assertEquals(1, run.length); + + assertEquals(32, run[0].length()); + + //serialize and read again + ByteArrayOutputStream out = new ByteArrayOutputStream(); + spec.writeOut(out); + + byte[] result = out.toByteArray(); + TextSpecInfoAtom spec2 = new TextSpecInfoAtom(result, 0, result.length); + TextSpecInfoAtom.TextSpecInfoRun[] run2 = spec2.getTextSpecInfoRuns(); + assertEquals(1, run2.length); + + assertEquals(32, run2[0].length()); + } +} diff --git a/src/testcases/org/apache/poi/hssf/HSSFTests.java b/src/testcases/org/apache/poi/hssf/HSSFTests.java index 5b597a67c..0a4fa66af 100644 --- a/src/testcases/org/apache/poi/hssf/HSSFTests.java +++ b/src/testcases/org/apache/poi/hssf/HSSFTests.java @@ -22,9 +22,7 @@ import junit.framework.TestSuite; import org.apache.poi.hssf.eventmodel.TestEventRecordFactory; import org.apache.poi.hssf.eventmodel.TestModelFactory; -import org.apache.poi.hssf.model.TestDrawingManager; -import org.apache.poi.hssf.model.TestFormulaParser; -import org.apache.poi.hssf.model.TestSheet; +import org.apache.poi.hssf.model.AllModelTests; import org.apache.poi.hssf.record.AllRecordTests; import org.apache.poi.hssf.usermodel.AllUserModelTests; import org.apache.poi.hssf.util.TestAreaReference; @@ -50,10 +48,10 @@ public final class HSSFTests { TestSuite suite = new TestSuite("Tests for org.apache.poi.hssf"); // $JUnit-BEGIN$ + suite.addTest(AllModelTests.suite()); suite.addTest(AllUserModelTests.suite()); suite.addTest(AllRecordTests.suite()); - suite.addTest(new TestSuite(TestFormulaParser.class)); suite.addTest(new TestSuite(TestAreaReference.class)); suite.addTest(new TestSuite(TestCellReference.class)); suite.addTest(new TestSuite(TestRangeAddress.class)); @@ -61,8 +59,6 @@ public final class HSSFTests { suite.addTest(new TestSuite(TestSheetReferences.class)); suite.addTest(new TestSuite(TestEventRecordFactory.class)); suite.addTest(new TestSuite(TestModelFactory.class)); - suite.addTest(new TestSuite(TestDrawingManager.class)); - suite.addTest(new TestSuite(TestSheet.class)); // $JUnit-END$ return suite; } diff --git a/src/testcases/org/apache/poi/hssf/data/24207.xls b/src/testcases/org/apache/poi/hssf/data/24207.xls new file mode 100755 index 000000000..eca56425e Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/24207.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/36947.xls b/src/testcases/org/apache/poi/hssf/data/36947.xls new file mode 100755 index 000000000..4c7bde6c2 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/36947.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/39634.xls b/src/testcases/org/apache/poi/hssf/data/39634.xls new file mode 100755 index 000000000..0f76bffe4 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/39634.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/44235.xls b/src/testcases/org/apache/poi/hssf/data/44235.xls new file mode 100755 index 000000000..39b76588a Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/44235.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/ex41187-19267.xls b/src/testcases/org/apache/poi/hssf/data/ex41187-19267.xls new file mode 100644 index 000000000..c07bfca59 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ex41187-19267.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/ex42570-20305.xls b/src/testcases/org/apache/poi/hssf/data/ex42570-20305.xls new file mode 100644 index 000000000..e3d4cc837 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ex42570-20305.xls differ diff --git a/src/testcases/org/apache/poi/hssf/model/AllModelTests.java b/src/testcases/org/apache/poi/hssf/model/AllModelTests.java new file mode 100755 index 000000000..19ef43706 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/model/AllModelTests.java @@ -0,0 +1,40 @@ +/* ==================================================================== + 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.model; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Collects all tests for org.apache.poi.hssf.model. + * + * @author Josh Micich + */ +public final class AllModelTests { + + public static Test suite() { + TestSuite result = new TestSuite(AllModelTests.class.getName()); + result.addTestSuite(TestDrawingManager.class); + result.addTestSuite(TestDrawingManager2.class); + result.addTestSuite(TestFormulaParser.class); + result.addTestSuite(TestFormulaParserEval.class); + result.addTestSuite(TestSheet.class); + result.addTestSuite(TestSheetAdditional.class); + return result; + } +} diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index 9ce862b9b..1895705a5 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -65,6 +65,7 @@ public final class TestFormulaParser extends TestCase { * @return parsed token array already confirmed not null */ private static Ptg[] parseFormula(String s) { + // TODO - replace multiple copies of this code with calls to this method FormulaParser fp = new FormulaParser(s, null); fp.parse(); Ptg[] result = fp.getRPNPtg(); @@ -86,7 +87,6 @@ public final class TestFormulaParser extends TestCase { assertTrue("",(ptgs[0] instanceof IntPtg)); assertTrue("",(ptgs[1] instanceof IntPtg)); assertTrue("",(ptgs[2] instanceof AddPtg)); - } public void testFormulaWithSpace2() { @@ -169,8 +169,6 @@ public final class TestFormulaParser extends TestCase { assertEquals("If FALSE offset", (short)7, ifPtg.getData()); FuncVarPtg funcPtg = (FuncVarPtg)asts[8]; - - } /** @@ -190,8 +188,6 @@ public final class TestFormulaParser extends TestCase { assertTrue("It is not an if", ifFunc.isOptimizedIf()); assertTrue("Average Function set correctly", (asts[5] instanceof FuncVarPtg)); - - } public void testIfSingleCondition(){ @@ -213,8 +209,6 @@ public final class TestFormulaParser extends TestCase { assertTrue("Ptg is not a Variable Function", (asts[6] instanceof FuncVarPtg)); FuncVarPtg funcPtg = (FuncVarPtg)asts[6]; assertEquals("Arguments", 2, funcPtg.getNumberOfOperands()); - - } public void testSumIf() { @@ -223,7 +217,6 @@ public final class TestFormulaParser extends TestCase { fp.parse(); Ptg[] asts = fp.getRPNPtg(); assertEquals("4 Ptgs expected", 4, asts.length); - } /** @@ -235,51 +228,35 @@ public final class TestFormulaParser extends TestCase { String currencyCell = "F3"; String function="\"TOTAL[\"&"+currencyCell+"&\"]\""; - FormulaParser fp = new FormulaParser(function, null); - fp.parse(); - Ptg[] asts = fp.getRPNPtg(); + Ptg[] asts = parseFormula(function); assertEquals("5 ptgs expected", 5, asts.length); assertTrue ("Ptg[0] is a string", (asts[0] instanceof StringPtg)); StringPtg firstString = (StringPtg)asts[0]; assertEquals("TOTAL[", firstString.getValue()); //the PTG order isn't 100% correct but it still works - dmui - - } public void testSimpleLogical() { - FormulaParser fp=new FormulaParser("IF(A1=1,\"*\",IF(4<>1,\"first\",\"second\"))",null); - fp.parse(); - Ptg[] ptgs = fp.getRPNPtg(); - assertTrue("Ptg array should not be null", ptgs !=null); + Ptg[] ptgs = parseFormula("IF(3>=1,\"*\",IF(4<>1,\"first\",\"second\"))"); assertEquals("Ptg array length", 17, ptgs.length); assertEquals("6th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[5].getClass()); assertEquals("9th Ptg is not a not equal ptg",NotEqualPtg.class,ptgs[8].getClass()); assertEquals("15th Ptg is not the inner IF variable function ptg",FuncVarPtg.class,ptgs[14].getClass()); - } public void testMacroFunction() { @@ -302,16 +279,15 @@ public final class TestFormulaParser extends TestCase { Ptg[] ptg = fp.getRPNPtg(); assertTrue("first ptg is string",ptg[0] instanceof StringPtg); assertTrue("second ptg is string",ptg[1] instanceof StringPtg); - } - public void testConcatenate(){ - FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")",null); - fp.parse(); - Ptg[] ptg = fp.getRPNPtg(); - assertTrue("first ptg is string",ptg[0] instanceof StringPtg); - assertTrue("second ptg is string",ptg[1] instanceof StringPtg); - } + public void testConcatenate() { + FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")", null); + fp.parse(); + Ptg[] ptg = fp.getRPNPtg(); + assertTrue("first ptg is string", ptg[0] instanceof StringPtg); + assertTrue("second ptg is string", ptg[1] instanceof StringPtg); + } public void testWorksheetReferences() { @@ -395,16 +371,16 @@ public final class TestFormulaParser extends TestCase { /** bug 33160, testcase by Amol Deshmukh*/ public void testSimpleLongFormula() { - FormulaParser fp = new FormulaParser("40000/2", null); - fp.parse(); - Ptg[] ptgs = fp.getRPNPtg(); - assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3); - assertTrue("IntPtg",(ptgs[0] instanceof IntPtg)); - assertTrue("IntPtg",(ptgs[1] instanceof IntPtg)); - assertTrue("DividePtg",(ptgs[2] instanceof DividePtg)); + FormulaParser fp = new FormulaParser("40000/2", null); + fp.parse(); + Ptg[] ptgs = fp.getRPNPtg(); + assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3); + assertTrue("IntPtg", (ptgs[0] instanceof IntPtg)); + assertTrue("IntPtg", (ptgs[1] instanceof IntPtg)); + assertTrue("DividePtg", (ptgs[2] instanceof DividePtg)); } - /** bug 35027, underscore in sheet name*/ + /** bug 35027, underscore in sheet name */ public void testUnderscore() { HSSFWorkbook wb = new HSSFWorkbook(); @@ -775,8 +751,34 @@ public final class TestFormulaParser extends TestCase { StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class); assertEquals(expectedValue, sp.getValue()); } + public void testParseStringLiterals_bug28754() { - public void testPaseStringLiterals() { + StringPtg sp; + try { + sp = (StringPtg) parseSingleToken("\"test\"\"ing\"", StringPtg.class); + } catch (RuntimeException e) { + if(e.getMessage().startsWith("Cannot Parse")) { + throw new AssertionFailedError("Identified bug 28754a"); + } + throw e; + } + assertEquals("test\"ing", sp.getValue()); + + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + wb.setSheetName(0, "Sheet1"); + + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell((short)0); + cell.setCellFormula("right(\"test\"\"ing\", 3)"); + String actualCellFormula = cell.getCellFormula(); + if("RIGHT(\"test\"ing\",3)".equals(actualCellFormula)) { + throw new AssertionFailedError("Identified bug 28754b"); + } + assertEquals("RIGHT(\"test\"\"ing\",3)", actualCellFormula); + } + + public void testParseStringLiterals() { confirmStringParse("goto considered harmful"); confirmStringParse("goto 'considered' harmful"); @@ -810,10 +812,8 @@ public final class TestFormulaParser extends TestCase { parseExpectedException("#DIV/ 0+2"); - if (false) { // TODO - add functionality to detect func arg count mismatch - parseExpectedException("IF(TRUE)"); - parseExpectedException("countif(A1:B5, C1, D1)"); - } + parseExpectedException("IF(TRUE)"); + parseExpectedException("countif(A1:B5, C1, D1)"); } private static void parseExpectedException(String formula) { @@ -887,8 +887,16 @@ public final class TestFormulaParser extends TestCase { assertTrue(e.getMessage().startsWith("Too few arguments suppled to operation token")); } } + /** + * Make sure that POI uses the right Func Ptg when encoding formulas. Functions with variable + * number of args should get FuncVarPtg, functions with fixed args should get FuncPtg.

    + * + * Prior to the fix for bug 44675 POI would encode FuncVarPtg for all functions. In many cases + * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases + * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead. + */ public void testFuncPtgSelection() { - HSSFWorkbook book = new HSSFWorkbook(); + HSSFWorkbook book = new HSSFWorkbook(); Ptg[] ptgs; ptgs = FormulaParser.parse("countif(A1:A2, 1)", book); assertEquals(3, ptgs.length); diff --git a/src/testcases/org/apache/poi/hssf/model/TestSheet.java b/src/testcases/org/apache/poi/hssf/model/TestSheet.java index 964caedc2..9281eb80d 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestSheet.java +++ b/src/testcases/org/apache/poi/hssf/model/TestSheet.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.model; @@ -34,8 +32,7 @@ import java.util.List; * * @author Glen Stampoultzis (glens at apache.org) */ -public class TestSheet extends TestCase -{ +public final class TestSheet extends TestCase { public void testCreateSheet() throws Exception { // Check we're adding row and cell aggregates @@ -76,6 +73,21 @@ public class TestSheet extends TestCase if ((regionsToAdd % 1027) != 0) recordsExpected++; assertTrue("The " + regionsToAdd + " merged regions should have been spread out over " + recordsExpected + " records, not " + recordsAdded, recordsAdded == recordsExpected); + // Check we can't add one with invalid date + try { + sheet.addMergedRegion(10, (short)10, 9, (short)12); + fail("Expected an exception to occur"); + } catch(IllegalArgumentException e) { + // occurs during successful test + assertEquals("The 'to' row (9) must not be less than the 'from' row (10)", e.getMessage()); + } + try { + sheet.addMergedRegion(10, (short)10, 12, (short)9); + fail("Expected an exception to occur"); + } catch(IllegalArgumentException e) { + // occurs during successful test + assertEquals("The 'to' col (9) must not be less than the 'from' col (10)", e.getMessage()); + } } public void testRemoveMergedRegion() @@ -113,9 +125,9 @@ public class TestSheet extends TestCase MergeCellsRecord merged = new MergeCellsRecord(); merged.addArea(0, (short)0, 1, (short)2); - records.add(new RowRecord()); - records.add(new RowRecord()); - records.add(new RowRecord()); + records.add(new RowRecord(0)); + records.add(new RowRecord(1)); + records.add(new RowRecord(2)); records.add(merged); Sheet sheet = Sheet.createSheet(records, 0); @@ -142,20 +154,11 @@ public class TestSheet extends TestCase */ public void testRowAggregation() { List records = new ArrayList(); - RowRecord row = new RowRecord(); - row.setRowNumber(0); - records.add(row); - - row = new RowRecord(); - row.setRowNumber(1); - records.add(row); + records.add(new RowRecord(0)); + records.add(new RowRecord(1)); records.add(new StringRecord()); - - row = new RowRecord(); - row.setRowNumber(2); - records.add(row); - + records.add(new RowRecord(2)); Sheet sheet = Sheet.createSheet(records, 0); assertNotNull("Row [2] was skipped", sheet.getRow(2)); @@ -197,9 +200,9 @@ public class TestSheet extends TestCase Iterator iterator = sheet.getRowBreaks(); while (iterator.hasNext()) { PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); - int main = (int)breakItem.main; + int main = breakItem.main; if (main != 0 && main != 10 && main != 11) fail("Invalid page break"); - if (main == 0) is0 = true; + if (main == 0) is0 = true; if (main == 10) is10= true; if (main == 11) is11 = true; } @@ -216,8 +219,6 @@ public class TestSheet extends TestCase assertFalse("row should be removed", sheet.isRowBroken(10)); assertEquals("no more breaks", 0, sheet.getNumRowBreaks()); - - } /** @@ -256,10 +257,10 @@ public class TestSheet extends TestCase Iterator iterator = sheet.getColumnBreaks(); while (iterator.hasNext()) { PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); - int main = (int)breakItem.main; + int main = breakItem.main; if (main != 0 && main != 1 && main != 10 && main != 15) fail("Invalid page break"); - if (main == 0) is0 = true; - if (main == 1) is1 = true; + if (main == 0) is0 = true; + if (main == 1) is1 = true; if (main == 10) is10= true; if (main == 15) is15 = true; } @@ -286,72 +287,69 @@ public class TestSheet extends TestCase * works as designed. */ public void testXFIndexForColumn() { - try{ - final short TEST_IDX = 10; - final short DEFAULT_IDX = 0xF; // 15 - short xfindex = Short.MIN_VALUE; - Sheet sheet = Sheet.createSheet(); - - // without ColumnInfoRecord - xfindex = sheet.getXFIndexForColAt((short) 0); - assertEquals(DEFAULT_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 1); - assertEquals(DEFAULT_IDX, xfindex); - - ColumnInfoRecord nci = ( ColumnInfoRecord ) sheet.createColInfo(); - sheet.columns.insertColumn(nci); - - // single column ColumnInfoRecord - nci.setFirstColumn((short) 2); - nci.setLastColumn((short) 2); - nci.setXFIndex(TEST_IDX); - xfindex = sheet.getXFIndexForColAt((short) 0); - assertEquals(DEFAULT_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 1); - assertEquals(DEFAULT_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 2); - assertEquals(TEST_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 3); - assertEquals(DEFAULT_IDX, xfindex); + final short TEST_IDX = 10; + final short DEFAULT_IDX = 0xF; // 15 + short xfindex = Short.MIN_VALUE; + Sheet sheet = Sheet.createSheet(); + + // without ColumnInfoRecord + xfindex = sheet.getXFIndexForColAt((short) 0); + assertEquals(DEFAULT_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 1); + assertEquals(DEFAULT_IDX, xfindex); + + ColumnInfoRecord nci = ( ColumnInfoRecord ) sheet.createColInfo(); + sheet.columns.insertColumn(nci); + + // single column ColumnInfoRecord + nci.setFirstColumn((short) 2); + nci.setLastColumn((short) 2); + nci.setXFIndex(TEST_IDX); + xfindex = sheet.getXFIndexForColAt((short) 0); + assertEquals(DEFAULT_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 1); + assertEquals(DEFAULT_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 2); + assertEquals(TEST_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 3); + assertEquals(DEFAULT_IDX, xfindex); - // ten column ColumnInfoRecord - nci.setFirstColumn((short) 2); - nci.setLastColumn((short) 11); - nci.setXFIndex(TEST_IDX); - xfindex = sheet.getXFIndexForColAt((short) 1); - assertEquals(DEFAULT_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 2); - assertEquals(TEST_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 6); - assertEquals(TEST_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 11); - assertEquals(TEST_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 12); - assertEquals(DEFAULT_IDX, xfindex); + // ten column ColumnInfoRecord + nci.setFirstColumn((short) 2); + nci.setLastColumn((short) 11); + nci.setXFIndex(TEST_IDX); + xfindex = sheet.getXFIndexForColAt((short) 1); + assertEquals(DEFAULT_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 2); + assertEquals(TEST_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 6); + assertEquals(TEST_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 11); + assertEquals(TEST_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 12); + assertEquals(DEFAULT_IDX, xfindex); - // single column ColumnInfoRecord starting at index 0 - nci.setFirstColumn((short) 0); - nci.setLastColumn((short) 0); - nci.setXFIndex(TEST_IDX); - xfindex = sheet.getXFIndexForColAt((short) 0); - assertEquals(TEST_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 1); - assertEquals(DEFAULT_IDX, xfindex); + // single column ColumnInfoRecord starting at index 0 + nci.setFirstColumn((short) 0); + nci.setLastColumn((short) 0); + nci.setXFIndex(TEST_IDX); + xfindex = sheet.getXFIndexForColAt((short) 0); + assertEquals(TEST_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 1); + assertEquals(DEFAULT_IDX, xfindex); - // ten column ColumnInfoRecord starting at index 0 - nci.setFirstColumn((short) 0); - nci.setLastColumn((short) 9); - nci.setXFIndex(TEST_IDX); - xfindex = sheet.getXFIndexForColAt((short) 0); - assertEquals(TEST_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 7); - assertEquals(TEST_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 9); - assertEquals(TEST_IDX, xfindex); - xfindex = sheet.getXFIndexForColAt((short) 10); - assertEquals(DEFAULT_IDX, xfindex); - } - catch(Exception e){e.printStackTrace();fail(e.getMessage());} + // ten column ColumnInfoRecord starting at index 0 + nci.setFirstColumn((short) 0); + nci.setLastColumn((short) 9); + nci.setXFIndex(TEST_IDX); + xfindex = sheet.getXFIndexForColAt((short) 0); + assertEquals(TEST_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 7); + assertEquals(TEST_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 9); + assertEquals(TEST_IDX, xfindex); + xfindex = sheet.getXFIndexForColAt((short) 10); + assertEquals(DEFAULT_IDX, xfindex); } - } + diff --git a/src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java b/src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java index 69ab3f298..f1c3b7c9c 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java +++ b/src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java @@ -19,125 +19,18 @@ package org.apache.poi.hssf.model; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import junit.framework.TestCase; import org.apache.poi.hssf.record.ColumnInfoRecord; -import org.apache.poi.hssf.record.MergeCellsRecord; -import org.apache.poi.hssf.record.PageBreakRecord; -import org.apache.poi.hssf.record.RowRecord; -import org.apache.poi.hssf.record.StringRecord; /** * @author Tony Poppleton */ -public class TestSheetAdditional extends TestCase -{ - /** - * Constructor for SheetTest. - * @param arg0 - */ - public TestSheetAdditional(String arg0) - { - super(arg0); - } +public final class TestSheetAdditional extends TestCase { - public void testAddMergedRegion() - { - Sheet sheet = Sheet.createSheet(); - int regionsToAdd = 4096; - int startRecords = sheet.getRecords().size(); - - //simple test that adds a load of regions - for (int n = 0; n < regionsToAdd; n++) - { - int index = sheet.addMergedRegion(0, (short) 0, 1, (short) 1); - assertTrue("Merged region index expected to be " + n + " got " + index, index == n); - } - - //test all the regions were indeed added - assertTrue(sheet.getNumMergedRegions() == regionsToAdd); - - //test that the regions were spread out over the appropriate number of records - int recordsAdded = sheet.getRecords().size() - startRecords; - int recordsExpected = regionsToAdd/1027; - if ((regionsToAdd % 1027) != 0) - recordsExpected++; - assertTrue("The " + regionsToAdd + " merged regions should have been spread out over " + recordsExpected + " records, not " + recordsAdded, recordsAdded == recordsExpected); - - // Check we can't add one with invalud date - try { - sheet.addMergedRegion(10, (short)10, 9, (short)12); - fail(); - } catch(IllegalArgumentException e) {} - try { - sheet.addMergedRegion(10, (short)10, 12, (short)9); - fail(); - } catch(IllegalArgumentException e) {} - } - - public void testRemoveMergedRegion() - { - Sheet sheet = Sheet.createSheet(); - int regionsToAdd = 4096; - - for (int n = 0; n < regionsToAdd; n++) - sheet.addMergedRegion(0, (short) 0, 1, (short) 1); - - int records = sheet.getRecords().size(); - - //remove a third from the beginning - for (int n = 0; n < regionsToAdd/3; n++) - { - sheet.removeMergedRegion(0); - //assert they have been deleted - assertTrue("Num of regions should be " + (regionsToAdd - n - 1) + " not " + sheet.getNumMergedRegions(), sheet.getNumMergedRegions() == regionsToAdd - n - 1); - } - - //assert any record removing was done - int recordsRemoved = (regionsToAdd/3)/1027; //doesn't work for particular values of regionsToAdd - assertTrue("Expected " + recordsRemoved + " record to be removed from the starting " + records + ". Currently there are " + sheet.getRecords().size() + " records", records - sheet.getRecords().size() == recordsRemoved); - } - - /** - * Bug: 22922 (Reported by Xuemin Guan) - *

    - * Remove mergedregion fails when a sheet loses records after an initial CreateSheet - * fills up the records. - * - */ - public void testMovingMergedRegion() { - List records = new ArrayList(); - - MergeCellsRecord merged = new MergeCellsRecord(); - merged.addArea(0, (short)0, 1, (short)2); - records.add(new RowRecord()); - records.add(new RowRecord()); - records.add(new RowRecord()); - records.add(merged); - - Sheet sheet = Sheet.createSheet(records, 0); - sheet.records.remove(0); - - //stub object to throw off list INDEX operations - sheet.removeMergedRegion(0); - assertEquals("Should be no more merged regions", 0, sheet.getNumMergedRegions()); - } - - public void testGetMergedRegionAt() - { - //TODO - } - - public void testGetNumMergedRegions() - { - //TODO - } - - public void DISBALEDtestGetCellWidth() throws Exception - { + public void testGetCellWidth() { Sheet sheet = Sheet.createSheet(); ColumnInfoRecord nci = ( ColumnInfoRecord ) sheet.createColInfo(); @@ -146,14 +39,8 @@ public class TestSheetAdditional extends TestCase nci.setLastColumn((short)10); nci.setColumnWidth((short)100); - Field f = null; - f = Sheet.class.getDeclaredField("columnSizes"); - f.setAccessible(true); - List columnSizes = new ArrayList(); - f.set(sheet,columnSizes); - columnSizes.add(nci); - sheet.records.add(1 + sheet.dimsloc, nci); - sheet.dimsloc++; + + sheet.columns.insertColumn(nci); assertEquals((short)100,sheet.getColumnWidth((short)5)); assertEquals((short)100,sheet.getColumnWidth((short)6)); @@ -172,151 +59,6 @@ public class TestSheetAdditional extends TestCase assertEquals((short)100,sheet.getColumnWidth((short)10)); } - /** - * Makes sure all rows registered for this sheet are aggregated, they were being skipped - * - */ - public void testRowAggregation() { - List records = new ArrayList(); - RowRecord row = new RowRecord(); - row.setRowNumber(0); - records.add(row); - - row = new RowRecord(); - row.setRowNumber(1); - records.add(row); - - records.add(new StringRecord()); - - row = new RowRecord(); - row.setRowNumber(2); - records.add(row); - - - Sheet sheet = Sheet.createSheet(records, 0); - assertNotNull("Row [2] was skipped", sheet.getRow(2)); - - } - - /** - * Make sure page break functionality works (in memory) - * - */ - public void testRowPageBreaks(){ - short colFrom = 0; - short colTo = 255; - - Sheet sheet = Sheet.createSheet(); - sheet.setRowBreak(0, colFrom, colTo); - - assertTrue("no row break at 0", sheet.isRowBroken(0)); - assertEquals("1 row break available", 1, sheet.getNumRowBreaks()); - - sheet.setRowBreak(0, colFrom, colTo); - sheet.setRowBreak(0, colFrom, colTo); - - assertTrue("no row break at 0", sheet.isRowBroken(0)); - assertEquals("1 row break available", 1, sheet.getNumRowBreaks()); - - sheet.setRowBreak(10, colFrom, colTo); - sheet.setRowBreak(11, colFrom, colTo); - - assertTrue("no row break at 10", sheet.isRowBroken(10)); - assertTrue("no row break at 11", sheet.isRowBroken(11)); - assertEquals("3 row break available", 3, sheet.getNumRowBreaks()); - - - boolean is10 = false; - boolean is0 = false; - boolean is11 = false; - - Iterator iterator = sheet.getRowBreaks(); - while (iterator.hasNext()) { - PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); - int main = (int)breakItem.main; - if (main != 0 && main != 10 && main != 11) fail("Invalid page break"); - if (main == 0) is0 = true; - if (main == 10) is10= true; - if (main == 11) is11 = true; - } - - assertTrue("one of the breaks didnt make it", is0 && is10 && is11); - - sheet.removeRowBreak(11); - assertFalse("row should be removed", sheet.isRowBroken(11)); - - sheet.removeRowBreak(0); - assertFalse("row should be removed", sheet.isRowBroken(0)); - - sheet.removeRowBreak(10); - assertFalse("row should be removed", sheet.isRowBroken(10)); - - assertEquals("no more breaks", 0, sheet.getNumRowBreaks()); - - - } - - /** - * Make sure column pag breaks works properly (in-memory) - * - */ - public void testColPageBreaks(){ - short rowFrom = 0; - short rowTo = (short)65535; - - Sheet sheet = Sheet.createSheet(); - sheet.setColumnBreak((short)0, rowFrom, rowTo); - - assertTrue("no col break at 0", sheet.isColumnBroken((short)0)); - assertEquals("1 col break available", 1, sheet.getNumColumnBreaks()); - - sheet.setColumnBreak((short)0, rowFrom, rowTo); - - assertTrue("no col break at 0", sheet.isColumnBroken((short)0)); - assertEquals("1 col break available", 1, sheet.getNumColumnBreaks()); - - sheet.setColumnBreak((short)1, rowFrom, rowTo); - sheet.setColumnBreak((short)10, rowFrom, rowTo); - sheet.setColumnBreak((short)15, rowFrom, rowTo); - - assertTrue("no col break at 1", sheet.isColumnBroken((short)1)); - assertTrue("no col break at 10", sheet.isColumnBroken((short)10)); - assertTrue("no col break at 15", sheet.isColumnBroken((short)15)); - assertEquals("4 col break available", 4, sheet.getNumColumnBreaks()); - - boolean is10 = false; - boolean is0 = false; - boolean is1 = false; - boolean is15 = false; - - Iterator iterator = sheet.getColumnBreaks(); - while (iterator.hasNext()) { - PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); - int main = (int)breakItem.main; - if (main != 0 && main != 1 && main != 10 && main != 15) fail("Invalid page break"); - if (main == 0) is0 = true; - if (main == 1) is1 = true; - if (main == 10) is10= true; - if (main == 15) is15 = true; - } - - assertTrue("one of the breaks didnt make it", is0 && is1 && is10 && is15); - - sheet.removeColumnBreak((short)15); - assertFalse("column break should not be there", sheet.isColumnBroken((short)15)); - - sheet.removeColumnBreak((short)0); - assertFalse("column break should not be there", sheet.isColumnBroken((short)0)); - - sheet.removeColumnBreak((short)1); - assertFalse("column break should not be there", sheet.isColumnBroken((short)1)); - - sheet.removeColumnBreak((short)10); - assertFalse("column break should not be there", sheet.isColumnBroken((short)10)); - - assertEquals("no more breaks", 0, sheet.getNumColumnBreaks()); - } - } diff --git a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java index d2a1b3684..2b8e3c3ab 100755 --- a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java +++ b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java @@ -67,6 +67,7 @@ public final class AllRecordTests { result.addTestSuite(TestFormulaRecord.class); result.addTestSuite(TestFrameRecord.class); result.addTestSuite(TestHyperlinkRecord.class); + result.addTestSuite(TestLabelRecord.class); result.addTestSuite(TestLegendRecord.class); result.addTestSuite(TestLineFormatRecord.class); result.addTestSuite(TestLinkedDataRecord.class); diff --git a/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java b/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java index 714c0358f..7a9acb087 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java @@ -38,6 +38,8 @@ public final class TestExternalNameRecord extends TestCase { // data taken from bugzilla 44774 att 21790 private static final byte[] dataPlainName = { 0, 0, 0, 0, 0, 0, 9, 0, 82, 97, 116, 101, 95, 68, 97, 116, 101, 9, 0, 58, 0, 0, 0, 0, 4, 0, 8, 0 + // TODO - the last 2 bytes of formula data (8,0) seem weird. They encode to ConcatPtg, UnknownPtg + // UnknownPtg is otherwise not created by any other test cases }; private static ExternalNameRecord createSimpleENR(byte[] data) { diff --git a/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java b/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java index abe6943c7..4f5e39087 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java @@ -19,17 +19,15 @@ package org.apache.poi.hssf.record; import java.io.ByteArrayInputStream; - -import org.apache.poi.hssf.record.formula.AttrPtg; -import org.apache.poi.hssf.record.formula.ConcatPtg; -import org.apache.poi.hssf.record.formula.FuncVarPtg; -import org.apache.poi.hssf.record.formula.IntPtg; -import org.apache.poi.hssf.record.formula.RangePtg; -import org.apache.poi.hssf.record.formula.ReferencePtg; -import org.apache.poi.hssf.record.formula.UnknownPtg; +import java.util.List; import junit.framework.TestCase; +import org.apache.poi.hssf.record.formula.AttrPtg; +import org.apache.poi.hssf.record.formula.FuncVarPtg; +import org.apache.poi.hssf.record.formula.IntPtg; +import org.apache.poi.hssf.record.formula.ReferencePtg; + /** * Tests the serialization and deserialization of the FormulaRecord * class works correctly. @@ -57,7 +55,7 @@ public final class TestFormulaRecord extends TestCase { */ public void testCheckNanPreserve() { byte[] formulaByte = new byte[29]; - for (int i = 0; i < formulaByte.length; i++) formulaByte[i] = (byte)0; + formulaByte[4] = (byte)0x0F; formulaByte[6] = (byte)0x02; formulaByte[8] = (byte)0x07; @@ -91,8 +89,6 @@ public final class TestFormulaRecord extends TestCase { public void testExpFormula() { byte[] formulaByte = new byte[27]; - for (int i = 0; i < formulaByte.length; i++) formulaByte[i] = (byte)0; - formulaByte[4] =(byte)0x0F; formulaByte[14]=(byte)0x08; formulaByte[18]=(byte)0xE0; @@ -109,15 +105,14 @@ public final class TestFormulaRecord extends TestCase { public void testWithConcat() throws Exception { // =CHOOSE(2,A2,A3,A4) - byte[] data = new byte[] { + byte[] data = { 6, 0, 68, 0, 1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57, 64, 0, 0, 12, 0, 12, -4, 46, 0, 30, 2, 0, // Int - 2 25, 4, 3, 0, // Attr - 8, 0, // Concat - 17, 0, // Range - 26, 0, 35, 0, // Bit like an attr + 8, 0, 17, 0, 26, 0, // jumpTable + 35, 0, // chooseOffset 36, 1, 0, 0, -64, // Ref - A2 25, 8, 21, 0, // Attr 36, 2, 0, 0, -64, // Ref - A3 @@ -126,30 +121,24 @@ public final class TestFormulaRecord extends TestCase { 25, 8, 3, 0, // Attr 66, 4, 100, 0 // CHOOSE }; - RecordInputStream inp = new RecordInputStream( - new ByteArrayInputStream(data) - ); + RecordInputStream inp = new RecordInputStream( new ByteArrayInputStream(data)); inp.nextRecord(); FormulaRecord fr = new FormulaRecord(inp); - assertEquals(14, fr.getNumberOfExpressionTokens()); - assertEquals(IntPtg.class, fr.getParsedExpression().get(0).getClass()); - assertEquals(AttrPtg.class, fr.getParsedExpression().get(1).getClass()); - assertEquals(ConcatPtg.class, fr.getParsedExpression().get(2).getClass()); - assertEquals(UnknownPtg.class, fr.getParsedExpression().get(3).getClass()); - assertEquals(RangePtg.class, fr.getParsedExpression().get(4).getClass()); - assertEquals(UnknownPtg.class, fr.getParsedExpression().get(5).getClass()); - assertEquals(AttrPtg.class, fr.getParsedExpression().get(6).getClass()); - assertEquals(ReferencePtg.class, fr.getParsedExpression().get(7).getClass()); - assertEquals(AttrPtg.class, fr.getParsedExpression().get(8).getClass()); - assertEquals(ReferencePtg.class, fr.getParsedExpression().get(9).getClass()); - assertEquals(AttrPtg.class, fr.getParsedExpression().get(10).getClass()); - assertEquals(ReferencePtg.class, fr.getParsedExpression().get(11).getClass()); - assertEquals(AttrPtg.class, fr.getParsedExpression().get(12).getClass()); - assertEquals(FuncVarPtg.class, fr.getParsedExpression().get(13).getClass()); + List ptgs = fr.getParsedExpression(); + assertEquals(9, ptgs.size()); + assertEquals(IntPtg.class, ptgs.get(0).getClass()); + assertEquals(AttrPtg.class, ptgs.get(1).getClass()); + assertEquals(ReferencePtg.class, ptgs.get(2).getClass()); + assertEquals(AttrPtg.class, ptgs.get(3).getClass()); + assertEquals(ReferencePtg.class, ptgs.get(4).getClass()); + assertEquals(AttrPtg.class, ptgs.get(5).getClass()); + assertEquals(ReferencePtg.class, ptgs.get(6).getClass()); + assertEquals(AttrPtg.class, ptgs.get(7).getClass()); + assertEquals(FuncVarPtg.class, ptgs.get(8).getClass()); - FuncVarPtg choose = (FuncVarPtg)fr.getParsedExpression().get(13); + FuncVarPtg choose = (FuncVarPtg)ptgs.get(8); assertEquals("CHOOSE", choose.getName()); } diff --git a/src/testcases/org/apache/poi/hssf/record/TestLabelRecord.java b/src/testcases/org/apache/poi/hssf/record/TestLabelRecord.java new file mode 100644 index 000000000..10f363652 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestLabelRecord.java @@ -0,0 +1,41 @@ +/* ==================================================================== + 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.record; + +import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +/** + * Tests for LabelRecord + * + * @author Josh Micich + */ +public final class TestLabelRecord extends TestCase { + + public void testEmptyString() { + HSSFWorkbook wb; + try { + wb = HSSFTestDataSamples.openSampleWorkbook("ex42570-20305.xls"); + } catch (NullPointerException e) { + throw new AssertionFailedError("Identified bug 42570"); + } + HSSFTestDataSamples.writeOutAndReadBack(wb); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestRowRecordsAggregate.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestRowRecordsAggregate.java index 50f67129c..239fc2b88 100644 --- a/src/testcases/org/apache/poi/hssf/record/aggregates/TestRowRecordsAggregate.java +++ b/src/testcases/org/apache/poi/hssf/record/aggregates/TestRowRecordsAggregate.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,34 +14,28 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.hssf.record.aggregates; -import org.apache.poi.hssf.record.*; +import junit.framework.TestCase; -public class TestRowRecordsAggregate extends junit.framework.TestCase { - public TestRowRecordsAggregate(String name) { - super (name); - } +import org.apache.poi.hssf.record.RowRecord; + +/** + * + */ +public final class TestRowRecordsAggregate extends TestCase { public void testRowGet() { RowRecordsAggregate rra = new RowRecordsAggregate(); - RowRecord rr = new RowRecord(); - rr.setRowNumber(( short ) 4); + RowRecord rr = new RowRecord(4); rra.insertRow(rr); - RowRecord rr2 = new RowRecord(); rr2.setRowNumber((short) 1); - rra.insertRow(rr2); + rra.insertRow(new RowRecord(1)); RowRecord rr1 = rra.getRow(4); - assertTrue("Row Record should not be null", rr1!=null); - assertTrue("Row number is 1",rr1.getRowNumber() == 4); + assertNotNull(rr1); + assertEquals("Row number is 1", 4, rr1.getRowNumber()); assertTrue("Row record retrieved is identical ", rr1 == rr); } - - public static void main(String [] args) { - System.out - .println("Testing org.apache.poi.hssf.record.aggregates.RowRecordAggregate"); - junit.textui.TestRunner.run(TestRowRecordsAggregate.class); - } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java index 80f6616fa..a685fd2cb 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java @@ -40,6 +40,7 @@ public final class AllFormulaTests { result.addTestSuite(TestArea3DPtg.class); result.addTestSuite(TestAreaErrPtg.class); result.addTestSuite(TestAreaPtg.class); + result.addTestSuite(TestArrayPtg.class); result.addTestSuite(TestErrPtg.class); result.addTestSuite(TestExternalFunctionFormulas.class); result.addTestSuite(TestFuncPtg.class); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java new file mode 100644 index 000000000..16f80bb79 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java @@ -0,0 +1,95 @@ +/* ==================================================================== + 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.record.formula; + +import java.util.Arrays; + +import org.apache.poi.hssf.record.TestcaseRecordInputStream; +import org.apache.poi.hssf.record.UnicodeString; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +/** + * Tests for ArrayPtg + * + * @author Josh Micich + */ +public final class TestArrayPtg extends TestCase { + + private static final byte[] ENCODED_PTG_DATA = { + 0x40, 0x00, + 0x08, 0x00, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + private static final byte[] ENCODED_CONSTANT_DATA = { + 2, // 3 columns + 1, 0, // 2 rows + 4, 1, 0, 0, 0, 0, 0, 0, 0, // TRUE + 2, 4, 0, 0, 65, 66, 67, 68, // "ABCD" + 2, 1, 0, 0, 69, // "E" + 1, 0, 0, 0, 0, 0, 0, 0, 0, // 0 + 4, 0, 0, 0, 0, 0, 0, 0, 0, // FALSE + 2, 2, 0, 0, 70, 71, // "FG" + }; + + /** + * Lots of problems with ArrayPtg's encoding of + */ + public void testReadWriteTokenValueBytes() { + + ArrayPtg ptg = new ArrayPtgV(new TestcaseRecordInputStream(ArrayPtgV.sid, ENCODED_PTG_DATA)); + + ptg.readTokenValues(new TestcaseRecordInputStream(0, ENCODED_CONSTANT_DATA)); + assertEquals(3, ptg.getColumnCount()); + assertEquals(2, ptg.getRowCount()); + Object[] values = ptg.token_3_arrayValues; + assertEquals(6, values.length); + + + assertEquals(Boolean.TRUE, values[0]); + assertEquals(new UnicodeString("ABCD"), values[1]); + assertEquals(new Double(0), values[3]); + assertEquals(Boolean.FALSE, values[4]); + assertEquals(new UnicodeString("FG"), values[5]); + + byte[] outBuf = new byte[ENCODED_CONSTANT_DATA.length]; + ptg.writeTokenValueBytes(outBuf, 0); + + if(outBuf[0] == 4) { + throw new AssertionFailedError("Identified bug 42564b"); + } + assertTrue(Arrays.equals(ENCODED_CONSTANT_DATA, outBuf)); + } + + /** + * make sure constant elements are stored row by row + */ + public void testElementOrdering() { + ArrayPtg ptg = new ArrayPtgV(new TestcaseRecordInputStream(ArrayPtgV.sid, ENCODED_PTG_DATA)); + ptg.readTokenValues(new TestcaseRecordInputStream(0, ENCODED_CONSTANT_DATA)); + assertEquals(3, ptg.getColumnCount()); + assertEquals(2, ptg.getRowCount()); + + assertEquals(0, ptg.getValueIndex(0, 0)); + assertEquals(1, ptg.getValueIndex(1, 0)); + assertEquals(2, ptg.getValueIndex(2, 0)); + assertEquals(3, ptg.getValueIndex(0, 1)); + assertEquals(4, ptg.getValueIndex(1, 1)); + assertEquals(5, ptg.getValueIndex(2, 1)); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java index fd2b1cd12..89997b59d 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -26,45 +25,31 @@ import org.apache.poi.hssf.record.TestcaseRecordInputStream; * * @author Danny Mui (dmui at apache dot org) */ +public final class TestFuncPtg extends TestCase { -public class TestFuncPtg extends TestCase -{ - - public TestFuncPtg( String name ) - { - super( name ); - } - - - public static void main( java.lang.String[] args ) - { - junit.textui.TestRunner.run( TestFuncPtg.class ); - } - - /** - * Make sure the left overs are re-serialized on excel file reads to avoid - * the "Warning: Data may have been lost" prompt in excel. - *

    - * This ptg represents a LEN function extracted from excel - */ - - public void testLeftOvers() - { - byte[] fakeData = new byte[4]; - - //fakeData[0] = (byte) 0x41; - fakeData[0] = (byte) 0x20; //function index - fakeData[1] = (byte) 0; - fakeData[2] = (byte) 8; + public void testRead() { + // This ptg represents a LEN function extracted from excel + byte[] fakeData = { + 0x20, //function index + 0, + }; FuncPtg ptg = new FuncPtg( new TestcaseRecordInputStream((short)0, (short)fakeData.length, fakeData) ); - assertEquals( "Len formula index is not 32(20H)", (int) 0x20, ptg.getFunctionIndex() ); + assertEquals( "Len formula index is not 32(20H)", 0x20, ptg.getFunctionIndex() ); assertEquals( "Number of operands in the len formula", 1, ptg.getNumberOfOperands() ); assertEquals( "Function Name", "LEN", ptg.getName() ); assertEquals( "Ptg Size", 3, ptg.getSize() ); - //assertEquals("first leftover byte is not 0", (byte)0, ptg.leftOvers[0]); - //assertEquals("second leftover byte is not 8", (byte)8, ptg.leftOvers[1]); + } + + public void testClone() { + FuncPtg funcPtg = new FuncPtg(27); // ROUND() - takes 2 args + FuncPtg clone = (FuncPtg) funcPtg.clone(); + if (clone.getNumberOfOperands() == 0) { + fail("clone() did copy field numberOfOperands"); + } + assertEquals(2, clone.getNumberOfOperands()); + assertEquals("ROUND", clone.getName()); } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java index 5098c789a..8887445ad 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java @@ -28,7 +28,8 @@ import junit.framework.TestSuite; public class AllFormulaEvalTests { public static Test suite() { - TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.eval"); + TestSuite result = new TestSuite(AllFormulaEvalTests.class.getName()); + result.addTestSuite(TestAreaEval.class); result.addTestSuite(TestCircularReferences.class); result.addTestSuite(TestExternalFunction.class); result.addTestSuite(TestFormulaBugs.class); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestAreaEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestAreaEval.java new file mode 100644 index 000000000..6c855707b --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestAreaEval.java @@ -0,0 +1,62 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.eval; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import org.apache.poi.hssf.record.formula.Area3DPtg; + +/** + * Tests for AreaEval + * + * @author Josh Micich + */ +public final class TestAreaEval extends TestCase { + + public void testGetValue_bug44950() { + + Area3DPtg ptg = new Area3DPtg("B2:D3", (short)0); + NumberEval one = new NumberEval(1); + ValueEval[] values = { + one, + new NumberEval(2), + new NumberEval(3), + new NumberEval(4), + new NumberEval(5), + new NumberEval(6), + }; + AreaEval ae = new Area3DEval(ptg, values); + if (one == ae.getValueAt(1, 2)) { + throw new AssertionFailedError("Identified bug 44950 a"); + } + confirm(1, ae, 1, 1); + confirm(2, ae, 1, 2); + confirm(3, ae, 1, 3); + confirm(4, ae, 2, 1); + confirm(5, ae, 2, 2); + confirm(6, ae, 2, 3); + + } + + private static void confirm(int expectedValue, AreaEval ae, int row, int col) { + NumberEval v = (NumberEval) ae.getValueAt(row, col); + assertEquals(expectedValue, v.getNumberValue(), 0.0); + } + +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java b/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java index 48a76e31d..47137df4f 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; +import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; @@ -60,7 +61,32 @@ public final class ExcelFileFormatDocFunctionExtractor { private static final String SOURCE_DOC_FILE_NAME = "excelfileformat.odt"; + /** + * For simplicity, the output file is strictly simple ASCII. + * This method detects any unexpected characters. + */ + /* package */ static boolean isSimpleAscii(char c) { + + if (c>=0x21 && c<=0x7E) { + // everything from '!' to '~' (includes letters, digits, punctuation + return true; + } + // some specific whitespace chars below 0x21: + switch(c) { + case ' ': + case '\t': + case '\r': + case '\n': + return true; + } + return false; + } + + private static final class FunctionData { + // special characters from the ooo document + private static final int CHAR_ELLIPSIS_8230 = 8230; + private static final int CHAR_NDASH_8211 = 8211; private final int _index; private final boolean _hasFootnote; @@ -78,10 +104,30 @@ public final class ExcelFileFormatDocFunctionExtractor { _name = funcName; _minParams = minParams; _maxParams = maxParams; - _returnClass = returnClass; - _paramClasses = paramClasses; + _returnClass = convertSpecialChars(returnClass); + _paramClasses = convertSpecialChars(paramClasses); _isVolatile = isVolatile; } + private static String convertSpecialChars(String ss) { + StringBuffer sb = new StringBuffer(ss.length() + 4); + for(int i=0; i 0) { + throw new AssertionFailedError("identified bug 44306"); + } } } @@ -887,13 +886,66 @@ public final class TestBugs extends TestCase { writeOutAndReadBack(wb); assertTrue("no errors writing sample xls", true); } - + /** * Had a problem apparently, not sure what as it * works just fine... */ public void test44891() throws Exception { - HSSFWorkbook wb = openSample("44891.xls"); + HSSFWorkbook wb = openSample("44891.xls"); + assertTrue("no errors reading sample xls", true); + writeOutAndReadBack(wb); + assertTrue("no errors writing sample xls", true); + } + + /** + * Bug 44235: Ms Excel can't open save as excel file + * + * Works fine with poi-3.1-beta1. + */ + public void test44235() throws Exception { + HSSFWorkbook wb = openSample("44235.xls"); + assertTrue("no errors reading sample xls", true); + writeOutAndReadBack(wb); + assertTrue("no errors writing sample xls", true); + } + + /** + * Bug 21334: "File error: data may have been lost" with a file + * that contains macros and this formula: + * {=SUM(IF(FREQUENCY(IF(LEN(V4:V220)>0,MATCH(V4:V220,V4:V220,0),""),IF(LEN(V4:V220)>0,MATCH(V4:V220,V4:V220,0),""))>0,1))} + */ + public void test21334() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sh = wb.createSheet(); + HSSFCell cell = sh.createRow(0).createCell((short)0); + String formula = "SUM(IF(FREQUENCY(IF(LEN(V4:V220)>0,MATCH(V4:V220,V4:V220,0),\"\"),IF(LEN(V4:V220)>0,MATCH(V4:V220,V4:V220,0),\"\"))>0,1))"; + cell.setCellFormula(formula); + + HSSFWorkbook wb_sv = writeOutAndReadBack(wb); + HSSFCell cell_sv = wb_sv.getSheetAt(0).getRow(0).getCell((short)0); + assertEquals(formula, cell_sv.getCellFormula()); + } + + public void test36947() throws Exception { + HSSFWorkbook wb = openSample("36947.xls"); + assertTrue("no errors reading sample xls", true); + writeOutAndReadBack(wb); + assertTrue("no errors writing sample xls", true); + } + + /** + * Bug 42448: Can't parse SUMPRODUCT(A!C7:A!C67, B8:B68) / B69 + */ + public void test42448(){ + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFCell cell = wb.createSheet().createRow(0).createCell((short)0); + cell.setCellFormula("SUMPRODUCT(A!C7:A!C67, B8:B68) / B69"); + assertTrue("no errors parsing formula", true); + } + + public void test39634() throws Exception { + HSSFWorkbook wb = openSample("39634.xls"); assertTrue("no errors reading sample xls", true); writeOutAndReadBack(wb); assertTrue("no errors writing sample xls", true); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java index 0ef642917..349cfa8a8 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java @@ -22,12 +22,14 @@ import java.io.FileOutputStream; import java.util.Iterator; import java.util.List; +import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.FuncVarPtg; + /** * */ @@ -51,42 +53,41 @@ public final class TestFormulaEvaluatorBugs extends TestCase { */ public void test44636() throws Exception { // Open the existing file, tweak one value and - // re-calculate + // re-calculate HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("44636.xls"); - HSSFSheet sheet = wb.getSheetAt (0); - HSSFRow row = sheet.getRow (0); + HSSFSheet sheet = wb.getSheetAt(0); + HSSFRow row = sheet.getRow(0); - row.getCell((short)0).setCellValue(4.2); - row.getCell((short)2).setCellValue(25); + row.getCell((short) 0).setCellValue(4.2); + row.getCell((short) 2).setCellValue(25); HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); - assertEquals(4.2*25, row.getCell((short)3).getNumericCellValue(), 0.0001); + assertEquals(4.2 * 25, row.getCell((short) 3).getNumericCellValue(), 0.0001); // Save - File existing = new File(tmpDirName,"44636-existing.xls"); + File existing = new File(tmpDirName, "44636-existing.xls"); FileOutputStream out = new FileOutputStream(existing); wb.write(out); out.close(); System.err.println("Existing file for bug #44636 written to " + existing.toString()); - // Now, do a new file from scratch wb = new HSSFWorkbook(); sheet = wb.createSheet(); row = sheet.createRow(0); - row.createCell((short)0).setCellValue(1.2); - row.createCell((short)1).setCellValue(4.2); + row.createCell((short) 0).setCellValue(1.2); + row.createCell((short) 1).setCellValue(4.2); row = sheet.createRow(1); - row.createCell((short)0).setCellFormula("SUM(A1:B1)"); + row.createCell((short) 0).setCellFormula("SUM(A1:B1)"); HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); - assertEquals(5.4, row.getCell((short)0).getNumericCellValue(), 0.0001); + assertEquals(5.4, row.getCell((short) 0).getNumericCellValue(), 0.0001); // Save - File scratch = new File(tmpDirName,"44636-scratch.xls"); + File scratch = new File(tmpDirName, "44636-scratch.xls"); out = new FileOutputStream(scratch); wb.write(out); out.close(); @@ -105,62 +106,62 @@ public final class TestFormulaEvaluatorBugs extends TestCase { HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("44297.xls"); HSSFRow row; - HSSFCell cell; + HSSFCell cell; - HSSFSheet sheet = wb.getSheetAt(0); + HSSFSheet sheet = wb.getSheetAt(0); HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(sheet, wb); row = sheet.getRow(0); - cell = row.getCell((short)0); + cell = row.getCell((short) 0); assertEquals("31+46", cell.getCellFormula()); eva.setCurrentRow(row); assertEquals(77, eva.evaluate(cell).getNumberValue(), 0); row = sheet.getRow(1); - cell = row.getCell((short)0); + cell = row.getCell((short) 0); assertEquals("30+53", cell.getCellFormula()); eva.setCurrentRow(row); assertEquals(83, eva.evaluate(cell).getNumberValue(), 0); row = sheet.getRow(2); - cell = row.getCell((short)0); + cell = row.getCell((short) 0); assertEquals("SUM(A1:A2)", cell.getCellFormula()); eva.setCurrentRow(row); assertEquals(160, eva.evaluate(cell).getNumberValue(), 0); row = sheet.getRow(4); - cell = row.getCell((short)0); + cell = row.getCell((short) 0); assertEquals("32767+32768", cell.getCellFormula()); eva.setCurrentRow(row); assertEquals(65535, eva.evaluate(cell).getNumberValue(), 0); row = sheet.getRow(7); - cell = row.getCell((short)0); + cell = row.getCell((short) 0); assertEquals("32744+42333", cell.getCellFormula()); eva.setCurrentRow(row); assertEquals(75077, eva.evaluate(cell).getNumberValue(), 0); row = sheet.getRow(8); - cell = row.getCell((short)0); + cell = row.getCell((short) 0); assertEquals("327680.0/32768", cell.getCellFormula()); eva.setCurrentRow(row); assertEquals(10, eva.evaluate(cell).getNumberValue(), 0); row = sheet.getRow(9); - cell = row.getCell((short)0); + cell = row.getCell((short) 0); assertEquals("32767+32769", cell.getCellFormula()); eva.setCurrentRow(row); assertEquals(65536, eva.evaluate(cell).getNumberValue(), 0); row = sheet.getRow(10); - cell = row.getCell((short)0); + cell = row.getCell((short) 0); assertEquals("35000+36000", cell.getCellFormula()); eva.setCurrentRow(row); assertEquals(71000, eva.evaluate(cell).getNumberValue(), 0); row = sheet.getRow(11); - cell = row.getCell((short)0); + cell = row.getCell((short) 0); assertEquals("-1000000.0-3000000.0", cell.getCellFormula()); eva.setCurrentRow(row); assertEquals(-4000000, eva.evaluate(cell).getNumberValue(), 0); @@ -176,30 +177,29 @@ public final class TestFormulaEvaluatorBugs extends TestCase { HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SingleLetterRanges.xls"); - HSSFSheet sheet = wb.getSheetAt(0); + HSSFSheet sheet = wb.getSheetAt(0); HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(sheet, wb); - // =index(C:C,2,1) -> 2 + // =index(C:C,2,1) -> 2 HSSFRow rowIDX = sheet.getRow(3); - // =sum(C:C) -> 6 + // =sum(C:C) -> 6 HSSFRow rowSUM = sheet.getRow(4); - // =sum(C:D) -> 66 + // =sum(C:D) -> 66 HSSFRow rowSUM2D = sheet.getRow(5); // Test the sum - HSSFCell cellSUM = rowSUM.getCell((short)0); + HSSFCell cellSUM = rowSUM.getCell((short) 0); - FormulaRecordAggregate frec = - (FormulaRecordAggregate)cellSUM.getCellValueRecord(); + FormulaRecordAggregate frec = (FormulaRecordAggregate) cellSUM.getCellValueRecord(); List ops = frec.getFormulaRecord().getParsedExpression(); assertEquals(2, ops.size()); assertEquals(AreaPtg.class, ops.get(0).getClass()); assertEquals(FuncVarPtg.class, ops.get(1).getClass()); - // Actually stored as C1 to C65536 - // (last row is -1 === 65535) - AreaPtg ptg = (AreaPtg)ops.get(0); + // Actually stored as C1 to C65536 + // (last row is -1 === 65535) + AreaPtg ptg = (AreaPtg) ops.get(0); assertEquals(2, ptg.getFirstColumn()); assertEquals(2, ptg.getLastColumn()); assertEquals(0, ptg.getFirstRow()); @@ -207,26 +207,25 @@ public final class TestFormulaEvaluatorBugs extends TestCase { assertEquals("C:C", ptg.toFormulaString(wb)); // Will show as C:C, but won't know how many - // rows it covers as we don't have the sheet - // to hand when turning the Ptgs into a string + // rows it covers as we don't have the sheet + // to hand when turning the Ptgs into a string assertEquals("SUM(C:C)", cellSUM.getCellFormula()); eva.setCurrentRow(rowSUM); // But the evaluator knows the sheet, so it - // can do it properly + // can do it properly assertEquals(6, eva.evaluate(cellSUM).getNumberValue(), 0); - // Test the index // Again, the formula string will be right but - // lacking row count, evaluated will be right - HSSFCell cellIDX = rowIDX.getCell((short)0); + // lacking row count, evaluated will be right + HSSFCell cellIDX = rowIDX.getCell((short) 0); assertEquals("INDEX(C:C,2,1)", cellIDX.getCellFormula()); eva.setCurrentRow(rowIDX); assertEquals(2, eva.evaluate(cellIDX).getNumberValue(), 0); // Across two colums - HSSFCell cellSUM2D = rowSUM2D.getCell((short)0); + HSSFCell cellSUM2D = rowSUM2D.getCell((short) 0); assertEquals("SUM(C:D)", cellSUM2D.getCellFormula()); eva.setCurrentRow(rowSUM2D); assertEquals(66, eva.evaluate(cellSUM2D).getNumberValue(), 0); @@ -240,7 +239,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase { HSSFSheet sheet = wb.createSheet(); wb.setSheetName(0, "Sheet1"); HSSFRow row = sheet.createRow(0); - HSSFCell cell = row.createCell((short)0); + HSSFCell cell = row.createCell((short) 0); cell.setCellFormula("1=1"); @@ -253,29 +252,46 @@ public final class TestFormulaEvaluatorBugs extends TestCase { } assertEquals(true, cell.getBooleanCellValue()); } - + public void testClassCast_bug44861() throws Exception { - HSSFWorkbook wb = HSSFTestDataSamples. - openSampleWorkbook("44861.xls"); - + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("44861.xls"); + // Check direct HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); - + // And via calls int numSheets = wb.getNumberOfSheets(); - for(int i=0; i