From 0925fc31f5ebdf779241e9bc34d9cfbeebb4f7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Fri, 2 Sep 2022 13:26:24 +0200 Subject: [PATCH] move cleaned up jxl into this project, get rid of log4j dependency --- datastructures-xslx/NOTICE.txt | 15 + datastructures-xslx/build.gradle | 3 - .../src/main/java/jxl/BooleanCell.java | 45 + .../src/main/java/jxl/BooleanFormulaCell.java | 27 + .../src/main/java/jxl/Cell.java | 86 + .../src/main/java/jxl/CellFeatures.java | 74 + .../src/main/java/jxl/CellFormat.java | 28 + .../main/java/jxl/CellReferenceHelper.java | 254 ++ .../src/main/java/jxl/CellType.java | 97 + .../src/main/java/jxl/CellView.java | 196 ++ .../src/main/java/jxl/DateCell.java | 53 + .../src/main/java/jxl/DateFormulaCell.java | 27 + .../src/main/java/jxl/ErrorCell.java | 36 + .../src/main/java/jxl/ErrorFormulaCell.java | 27 + .../src/main/java/jxl/FormulaCell.java | 35 + .../src/main/java/jxl/HeaderFooter.java | 344 +++ .../src/main/java/jxl/Hyperlink.java | 106 + .../src/main/java/jxl/Image.java | 120 + .../src/main/java/jxl/JXLException.java | 34 + .../src/main/java/jxl/LabelCell.java | 33 + .../src/main/java/jxl/NumberCell.java | 43 + .../src/main/java/jxl/NumberFormulaCell.java | 27 + .../src/main/java/jxl/Range.java | 57 + .../src/main/java/jxl/Sheet.java | 278 ++ .../src/main/java/jxl/SheetSettings.java | 1204 ++++++++ .../src/main/java/jxl/StringFormulaCell.java | 29 + .../src/main/java/jxl/Workbook.java | 397 +++ .../src/main/java/jxl/WorkbookSettings.java | 798 ++++++ .../src/main/java/jxl/biff/AutoFilter.java | 65 + .../java/jxl/biff/AutoFilterInfoRecord.java | 56 + .../main/java/jxl/biff/AutoFilterRecord.java | 56 + .../main/java/jxl/biff/BaseCellFeatures.java | 489 ++++ .../main/java/jxl/biff/BaseCompoundFile.java | 349 +++ .../src/main/java/jxl/biff/BuiltInFormat.java | 166 ++ .../src/main/java/jxl/biff/BuiltInName.java | 110 + .../src/main/java/jxl/biff/BuiltInStyle.java | 70 + .../src/main/java/jxl/biff/ByteArray.java | 106 + .../src/main/java/jxl/biff/ByteData.java | 33 + .../src/main/java/jxl/biff/CellFinder.java | 196 ++ .../java/jxl/biff/CellReferenceHelper.java | 313 ++ .../main/java/jxl/biff/ConditionalFormat.java | 113 + .../biff/ConditionalFormatRangeRecord.java | 344 +++ .../jxl/biff/ConditionalFormatRecord.java | 56 + .../main/java/jxl/biff/ContinueRecord.java | 74 + .../src/main/java/jxl/biff/CountryCode.java | 150 + .../src/main/java/jxl/biff/DVParser.java | 906 ++++++ .../src/main/java/jxl/biff/DValParser.java | 149 + .../main/java/jxl/biff/DataValidation.java | 306 ++ .../java/jxl/biff/DataValidityListRecord.java | 151 + .../jxl/biff/DataValiditySettingsRecord.java | 282 ++ .../src/main/java/jxl/biff/DisplayFormat.java | 55 + .../src/main/java/jxl/biff/DoubleHelper.java | 84 + .../src/main/java/jxl/biff/EmptyCell.java | 207 ++ .../main/java/jxl/biff/EncodedURLHelper.java | 117 + .../main/java/jxl/biff/FilterModeRecord.java | 56 + .../src/main/java/jxl/biff/FontRecord.java | 501 ++++ .../src/main/java/jxl/biff/Fonts.java | 162 ++ .../src/main/java/jxl/biff/FormatRecord.java | 551 ++++ .../main/java/jxl/biff/FormattingRecords.java | 512 ++++ .../src/main/java/jxl/biff/FormulaData.java | 39 + .../src/main/java/jxl/biff/HeaderFooter.java | 615 ++++ .../src/main/java/jxl/biff/IndexMapping.java | 68 + .../src/main/java/jxl/biff/IntegerHelper.java | 140 + .../java/jxl/biff/NameRangeException.java | 35 + .../jxl/biff/NumFormatRecordsException.java | 34 + .../src/main/java/jxl/biff/PaletteRecord.java | 207 ++ .../src/main/java/jxl/biff/RangeImpl.java | 157 + .../src/main/java/jxl/biff/RecordData.java | 83 + .../main/java/jxl/biff/SheetRangeImpl.java | 293 ++ .../src/main/java/jxl/biff/StringHelper.java | 199 ++ .../src/main/java/jxl/biff/Type.java | 612 ++++ .../main/java/jxl/biff/WorkbookMethods.java | 54 + .../jxl/biff/WorkspaceInformationRecord.java | 143 + .../java/jxl/biff/WritableRecordData.java | 139 + .../src/main/java/jxl/biff/XCTRecord.java | 48 + .../src/main/java/jxl/biff/XFRecord.java | 1501 ++++++++++ .../jxl/biff/drawing/BStoreContainer.java | 86 + .../java/jxl/biff/drawing/BlipStoreEntry.java | 195 ++ .../main/java/jxl/biff/drawing/BlipType.java | 106 + .../main/java/jxl/biff/drawing/Button.java | 790 +++++ .../src/main/java/jxl/biff/drawing/Chart.java | 249 ++ .../main/java/jxl/biff/drawing/CheckBox.java | 716 +++++ .../src/main/java/jxl/biff/drawing/Chunk.java | 40 + .../main/java/jxl/biff/drawing/ChunkType.java | 64 + .../java/jxl/biff/drawing/ClientAnchor.java | 201 ++ .../java/jxl/biff/drawing/ClientData.java | 63 + .../java/jxl/biff/drawing/ClientTextBox.java | 63 + .../main/java/jxl/biff/drawing/ComboBox.java | 641 +++++ .../main/java/jxl/biff/drawing/Comment.java | 829 ++++++ .../src/main/java/jxl/biff/drawing/Dg.java | 105 + .../java/jxl/biff/drawing/DgContainer.java | 32 + .../src/main/java/jxl/biff/drawing/Dgg.java | 203 ++ .../java/jxl/biff/drawing/DggContainer.java | 32 + .../main/java/jxl/biff/drawing/Drawing.java | 990 +++++++ .../main/java/jxl/biff/drawing/Drawing2.java | 629 ++++ .../java/jxl/biff/drawing/DrawingData.java | 206 ++ .../biff/drawing/DrawingDataException.java | 35 + .../java/jxl/biff/drawing/DrawingGroup.java | 534 ++++ .../jxl/biff/drawing/DrawingGroupObject.java | 231 ++ .../java/jxl/biff/drawing/EscherAtom.java | 63 + .../jxl/biff/drawing/EscherContainer.java | 177 ++ .../java/jxl/biff/drawing/EscherDisplay.java | 189 ++ .../java/jxl/biff/drawing/EscherRecord.java | 179 ++ .../jxl/biff/drawing/EscherRecordData.java | 285 ++ .../jxl/biff/drawing/EscherRecordType.java | 101 + .../java/jxl/biff/drawing/EscherStream.java | 32 + .../biff/drawing/MsoDrawingGroupRecord.java | 68 + .../jxl/biff/drawing/MsoDrawingRecord.java | 108 + .../java/jxl/biff/drawing/NoteRecord.java | 157 + .../main/java/jxl/biff/drawing/ObjRecord.java | 382 +++ .../src/main/java/jxl/biff/drawing/Opt.java | 236 ++ .../main/java/jxl/biff/drawing/Origin.java | 34 + .../main/java/jxl/biff/drawing/PNGReader.java | 136 + .../main/java/jxl/biff/drawing/ShapeType.java | 79 + .../jxl/biff/drawing/SheetDrawingWriter.java | 432 +++ .../src/main/java/jxl/biff/drawing/Sp.java | 113 + .../java/jxl/biff/drawing/SpContainer.java | 41 + .../src/main/java/jxl/biff/drawing/Spgr.java | 57 + .../java/jxl/biff/drawing/SpgrContainer.java | 48 + .../jxl/biff/drawing/SplitMenuColors.java | 63 + .../jxl/biff/drawing/TextObjectRecord.java | 123 + .../src/main/java/jxl/biff/formula/Add.java | 59 + .../src/main/java/jxl/biff/formula/Area.java | 383 +++ .../main/java/jxl/biff/formula/Area3d.java | 433 +++ .../jxl/biff/formula/ArgumentSeparator.java | 32 + .../main/java/jxl/biff/formula/Attribute.java | 478 ++++ .../java/jxl/biff/formula/BinaryOperator.java | 210 ++ .../java/jxl/biff/formula/BooleanValue.java | 90 + .../jxl/biff/formula/BuiltInFunction.java | 263 ++ .../java/jxl/biff/formula/CellReference.java | 268 ++ .../jxl/biff/formula/CellReference3d.java | 299 ++ .../jxl/biff/formula/CellReferenceError.java | 82 + .../jxl/biff/formula/CloseParentheses.java | 31 + .../java/jxl/biff/formula/ColumnRange.java | 80 + .../java/jxl/biff/formula/ColumnRange3d.java | 122 + .../java/jxl/biff/formula/Concatenate.java | 59 + .../main/java/jxl/biff/formula/Divide.java | 60 + .../java/jxl/biff/formula/DoubleValue.java | 112 + .../src/main/java/jxl/biff/formula/Equal.java | 61 + .../java/jxl/biff/formula/ErrorConstant.java | 89 + .../java/jxl/biff/formula/ExternalSheet.java | 76 + .../jxl/biff/formula/FormulaErrorCode.java | 127 + .../jxl/biff/formula/FormulaException.java | 122 + .../java/jxl/biff/formula/FormulaParser.java | 239 ++ .../main/java/jxl/biff/formula/Function.java | 694 +++++ .../java/jxl/biff/formula/FunctionNames.java | 97 + .../java/jxl/biff/formula/GreaterEqual.java | 54 + .../java/jxl/biff/formula/GreaterThan.java | 54 + .../java/jxl/biff/formula/IntegerValue.java | 118 + .../main/java/jxl/biff/formula/LessEqual.java | 66 + .../main/java/jxl/biff/formula/LessThan.java | 54 + .../main/java/jxl/biff/formula/MemArea.java | 67 + .../main/java/jxl/biff/formula/MemFunc.java | 46 + .../src/main/java/jxl/biff/formula/Minus.java | 63 + .../java/jxl/biff/formula/MissingArg.java | 73 + .../main/java/jxl/biff/formula/Multiply.java | 54 + .../src/main/java/jxl/biff/formula/Name.java | 72 + .../main/java/jxl/biff/formula/NameRange.java | 137 + .../main/java/jxl/biff/formula/NotEqual.java | 55 + .../java/jxl/biff/formula/NumberValue.java | 34 + .../jxl/biff/formula/OpenParentheses.java | 32 + .../main/java/jxl/biff/formula/Operand.java | 102 + .../main/java/jxl/biff/formula/Operator.java | 84 + .../java/jxl/biff/formula/Parenthesis.java | 179 ++ .../java/jxl/biff/formula/ParseContext.java | 34 + .../main/java/jxl/biff/formula/ParseItem.java | 231 ++ .../java/jxl/biff/formula/ParsedThing.java | 34 + .../main/java/jxl/biff/formula/Parser.java | 113 + .../main/java/jxl/biff/formula/Percent.java | 70 + .../src/main/java/jxl/biff/formula/Plus.java | 58 + .../src/main/java/jxl/biff/formula/Power.java | 54 + .../java/jxl/biff/formula/RangeSeparator.java | 81 + .../jxl/biff/formula/SharedFormulaArea.java | 152 + .../formula/SharedFormulaCellReference.java | 155 + .../jxl/biff/formula/StringFormulaParser.java | 501 ++++ .../java/jxl/biff/formula/StringFunction.java | 66 + .../java/jxl/biff/formula/StringOperator.java | 148 + .../jxl/biff/formula/StringParseItem.java | 118 + .../java/jxl/biff/formula/StringValue.java | 121 + .../java/jxl/biff/formula/SubExpression.java | 105 + .../main/java/jxl/biff/formula/Subtract.java | 54 + .../src/main/java/jxl/biff/formula/Token.java | 197 ++ .../jxl/biff/formula/TokenFormulaParser.java | 474 +++ .../java/jxl/biff/formula/UnaryMinus.java | 62 + .../java/jxl/biff/formula/UnaryOperator.java | 180 ++ .../main/java/jxl/biff/formula/UnaryPlus.java | 62 + .../src/main/java/jxl/biff/formula/Union.java | 54 + .../jxl/biff/formula/VariableArgFunction.java | 311 ++ .../src/main/java/jxl/biff/formula/Yylex.java | 837 ++++++ .../src/main/java/jxl/common/Assert.java | 56 + .../main/java/jxl/common/AssertionFailed.java | 45 + .../src/main/java/jxl/common/BaseUnit.java | 32 + .../main/java/jxl/common/LengthConverter.java | 57 + .../src/main/java/jxl/common/LengthUnit.java | 39 + .../src/main/java/jxl/common/Logger.java | 156 + .../main/java/jxl/common/log/LoggerName.java | 29 + .../java/jxl/common/log/SimpleLogger.java | 156 + .../java/jxl/common/log/SimpleLoggerName.java | 29 + .../src/main/java/jxl/format/Alignment.java | 115 + .../src/main/java/jxl/format/BoldStyle.java | 75 + .../src/main/java/jxl/format/Border.java | 50 + .../main/java/jxl/format/BorderLineStyle.java | 110 + .../src/main/java/jxl/format/CellFormat.java | 142 + .../src/main/java/jxl/format/Colour.java | 237 ++ .../src/main/java/jxl/format/Font.java | 82 + .../src/main/java/jxl/format/Format.java | 40 + .../src/main/java/jxl/format/Orientation.java | 128 + .../src/main/java/jxl/format/PageOrder.java | 40 + .../main/java/jxl/format/PageOrientation.java | 48 + .../src/main/java/jxl/format/PaperSize.java | 406 +++ .../src/main/java/jxl/format/Pattern.java | 120 + .../src/main/java/jxl/format/RGB.java | 82 + .../src/main/java/jxl/format/ScriptStyle.java | 108 + .../main/java/jxl/format/UnderlineStyle.java | 113 + .../java/jxl/format/VerticalAlignment.java | 107 + .../main/java/jxl/read/biff/BOFRecord.java | 159 ++ .../read/biff/BaseSharedFormulaRecord.java | 164 ++ .../java/jxl/read/biff/BiffException.java | 96 + .../java/jxl/read/biff/BiffRecordReader.java | 74 + .../main/java/jxl/read/biff/BlankCell.java | 61 + .../jxl/read/biff/BooleanFormulaRecord.java | 157 + .../java/jxl/read/biff/BooleanRecord.java | 121 + .../jxl/read/biff/BottomMarginRecord.java | 36 + .../java/jxl/read/biff/BoundsheetRecord.java | 146 + .../read/biff/ButtonPropertySetRecord.java | 53 + .../java/jxl/read/biff/CalcModeRecord.java | 60 + .../jxl/read/biff/CellFeaturesAccessor.java | 41 + .../main/java/jxl/read/biff/CellValue.java | 191 ++ .../main/java/jxl/read/biff/CentreRecord.java | 53 + .../java/jxl/read/biff/CodepageRecord.java | 59 + .../java/jxl/read/biff/ColumnInfoRecord.java | 155 + .../main/java/jxl/read/biff/CompoundFile.java | 561 ++++ .../java/jxl/read/biff/CountryRecord.java | 83 + .../java/jxl/read/biff/DateFormulaRecord.java | 141 + .../main/java/jxl/read/biff/DateRecord.java | 305 ++ .../read/biff/DefaultColumnWidthRecord.java | 62 + .../jxl/read/biff/DefaultRowHeightRecord.java | 63 + .../java/jxl/read/biff/DimensionRecord.java | 126 + .../jxl/read/biff/ErrorFormulaRecord.java | 164 ++ .../main/java/jxl/read/biff/ErrorRecord.java | 80 + .../java/jxl/read/biff/Excel9FileRecord.java | 57 + .../jxl/read/biff/ExternalNameRecord.java | 98 + .../jxl/read/biff/ExternalSheetRecord.java | 181 ++ .../src/main/java/jxl/read/biff/File.java | 293 ++ .../main/java/jxl/read/biff/FooterRecord.java | 95 + .../src/main/java/jxl/read/biff/Formula.java | 36 + .../java/jxl/read/biff/FormulaRecord.java | 243 ++ .../java/jxl/read/biff/GuttersRecord.java | 57 + .../main/java/jxl/read/biff/HeaderRecord.java | 100 + .../java/jxl/read/biff/HideobjRecord.java | 59 + .../read/biff/HorizontalPageBreaksRecord.java | 101 + .../java/jxl/read/biff/HyperlinkRecord.java | 360 +++ .../main/java/jxl/read/biff/LabelRecord.java | 114 + .../java/jxl/read/biff/LabelSSTRecord.java | 83 + .../java/jxl/read/biff/LeftMarginRecord.java | 36 + .../main/java/jxl/read/biff/MarginRecord.java | 58 + .../java/jxl/read/biff/MergedCellsRecord.java | 86 + .../main/java/jxl/read/biff/MulBlankCell.java | 188 ++ .../java/jxl/read/biff/MulBlankRecord.java | 127 + .../main/java/jxl/read/biff/MulRKRecord.java | 146 + .../main/java/jxl/read/biff/NameRecord.java | 510 ++++ .../jxl/read/biff/NineteenFourRecord.java | 61 + .../jxl/read/biff/NumberFormulaRecord.java | 179 ++ .../main/java/jxl/read/biff/NumberRecord.java | 111 + .../main/java/jxl/read/biff/NumberValue.java | 225 ++ .../main/java/jxl/read/biff/PLSRecord.java | 45 + .../java/jxl/read/biff/PaletteRecord.java | 45 + .../main/java/jxl/read/biff/PaneRecord.java | 81 + .../java/jxl/read/biff/PasswordException.java | 33 + .../java/jxl/read/biff/PasswordRecord.java | 59 + .../jxl/read/biff/PrintGridLinesRecord.java | 54 + .../jxl/read/biff/PrintHeadersRecord.java | 54 + .../java/jxl/read/biff/ProtectRecord.java | 67 + .../src/main/java/jxl/read/biff/RKHelper.java | 62 + .../src/main/java/jxl/read/biff/RKRecord.java | 110 + .../java/jxl/read/biff/RStringRecord.java | 93 + .../src/main/java/jxl/read/biff/Record.java | 169 ++ .../java/jxl/read/biff/RefreshAllRecord.java | 60 + .../java/jxl/read/biff/RightMarginRecord.java | 36 + .../main/java/jxl/read/biff/RowRecord.java | 175 ++ .../main/java/jxl/read/biff/SCLRecord.java | 62 + .../main/java/jxl/read/biff/SSTRecord.java | 391 +++ .../java/jxl/read/biff/SaveRecalcRecord.java | 60 + .../main/java/jxl/read/biff/SetupRecord.java | 255 ++ .../read/biff/SharedBooleanFormulaRecord.java | 146 + .../read/biff/SharedDateFormulaRecord.java | 182 ++ .../read/biff/SharedErrorFormulaRecord.java | 168 ++ .../jxl/read/biff/SharedFormulaRecord.java | 214 ++ .../read/biff/SharedNumberFormulaRecord.java | 187 ++ .../read/biff/SharedStringFormulaRecord.java | 241 ++ .../main/java/jxl/read/biff/SheetImpl.java | 1137 ++++++++ .../main/java/jxl/read/biff/SheetReader.java | 1628 +++++++++++ .../main/java/jxl/read/biff/SortRecord.java | 170 ++ .../jxl/read/biff/StringFormulaRecord.java | 268 ++ .../java/jxl/read/biff/SupbookRecord.java | 277 ++ .../java/jxl/read/biff/TemplateRecord.java | 57 + .../java/jxl/read/biff/TopMarginRecord.java | 36 + .../read/biff/VerticalPageBreaksRecord.java | 101 + .../java/jxl/read/biff/Window2Record.java | 194 ++ .../jxl/read/biff/WindowProtectedRecord.java | 60 + .../java/jxl/read/biff/WorkbookParser.java | 1108 +++++++ .../java/jxl/read/biff/WriteAccessRecord.java | 63 + .../src/main/java/jxl/write/Alignment.java | 37 + .../src/main/java/jxl/write/Blank.java | 89 + .../src/main/java/jxl/write/BoldStyle.java | 35 + .../src/main/java/jxl/write/Boolean.java | 97 + .../src/main/java/jxl/write/Border.java | 33 + .../main/java/jxl/write/BorderLineStyle.java | 32 + .../src/main/java/jxl/write/Colour.java | 47 + .../src/main/java/jxl/write/DateFormat.java | 49 + .../src/main/java/jxl/write/DateFormats.java | 200 ++ .../src/main/java/jxl/write/DateTime.java | 167 ++ .../src/main/java/jxl/write/Font.java | 212 ++ .../src/main/java/jxl/write/Formula.java | 73 + .../src/main/java/jxl/write/Label.java | 97 + .../src/main/java/jxl/write/Number.java | 99 + .../src/main/java/jxl/write/NumberFormat.java | 135 + .../main/java/jxl/write/NumberFormats.java | 273 ++ .../src/main/java/jxl/write/Pattern.java | 48 + .../java/jxl/write/VerticalAlignment.java | 35 + .../src/main/java/jxl/write/WritableCell.java | 61 + .../java/jxl/write/WritableCellFeatures.java | 156 + .../java/jxl/write/WritableCellFormat.java | 219 ++ .../src/main/java/jxl/write/WritableFont.java | 333 +++ .../java/jxl/write/WritableHyperlink.java | 232 ++ .../main/java/jxl/write/WritableImage.java | 207 ++ .../main/java/jxl/write/WritableSheet.java | 433 +++ .../main/java/jxl/write/WritableWorkbook.java | 306 ++ .../main/java/jxl/write/WriteException.java | 36 + .../java/jxl/write/biff/ArbitraryRecord.java | 69 + .../main/java/jxl/write/biff/BOFRecord.java | 114 + .../java/jxl/write/biff/BackupRecord.java | 66 + .../main/java/jxl/write/biff/BlankRecord.java | 101 + .../java/jxl/write/biff/BookboolRecord.java | 64 + .../java/jxl/write/biff/BooleanRecord.java | 150 + .../jxl/write/biff/BottomMarginRecord.java | 31 + .../java/jxl/write/biff/BoundsheetRecord.java | 110 + .../write/biff/ButtonPropertySetRecord.java | 68 + .../java/jxl/write/biff/CalcCountRecord.java | 65 + .../java/jxl/write/biff/CalcModeRecord.java | 87 + .../main/java/jxl/write/biff/CellValue.java | 595 ++++ .../java/jxl/write/biff/CellXFRecord.java | 214 ++ .../java/jxl/write/biff/CodepageRecord.java | 53 + .../java/jxl/write/biff/ColumnInfoRecord.java | 389 +++ .../write/biff/ColumnsExceededException.java | 33 + .../java/jxl/write/biff/CompoundFile.java | 1056 +++++++ .../java/jxl/write/biff/CompoundFile.java3 | 1020 +++++++ .../java/jxl/write/biff/CompoundFile.java4 | 1025 +++++++ .../CopyAdditionalPropertySetsException.java | 33 + .../java/jxl/write/biff/CountryRecord.java | 80 + .../java/jxl/write/biff/DBCellRecord.java | 116 + .../main/java/jxl/write/biff/DSFRecord.java | 54 + .../java/jxl/write/biff/DateFormatRecord.java | 47 + .../main/java/jxl/write/biff/DateRecord.java | 320 +++ .../jxl/write/biff/DefaultColumnWidth.java | 59 + .../write/biff/DefaultRowHeightRecord.java | 73 + .../main/java/jxl/write/biff/DeltaRecord.java | 76 + .../java/jxl/write/biff/DimensionRecord.java | 70 + .../main/java/jxl/write/biff/EOFRecord.java | 45 + .../java/jxl/write/biff/Excel9FileRecord.java | 47 + .../java/jxl/write/biff/ExcelDataOutput.java | 61 + .../jxl/write/biff/ExtendedSSTRecord.java | 108 + .../jxl/write/biff/ExternalNameRecord.java | 70 + .../jxl/write/biff/ExternalSheetRecord.java | 225 ++ .../src/main/java/jxl/write/biff/File.java | 174 ++ .../java/jxl/write/biff/FileDataOutput.java | 114 + .../java/jxl/write/biff/FooterRecord.java | 84 + .../java/jxl/write/biff/FormulaRecord.java | 344 +++ .../write/biff/FunctionGroupCountRecord.java | 64 + .../java/jxl/write/biff/GridSetRecord.java | 65 + .../java/jxl/write/biff/GuttersRecord.java | 112 + .../java/jxl/write/biff/HeaderRecord.java | 83 + .../java/jxl/write/biff/HideobjRecord.java | 66 + .../write/biff/HorizontalCentreRecord.java | 66 + .../biff/HorizontalPageBreaksRecord.java | 68 + .../java/jxl/write/biff/HyperlinkRecord.java | 1140 ++++++++ .../main/java/jxl/write/biff/IndexRecord.java | 99 + .../jxl/write/biff/InterfaceEndRecord.java | 45 + .../jxl/write/biff/InterfaceHeaderRecord.java | 49 + .../java/jxl/write/biff/IterationRecord.java | 65 + .../jxl/write/biff/JxlWriteException.java | 71 + .../main/java/jxl/write/biff/LabelRecord.java | 214 ++ .../java/jxl/write/biff/LeftMarginRecord.java | 31 + .../main/java/jxl/write/biff/MMSRecord.java | 68 + .../java/jxl/write/biff/MarginRecord.java | 57 + .../java/jxl/write/biff/MemoryDataOutput.java | 108 + .../main/java/jxl/write/biff/MergedCells.java | 278 ++ .../jxl/write/biff/MergedCellsRecord.java | 88 + .../main/java/jxl/write/biff/MulRKRecord.java | 114 + .../main/java/jxl/write/biff/NameRecord.java | 618 ++++ .../jxl/write/biff/NineteenFourRecord.java | 65 + .../jxl/write/biff/NumberFormatRecord.java | 131 + .../java/jxl/write/biff/NumberRecord.java | 167 ++ .../java/jxl/write/biff/ObjProjRecord.java | 59 + .../jxl/write/biff/ObjectProtectRecord.java | 64 + .../main/java/jxl/write/biff/PLSRecord.java | 67 + .../java/jxl/write/biff/PaletteRecord.java | 53 + .../main/java/jxl/write/biff/PaneRecord.java | 98 + .../java/jxl/write/biff/PasswordRecord.java | 109 + .../java/jxl/write/biff/PrecisionRecord.java | 63 + .../jxl/write/biff/PrintGridLinesRecord.java | 64 + .../jxl/write/biff/PrintHeadersRecord.java | 64 + .../jxl/write/biff/Prot4RevPassRecord.java | 52 + .../java/jxl/write/biff/Prot4RevRecord.java | 65 + .../java/jxl/write/biff/ProtectRecord.java | 64 + .../write/biff/ReadBooleanFormulaRecord.java | 47 + .../jxl/write/biff/ReadDateFormulaRecord.java | 70 + .../write/biff/ReadErrorFormulaRecord.java | 118 + .../jxl/write/biff/ReadFormulaRecord.java | 402 +++ .../write/biff/ReadNumberFormulaRecord.java | 110 + .../write/biff/ReadStringFormulaRecord.java | 106 + .../java/jxl/write/biff/RefModeRecord.java | 51 + .../java/jxl/write/biff/RefreshAllRecord.java | 66 + .../jxl/write/biff/RightMarginRecord.java | 31 + .../main/java/jxl/write/biff/RowRecord.java | 659 +++++ .../jxl/write/biff/RowsExceededException.java | 33 + .../main/java/jxl/write/biff/SCLRecord.java | 62 + .../jxl/write/biff/SSTContinueRecord.java | 207 ++ .../main/java/jxl/write/biff/SSTRecord.java | 156 + .../java/jxl/write/biff/SaveRecalcRecord.java | 64 + .../jxl/write/biff/ScenarioProtectRecord.java | 64 + .../java/jxl/write/biff/SelectionRecord.java | 88 + .../main/java/jxl/write/biff/SetupRecord.java | 235 ++ .../java/jxl/write/biff/SharedStrings.java | 171 ++ .../main/java/jxl/write/biff/SheetCopier.java | 965 +++++++ .../main/java/jxl/write/biff/SheetWriter.java | 1045 +++++++ .../main/java/jxl/write/biff/SortRecord.java | 113 + .../java/jxl/write/biff/StringRecord.java | 59 + .../java/jxl/write/biff/StyleXFRecord.java | 63 + .../src/main/java/jxl/write/biff/Styles.java | 193 ++ .../java/jxl/write/biff/SupbookRecord.java | 301 ++ .../main/java/jxl/write/biff/TabIdRecord.java | 58 + .../java/jxl/write/biff/TemplateRecord.java | 47 + .../java/jxl/write/biff/TopMarginRecord.java | 31 + .../java/jxl/write/biff/UsesElfsRecord.java | 62 + .../jxl/write/biff/VerticalCentreRecord.java | 66 + .../write/biff/VerticalPageBreaksRecord.java | 68 + .../java/jxl/write/biff/Weird1Record.java | 48 + .../java/jxl/write/biff/Window1Record.java | 80 + .../java/jxl/write/biff/Window2Record.java | 98 + .../jxl/write/biff/WindowProtectRecord.java | 64 + .../jxl/write/biff/WritableFontRecord.java | 157 + .../java/jxl/write/biff/WritableFonts.java | 49 + .../write/biff/WritableFormattingRecords.java | 225 ++ .../jxl/write/biff/WritableSheetCopier.java | 513 ++++ .../jxl/write/biff/WritableSheetImpl.java | 2535 +++++++++++++++++ .../jxl/write/biff/WritableWorkbookImpl.java | 1703 +++++++++++ .../jxl/write/biff/WriteAccessRecord.java | 73 + .../xbib/datastructures/xslx}/TestSJXLSX.java | 11 +- gradle.properties | 2 +- settings.gradle | 3 +- 451 files changed, 80884 insertions(+), 8 deletions(-) create mode 100644 datastructures-xslx/NOTICE.txt delete mode 100644 datastructures-xslx/build.gradle create mode 100755 datastructures-xslx/src/main/java/jxl/BooleanCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/BooleanFormulaCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/Cell.java create mode 100755 datastructures-xslx/src/main/java/jxl/CellFeatures.java create mode 100755 datastructures-xslx/src/main/java/jxl/CellFormat.java create mode 100755 datastructures-xslx/src/main/java/jxl/CellReferenceHelper.java create mode 100755 datastructures-xslx/src/main/java/jxl/CellType.java create mode 100755 datastructures-xslx/src/main/java/jxl/CellView.java create mode 100755 datastructures-xslx/src/main/java/jxl/DateCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/DateFormulaCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/ErrorCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/ErrorFormulaCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/FormulaCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/HeaderFooter.java create mode 100755 datastructures-xslx/src/main/java/jxl/Hyperlink.java create mode 100755 datastructures-xslx/src/main/java/jxl/Image.java create mode 100755 datastructures-xslx/src/main/java/jxl/JXLException.java create mode 100755 datastructures-xslx/src/main/java/jxl/LabelCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/NumberCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/NumberFormulaCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/Range.java create mode 100755 datastructures-xslx/src/main/java/jxl/Sheet.java create mode 100755 datastructures-xslx/src/main/java/jxl/SheetSettings.java create mode 100755 datastructures-xslx/src/main/java/jxl/StringFormulaCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/Workbook.java create mode 100755 datastructures-xslx/src/main/java/jxl/WorkbookSettings.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/AutoFilter.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/AutoFilterInfoRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/AutoFilterRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/BaseCellFeatures.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/BaseCompoundFile.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/BuiltInFormat.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/BuiltInName.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/BuiltInStyle.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/ByteArray.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/ByteData.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/CellFinder.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/CellReferenceHelper.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/ConditionalFormat.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRangeRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/ContinueRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/CountryCode.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/DVParser.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/DValParser.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/DataValidation.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/DataValidityListRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/DataValiditySettingsRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/DisplayFormat.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/DoubleHelper.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/EmptyCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/EncodedURLHelper.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/FilterModeRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/FontRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/Fonts.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/FormatRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/FormattingRecords.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/FormulaData.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/HeaderFooter.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/IndexMapping.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/IntegerHelper.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/NameRangeException.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/NumFormatRecordsException.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/PaletteRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/RangeImpl.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/RecordData.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/SheetRangeImpl.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/StringHelper.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/Type.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/WorkbookMethods.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/WorkspaceInformationRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/WritableRecordData.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/XCTRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/XFRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/BStoreContainer.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/BlipStoreEntry.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/BlipType.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Button.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Chart.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/CheckBox.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Chunk.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/ChunkType.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/ClientAnchor.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/ClientData.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/ClientTextBox.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/ComboBox.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Comment.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Dg.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/DgContainer.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Dgg.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/DggContainer.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing2.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingData.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingDataException.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroup.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroupObject.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/EscherAtom.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/EscherContainer.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/EscherDisplay.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordData.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordType.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/EscherStream.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingGroupRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/NoteRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/ObjRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Opt.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Origin.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/PNGReader.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/ShapeType.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/SheetDrawingWriter.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Sp.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/SpContainer.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/Spgr.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/SpgrContainer.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/SplitMenuColors.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/drawing/TextObjectRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Add.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Area.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Area3d.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/ArgumentSeparator.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Attribute.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/BinaryOperator.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/BooleanValue.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/BuiltInFunction.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/CellReference.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/CellReference3d.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/CellReferenceError.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/CloseParentheses.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange3d.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Concatenate.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Divide.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/DoubleValue.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Equal.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/ErrorConstant.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/ExternalSheet.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/FormulaErrorCode.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/FormulaException.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/FormulaParser.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Function.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/FunctionNames.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/GreaterEqual.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/GreaterThan.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/IntegerValue.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/LessEqual.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/LessThan.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/MemArea.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/MemFunc.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Minus.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/MissingArg.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Multiply.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Name.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/NameRange.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/NotEqual.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/NumberValue.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/OpenParentheses.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Operand.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Operator.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Parenthesis.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/ParseContext.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/ParseItem.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/ParsedThing.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Parser.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Percent.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Plus.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Power.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/RangeSeparator.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaArea.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaCellReference.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/StringFormulaParser.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/StringFunction.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/StringOperator.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/StringParseItem.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/StringValue.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/SubExpression.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Subtract.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Token.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/TokenFormulaParser.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/UnaryMinus.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/UnaryOperator.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/UnaryPlus.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Union.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/VariableArgFunction.java create mode 100755 datastructures-xslx/src/main/java/jxl/biff/formula/Yylex.java create mode 100755 datastructures-xslx/src/main/java/jxl/common/Assert.java create mode 100755 datastructures-xslx/src/main/java/jxl/common/AssertionFailed.java create mode 100755 datastructures-xslx/src/main/java/jxl/common/BaseUnit.java create mode 100755 datastructures-xslx/src/main/java/jxl/common/LengthConverter.java create mode 100755 datastructures-xslx/src/main/java/jxl/common/LengthUnit.java create mode 100755 datastructures-xslx/src/main/java/jxl/common/Logger.java create mode 100755 datastructures-xslx/src/main/java/jxl/common/log/LoggerName.java create mode 100755 datastructures-xslx/src/main/java/jxl/common/log/SimpleLogger.java create mode 100755 datastructures-xslx/src/main/java/jxl/common/log/SimpleLoggerName.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/Alignment.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/BoldStyle.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/Border.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/BorderLineStyle.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/CellFormat.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/Colour.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/Font.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/Format.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/Orientation.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/PageOrder.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/PageOrientation.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/PaperSize.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/Pattern.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/RGB.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/ScriptStyle.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/UnderlineStyle.java create mode 100755 datastructures-xslx/src/main/java/jxl/format/VerticalAlignment.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/BOFRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/BaseSharedFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/BiffException.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/BiffRecordReader.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/BlankCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/BooleanFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/BooleanRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/BottomMarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/BoundsheetRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/ButtonPropertySetRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/CalcModeRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/CellFeaturesAccessor.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/CellValue.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/CentreRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/CodepageRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/ColumnInfoRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/CompoundFile.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/CountryRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/DateFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/DateRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/DefaultColumnWidthRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/DefaultRowHeightRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/DimensionRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/ErrorFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/ErrorRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/Excel9FileRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/ExternalNameRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/ExternalSheetRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/File.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/FooterRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/Formula.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/FormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/GuttersRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/HeaderRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/HideobjRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/HorizontalPageBreaksRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/HyperlinkRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/LabelRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/LabelSSTRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/LeftMarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/MarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/MergedCellsRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/MulBlankCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/MulBlankRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/MulRKRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/NameRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/NineteenFourRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/NumberFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/NumberRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/NumberValue.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/PLSRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/PaletteRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/PaneRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/PasswordException.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/PasswordRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/PrintGridLinesRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/PrintHeadersRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/ProtectRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/RKHelper.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/RKRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/RStringRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/Record.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/RefreshAllRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/RightMarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/RowRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SCLRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SSTRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SaveRecalcRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SetupRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SharedBooleanFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SharedDateFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SharedErrorFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SharedFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SharedNumberFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SharedStringFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SheetImpl.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SheetReader.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SortRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/StringFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/SupbookRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/TemplateRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/TopMarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/VerticalPageBreaksRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/Window2Record.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/WindowProtectedRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/WorkbookParser.java create mode 100755 datastructures-xslx/src/main/java/jxl/read/biff/WriteAccessRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Alignment.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Blank.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/BoldStyle.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Boolean.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Border.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/BorderLineStyle.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Colour.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/DateFormat.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/DateFormats.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/DateTime.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Font.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Formula.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Label.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Number.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/NumberFormat.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/NumberFormats.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/Pattern.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/VerticalAlignment.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/WritableCell.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/WritableCellFeatures.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/WritableCellFormat.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/WritableFont.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/WritableHyperlink.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/WritableImage.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/WritableSheet.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/WritableWorkbook.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/WriteException.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ArbitraryRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/BOFRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/BackupRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/BlankRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/BookboolRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/BooleanRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/BottomMarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/BoundsheetRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ButtonPropertySetRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CalcCountRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CalcModeRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CellValue.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CellXFRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CodepageRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ColumnInfoRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ColumnsExceededException.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java3 create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java4 create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CopyAdditionalPropertySetsException.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/CountryRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/DBCellRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/DSFRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/DateFormatRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/DateRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/DefaultColumnWidth.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/DefaultRowHeightRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/DeltaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/DimensionRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/EOFRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/Excel9FileRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ExcelDataOutput.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ExtendedSSTRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ExternalNameRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ExternalSheetRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/File.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/FileDataOutput.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/FooterRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/FormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/FunctionGroupCountRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/GridSetRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/GuttersRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/HeaderRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/HideobjRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/HorizontalCentreRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/HorizontalPageBreaksRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/HyperlinkRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/IndexRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/InterfaceEndRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/InterfaceHeaderRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/IterationRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/JxlWriteException.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/LabelRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/LeftMarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/MMSRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/MarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/MemoryDataOutput.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/MergedCells.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/MergedCellsRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/MulRKRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/NameRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/NineteenFourRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/NumberFormatRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/NumberRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ObjProjRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ObjectProtectRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/PLSRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/PaletteRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/PaneRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/PasswordRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/PrecisionRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/PrintGridLinesRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/PrintHeadersRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevPassRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ProtectRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ReadBooleanFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ReadDateFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ReadErrorFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ReadFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ReadNumberFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ReadStringFormulaRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/RefModeRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/RefreshAllRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/RightMarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/RowRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/RowsExceededException.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SCLRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SSTContinueRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SSTRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SaveRecalcRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/ScenarioProtectRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SelectionRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SetupRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SharedStrings.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SheetCopier.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SheetWriter.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SortRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/StringRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/StyleXFRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/Styles.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/SupbookRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/TabIdRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/TemplateRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/TopMarginRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/UsesElfsRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/VerticalCentreRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/VerticalPageBreaksRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/Weird1Record.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/Window1Record.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/Window2Record.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/WindowProtectRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/WritableFontRecord.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/WritableFonts.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/WritableFormattingRecords.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetCopier.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetImpl.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/WritableWorkbookImpl.java create mode 100755 datastructures-xslx/src/main/java/jxl/write/biff/WriteAccessRecord.java rename datastructures-xslx/src/{main/java/com/incesoft/tools/excel/xlsx => test/java/org/xbib/datastructures/xslx}/TestSJXLSX.java (94%) diff --git a/datastructures-xslx/NOTICE.txt b/datastructures-xslx/NOTICE.txt new file mode 100644 index 0000000..61b7dfb --- /dev/null +++ b/datastructures-xslx/NOTICE.txt @@ -0,0 +1,15 @@ +datastructures-xslx is composed of the following works + +jxl +https://sourceforge.net/projects/jexcelapi/ +GNU Library or Lesser General Public License version 2.0 (LGPLv2) + +with the log4j dependency removed + +and + +com.incesoft.tools.excel +https://code.google.com/archive/p/sjxslx/ -> https://github.com/davidpelfree/sjxlsx +License: Apache License 2.0 + +The code will be refactored to the org.xbib package group. \ No newline at end of file diff --git a/datastructures-xslx/build.gradle b/datastructures-xslx/build.gradle deleted file mode 100644 index cc87491..0000000 --- a/datastructures-xslx/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - implementation libs.jxl -} diff --git a/datastructures-xslx/src/main/java/jxl/BooleanCell.java b/datastructures-xslx/src/main/java/jxl/BooleanCell.java new file mode 100755 index 0000000..8504e00 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/BooleanCell.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * This type represents the Microsoft concept of a Boolean. Accordingly, this + * cell represents either TRUE, FALSE or an error condition. This third + * state naturally makes handling BooleanCells quite tricky, and use of + * the specific access methods should be handled with care + */ +public interface BooleanCell extends Cell { + /** + * Gets the boolean value stored in this cell. If this cell contains an + * error, then returns FALSE. Always query this cell type using the + * accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + boolean getValue(); +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/BooleanFormulaCell.java b/datastructures-xslx/src/main/java/jxl/BooleanFormulaCell.java new file mode 100755 index 0000000..2a69c81 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/BooleanFormulaCell.java @@ -0,0 +1,27 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for numbers + */ +public interface BooleanFormulaCell extends BooleanCell, FormulaCell { +} diff --git a/datastructures-xslx/src/main/java/jxl/Cell.java b/datastructures-xslx/src/main/java/jxl/Cell.java new file mode 100755 index 0000000..7644dce --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Cell.java @@ -0,0 +1,86 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import jxl.format.CellFormat; + +/** + * Represents an individual Cell within a Sheet. May be queried for its + * type and its content + */ +public interface Cell { + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + int getRow(); + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + int getColumn(); + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + CellType getType(); + + /** + * Indicates whether or not this cell is hidden, by virtue of either + * the entire row or column being collapsed + * + * @return TRUE if this cell is hidden, FALSE otherwise + */ + boolean isHidden(); + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + String getContents(); + + /** + * Gets the cell format which applies to this cell + * Note that for cell with a cell type of EMPTY, which has no formatting + * information, this method will return null. Some empty cells (eg. on + * template spreadsheets) may have a cell type of EMPTY, but will + * actually contain formatting information + * + * @return the cell format applied to this cell, or NULL if this is an + * empty cell + */ + CellFormat getCellFormat(); + + /** + * Gets any special cell features, such as comments (notes) or cell + * validation present for this cell + * + * @return the cell features, or NULL if this cell has no special features + */ + CellFeatures getCellFeatures(); +} diff --git a/datastructures-xslx/src/main/java/jxl/CellFeatures.java b/datastructures-xslx/src/main/java/jxl/CellFeatures.java new file mode 100755 index 0000000..5ac5ea7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/CellFeatures.java @@ -0,0 +1,74 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import jxl.biff.BaseCellFeatures; + +/** + * Container for any additional cell features + */ +public class CellFeatures extends BaseCellFeatures { + /** + * Constructor + */ + public CellFeatures() { + super(); + } + + /** + * Copy constructor + * + * @param cf cell to copy + */ + protected CellFeatures(CellFeatures cf) { + super(cf); + } + + /** + * Accessor for the cell comment + * + * @return the cell comment, or NULL if this cell doesn't have + * a comment associated with it + */ + public String getComment() { + return super.getComment(); + } + + /** + * Gets the data validation list + * + * @return the data validation list + */ + public String getDataValidationList() { + return super.getDataValidationList(); + } + + /** + * Gets the range of cells to which the data validation applies. If the + * validation applies to just this cell, this will be reflected in the + * returned range + * + * @return the range to which the same validation extends, or NULL if this + * cell doesn't have a validation + */ + public Range getSharedDataValidationRange() { + return super.getSharedDataValidationRange(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/CellFormat.java b/datastructures-xslx/src/main/java/jxl/CellFormat.java new file mode 100755 index 0000000..0932039 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/CellFormat.java @@ -0,0 +1,28 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * Interface for cell formats - used for typing information + * + * @deprecated Repackaged as jxl.format.CellFormat + */ +public interface CellFormat extends jxl.format.CellFormat { +} diff --git a/datastructures-xslx/src/main/java/jxl/CellReferenceHelper.java b/datastructures-xslx/src/main/java/jxl/CellReferenceHelper.java new file mode 100755 index 0000000..32a2d47 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/CellReferenceHelper.java @@ -0,0 +1,254 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import jxl.write.WritableWorkbook; + +/** + * Exposes some cell reference helper methods to the public interface. + * This class merely delegates to the internally used reference helper + */ +public final class CellReferenceHelper { + /** + * Hide the default constructor + */ + private CellReferenceHelper() { + } + + /** + * Appends the cell reference for the column and row passed in to the string + * buffer + * + * @param column the column + * @param row the row + * @param buf the string buffer to append + */ + public static void getCellReference(int column, int row, StringBuffer buf) { + jxl.biff.CellReferenceHelper.getCellReference(column, row, buf); + } + + /** + * Overloaded method which prepends $ for absolute reference + * + * @param column the column number + * @param colabs TRUE if the column reference is absolute + * @param row the row number + * @param rowabs TRUE if the row reference is absolute + * @param buf the string buffer + */ + public static void getCellReference(int column, + boolean colabs, + int row, + boolean rowabs, + StringBuffer buf) { + jxl.biff.CellReferenceHelper.getCellReference(column, colabs, + row, rowabs, + buf); + } + + + /** + * Gets the cell reference for the specified column and row + * + * @param column the column + * @param row the row + * @return the cell reference + */ + public static String getCellReference(int column, int row) { + return jxl.biff.CellReferenceHelper.getCellReference(column, row); + } + + /** + * Gets the columnn number of the string cell reference + * + * @param s the string to parse + * @return the column portion of the cell reference + */ + public static int getColumn(String s) { + return jxl.biff.CellReferenceHelper.getColumn(s); + } + + /** + * Gets the column letter corresponding to the 0-based column number + * + * @param c the column number + * @return the letter for that column number + */ + public static String getColumnReference(int c) { + return jxl.biff.CellReferenceHelper.getColumnReference(c); + } + + /** + * Gets the row number of the cell reference + * + * @param s the cell reference + * @return the row number + */ + public static int getRow(String s) { + return jxl.biff.CellReferenceHelper.getRow(s); + } + + /** + * Sees if the column component is relative or not + * + * @param s the cell + * @return TRUE if the column is relative, FALSE otherwise + */ + public static boolean isColumnRelative(String s) { + return jxl.biff.CellReferenceHelper.isColumnRelative(s); + } + + /** + * Sees if the row component is relative or not + * + * @param s the cell + * @return TRUE if the row is relative, FALSE otherwise + */ + public static boolean isRowRelative(String s) { + return jxl.biff.CellReferenceHelper.isRowRelative(s); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet index + * @param column the column index + * @param row the row index + * @param workbook the workbook + * @param buf a string buffer + */ + public static void getCellReference + (int sheet, int column, int row, + Workbook workbook, StringBuffer buf) { + jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet + * @param column the column + * @param row the row + * @param workbook the workbook + * @param buf the buffer + */ + public static void getCellReference(int sheet, + int column, + int row, + WritableWorkbook workbook, + StringBuffer buf) { + jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet + * @param column the column + * @param colabs TRUE if the column is an absolute reference + * @param row the row + * @param rowabs TRUE if the row is an absolute reference + * @param workbook the workbook + * @param buf the string buffer + */ + public static void getCellReference(int sheet, + int column, + boolean colabs, + int row, + boolean rowabs, + Workbook workbook, + StringBuffer buf) { + jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, colabs, row, rowabs, + (jxl.biff.formula.ExternalSheet) workbook, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet + * @param column the column + * @param row the row + * @param workbook the workbook + * @return the cell reference in the form 'Sheet 1'!A1 + */ + public static String getCellReference(int sheet, + int column, + int row, + Workbook workbook) { + return jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet + * @param column the column + * @param row the row + * @param workbook the workbook + * @return the cell reference in the form 'Sheet 1'!A1 + */ + public static String getCellReference(int sheet, + int column, + int row, + WritableWorkbook workbook) { + return jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook); + } + + + /** + * Gets the sheet name from the cell reference string + * + * @param ref the cell reference + * @return the sheet name + */ + public static String getSheet(String ref) { + return jxl.biff.CellReferenceHelper.getSheet(ref); + } + + /** + * Gets the cell reference for the cell + * + * @param the cell + */ + public static String getCellReference(Cell c) { + return getCellReference(c.getColumn(), c.getRow()); + } + + /** + * Gets the cell reference for the cell + * + * @param c the cell + * @param sb string buffer + */ + public static void getCellReference(Cell c, StringBuffer sb) { + getCellReference(c.getColumn(), c.getRow(), sb); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/CellType.java b/datastructures-xslx/src/main/java/jxl/CellType.java new file mode 100755 index 0000000..a8c1a97 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/CellType.java @@ -0,0 +1,97 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * An enumeration type listing the available content types for a cell + */ +public final class CellType { + + /** + * An empty cell can still contain formatting information and comments + */ + public static final CellType EMPTY = new CellType("Empty"); + /** + * + */ + public static final CellType LABEL = new CellType("Label"); + /** + * + */ + public static final CellType NUMBER = new CellType("Number"); + /** + * + */ + public static final CellType BOOLEAN = new CellType("Boolean"); + /** + * + */ + public static final CellType ERROR = new CellType("Error"); + /** + * + */ + public static final CellType NUMBER_FORMULA = + new CellType("Numerical Formula"); + /** + * + */ + public static final CellType DATE_FORMULA = new CellType("Date Formula"); + /** + * + */ + public static final CellType STRING_FORMULA = new CellType("String Formula"); + /** + * + */ + public static final CellType BOOLEAN_FORMULA = + new CellType("Boolean Formula"); + /** + * + */ + public static final CellType FORMULA_ERROR = new CellType("Formula Error"); + /** + * + */ + public static final CellType DATE = new CellType("Date"); + /** + * The text description of this cell type + */ + private final String description; + /** + * Private constructor + * + * @param desc the description of this type + */ + private CellType(String desc) { + description = desc; + } + + /** + * Returns a string description of this cell + * + * @return the string description for this type + */ + public String toString() { + return description; + } + +} + + diff --git a/datastructures-xslx/src/main/java/jxl/CellView.java b/datastructures-xslx/src/main/java/jxl/CellView.java new file mode 100755 index 0000000..117a8ba --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/CellView.java @@ -0,0 +1,196 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import jxl.format.CellFormat; + +/** + * This is a bean which client applications may use to get/set various + * properties for a row or column on a spreadsheet + */ +public final class CellView { + /** + * The dimension for the associated group of cells. For columns this + * will be width in characters, for rows this will be the + * height in points + * This attribute is deprecated in favour of the size attribute + */ + private int dimension; + + /** + * The size for the associated group of cells. For columns this + * will be width in characters multiplied by 256, for rows this will be the + * height in points + */ + private int size; + + /** + * Indicates whether the deprecated function was used to set the dimension + */ + private boolean depUsed; + + /** + * Indicates whether or not this sheet is hidden + */ + private boolean hidden; + + /** + * The cell format for the row/column + */ + private CellFormat format; + + /** + * Indicates that this column/row should be autosized + */ + private boolean autosize; + + /** + * Default constructor + */ + public CellView() { + hidden = false; + depUsed = false; + dimension = 1; + size = 1; + autosize = false; + } + + /** + * Copy constructor + */ + public CellView(CellView cv) { + hidden = cv.hidden; + depUsed = cv.depUsed; + dimension = cv.dimension; + size = cv.size; + autosize = cv.autosize; + } + + /** + * Accessor for the hidden nature of this row/column + * + * @return TRUE if this row/column is hidden, FALSE otherwise + */ + public boolean isHidden() { + return hidden; + } + + /** + * Sets the hidden status of this row/column + * + * @param h the hidden flag + */ + public void setHidden(boolean h) { + hidden = h; + } + + /** + * Gets the width of the column in characters or the height of the + * row in 1/20ths + * + * @return the dimension + * @deprecated use getSize() instead + */ + public int getDimension() { + return dimension; + } + + /** + * Sets the dimension for this view + * + * @param d the width of the column in characters, or the height of the + * row in 1/20ths of a point + * @deprecated use the setSize method instead + */ + public void setDimension(int d) { + dimension = d; + depUsed = true; + } + + /** + * Gets the width of the column in characters multiplied by 256, or the + * height of the row in 1/20ths of a point + * + * @return the dimension + */ + public int getSize() { + return size; + } + + /** + * Sets the dimension for this view + * + * @param d the width of the column in characters multiplied by 256, + * or the height of the row in 1/20ths of a point + */ + public void setSize(int d) { + size = d; + depUsed = false; + } + + /** + * Accessor for the cell format for this group. + * + * @return the format for the column/row, or NULL if no format was + * specified + */ + public CellFormat getFormat() { + return format; + } + + /** + * Sets the cell format for this group of cells + * + * @param cf the format for every cell in the column/row + */ + public void setFormat(CellFormat cf) { + format = cf; + } + + /** + * Accessor for the depUsed attribute + * + * @return TRUE if the deprecated methods were used to set the size, + * FALSE otherwise + */ + public boolean depUsed() { + return depUsed; + } + + /** + * Accessor for the autosize flag + * NOTE: use of the autosize function is very processor intensive, so + * use with care + * + * @return TRUE if this row/column is to be autosized + */ + public boolean isAutosize() { + return autosize; + } + + /** + * Sets the autosize flag. Currently, this only works for column views + * + * @param a autosize + */ + public void setAutosize(boolean a) { + autosize = a; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/DateCell.java b/datastructures-xslx/src/main/java/jxl/DateCell.java new file mode 100755 index 0000000..9b73ba1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/DateCell.java @@ -0,0 +1,53 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.text.DateFormat; +import java.util.Date; + +/** + * A date cell + */ +public interface DateCell extends Cell { + /** + * Gets the date contained in this cell + * + * @return the cell contents + */ + Date getDate(); + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + boolean isTime(); + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + DateFormat getDateFormat(); +} diff --git a/datastructures-xslx/src/main/java/jxl/DateFormulaCell.java b/datastructures-xslx/src/main/java/jxl/DateFormulaCell.java new file mode 100755 index 0000000..b798d7f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/DateFormulaCell.java @@ -0,0 +1,27 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * A mixin interface for date formulas, which combines the interfaces + * for formulas and for dates + */ +public interface DateFormulaCell extends DateCell, FormulaCell { +} diff --git a/datastructures-xslx/src/main/java/jxl/ErrorCell.java b/datastructures-xslx/src/main/java/jxl/ErrorCell.java new file mode 100755 index 0000000..ab7f1ee --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/ErrorCell.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * This type represents a cell which contains an error. This error will + * usually, but not always be the result of some error resulting from + * a formula + */ +public interface ErrorCell extends Cell { + /** + * Gets the error code for this cell. If this cell does not represent + * an error, then it returns 0. Always use the method isError() to + * determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + int getErrorCode(); +} diff --git a/datastructures-xslx/src/main/java/jxl/ErrorFormulaCell.java b/datastructures-xslx/src/main/java/jxl/ErrorFormulaCell.java new file mode 100755 index 0000000..0819934 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/ErrorFormulaCell.java @@ -0,0 +1,27 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for numbers + */ +public interface ErrorFormulaCell extends ErrorCell, FormulaCell { +} diff --git a/datastructures-xslx/src/main/java/jxl/FormulaCell.java b/datastructures-xslx/src/main/java/jxl/FormulaCell.java new file mode 100755 index 0000000..84cc24f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/FormulaCell.java @@ -0,0 +1,35 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import jxl.biff.formula.FormulaException; + +/** + * Interface for formulas which allow clients to read the Excel formula + */ +public interface FormulaCell extends Cell { + /** + * Gets the formula as a string + * + * @return the formula as a string + * @throws FormulaException if an error occurred whilst parsing + */ + String getFormula() throws FormulaException; +} diff --git a/datastructures-xslx/src/main/java/jxl/HeaderFooter.java b/datastructures-xslx/src/main/java/jxl/HeaderFooter.java new file mode 100755 index 0000000..a0520d3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/HeaderFooter.java @@ -0,0 +1,344 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan, Eric Jung + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * Class which represents an Excel header or footer. + */ +public final class HeaderFooter extends jxl.biff.HeaderFooter { + /** + * Default constructor. + */ + public HeaderFooter() { + super(); + } + + /** + * Copy constructor + * + * @param hf the item to copy + */ + public HeaderFooter(HeaderFooter hf) { + super(hf); + } + + /** + * Constructor used when reading workbooks to separate the left, right + * a central part of the strings into their constituent parts + * + * @param s the header string + */ + public HeaderFooter(String s) { + super(s); + } + + /** + * Retrieves a Stringified + * version of this object + * + * @return the header string + */ + public String toString() { + return super.toString(); + } + + /** + * Accessor for the contents which appear on the right hand side of the page + * + * @return the right aligned contents + */ + public Contents getRight() { + return (Contents) super.getRightText(); + } + + /** + * Accessor for the contents which in the centre of the page + * + * @return the centrally aligned contents + */ + public Contents getCentre() { + return (Contents) super.getCentreText(); + } + + /** + * Accessor for the contents which appear on the left hand side of the page + * + * @return the left aligned contents + */ + public Contents getLeft() { + return (Contents) super.getLeftText(); + } + + /** + * Clears the contents of the header/footer + */ + public void clear() { + super.clear(); + } + + /** + * Creates internal class of the appropriate type + * + * @return the created contents + */ + protected jxl.biff.HeaderFooter.Contents createContents() { + return new Contents(); + } + + /** + * Creates internal class of the appropriate type + * + * @param s the string to create the contents + * @return the created contents + */ + protected jxl.biff.HeaderFooter.Contents createContents(String s) { + return new Contents(s); + } + + /** + * Creates internal class of the appropriate type + * + * @param c the contents to copy + * @return the new contents + */ + protected jxl.biff.HeaderFooter.Contents + createContents(jxl.biff.HeaderFooter.Contents c) { + return new Contents((Contents) c); + } + + /** + * The contents - a simple wrapper around a string buffer + */ + public static class Contents extends jxl.biff.HeaderFooter.Contents { + /** + * The constructor + */ + Contents() { + super(); + } + + /** + * Constructor used when reading worksheets. The string contains all + * the formatting (but not alignment characters + * + * @param s the format string + */ + Contents(String s) { + super(s); + } + + /** + * Copy constructor + * + * @param copy the contents to copy + */ + Contents(Contents copy) { + super(copy); + } + + /** + * Appends the text to the string buffer + * + * @param txt the text to append + */ + public void append(String txt) { + super.append(txt); + } + + /** + * Turns bold printing on or off. Bold printing + * is initially off. Text subsequently appended to + * this object will be bolded until this method is + * called again. + */ + public void toggleBold() { + super.toggleBold(); + } + + /** + * Turns underline printing on or off. Underline printing + * is initially off. Text subsequently appended to + * this object will be underlined until this method is + * called again. + */ + public void toggleUnderline() { + super.toggleUnderline(); + } + + /** + * Turns italics printing on or off. Italics printing + * is initially off. Text subsequently appended to + * this object will be italicized until this method is + * called again. + */ + public void toggleItalics() { + super.toggleItalics(); + } + + /** + * Turns strikethrough printing on or off. Strikethrough printing + * is initially off. Text subsequently appended to + * this object will be striked out until this method is + * called again. + */ + public void toggleStrikethrough() { + super.toggleStrikethrough(); + } + + /** + * Turns double-underline printing on or off. Double-underline printing + * is initially off. Text subsequently appended to + * this object will be double-underlined until this method is + * called again. + */ + public void toggleDoubleUnderline() { + super.toggleDoubleUnderline(); + } + + /** + * Turns superscript printing on or off. Superscript printing + * is initially off. Text subsequently appended to + * this object will be superscripted until this method is + * called again. + */ + public void toggleSuperScript() { + super.toggleSuperScript(); + } + + /** + * Turns subscript printing on or off. Subscript printing + * is initially off. Text subsequently appended to + * this object will be subscripted until this method is + * called again. + */ + public void toggleSubScript() { + super.toggleSubScript(); + } + + /** + * Turns outline printing on or off (Macintosh only). + * Outline printing is initially off. Text subsequently appended + * to this object will be outlined until this method is + * called again. + */ + public void toggleOutline() { + super.toggleOutline(); + } + + /** + * Turns shadow printing on or off (Macintosh only). + * Shadow printing is initially off. Text subsequently appended + * to this object will be shadowed until this method is + * called again. + */ + public void toggleShadow() { + super.toggleShadow(); + } + + /** + * Sets the font of text subsequently appended to this + * object.. Previously appended text is not affected. + *

+ * Note: no checking is performed to + * determine if fontName is a valid font. + * + * @param fontName name of the font to use + */ + public void setFontName(String fontName) { + super.setFontName(fontName); + } + + /** + * Sets the font size of text subsequently appended to this + * object. Previously appended text is not affected. + *

+ * Valid point sizes are between 1 and 99 (inclusive). If + * size is outside this range, this method returns false + * and does not change font size. If size is within this + * range, the font size is changed and true is returned. + * + * @param size The size in points. Valid point sizes are + * between 1 and 99 (inclusive). + * @return true if the font size was changed, false if font + * size was not changed because 1 > size > 99. + */ + public boolean setFontSize(int size) { + return super.setFontSize(size); + } + + /** + * Appends the page number + */ + public void appendPageNumber() { + super.appendPageNumber(); + } + + /** + * Appends the total number of pages + */ + public void appendTotalPages() { + super.appendTotalPages(); + } + + /** + * Appends the current date + */ + public void appendDate() { + super.appendDate(); + } + + /** + * Appends the current time + */ + public void appendTime() { + super.appendTime(); + } + + /** + * Appends the workbook name + */ + public void appendWorkbookName() { + super.appendWorkbookName(); + } + + /** + * Appends the worksheet name + */ + public void appendWorkSheetName() { + super.appendWorkSheetName(); + } + + /** + * Clears the contents of this portion + */ + public void clear() { + super.clear(); + } + + /** + * Queries if the contents are empty + * + * @return TRUE if the contents are empty, FALSE otherwise + */ + public boolean empty() { + return super.empty(); + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/Hyperlink.java b/datastructures-xslx/src/main/java/jxl/Hyperlink.java new file mode 100755 index 0000000..c8be8e5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Hyperlink.java @@ -0,0 +1,106 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.io.File; +import java.net.URL; + +/** + * Hyperlink information. Only URLs or file links are supported + *

+ * Hyperlinks may apply to a range of cells; in such cases the methods + * getRow and getColumn return the cell at the top left of the range + * the hyperlink refers to. Hyperlinks have no specific cell format + * information applied to them, so the getCellFormat method will return null + */ +public interface Hyperlink { + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + int getRow(); + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + int getColumn(); + + /** + * Gets the range of cells which activate this hyperlink + * The get sheet index methods will all return -1, because the + * cells will all be present on the same sheet + * + * @return the range of cells which activate the hyperlink + */ + Range getRange(); + + /** + * Determines whether this is a hyperlink to a file + * + * @return TRUE if this is a hyperlink to a file, FALSE otherwise + */ + boolean isFile(); + + /** + * Determines whether this is a hyperlink to a web resource + * + * @return TRUE if this is a URL + */ + boolean isURL(); + + /** + * Determines whether this is a hyperlink to a location in this workbook + * + * @return TRUE if this is a link to an internal location + */ + boolean isLocation(); + + /** + * Returns the row number of the bottom right cell + * + * @return the row number of this cell + */ + int getLastRow(); + + /** + * Returns the column number of the bottom right cell + * + * @return the column number of this cell + */ + int getLastColumn(); + + /** + * Gets the URL referenced by this Hyperlink + * + * @return the URL, or NULL if this hyperlink is not a URL + */ + URL getURL(); + + /** + * Returns the local file eferenced by this Hyperlink + * + * @return the file, or NULL if this hyperlink is not a file + */ + File getFile(); +} + diff --git a/datastructures-xslx/src/main/java/jxl/Image.java b/datastructures-xslx/src/main/java/jxl/Image.java new file mode 100755 index 0000000..e3600a8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Image.java @@ -0,0 +1,120 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.io.File; +import jxl.common.LengthUnit; + +/** + * Accessor functions for an image + */ +public interface Image { + /** + * Accessor for the image position + * + * @return the column number at which the image is positioned + */ + double getColumn(); + + /** + * Accessor for the image position + * + * @return the row number at which the image is positioned + */ + double getRow(); + + /** + * Accessor for the image dimensions + * + * @return the number of columns this image spans + */ + double getWidth(); + + /** + * Accessor for the image dimensions + * + * @return the number of rows which this image spans + */ + double getHeight(); + + /** + * Accessor for the image file + * + * @return the file which the image references + */ + File getImageFile(); + + /** + * Accessor for the image data + * + * @return the image data + */ + byte[] getImageData(); + + /** + * Get the width of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the width of the image within Excel + */ + double getWidth(LengthUnit unit); + + /** + * Get the height of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the height of the image within Excel + */ + double getHeight(LengthUnit unit); + + /** + * Gets the width of the image. Note that this is the width of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image width in pixels + */ + int getImageWidth(); + + /** + * Gets the height of the image. Note that this is the height of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image height in pixels + */ + int getImageHeight(); + + /** + * Gets the horizontal resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + double getHorizontalResolution(LengthUnit unit); + + /** + * Gets the vertical resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + double getVerticalResolution(LengthUnit unit); +} diff --git a/datastructures-xslx/src/main/java/jxl/JXLException.java b/datastructures-xslx/src/main/java/jxl/JXLException.java new file mode 100755 index 0000000..fa32309 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/JXLException.java @@ -0,0 +1,34 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * Base exception class for JExcelAPI exceptions + */ +public class JXLException extends Exception { + /** + * Constructor + * + * @param message the exception message + */ + protected JXLException(String message) { + super(message); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/LabelCell.java b/datastructures-xslx/src/main/java/jxl/LabelCell.java new file mode 100755 index 0000000..938efa4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/LabelCell.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * A label cell + */ +public interface LabelCell extends Cell { + /** + * Gets the label for this cell. The value returned will be the same + * as for the getContents method in the base class + * + * @return the cell contents + */ + String getString(); +} diff --git a/datastructures-xslx/src/main/java/jxl/NumberCell.java b/datastructures-xslx/src/main/java/jxl/NumberCell.java new file mode 100755 index 0000000..bc88e11 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/NumberCell.java @@ -0,0 +1,43 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.text.NumberFormat; + +/** + * A cell which contains a numerical value + */ +public interface NumberCell extends Cell { + /** + * Gets the double contents for this cell. + * + * @return the cell contents + */ + double getValue(); + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + NumberFormat getNumberFormat(); +} + diff --git a/datastructures-xslx/src/main/java/jxl/NumberFormulaCell.java b/datastructures-xslx/src/main/java/jxl/NumberFormulaCell.java new file mode 100755 index 0000000..148fa5a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/NumberFormulaCell.java @@ -0,0 +1,27 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for numbers + */ +public interface NumberFormulaCell extends NumberCell, FormulaCell { +} diff --git a/datastructures-xslx/src/main/java/jxl/Range.java b/datastructures-xslx/src/main/java/jxl/Range.java new file mode 100755 index 0000000..dd4be76 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Range.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * Represents a 3-D range of cells in a workbook. This object is + * returned by the method findByName in a workbook + */ +public interface Range { + /** + * Gets the cell at the top left of this range + * + * @return the cell at the top left + */ + Cell getTopLeft(); + + /** + * Gets the cell at the bottom right of this range + * + * @return the cell at the bottom right + */ + Cell getBottomRight(); + + /** + * Gets the index of the first sheet in the range + * + * @return the index of the first sheet in the range + */ + int getFirstSheetIndex(); + + /** + * Gets the index of the last sheet in the range + * + * @return the index of the last sheet in the range + */ + int getLastSheetIndex(); +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/Sheet.java b/datastructures-xslx/src/main/java/jxl/Sheet.java new file mode 100755 index 0000000..1aa5ceb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Sheet.java @@ -0,0 +1,278 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.util.regex.Pattern; +import jxl.format.CellFormat; + +/** + * Represents a sheet within a workbook. Provides a handle to the individual + * cells, or lines of cells (grouped by Row or Column) + */ +public interface Sheet { + /** + * Returns the cell specified at this row and at this column. + * If a column/row combination forms part of a merged group of cells + * then (unless it is the first cell of the group) a blank cell + * will be returned + * + * @param column the column number + * @param row the row number + * @return the cell at the specified co-ordinates + */ + Cell getCell(int column, int row); + + /** + * Returns the cell for the specified location eg. "A4". Note that this + * method is identical to calling getCell(CellReferenceHelper.getColumn(loc), + * CellReferenceHelper.getRow(loc)) and its implicit performance + * overhead for string parsing. As such,this method should therefore + * be used sparingly + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + Cell getCell(String loc); + + /** + * Returns the number of rows in this sheet + * + * @return the number of rows in this sheet + */ + int getRows(); + + /** + * Returns the number of columns in this sheet + * + * @return the number of columns in this sheet + */ + int getColumns(); + + /** + * Gets all the cells on the specified row + * + * @param row the rows whose cells are to be returned + * @return the cells on the given row + */ + Cell[] getRow(int row); + + /** + * Gets all the cells on the specified column + * + * @param col the column whose cells are to be returned + * @return the cells on the specified column + */ + Cell[] getColumn(int col); + + /** + * Gets the name of this sheet + * + * @return the name of the sheet + */ + String getName(); + + /** + * Determines whether the sheet is hidden + * + * @return whether or not the sheet is hidden + * @deprecated in favour of the getSettings() method + */ + boolean isHidden(); + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + * @deprecated in favour of the getSettings() method + */ + boolean isProtected(); + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + Cell findCell(String contents); + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse); + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the rang + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse); + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell method in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + LabelCell findLabelCell(String contents); + + /** + * Gets the hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + Hyperlink[] getHyperlinks(); + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + Range[] getMergedCells(); + + /** + * Gets the settings used on a particular sheet + * + * @return the sheet settings + */ + SheetSettings getSettings(); + + /** + * Gets the column format for the specified column + * + * @param col the column number + * @return the column format, or NULL if the column has no specific format + * @deprecated Use getColumnView and the CellView bean instead + */ + CellFormat getColumnFormat(int col); + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column width, or the default width if the column has no + * specified format + * @deprecated Use getColumnView instead + */ + int getColumnWidth(int col); + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column format, or the default format if no override is + * specified + */ + CellView getColumnView(int col); + + /** + * Gets the row height for the specified column + * + * @param row the row number + * @return the row height, or the default height if the column has no + * specified format + * @deprecated use getRowView instead + */ + int getRowHeight(int row); + + /** + * Gets the row height for the specified column + * + * @param row the row number + * @return the row format, which may be the default format if no format + * is specified + */ + CellView getRowView(int row); + + /** + * Accessor for the number of images on the sheet + * + * @return the number of images on this sheet + */ + int getNumberOfImages(); + + /** + * Accessor for the image + * + * @param i the 0 based image number + * @return the image at the specified position + */ + Image getDrawing(int i); + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + int[] getRowPageBreaks(); + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + int[] getColumnPageBreaks(); + +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/SheetSettings.java b/datastructures-xslx/src/main/java/jxl/SheetSettings.java new file mode 100755 index 0000000..3eb20b2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/SheetSettings.java @@ -0,0 +1,1204 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import jxl.biff.SheetRangeImpl; +import jxl.common.Assert; +import jxl.format.PageOrder; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; + +/** + * This is a bean which client applications may use to get/set various + * properties which are associated with a particular worksheet, such + * as headers and footers, page orientation etc. + */ +public final class SheetSettings { + /** + * The default value for the default row height + */ + public static final int DEFAULT_DEFAULT_ROW_HEIGHT = 0xff; + // *** + // The defaults + // ** + private static final PageOrientation DEFAULT_ORIENTATION = + PageOrientation.PORTRAIT; + private static final PageOrder DEFAULT_ORDER = + PageOrder.RIGHT_THEN_DOWN; + private static final PaperSize DEFAULT_PAPER_SIZE = PaperSize.A4; + private static final double DEFAULT_HEADER_MARGIN = 0.5; + private static final double DEFAULT_FOOTER_MARGIN = 0.5; + private static final int DEFAULT_PRINT_RESOLUTION = 0x12c; + private static final double DEFAULT_WIDTH_MARGIN = 0.75; + private static final double DEFAULT_HEIGHT_MARGIN = 1; + private static final int DEFAULT_DEFAULT_COLUMN_WIDTH = 8; + private static final int DEFAULT_ZOOM_FACTOR = 100; + private static final int DEFAULT_NORMAL_MAGNIFICATION = 100; + private static final int DEFAULT_PAGE_BREAK_PREVIEW_MAGNIFICATION = 60; + /** + * The page orientation + */ + private PageOrientation orientation; + /** + * The page order + */ + private PageOrder pageOrder; + /** + * The paper size for printing + */ + private PaperSize paperSize; + /** + * Indicates whether or not this sheet is protected + */ + private boolean sheetProtected; + /** + * Indicates whether or not this sheet is hidden + */ + private boolean hidden; + /** + * Indicates whether or not this sheet is selected + */ + private boolean selected; + /** + * The header + */ + private HeaderFooter header; + /** + * The margin allocated for any page headers, in inches + */ + private double headerMargin; + /** + * The footer + */ + private HeaderFooter footer; + /** + * The margin allocated for any page footers, in inches + */ + private double footerMargin; + /** + * The scale factor used when printing + */ + private int scaleFactor; + /** + * The zoom factor used when viewing. Note the difference between + * this and the scaleFactor which is used when printing + */ + private int zoomFactor; + /** + * The page number at which to commence printing + */ + private int pageStart; + /** + * The number of pages into which this excel sheet is squeezed widthwise + */ + private int fitWidth; + /** + * The number of pages into which this excel sheet is squeezed heightwise + */ + private int fitHeight; + /** + * The horizontal print resolution + */ + private int horizontalPrintResolution; + /** + * The vertical print resolution + */ + private int verticalPrintResolution; + /** + * The margin from the left hand side of the paper in inches + */ + private double leftMargin; + /** + * The margin from the right hand side of the paper in inches + */ + private double rightMargin; + /** + * The margin from the top of the paper in inches + */ + private double topMargin; + /** + * The margin from the bottom of the paper in inches + */ + private double bottomMargin; + /** + * Indicates whether to fit the print to the pages or scale the output + * This field is manipulated indirectly by virtue of the setFitWidth/Height + * methods + */ + private boolean fitToPages; + /** + * Indicates whether grid lines should be displayed + */ + private boolean showGridLines; + /** + * Indicates whether grid lines should be printed + */ + private boolean printGridLines; + /** + * Indicates whether sheet headings should be printed + */ + private boolean printHeaders; + /** + * Indicates the view mode + */ + private boolean pageBreakPreviewMode; + /** + * Indicates whether the sheet should display zero values + */ + private boolean displayZeroValues; + /** + * The password for protected sheets + */ + private String password; + /** + * The password hashcode - used when copying sheets + */ + private int passwordHash; + /** + * The default column width, in characters + */ + private int defaultColumnWidth; + /** + * The default row height, in 1/20th of a point + */ + private int defaultRowHeight; + /** + * The horizontal freeze pane + */ + private int horizontalFreeze; + /** + * The vertical freeze position + */ + private int verticalFreeze; + /** + * Vertical centre flag + */ + private boolean verticalCentre; + /** + * Horizontal centre flag + */ + private boolean horizontalCentre; + /** + * The number of copies to print + */ + private int copies; + /** + * Automatic formula calculation + */ + private boolean automaticFormulaCalculation; + /** + * Recalculate the formulas before save + */ + private boolean recalculateFormulasBeforeSave; + /** + * The magnification factor for use during page break preview mode (in + * percent) + */ + private int pageBreakPreviewMagnification; + /** + * The magnification factor for use during normal mode (in percent) + */ + private int normalMagnification; + /** + * The print area + */ + private Range printArea; + /** + * The print row titles + */ + private Range printTitlesRow; + /** + * The print column titles + */ + private Range printTitlesCol; + + // The publicly accessible values + /** + * A handle to the sheet - used internally for ranges + */ + private final Sheet sheet; + + /** + * Default constructor + */ + public SheetSettings(Sheet s) { + sheet = s; // for internal use, when accessing ranges + orientation = DEFAULT_ORIENTATION; + pageOrder = DEFAULT_ORDER; + paperSize = DEFAULT_PAPER_SIZE; + sheetProtected = false; + hidden = false; + selected = false; + headerMargin = DEFAULT_HEADER_MARGIN; + footerMargin = DEFAULT_FOOTER_MARGIN; + horizontalPrintResolution = DEFAULT_PRINT_RESOLUTION; + verticalPrintResolution = DEFAULT_PRINT_RESOLUTION; + leftMargin = DEFAULT_WIDTH_MARGIN; + rightMargin = DEFAULT_WIDTH_MARGIN; + topMargin = DEFAULT_HEIGHT_MARGIN; + bottomMargin = DEFAULT_HEIGHT_MARGIN; + fitToPages = false; + showGridLines = true; + printGridLines = false; + printHeaders = false; + pageBreakPreviewMode = false; + displayZeroValues = true; + defaultColumnWidth = DEFAULT_DEFAULT_COLUMN_WIDTH; + defaultRowHeight = DEFAULT_DEFAULT_ROW_HEIGHT; + zoomFactor = DEFAULT_ZOOM_FACTOR; + pageBreakPreviewMagnification = DEFAULT_PAGE_BREAK_PREVIEW_MAGNIFICATION; + normalMagnification = DEFAULT_NORMAL_MAGNIFICATION; + horizontalFreeze = 0; + verticalFreeze = 0; + copies = 1; + header = new HeaderFooter(); + footer = new HeaderFooter(); + automaticFormulaCalculation = true; + recalculateFormulasBeforeSave = true; + } + + /** + * Copy constructor. Called when copying sheets + * + * @param copy the settings to copy + */ + public SheetSettings(SheetSettings copy, Sheet s) { + Assert.verify(copy != null); + + sheet = s; // for internal use when accessing ranges + orientation = copy.orientation; + pageOrder = copy.pageOrder; + paperSize = copy.paperSize; + sheetProtected = copy.sheetProtected; + hidden = copy.hidden; + selected = false; // don't copy the selected flag + headerMargin = copy.headerMargin; + footerMargin = copy.footerMargin; + scaleFactor = copy.scaleFactor; + pageStart = copy.pageStart; + fitWidth = copy.fitWidth; + fitHeight = copy.fitHeight; + horizontalPrintResolution = copy.horizontalPrintResolution; + verticalPrintResolution = copy.verticalPrintResolution; + leftMargin = copy.leftMargin; + rightMargin = copy.rightMargin; + topMargin = copy.topMargin; + bottomMargin = copy.bottomMargin; + fitToPages = copy.fitToPages; + password = copy.password; + passwordHash = copy.passwordHash; + defaultColumnWidth = copy.defaultColumnWidth; + defaultRowHeight = copy.defaultRowHeight; + zoomFactor = copy.zoomFactor; + pageBreakPreviewMagnification = copy.pageBreakPreviewMagnification; + normalMagnification = copy.normalMagnification; + showGridLines = copy.showGridLines; + displayZeroValues = copy.displayZeroValues; + pageBreakPreviewMode = copy.pageBreakPreviewMode; + horizontalFreeze = copy.horizontalFreeze; + verticalFreeze = copy.verticalFreeze; + horizontalCentre = copy.horizontalCentre; + verticalCentre = copy.verticalCentre; + copies = copy.copies; + header = new HeaderFooter(copy.header); + footer = new HeaderFooter(copy.footer); + automaticFormulaCalculation = copy.automaticFormulaCalculation; + recalculateFormulasBeforeSave = copy.recalculateFormulasBeforeSave; + + if (copy.printArea != null) { + printArea = new SheetRangeImpl + (sheet, + copy.getPrintArea().getTopLeft().getColumn(), + copy.getPrintArea().getTopLeft().getRow(), + copy.getPrintArea().getBottomRight().getColumn(), + copy.getPrintArea().getBottomRight().getRow()); + } + + if (copy.printTitlesRow != null) { + printTitlesRow = new SheetRangeImpl + (sheet, + copy.getPrintTitlesRow().getTopLeft().getColumn(), + copy.getPrintTitlesRow().getTopLeft().getRow(), + copy.getPrintTitlesRow().getBottomRight().getColumn(), + copy.getPrintTitlesRow().getBottomRight().getRow()); + } + + if (copy.printTitlesCol != null) { + printTitlesCol = new SheetRangeImpl + (sheet, + copy.getPrintTitlesCol().getTopLeft().getColumn(), + copy.getPrintTitlesCol().getTopLeft().getRow(), + copy.getPrintTitlesCol().getBottomRight().getColumn(), + copy.getPrintTitlesCol().getBottomRight().getRow()); + } + } + + /** + * Accessor for the orientation + * + * @return the orientation + */ + public PageOrientation getOrientation() { + return orientation; + } + + /** + * Sets the paper orientation for printing this sheet + * + * @param po the orientation + */ + public void setOrientation(PageOrientation po) { + orientation = po; + } + + /** + * Accessor for the order + * + * @return + */ + public PageOrder getPageOrder() { + return pageOrder; + } + + /** + * Sets the page order for printing this sheet + * + * @param order + */ + public void setPageOrder(PageOrder order) { + this.pageOrder = order; + } + + /** + * Accessor for the paper size + * + * @return the paper size + */ + public PaperSize getPaperSize() { + return paperSize; + } + + /** + * Sets the paper size to be used when printing this sheet + * + * @param ps the paper size + */ + public void setPaperSize(PaperSize ps) { + paperSize = ps; + } + + /** + * Queries whether this sheet is protected (ie. read only) + * + * @return TRUE if this sheet is read only, FALSE otherwise + */ + public boolean isProtected() { + return sheetProtected; + } + + /** + * Sets the protected (ie. read only) status of this sheet + * + * @param p the protected status + */ + public void setProtected(boolean p) { + sheetProtected = p; + } + + /** + * Accessor for the header margin + * + * @return the header margin + */ + public double getHeaderMargin() { + return headerMargin; + } + + /** + * Sets the margin for any page headers + * + * @param d the margin in inches + */ + public void setHeaderMargin(double d) { + headerMargin = d; + } + + /** + * Accessor for the footer margin + * + * @return the footer margin + */ + public double getFooterMargin() { + return footerMargin; + } + + /** + * Sets the margin for any page footer + * + * @param d the footer margin in inches + */ + public void setFooterMargin(double d) { + footerMargin = d; + } + + /** + * Accessor for the hidden nature of this sheet + * + * @return TRUE if this sheet is hidden, FALSE otherwise + */ + public boolean isHidden() { + return hidden; + } + + /** + * Sets the hidden status of this worksheet + * + * @param h the hidden flag + */ + public void setHidden(boolean h) { + hidden = h; + } + + /** + * Sets this sheet to be when it is opened in excel + * + * @deprecated use overloaded version which takes a boolean + */ + public void setSelected() { + setSelected(true); + } + + /** + * Accessor for the selected nature of the sheet + * + * @return TRUE if this sheet is selected, FALSE otherwise + */ + public boolean isSelected() { + return selected; + } + + /** + * Sets this sheet to be when it is opened in excel + * + * @param s sets whether this sheet is selected or not + */ + public void setSelected(boolean s) { + selected = s; + } + + /** + * Accessor for the scale factor + * + * @return the scale factor + */ + public int getScaleFactor() { + return scaleFactor; + } + + /** + * Sets the scale factor for this sheet to be used when printing. The + * parameter is a percentage, therefore setting a scale factor of 100 will + * print at normal size, 50 half size, 200 double size etc + * + * @param sf the scale factor as a percentage + */ + public void setScaleFactor(int sf) { + scaleFactor = sf; + fitToPages = false; + } + + /** + * Accessor for the page start + * + * @return the page start + */ + public int getPageStart() { + return pageStart; + } + + /** + * Sets the page number at which to commence printing + * + * @param ps the page start number + */ + public void setPageStart(int ps) { + pageStart = ps; + } + + /** + * Accessor for the fit width + * + * @return the number of pages this sheet will be printed into widthwise + */ + public int getFitWidth() { + return fitWidth; + } + + /** + * Sets the number of pages widthwise which this sheet should be + * printed into + * + * @param fw the number of pages + */ + public void setFitWidth(int fw) { + fitWidth = fw; + fitToPages = true; + } + + /** + * Accessor for the fit height + * + * @return the number of pages this sheet will be printed into heightwise + */ + public int getFitHeight() { + return fitHeight; + } + + /** + * Sets the number of pages vertically that this sheet will be printed into + * + * @param fh the number of pages this sheet will be printed into heightwise + */ + public void setFitHeight(int fh) { + fitHeight = fh; + fitToPages = true; + } + + /** + * Accessor for the horizontal print resolution + * + * @return the horizontal print resolution + */ + public int getHorizontalPrintResolution() { + return horizontalPrintResolution; + } + + /** + * Sets the horizontal print resolution + * + * @param hpw the print resolution + */ + public void setHorizontalPrintResolution(int hpw) { + horizontalPrintResolution = hpw; + } + + /** + * Accessor for the vertical print resolution + * + * @return the vertical print resolution + */ + public int getVerticalPrintResolution() { + return verticalPrintResolution; + } + + /** + * Sets the vertical print reslution + * + * @param vpw the vertical print resolution + */ + public void setVerticalPrintResolution(int vpw) { + verticalPrintResolution = vpw; + } + + /** + * Accessor for the right margin + * + * @return the right margin in inches + */ + public double getRightMargin() { + return rightMargin; + } + + /** + * Sets the right margin + * + * @param m the right margin in inches + */ + public void setRightMargin(double m) { + rightMargin = m; + } + + /** + * Accessor for the left margin + * + * @return the left margin in inches + */ + public double getLeftMargin() { + return leftMargin; + } + + /** + * Sets the left margin + * + * @param m the left margin in inches + */ + public void setLeftMargin(double m) { + leftMargin = m; + } + + /** + * Accessor for the top margin + * + * @return the top margin in inches + */ + public double getTopMargin() { + return topMargin; + } + + /** + * Sets the top margin + * + * @param m the top margin in inches + */ + public void setTopMargin(double m) { + topMargin = m; + } + + /** + * Accessor for the bottom margin + * + * @return the bottom margin in inches + */ + public double getBottomMargin() { + return bottomMargin; + } + + /** + * Sets the bottom margin + * + * @param m the bottom margin in inches + */ + public void setBottomMargin(double m) { + bottomMargin = m; + } + + /** + * Gets the default margin width + * + * @return the default margin width + */ + public double getDefaultWidthMargin() { + return DEFAULT_WIDTH_MARGIN; + } + + /** + * Gets the default margin height + * + * @return the default margin height + */ + public double getDefaultHeightMargin() { + return DEFAULT_HEIGHT_MARGIN; + } + + /** + * Accessor for the fit width print flag + * + * @return TRUE if the print is to fit to pages, false otherwise + */ + public boolean getFitToPages() { + return fitToPages; + } + + /** + * Accessor for the fit to pages flag + * + * @param b TRUE to fit to pages, FALSE to use a scale factor + */ + public void setFitToPages(boolean b) { + fitToPages = b; + } + + /** + * Accessor for the password + * + * @return the password to unlock this sheet, or NULL if not protected + */ + public String getPassword() { + return password; + } + + /** + * Sets the password for this sheet + * + * @param s the password + */ + public void setPassword(String s) { + password = s; + } + + /** + * Accessor for the password hash - used only when copying sheets + * + * @return passwordHash + */ + public int getPasswordHash() { + return passwordHash; + } + + /** + * Accessor for the password hash - used only when copying sheets + * + * @param ph the password hash + */ + public void setPasswordHash(int ph) { + passwordHash = ph; + } + + /** + * Accessor for the default column width + * + * @return the default column width, in characters + */ + public int getDefaultColumnWidth() { + return defaultColumnWidth; + } + + /** + * Sets the default column width + * + * @param w the new default column width + */ + public void setDefaultColumnWidth(int w) { + defaultColumnWidth = w; + } + + /** + * Accessor for the default row height + * + * @return the default row height, in 1/20ths of a point + */ + public int getDefaultRowHeight() { + return defaultRowHeight; + } + + /** + * Sets the default row height + * + * @param h the default row height, in 1/20ths of a point + */ + public void setDefaultRowHeight(int h) { + defaultRowHeight = h; + } + + /** + * Accessor for the zoom factor. Do not confuse zoom factor (which relates + * to the on screen view) with scale factor (which refers to the scale factor + * when printing) + * + * @return the zoom factor as a percentage + */ + public int getZoomFactor() { + return zoomFactor; + } + + /** + * Sets the zoom factor. Do not confuse zoom factor (which relates + * to the on screen view) with scale factor (which refers to the scale factor + * when printing) + * + * @param zf the zoom factor as a percentage + */ + public void setZoomFactor(int zf) { + zoomFactor = zf; + } + + /** + * Accessor for the page break preview mangificaton factor. + * Do not confuse zoom factor or scale factor + * + * @return the page break preview magnification a percentage + */ + public int getPageBreakPreviewMagnification() { + return pageBreakPreviewMagnification; + } + + /** + * Accessor for the page break preview magnificaton factor. + * Do not confuse zoom factor or scale factor + * + * @param f the page break preview magnification as a percentage + */ + public void setPageBreakPreviewMagnification(int f) { + pageBreakPreviewMagnification = f; + } + + /** + * Accessor for the nomral view magnificaton factor. + * Do not confuse zoom factor or scale factor + * + * @return the page break preview magnification a percentage + */ + public int getNormalMagnification() { + return normalMagnification; + } + + /** + * Accessor for the normal magnificaton factor. + * Do not confuse zoom factor or scale factor + * + * @param f the page break preview magnification as a percentage + */ + public void setNormalMagnification(int f) { + normalMagnification = f; + } + + + /** + * Accessor for the displayZeroValues property + * + * @return TRUE to display zero values, FALSE not to bother + */ + public boolean getDisplayZeroValues() { + return displayZeroValues; + } + + /** + * Sets the displayZeroValues property + * + * @param b TRUE to show zero values, FALSE not to bother + */ + public void setDisplayZeroValues(boolean b) { + displayZeroValues = b; + } + + /** + * Accessor for the showGridLines property + * + * @return TRUE if grid lines will be shown, FALSE otherwise + */ + public boolean getShowGridLines() { + return showGridLines; + } + + /** + * Sets the showGridLines property + * + * @param b TRUE to show grid lines on this sheet, FALSE otherwise + */ + public void setShowGridLines(boolean b) { + showGridLines = b; + } + + /** + * Accessor for the pageBreakPreview mode + * + * @return TRUE if page break preview is enabled, FALSE otherwise + */ + public boolean getPageBreakPreviewMode() { + return pageBreakPreviewMode; + } + + /** + * Sets the pageBreakPreviewMode property + * + * @param b TRUE to launch in page break preview mode, FALSE otherwise + */ + public void setPageBreakPreviewMode(boolean b) { + pageBreakPreviewMode = b; + } + + /** + * Accessor for the printGridLines property + * + * @return TRUE if grid lines will be printed, FALSE otherwise + */ + public boolean getPrintGridLines() { + return printGridLines; + } + + /** + * Sets the printGridLines property + * + * @param b TRUE to print grid lines on this sheet, FALSE otherwise + */ + public void setPrintGridLines(boolean b) { + printGridLines = b; + } + + /** + * Accessor for the printHeaders property + * + * @return TRUE if headers will be printed, FALSE otherwise + */ + public boolean getPrintHeaders() { + return printHeaders; + } + + /** + * Sets the printHeaders property + * + * @param b TRUE to print headers on this sheet, FALSE otherwise + */ + public void setPrintHeaders(boolean b) { + printHeaders = b; + } + + /** + * Gets the row at which the pane is frozen horizontally + * + * @return the row at which the pane is horizontally frozen, or 0 if there + * is no freeze + */ + public int getHorizontalFreeze() { + return horizontalFreeze; + } + + /** + * Sets the row at which the pane is frozen horizontally + * + * @param row the row number to freeze at + */ + public void setHorizontalFreeze(int row) { + horizontalFreeze = Math.max(row, 0); + } + + /** + * Gets the column at which the pane is frozen vertically + * + * @return the column at which the pane is vertically frozen, or 0 if there + * is no freeze + */ + public int getVerticalFreeze() { + return verticalFreeze; + } + + /** + * Sets the row at which the pane is frozen vertically + * + * @param col the column number to freeze at + */ + public void setVerticalFreeze(int col) { + verticalFreeze = Math.max(col, 0); + } + + /** + * Accessor for the number of copies to print + * + * @return the number of copies + */ + public int getCopies() { + return copies; + } + + /** + * Sets the number of copies + * + * @param c the number of copies + */ + public void setCopies(int c) { + copies = c; + } + + /** + * Accessor for the header + * + * @return the header + */ + public HeaderFooter getHeader() { + return header; + } + + /** + * Sets the header + * + * @param h the header + */ + public void setHeader(HeaderFooter h) { + header = h; + } + + /** + * Accessor for the footer + * + * @return the footer + */ + public HeaderFooter getFooter() { + return footer; + } + + /** + * Sets the footer + * + * @param f the footer + */ + public void setFooter(HeaderFooter f) { + footer = f; + } + + /** + * Accessor for the horizontal centre + * + * @return Returns the horizontalCentre. + */ + public boolean isHorizontalCentre() { + return horizontalCentre; + } + + /** + * Sets the horizontal centre + * + * @param horizCentre The horizontalCentre to set. + */ + public void setHorizontalCentre(boolean horizCentre) { + this.horizontalCentre = horizCentre; + } + + /** + * Accessor for the vertical centre + * + * @return Returns the verticalCentre. + */ + public boolean isVerticalCentre() { + return verticalCentre; + } + + /** + * Sets the vertical centre + * + * @param vertCentre The verticalCentre to set. + */ + public void setVerticalCentre(boolean vertCentre) { + this.verticalCentre = vertCentre; + } + + /** + * Retrieves the automatic formula calculation flag + * + * @return TRUE if formulas are calculated automatically, FALSE if they + * are calculated manually + */ + public boolean getAutomaticFormulaCalculation() { + return automaticFormulaCalculation; + } + + /** + * Sets the automatic formula calculation flag + * + * @param auto - TRUE to automatically calculate the formulas, + * FALSE otherwise + */ + public void setAutomaticFormulaCalculation(boolean auto) { + automaticFormulaCalculation = auto; + } + + /** + * Retrieves the recalculate formulas before save flag + * + * @return TRUE if formulas are calculated before the sheet is saved, + * FALSE otherwise + */ + public boolean getRecalculateFormulasBeforeSave() { + return recalculateFormulasBeforeSave; + } + + /** + * Sets the recalculate formulas when the sheet is saved flag + * + * @param recalc - TRUE to automatically calculate the formulas when the, + * spreadsheet is saved, FALSE otherwise + */ + public void setRecalculateFormulasBeforeSave(boolean recalc) { + recalculateFormulasBeforeSave = recalc; + } + + /** + * Sets the print area for this sheet + * + * @param firstCol the first column of the print area + * @param firstRow the first row of the print area + * @param lastCol the last column of the print area + * @param lastRow the last row of the print area + */ + public void setPrintArea(int firstCol, + int firstRow, + int lastCol, + int lastRow) { + printArea = new SheetRangeImpl(sheet, firstCol, firstRow, + lastCol, lastRow); + } + + /** + * Accessor for the print area + * + * @return the print area, or NULL if one is not defined for this sheet + */ + public Range getPrintArea() { + return printArea; + } + + /** + * Sets both of the print titles for this sheet + * + * @param firstRow the first row of the print row titles + * @param lastRow the last row of the print row titles + * @param firstCol the first column of the print column titles + * @param lastCol the last column of the print column titles + */ + public void setPrintTitles(int firstRow, + int lastRow, + int firstCol, + int lastCol) { + setPrintTitlesRow(firstRow, lastRow); + setPrintTitlesCol(firstCol, lastCol); + } + + /** + * Sets the print row titles for this sheet + * + * @param firstRow the first row of the print titles + * @param lastRow the last row of the print titles + */ + public void setPrintTitlesRow(int firstRow, + int lastRow) { + printTitlesRow = new SheetRangeImpl(sheet, 0, firstRow, + 255, lastRow); + } + + /** + * Sets the print column titles for this sheet + * + * @param firstRow the first row of the print titles + * @param lastRow the last row of the print titles + */ + public void setPrintTitlesCol(int firstCol, + int lastCol) { + printTitlesCol = new SheetRangeImpl(sheet, firstCol, 0, + lastCol, 65535); + } + + /** + * Accessor for the print row titles + * + * @return the print row titles, or NULL if one is not defined for this sheet + */ + public Range getPrintTitlesRow() { + return printTitlesRow; + } + + /** + * Accessor for the print column titles + * + * @return the print column titles, or NULL if one is not defined for this + * sheet + */ + public Range getPrintTitlesCol() { + return printTitlesCol; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/StringFormulaCell.java b/datastructures-xslx/src/main/java/jxl/StringFormulaCell.java new file mode 100755 index 0000000..4a1793a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/StringFormulaCell.java @@ -0,0 +1,29 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for strings + */ +public interface StringFormulaCell extends LabelCell, FormulaCell { +} + + diff --git a/datastructures-xslx/src/main/java/jxl/Workbook.java b/datastructures-xslx/src/main/java/jxl/Workbook.java new file mode 100755 index 0000000..97315a2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Workbook.java @@ -0,0 +1,397 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import jxl.read.biff.BiffException; +import jxl.read.biff.File; +import jxl.read.biff.PasswordException; +import jxl.read.biff.WorkbookParser; +import jxl.write.WritableWorkbook; +import jxl.write.biff.WritableWorkbookImpl; + +/** + * Represents a Workbook. Contains the various factory methods and provides + * a variety of accessors which provide access to the work sheets. + */ +public abstract class Workbook { + /** + * The current version of the software + */ + private static final String VERSION = "2.6.12"; + + /** + * The constructor + */ + protected Workbook() { + } + + /** + * Accessor for the software version + * + * @return the version + */ + public static String getVersion() { + return VERSION; + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param file the excel 97 spreadsheet to parse + * @return a workbook instance + * @throws IOException + * @throws BiffException + */ + public static Workbook getWorkbook(java.io.File file) + throws IOException, BiffException { + return getWorkbook(file, new WorkbookSettings()); + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param file the excel 97 spreadsheet to parse + * @param ws the settings for the workbook + * @return a workbook instance + * @throws IOException + * @throws BiffException + */ + public static Workbook getWorkbook(java.io.File file, WorkbookSettings ws) + throws IOException, BiffException { + FileInputStream fis = new FileInputStream(file); + + // Always close down the input stream, regardless of whether or not the + // file can be parsed. Thanks to Steve Hahn for this + File dataFile = null; + + try { + dataFile = new File(fis, ws); + } catch (IOException e) { + fis.close(); + throw e; + } catch (BiffException e) { + fis.close(); + throw e; + } + + fis.close(); + + Workbook workbook = new WorkbookParser(dataFile, ws); + workbook.parse(); + + return workbook; + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param is an open stream which is the the excel 97 spreadsheet to parse + * @return a workbook instance + * @throws IOException + * @throws BiffException + */ + public static Workbook getWorkbook(InputStream is) + throws IOException, BiffException { + return getWorkbook(is, new WorkbookSettings()); + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param is an open stream which is the the excel 97 spreadsheet to parse + * @param ws the settings for the workbook + * @return a workbook instance + * @throws IOException + * @throws BiffException + */ + public static Workbook getWorkbook(InputStream is, WorkbookSettings ws) + throws IOException, BiffException { + File dataFile = new File(is, ws); + + Workbook workbook = new WorkbookParser(dataFile, ws); + workbook.parse(); + + return workbook; + } + + /** + * Creates a writable workbook with the given file name + * + * @param file the workbook to copy + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(java.io.File file) + throws IOException { + return createWorkbook(file, new WorkbookSettings()); + } + + /** + * Creates a writable workbook with the given file name + * + * @param file the file to copy from + * @param ws the global workbook settings + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(java.io.File file, + WorkbookSettings ws) + throws IOException { + FileOutputStream fos = new FileOutputStream(file); + WritableWorkbook w = new WritableWorkbookImpl(fos, true, ws); + return w; + } + + /** + * Creates a writable workbook with the given filename as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param file the output file for the copy + * @param in the workbook to copy + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(java.io.File file, + Workbook in) + throws IOException { + return createWorkbook(file, in, new WorkbookSettings()); + } + + /** + * Creates a writable workbook with the given filename as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param file the output file for the copy + * @param in the workbook to copy + * @param ws the configuration for this workbook + * @return a writable workbook + */ + public static WritableWorkbook createWorkbook(java.io.File file, + Workbook in, + WorkbookSettings ws) + throws IOException { + FileOutputStream fos = new FileOutputStream(file); + WritableWorkbook w = new WritableWorkbookImpl(fos, in, true, ws); + return w; + } + + /** + * Creates a writable workbook as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param os the stream to write to + * @param in the workbook to copy + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os, + Workbook in) + throws IOException { + return createWorkbook(os, in, ((WorkbookParser) in).getSettings()); + } + + /** + * Creates a writable workbook as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param os the output stream to write to + * @param in the workbook to copy + * @param ws the configuration for this workbook + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os, + Workbook in, + WorkbookSettings ws) + throws IOException { + WritableWorkbook w = new WritableWorkbookImpl(os, in, false, ws); + return w; + } + + /** + * Creates a writable workbook. When the workbook is closed, + * it will be streamed directly to the output stream. In this + * manner, a generated excel spreadsheet can be passed from + * a servlet to the browser over HTTP + * + * @param os the output stream + * @return the writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os) + throws IOException { + return createWorkbook(os, new WorkbookSettings()); + } + + /** + * Creates a writable workbook. When the workbook is closed, + * it will be streamed directly to the output stream. In this + * manner, a generated excel spreadsheet can be passed from + * a servlet to the browser over HTTP + * + * @param os the output stream + * @param ws the configuration for this workbook + * @return the writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os, + WorkbookSettings ws) + throws IOException { + WritableWorkbook w = new WritableWorkbookImpl(os, false, ws); + return w; + } + + /** + * Gets the sheets within this workbook. Use of this method for + * large worksheets can cause performance problems. + * + * @return an array of the individual sheets + */ + public abstract Sheet[] getSheets(); + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public abstract String[] getSheetNames(); + + /** + * Gets the specified sheet within this workbook + * As described in the accompanying technical notes, each call + * to getSheet forces a reread of the sheet (for memory reasons). + * Therefore, do not make unnecessary calls to this method. Furthermore, + * do not hold unnecessary references to Sheets in client code, as + * this will prevent the garbage collector from freeing the memory + * + * @param index the zero based index of the reQuired sheet + * @return The sheet specified by the index + * @throws IndexOutOfBoundException when index refers to a non-existent + * sheet + */ + public abstract Sheet getSheet(int index) + throws IndexOutOfBoundsException; + + /** + * Gets the sheet with the specified name from within this workbook. + * As described in the accompanying technical notes, each call + * to getSheet forces a reread of the sheet (for memory reasons). + * Therefore, do not make unnecessary calls to this method. Furthermore, + * do not hold unnecessary references to Sheets in client code, as + * this will prevent the garbage collector from freeing the memory + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public abstract Sheet getSheet(String name); + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public abstract int getNumberOfSheets(); + + /** + * Gets the named cell from this workbook. If the name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be found, null is returned. + * This is a convenience function to quickly access the contents + * of a single cell. If you need further information (such as the + * sheet or adjacent cells in the range) use the functionally + * richer method, findByName which returns a list of ranges + * + * @param name the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public abstract Cell findCellByName(String name); + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public abstract Cell getCell(String loc); + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param name the name of the cell/range to search for + * @return the range of cells, or NULL if the range does not exist + */ + public abstract Range[] findByName(String name); + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public abstract String[] getRangeNames(); + + /** + * Determines whether the sheet is protected + * + * @return TRUE if the workbook is protected, FALSE otherwise + */ + public abstract boolean isProtected(); + + /** + * Parses the excel file. + * If the workbook is password protected a PasswordException is thrown + * in case consumers of the API wish to handle this in a particular way + * + * @throws BiffException + * @throws PasswordException + */ + protected abstract void parse() throws BiffException, PasswordException; + + /** + * Closes this workbook, and frees makes any memory allocated available + * for garbage collection + */ + public abstract void close(); +} + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/WorkbookSettings.java b/datastructures-xslx/src/main/java/jxl/WorkbookSettings.java new file mode 100755 index 0000000..510aa2c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/WorkbookSettings.java @@ -0,0 +1,798 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.io.File; +import java.util.HashMap; +import java.util.Locale; +import jxl.biff.CountryCode; +import jxl.biff.formula.FunctionNames; +import jxl.common.Logger; + +/** + * This is a bean which client applications may use to set various advanced + * workbook properties. Use of this bean is not mandatory, and its absence + * will merely result in workbooks being read/written using the default + * settings + */ +public final class WorkbookSettings { + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab. + */ + public final static int HIDEOBJ_HIDE_ALL = 2; + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab. + */ + public final static int HIDEOBJ_SHOW_PLACEHOLDERS = 1; + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab. + */ + public final static int HIDEOBJ_SHOW_ALL = 0; + // ** + // The default values + // ** + private static final int DEFAULT_INITIAL_FILE_SIZE = 5 * 1024 * 1024; + // 5 megabytes + private static final int DEFAULT_ARRAY_GROW_SIZE = 1024 * 1024; // 1 megabyte + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(WorkbookSettings.class); + /** + * The amount of memory allocated to store the workbook data when + * reading a worksheet. For processeses reading many small workbooks inside + * a WAS it might be necessary to reduce the default size + */ + private int initialFileSize; + /** + * The amount of memory allocated to the array containing the workbook + * data when its current amount is exhausted. + */ + private int arrayGrowSize; + /** + * Flag to indicate whether the drawing feature is enabled or not + * Drawings deactivated using -Djxl.nodrawings=true on the JVM command line + * Activated by default or by using -Djxl.nodrawings=false on the JVM command + * line + */ + private boolean drawingsDisabled; + /** + * Flag to indicate whether the name feature is enabled or not + * Names deactivated using -Djxl.nonames=true on the JVM command line + * Activated by default or by using -Djxl.nonames=false on the JVM command + * line + */ + private boolean namesDisabled; + /** + * Flag to indicate whether formula cell references should be adjusted + * following row/column insertion/deletion + */ + private boolean formulaReferenceAdjustDisabled; + /** + * Flag to indicate whether the system hint garbage collection + * is enabled or not. + * As a rule of thumb, it is desirable to enable garbage collection + * when reading large spreadsheets from a batch process or from the + * command line, but better to deactivate the feature when reading + * large spreadsheets within a WAS, as the calls to System.gc() not + * only garbage collect the junk in JExcelApi, but also in the + * webservers JVM and can cause significant slowdown + * GC deactivated using -Djxl.nogc=true on the JVM command line + * Activated by default or by using -Djxl.nogc=false on the JVM command line + */ + private boolean gcDisabled; + /** + * Flag to indicate whether the rationalization of cell formats is + * disabled or not. + * Rationalization is enabled by default, but may be disabled for + * performance reasons. It can be deactivated using -Djxl.norat=true on + * the JVM command line + */ + private boolean rationalizationDisabled; + /** + * Flag to indicate whether or not the merged cell checking has been + * disabled + */ + private boolean mergedCellCheckingDisabled; + /** + * Flag to indicate whether the copying of additional property sets + * are disabled + */ + private boolean propertySetsDisabled; + /** + * Flag to indicate that cell validation criteria are ignored + */ + private boolean cellValidationDisabled; + /** + * Flag to indicate whether or not to ignore blank cells when processing + * sheets. Cells which are identified as blank can still have associated + * cell formats which the processing program may still need to read + */ + private boolean ignoreBlankCells; + /** + * Flag to indicate whether auto filtering should be read/copied + */ + private boolean autoFilterDisabled; + /** + * Flag to indicate whether a temporary file should be used when + * writing out the workbook + */ + private boolean useTemporaryFileDuringWrite; + /** + * The directory for used for the temporary file during write. If this + * is NULL, the default system directory is used + */ + private File temporaryFileDuringWriteDirectory; + /** + * The locale. Normally this is the same as the system locale, but there + * may be cases (eg. where you are uploading many spreadsheets from foreign + * sources) where you may want to specify the locale on an individual + * worksheet basis + * The locale may also be specified on the command line using the lang and + * country System properties eg. -Djxl.lang=en -Djxl.country=UK for UK + * English + */ + private Locale locale; + /** + * The locale specific function names for this workbook + */ + private FunctionNames functionNames; + /** + * The character encoding used for reading non-unicode strings. This can + * be different from the default platform encoding if processing spreadsheets + * from abroad. This may also be set using the system property jxl.encoding + */ + private String encoding; + /** + * The character set used by the readable spreadsheeet + */ + private int characterSet; + /** + * The display language used by Excel (ISO 3166 mnemonic) + */ + private String excelDisplayLanguage; + /** + * The regional settings used by Excel (ISO 3166 mnemonic) + */ + private String excelRegionalSettings; + /** + * A hash map of function names keyed on locale + */ + private final HashMap localeFunctionNames; + /** + * Flag to indicate whether all external data and pivot stuff should + * refreshed + */ + private boolean refreshAll; + /** + * Flag to indicate whether the file is a template or not (Usually with .xlt + * file name extension) + */ + private boolean template; + /** + * Flag to indicate whether the file has been written by excel 2000. + *

+ * The EXCEL9FILE record indicates the file was written by Excel 2000. It has + * no record data field and is C0010000h. Any application other than Excel + * 2000 that edits the file should not write out this record. + *

+ * However, it seemas that excel 2003 + 2007 still set this flag.... + */ + private boolean excel9file = false; + /** + * The WINDOWPROTECT record stores an option from the Protect Workbook + * dialog box. + *

+ * =1 if the workbook windows are protected + */ + private boolean windowProtected; + /** + * Write access user name. + * When not set (null) then we set it to Java Excel API + Version number + */ + private String writeAccess; + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab. + */ + private int hideobj; + + /** + * Default constructor + */ + public WorkbookSettings() { + initialFileSize = DEFAULT_INITIAL_FILE_SIZE; + arrayGrowSize = DEFAULT_ARRAY_GROW_SIZE; + localeFunctionNames = new HashMap(); + excelDisplayLanguage = CountryCode.USA.getCode(); + excelRegionalSettings = CountryCode.UK.getCode(); + refreshAll = false; + template = false; + excel9file = false; + windowProtected = false; + hideobj = HIDEOBJ_SHOW_ALL; + + // Initialize other properties from the system properties + try { + boolean suppressWarnings = Boolean.getBoolean("jxl.nowarnings"); + setSuppressWarnings(suppressWarnings); + drawingsDisabled = Boolean.getBoolean("jxl.nodrawings"); + namesDisabled = Boolean.getBoolean("jxl.nonames"); + gcDisabled = Boolean.getBoolean("jxl.nogc"); + rationalizationDisabled = Boolean.getBoolean("jxl.norat"); + mergedCellCheckingDisabled = + Boolean.getBoolean("jxl.nomergedcellchecks"); + formulaReferenceAdjustDisabled = + Boolean.getBoolean("jxl.noformulaadjust"); + propertySetsDisabled = Boolean.getBoolean("jxl.nopropertysets"); + ignoreBlankCells = Boolean.getBoolean("jxl.ignoreblanks"); + cellValidationDisabled = Boolean.getBoolean("jxl.nocellvalidation"); + autoFilterDisabled = !Boolean.getBoolean("jxl.autofilter"); + // autofilter currently disabled by default + useTemporaryFileDuringWrite = + Boolean.getBoolean("jxl.usetemporaryfileduringwrite"); + String tempdir = + System.getProperty("jxl.temporaryfileduringwritedirectory"); + + if (tempdir != null) { + temporaryFileDuringWriteDirectory = new File(tempdir); + } + + encoding = System.getProperty("file.encoding"); + } catch (SecurityException e) { + logger.warn("Error accessing system properties.", e); + } + + // Initialize the locale to the system locale + try { + if (System.getProperty("jxl.lang") == null || + System.getProperty("jxl.country") == null) { + locale = Locale.getDefault(); + } else { + locale = new Locale(System.getProperty("jxl.lang"), + System.getProperty("jxl.country")); + } + + if (System.getProperty("jxl.encoding") != null) { + encoding = System.getProperty("jxl.encoding"); + } + } catch (SecurityException e) { + logger.warn("Error accessing system properties.", e); + locale = Locale.getDefault(); + } + } + + /** + * Accessor for the array grow size property + * + * @return the array grow size + */ + public int getArrayGrowSize() { + return arrayGrowSize; + } + + /** + * Sets the amount of memory by which to increase the amount of + * memory allocated to storing the workbook data. + * For processeses reading many small workbooks + * inside a WAS it might be necessary to reduce the default size + * Default value is 1 megabyte + * + * @param sz the file size in bytes + */ + public void setArrayGrowSize(int sz) { + arrayGrowSize = sz; + } + + /** + * Accessor for the initial file size property + * + * @return the initial file size + */ + public int getInitialFileSize() { + return initialFileSize; + } + + /** + * Sets the initial amount of memory allocated to store the workbook data + * when reading a worksheet. For processeses reading many small workbooks + * inside a WAS it might be necessary to reduce the default size + * Default value is 5 megabytes + * + * @param sz the file size in bytes + */ + public void setInitialFileSize(int sz) { + initialFileSize = sz; + } + + /** + * Gets the drawings disabled flag + * + * @return TRUE if drawings are disabled, FALSE otherwise + */ + public boolean getDrawingsDisabled() { + return drawingsDisabled; + } + + /** + * Disables the handling of drawings + * + * @param b TRUE to disable the names feature, FALSE otherwise + */ + public void setDrawingsDisabled(boolean b) { + drawingsDisabled = b; + } + + /** + * Accessor for the disabling of garbage collection + * + * @return FALSE if JExcelApi hints for garbage collection, TRUE otherwise + */ + public boolean getGCDisabled() { + return gcDisabled; + } + + /** + * Sets the garbage collection disabled + * + * @param disabled TRUE to disable garbage collection, FALSE to enable it + */ + public void setGCDisabled(boolean disabled) { + gcDisabled = disabled; + } + + /** + * Accessor for the disabling of interpretation of named ranges + * + * @return FALSE if named cells are interpreted, TRUE otherwise + */ + public boolean getNamesDisabled() { + return namesDisabled; + } + + /** + * Disables the handling of names + * + * @param b TRUE to disable the names feature, FALSE otherwise + */ + public void setNamesDisabled(boolean b) { + namesDisabled = b; + } + + /** + * Sets whether or not to rationalize the cell formats before + * writing out the sheet. The default value is true + * + * @param r the rationalization flag + */ + public void setRationalization(boolean r) { + rationalizationDisabled = !r; + } + + /** + * Accessor to retrieve the rationalization flag + * + * @return TRUE if rationalization is off, FALSE if rationalization is on + */ + public boolean getRationalizationDisabled() { + return rationalizationDisabled; + } + + /** + * Accessor to retrieve the merged cell checking flag + * + * @return TRUE if merged cell checking is off, FALSE if it is on + */ + public boolean getMergedCellCheckingDisabled() { + return mergedCellCheckingDisabled; + } + + /** + * Accessor to set the merged cell checking + * + * @param b - TRUE to enable merged cell checking, FALSE otherwise + */ + public void setMergedCellChecking(boolean b) { + mergedCellCheckingDisabled = !b; + } + + /** + * Sets whether or not to enable any property sets (such as macros) + * to be copied along with the workbook + * Leaving this feature enabled will result in the JXL process using + * more memory + * + * @param r the property sets flag + */ + public void setPropertySets(boolean r) { + propertySetsDisabled = !r; + } + + /** + * Accessor to retrieve the property sets disabled flag + * + * @return TRUE if property sets are disabled, FALSE otherwise + */ + public boolean getPropertySetsDisabled() { + return propertySetsDisabled; + } + + /** + * Accessor to set the suppress warnings flag. Due to the change + * in logging in version 2.4, this will now set the warning + * behaviour across the JVM (depending on the type of logger used) + * + * @param w the flag + */ + public void setSuppressWarnings(boolean w) { + logger.setSuppressWarnings(w); + } + + /** + * Accessor for the formula adjust disabled + * + * @return TRUE if formulas are adjusted following row/column inserts/deletes + * FALSE otherwise + */ + public boolean getFormulaAdjust() { + return !formulaReferenceAdjustDisabled; + } + + /** + * Setter for the formula adjust disabled property + * + * @param b TRUE to adjust formulas, FALSE otherwise + */ + public void setFormulaAdjust(boolean b) { + formulaReferenceAdjustDisabled = !b; + } + + /** + * Returns the locale used by JExcelAPI to read the spreadsheet + * + * @return the locale + */ + public Locale getLocale() { + return locale; + } + + /** + * Sets the locale used by JExcelApi to generate the spreadsheet. + * Setting this value has no effect on the language or region of + * the generated excel file + * + * @param l the locale + */ + public void setLocale(Locale l) { + locale = l; + } + + /** + * Accessor for the character encoding + * + * @return the character encoding for this workbook + */ + public String getEncoding() { + return encoding; + } + + /** + * Sets the encoding for this workbook + * + * @param enc the encoding + */ + public void setEncoding(String enc) { + encoding = enc; + } + + /** + * Gets the function names. This is used by the formula parsing package + * in order to get the locale specific function names for this particular + * workbook + * + * @return the list of function names + */ + public FunctionNames getFunctionNames() { + if (functionNames == null) { + functionNames = (FunctionNames) localeFunctionNames.get(locale); + + // have not previously accessed function names for this locale, + // so create a brand new one and add it to the list + if (functionNames == null) { + functionNames = new FunctionNames(locale); + localeFunctionNames.put(locale, functionNames); + } + } + + return functionNames; + } + + /** + * Accessor for the character set. This value is only used for reading + * and has no effect when writing out the spreadsheet + * + * @return the character set used by this spreadsheet + */ + public int getCharacterSet() { + return characterSet; + } + + /** + * Sets the character set. This is only used when the spreadsheet is + * read, and has no effect when the spreadsheet is written + * + * @param cs the character set encoding value + */ + public void setCharacterSet(int cs) { + characterSet = cs; + } + + /** + * Accessor for the ignore blanks flag + * + * @return TRUE if blank cells are being ignored, FALSE otherwise + */ + public boolean getIgnoreBlanks() { + return ignoreBlankCells; + } + + /** + * Sets the ignore blanks flag + * + * @param ignoreBlanks TRUE to ignore blanks, FALSE to take them into account + */ + public void setIgnoreBlanks(boolean ignoreBlanks) { + ignoreBlankCells = ignoreBlanks; + } + + /** + * Accessor for the ignore cell validation + * + * @return TRUE if cell validation is disabled + */ + public boolean getCellValidationDisabled() { + return cellValidationDisabled; + } + + /** + * Sets the ignore cell validation flag + * + * @param cv TRUE to disable cell validation, FALSE to enable it + */ + public void setCellValidationDisabled(boolean cv) { + cellValidationDisabled = cv; + } + + /** + * Returns the two character ISO 3166 mnemonic used by excel for user + * language displayto display + * + * @return the display language + */ + public String getExcelDisplayLanguage() { + return excelDisplayLanguage; + } + + /** + * Sets the language in which the generated file will display + * + * @param code the two character ISO 3166 country code + */ + public void setExcelDisplayLanguage(String code) { + excelDisplayLanguage = code; + } + + /** + * Returns the two character ISO 3166 mnemonic used by excel for + * its regional settings + * + * @return the regional settings + */ + public String getExcelRegionalSettings() { + return excelRegionalSettings; + } + + /** + * Sets the regional settings for the generated excel file + * + * @param code the two character ISO 3166 country code + */ + public void setExcelRegionalSettings(String code) { + excelRegionalSettings = code; + } + + /** + * Accessor for the autofilter disabled feature + * + * @return TRUE if autofilter is disabled, FALSE otherwise + */ + public boolean getAutoFilterDisabled() { + return autoFilterDisabled; + } + + /** + * Sets the autofilter disabled + * + * @param disabled + */ + public void setAutoFilterDisabled(boolean disabled) { + autoFilterDisabled = disabled; + } + + /** + * Accessor for the temporary file during write. If this is set, then + * when the workbook is written a temporary file will be used to store + * the interim binary data, otherwise it will take place in memory. Setting + * this flag involves an assessment of the trade-offs between memory usage + * and performance + * + * @return TRUE if a temporary is file is used during writing, + * FALSE otherwise + */ + public boolean getUseTemporaryFileDuringWrite() { + return useTemporaryFileDuringWrite; + } + + /** + * Sets whether a temporary file is used during the generation of + * the workbook. If not set, the workbook will take place entirely in + * memory. Setting + * this flag involves an assessment of the trade-offs between memory usage + * and performance + * + * @return TRUE if a temporary is file is used during writing, + * FALSE otherwise + */ + public void setUseTemporaryFileDuringWrite(boolean temp) { + useTemporaryFileDuringWrite = temp; + } + + /** + * Used in conjunction with the UseTemporaryFileDuringWrite setting to + * set the target directory for the temporary files. This value can + * be NULL, in which case the normal system default temporary directory + * is used instead + * + * @return the temporary directory used during write, or NULL if it is + * not set + */ + public File getTemporaryFileDuringWriteDirectory() { + return temporaryFileDuringWriteDirectory; + } + + /** + * Used in conjunction with the UseTemporaryFileDuringWrite setting to + * set the target directory for the temporary files. If this is not set, + * the system default temporary directory is used. + * This has no effect unless the useTemporaryFileDuringWrite setting + * is TRUE + * + * @param dir the directory to which temporary files should be written + */ + public void setTemporaryFileDuringWriteDirectory(File dir) { + temporaryFileDuringWriteDirectory = dir; + } + + /** + * When true then Refresh All should be done on all external data ranges and + * PivotTables when loading the workbook (the default is =0) + * + * @return the refreshAll value + */ + public boolean getRefreshAll() { + return refreshAll; + } + + /** + * When true then Refresh All should be done on all external data ranges and + * PivotTables when loading the workbook (the default is =0) + * + * @param refreshAll the refreshAll to set + */ + public void setRefreshAll(boolean refreshAll) { + this.refreshAll = refreshAll; + } + + /** + * Workbook Is a Template + * + * @return the template + */ + public boolean getTemplate() { + return template; + } + + /** + * Workbook Is a Template + * + * @param template the template to set + */ + public void setTemplate(boolean template) { + this.template = template; + } + + /** + * Has this file been written by excel 2000? + * + * @return the excel9file + */ + public boolean getExcel9File() { + return excel9file; + } + + /** + * @param excel9file the excel9file to set + */ + public void setExcel9File(boolean excel9file) { + this.excel9file = excel9file; + } + + /** + * @return the windowprotected + */ + public boolean getWindowProtected() { + return windowProtected; + } + + /** + * @param windowprotected the windowprotected to set + */ + public void setWindowProtected(boolean windowprotected) { + this.windowProtected = windowProtected; + } + + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab + *

+ * Possible values are: + * HIDEOBJ_HIDE_ALL, HIDEOBJ_SHOW_ALL and HIDEOBJ_SHOW_PLACEHOLDERS + * + * @return the hideobj + */ + public int getHideobj() { + return hideobj; + } + + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab + *

+ * Possible values are: + * HIDEOBJ_HIDE_ALL, HIDEOBJ_SHOW_ALL and HIDEOBJ_SHOW_PLACEHOLDERS + * + * @param hideobj the hideobj to set + */ + public void setHideobj(int hideobj) { + this.hideobj = hideobj; + } + + /** + * @return the writeAccess + */ + public String getWriteAccess() { + return writeAccess; + } + + /** + * @param writeAccess the writeAccess to set + */ + public void setWriteAccess(String writeAccess) { + this.writeAccess = writeAccess; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/AutoFilter.java b/datastructures-xslx/src/main/java/jxl/biff/AutoFilter.java new file mode 100755 index 0000000..da7bf70 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/AutoFilter.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import jxl.write.biff.File; + +/** + * Information for autofiltering + */ +public class AutoFilter { + private final FilterModeRecord filterMode; + private final AutoFilterInfoRecord autoFilterInfo; + private AutoFilterRecord autoFilter; + + /** + * Constructor + */ + public AutoFilter(FilterModeRecord fmr, + AutoFilterInfoRecord afir) { + filterMode = fmr; + autoFilterInfo = afir; + } + + public void add(AutoFilterRecord af) { + autoFilter = af; // make this into a list sometime + } + + /** + * Writes out the data validation + * + * @param outputFile the output file + * @throws IOException + */ + public void write(File outputFile) throws IOException { + if (filterMode != null) { + outputFile.write(filterMode); + } + + if (autoFilterInfo != null) { + outputFile.write(autoFilterInfo); + } + + if (autoFilter != null) { + outputFile.write(autoFilter); + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/AutoFilterInfoRecord.java b/datastructures-xslx/src/main/java/jxl/biff/AutoFilterInfoRecord.java new file mode 100755 index 0000000..8ce86ed --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/AutoFilterInfoRecord.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class AutoFilterInfoRecord extends WritableRecordData { + // The logger + private static final Logger logger = Logger.getLogger(AutoFilterInfoRecord.class); + + /** + * The data + */ + private final byte[] data; + + + /** + * Constructor + */ + public AutoFilterInfoRecord(Record t) { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/AutoFilterRecord.java b/datastructures-xslx/src/main/java/jxl/biff/AutoFilterRecord.java new file mode 100755 index 0000000..5f4994e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/AutoFilterRecord.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class AutoFilterRecord extends WritableRecordData { + // The logger + private static final Logger logger = Logger.getLogger(AutoFilterRecord.class); + + /** + * The data + */ + private final byte[] data; + + + /** + * Constructor + */ + public AutoFilterRecord(Record t) { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/BaseCellFeatures.java b/datastructures-xslx/src/main/java/jxl/biff/BaseCellFeatures.java new file mode 100755 index 0000000..8023531 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BaseCellFeatures.java @@ -0,0 +1,489 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.util.Collection; +import jxl.CellReferenceHelper; +import jxl.Range; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Comment; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.biff.CellValue; + +/** + * Container for any additional cell features + */ +public class BaseCellFeatures { + public static final ValidationCondition BETWEEN = + new ValidationCondition(DVParser.BETWEEN); + public static final ValidationCondition NOT_BETWEEN = + new ValidationCondition(DVParser.NOT_BETWEEN); + public static final ValidationCondition EQUAL = + new ValidationCondition(DVParser.EQUAL); + public static final ValidationCondition NOT_EQUAL = + new ValidationCondition(DVParser.NOT_EQUAL); + public static final ValidationCondition GREATER_THAN = + new ValidationCondition(DVParser.GREATER_THAN); + public static final ValidationCondition LESS_THAN = + new ValidationCondition(DVParser.LESS_THAN); + public static final ValidationCondition GREATER_EQUAL = + new ValidationCondition(DVParser.GREATER_EQUAL); + public static final ValidationCondition LESS_EQUAL = + new ValidationCondition(DVParser.LESS_EQUAL); + // Constants + private final static double defaultCommentWidth = 3; + private final static double defaultCommentHeight = 4; + /** + * The logger + */ + public static Logger logger = Logger.getLogger(BaseCellFeatures.class); + /** + * The comment + */ + private String comment; + /** + * The comment width in cells + */ + private double commentWidth; + /** + * The comment height in cells + */ + private double commentHeight; + /** + * A handle to the drawing object + */ + private Comment commentDrawing; + /** + * A handle to the combo box object + */ + private ComboBox comboBox; + /** + * The data validation settings + */ + private DataValiditySettingsRecord validationSettings; + /** + * The DV Parser used to contain the validation details + */ + private DVParser dvParser; + /** + * Indicates whether a drop down is required + */ + private boolean dropDown; + /** + * Indicates whether this cell features has data validation + */ + private boolean dataValidation; + /** + * The cell to which this is attached, and which may need to be notified + */ + private CellValue writableCell; + /** + * Constructor + */ + protected BaseCellFeatures() { + } + + /** + * Copy constructor + * + * @param the cell to copy + */ + public BaseCellFeatures(BaseCellFeatures cf) { + // The comment stuff + comment = cf.comment; + commentWidth = cf.commentWidth; + commentHeight = cf.commentHeight; + + // The data validation stuff. + dropDown = cf.dropDown; + dataValidation = cf.dataValidation; + + validationSettings = cf.validationSettings; // ? + + if (cf.dvParser != null) { + dvParser = new DVParser(cf.dvParser); + } + } + + /** + * Accessor for the cell comment + */ + protected String getComment() { + return comment; + } + + /** + * Sets the cell comment + * + * @param s the comment + */ + public void setComment(String s) { + setComment(s, defaultCommentWidth, defaultCommentHeight); + } + + /** + * Accessor for the comment width + */ + public double getCommentWidth() { + return commentWidth; + } + + /** + * Accessor for the comment height + */ + public double getCommentHeight() { + return commentHeight; + } + + /** + * Called by the cell when the features are added + * + * @param wc the writable cell + */ + public final void setWritableCell(CellValue wc) { + writableCell = wc; + } + + /** + * Internal method to set the cell comment. Used when reading + */ + public void setReadComment(String s, double w, double h) { + comment = s; + commentWidth = w; + commentHeight = h; + } + + /** + * Internal method to set the data validation. Used when reading + */ + public void setValidationSettings(DataValiditySettingsRecord dvsr) { + Assert.verify(dvsr != null); + + validationSettings = dvsr; + dataValidation = true; + } + + /** + * Sets the cell comment + * + * @param s the comment + * @param height the height of the comment box in cells + * @param width the width of the comment box in cells + */ + public void setComment(String s, double width, double height) { + comment = s; + commentWidth = width; + commentHeight = height; + + if (commentDrawing != null) { + commentDrawing.setCommentText(s); + commentDrawing.setWidth(width); + commentDrawing.setWidth(height); + // commentDrawing is set up when trying to modify a copied cell + } + } + + /** + * Removes the cell comment, if present + */ + public void removeComment() { + // Set the comment string to be empty + comment = null; + + // Remove the drawing from the drawing group + if (commentDrawing != null) { + // do not call DrawingGroup.remove() because comments are not present + // on the Workbook DrawingGroup record + writableCell.removeComment(commentDrawing); + commentDrawing = null; + } + } + + /** + * Public function which removes any data validation, if present + */ + public void removeDataValidation() { + if (!dataValidation) { + return; + } + + // If the data validation is shared, then generate a warning + DVParser dvp = getDVParser(); + if (dvp.extendedCellsValidation()) { + logger.warn("Cannot remove data validation from " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of the shared reference " + + CellReferenceHelper.getCellReference(dvp.getFirstColumn(), + dvp.getFirstRow()) + + "-" + + CellReferenceHelper.getCellReference(dvp.getLastColumn(), + dvp.getLastRow())); + return; + } + + // Remove the validation from the WritableSheet object if present + writableCell.removeDataValidation(); + clearValidationSettings(); + } + + /** + * Internal function which removes any data validation, including + * shared ones, if present. This is called from WritableSheetImpl + * in response to a call to removeDataValidation + */ + public void removeSharedDataValidation() { + if (!dataValidation) { + return; + } + + // Remove the validation from the WritableSheet object if present + writableCell.removeDataValidation(); + clearValidationSettings(); + } + + /** + * Accessor for the comment drawing + */ + public final Comment getCommentDrawing() { + return commentDrawing; + } + + /** + * Sets the comment drawing object + */ + public final void setCommentDrawing(Comment c) { + commentDrawing = c; + } + + /** + * Gets the data validation list as a formula. Used only when reading + * + * @return the validation formula as a list + */ + public String getDataValidationList() { + if (validationSettings == null) { + return null; + } + + return validationSettings.getValidationFormula(); + } + + /** + * The list of items to validate for this cell. For each object in the + * collection, the toString() method will be called and the data entered + * will be validated against that string + * + * @param c the list of valid values + */ + public void setDataValidationList(Collection c) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(c); + dropDown = true; + dataValidation = true; + } + + /** + * The list of items to validate for this cell in the form of a cell range. + * + * @param c the list of valid values + */ + public void setDataValidationRange(int col1, int r1, int col2, int r2) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(col1, r1, col2, r2); + dropDown = true; + dataValidation = true; + } + + /** + * Sets the data validation based upon a named range + */ + public void setDataValidationRange(String namedRange) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(namedRange); + dropDown = true; + dataValidation = true; + } + + /** + * Sets the data validation based upon a numerical condition + */ + public void setNumberValidation(double val, ValidationCondition c) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(val, Double.NaN, c.getCondition()); + dropDown = false; + dataValidation = true; + } + + public void setNumberValidation(double val1, double val2, + ValidationCondition c) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(val1, val2, c.getCondition()); + dropDown = false; + dataValidation = true; + } + + /** + * Accessor for the data validation + * + * @return TRUE if this has a data validation associated with it, + * FALSE otherwise + */ + public boolean hasDataValidation() { + return dataValidation; + } + + /** + * Clears out any existing validation settings + */ + private void clearValidationSettings() { + validationSettings = null; + dvParser = null; + dropDown = false; + comboBox = null; + dataValidation = false; + } + + /** + * Accessor for whether a drop down is required + * + * @return TRUE if this requires a drop down, FALSE otherwise + */ + public boolean hasDropDown() { + return dropDown; + } + + /** + * Sets the combo box drawing object for list validations + * + * @param cb the combo box + */ + public void setComboBox(ComboBox cb) { + comboBox = cb; + } + + /** + * Gets the dv parser + */ + public DVParser getDVParser() { + // straightforward - this was created as a writable cell + if (dvParser != null) { + return dvParser; + } + + // this was copied from a readable cell, and then copied again + if (validationSettings != null) { + dvParser = new DVParser(validationSettings.getDVParser()); + return dvParser; + } + + return null; // keep the compiler happy + } + + /** + * Use the same data validation logic as the specified cell features + * + * @param cf the data validation to reuse + */ + public void shareDataValidation(BaseCellFeatures source) { + if (dataValidation) { + logger.warn("Attempting to share a data validation on cell " + + CellReferenceHelper.getCellReference(writableCell) + + " which already has a data validation"); + return; + } + clearValidationSettings(); + dvParser = source.getDVParser(); + validationSettings = null; + dataValidation = true; + dropDown = source.dropDown; + comboBox = source.comboBox; + } + + /** + * Gets the range of cells to which the data validation applies. If the + * validation applies to just this cell, this will be reflected in the + * returned range + * + * @return the range to which the same validation extends, or NULL if this + * cell doesn't have a validation + */ + public Range getSharedDataValidationRange() { + if (!dataValidation) { + return null; + } + + DVParser dvp = getDVParser(); + + return new SheetRangeImpl(writableCell.getSheet(), + dvp.getFirstColumn(), + dvp.getFirstRow(), + dvp.getLastColumn(), + dvp.getLastRow()); + } + + // Validation conditions + protected static class ValidationCondition { + private static ValidationCondition[] types = new ValidationCondition[0]; + private final DVParser.Condition condition; + + ValidationCondition(DVParser.Condition c) { + condition = c; + ValidationCondition[] oldtypes = types; + types = new ValidationCondition[oldtypes.length + 1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + public DVParser.Condition getCondition() { + return condition; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/BaseCompoundFile.java b/datastructures-xslx/src/main/java/jxl/biff/BaseCompoundFile.java new file mode 100755 index 0000000..3c93ced --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BaseCompoundFile.java @@ -0,0 +1,349 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Contains the common data for a compound file + */ +public abstract class BaseCompoundFile { + /** + * The standard property sets + */ + public final static String ROOT_ENTRY_NAME = "Root Entry"; + public final static String WORKBOOK_NAME = "Workbook"; + public final static String SUMMARY_INFORMATION_NAME = + "\u0005SummaryInformation"; + public final static String DOCUMENT_SUMMARY_INFORMATION_NAME = + "\u0005DocumentSummaryInformation"; + public final static String COMP_OBJ_NAME = + "\u0001CompObj"; + public final static String[] STANDARD_PROPERTY_SETS = + new String[]{ROOT_ENTRY_NAME, WORKBOOK_NAME, + SUMMARY_INFORMATION_NAME, + DOCUMENT_SUMMARY_INFORMATION_NAME}; + /** + * Property storage types + */ + public final static int NONE_PS_TYPE = 0; + public final static int DIRECTORY_PS_TYPE = 1; + public final static int FILE_PS_TYPE = 2; + public final static int ROOT_ENTRY_PS_TYPE = 5; + /** + * The identifier at the beginning of every OLE file + */ + protected static final byte[] IDENTIFIER = new byte[] + {(byte) 0xd0, + (byte) 0xcf, + (byte) 0x11, + (byte) 0xe0, + (byte) 0xa1, + (byte) 0xb1, + (byte) 0x1a, + (byte) 0xe1}; + /** + * + */ + protected static final int NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c; + /** + * + */ + protected static final int SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c; + + // property storage offsets + /** + * + */ + protected static final int NUM_SMALL_BLOCK_DEPOT_BLOCKS_POS = 0x40; + /** + * + */ + protected static final int ROOT_START_BLOCK_POS = 0x30; + /** + * + */ + protected static final int BIG_BLOCK_SIZE = 0x200; + /** + * + */ + protected static final int SMALL_BLOCK_SIZE = 0x40; + /** + * + */ + protected static final int EXTENSION_BLOCK_POS = 0x44; + /** + * + */ + protected static final int NUM_EXTENSION_BLOCK_POS = 0x48; + /** + * + */ + protected static final int PROPERTY_STORAGE_BLOCK_SIZE = 0x80; + /** + * + */ + protected static final int BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c; + /** + * + */ + protected static final int SMALL_BLOCK_THRESHOLD = 0x1000; + /** + * + */ + private static final int SIZE_OF_NAME_POS = 0x40; + /** + * + */ + private static final int TYPE_POS = 0x42; + /** + * + */ + private static final int COLOUR_POS = 0x43; + /** + * + */ + private static final int PREVIOUS_POS = 0x44; + /** + * + */ + private static final int NEXT_POS = 0x48; + /** + * + */ + private static final int CHILD_POS = 0x4c; + /** + * + */ + private static final int START_BLOCK_POS = 0x74; + /** + * + */ + private static final int SIZE_POS = 0x78; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(BaseCompoundFile.class); + + + /** + * Constructor + */ + protected BaseCompoundFile() { + } + + /** + * Inner class to represent the property storage sets. Access is public + * to allow access from the PropertySetsReader demo utility + */ + public class PropertyStorage { + /** + * The name of this property set + */ + public String name; + /** + * The type of the property set + */ + public int type; + /** + * The colour of the property set + */ + public int colour; + /** + * The block number in the stream which this property sets starts at + */ + public int startBlock; + /** + * The size, in bytes, of this property set + */ + public int size; + /** + * The previous property set + */ + public int previous; + /** + * The next property set + */ + public int next; + /** + * The child for this property set + */ + public int child; + + /** + * The data that created this set + */ + public byte[] data; + + /** + * Constructs a property set + * + * @param d the bytes + */ + public PropertyStorage(byte[] d) { + data = d; + int nameSize = IntegerHelper.getInt(data[SIZE_OF_NAME_POS], + data[SIZE_OF_NAME_POS + 1]); + + if (nameSize > SIZE_OF_NAME_POS) { + logger.warn("property set name exceeds max length - truncating"); + nameSize = SIZE_OF_NAME_POS; + + } + type = data[TYPE_POS]; + colour = data[COLOUR_POS]; + + startBlock = IntegerHelper.getInt + (data[START_BLOCK_POS], + data[START_BLOCK_POS + 1], + data[START_BLOCK_POS + 2], + data[START_BLOCK_POS + 3]); + size = IntegerHelper.getInt + (data[SIZE_POS], + data[SIZE_POS + 1], + data[SIZE_POS + 2], + data[SIZE_POS + 3]); + previous = IntegerHelper.getInt + (data[PREVIOUS_POS], + data[PREVIOUS_POS + 1], + data[PREVIOUS_POS + 2], + data[PREVIOUS_POS + 3]); + next = IntegerHelper.getInt + (data[NEXT_POS], + data[NEXT_POS + 1], + data[NEXT_POS + 2], + data[NEXT_POS + 3]); + child = IntegerHelper.getInt + (data[CHILD_POS], + data[CHILD_POS + 1], + data[CHILD_POS + 2], + data[CHILD_POS + 3]); + + int chars = 0; + if (nameSize > 2) { + chars = (nameSize - 1) / 2; + } + + StringBuffer n = new StringBuffer(); + for (int i = 0; i < chars; i++) { + n.append((char) data[i * 2]); + } + + name = n.toString(); + } + + /** + * Constructs an empty property set. Used when writing the file + * + * @param name the property storage name + */ + public PropertyStorage(String name) { + data = new byte[PROPERTY_STORAGE_BLOCK_SIZE]; + + Assert.verify(name.length() < 32); + + IntegerHelper.getTwoBytes((name.length() + 1) * 2, + data, + SIZE_OF_NAME_POS); + // add one to the name length to allow for the null character at + // the end + for (int i = 0; i < name.length(); i++) { + data[i * 2] = (byte) name.charAt(i); + } + } + + /** + * Sets the type + * + * @param t the type + */ + public void setType(int t) { + type = t; + data[TYPE_POS] = (byte) t; + } + + /** + * Sets the number of the start block + * + * @param sb the number of the start block + */ + public void setStartBlock(int sb) { + startBlock = sb; + IntegerHelper.getFourBytes(sb, data, START_BLOCK_POS); + } + + /** + * Sets the size of the file + * + * @param s the size + */ + public void setSize(int s) { + size = s; + IntegerHelper.getFourBytes(s, data, SIZE_POS); + } + + /** + * Sets the previous block + * + * @param prev the previous block + */ + public void setPrevious(int prev) { + previous = prev; + IntegerHelper.getFourBytes(prev, data, PREVIOUS_POS); + } + + /** + * Sets the next block + * + * @param nxt the next block + */ + public void setNext(int nxt) { + next = nxt; + IntegerHelper.getFourBytes(next, data, NEXT_POS); + } + + /** + * Sets the child + * + * @param dir the child + */ + public void setChild(int dir) { + child = dir; + IntegerHelper.getFourBytes(child, data, CHILD_POS); + } + + /** + * Sets the colour + * + * @param col colour + */ + public void setColour(int col) { + colour = col == 0 ? 0 : 1; + data[COLOUR_POS] = (byte) colour; + } + + } + +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/BuiltInFormat.java b/datastructures-xslx/src/main/java/jxl/biff/BuiltInFormat.java new file mode 100755 index 0000000..f424e79 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BuiltInFormat.java @@ -0,0 +1,166 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.format.Format; + +/** + * The excel string for the various built in formats. Used to present + * the cell format information back to the user + *

+ * The difference between this class and the various format object contained + * in the jxl.write package is that this object contains the Excel strings, + * not their java equivalents + */ +final class BuiltInFormat implements Format, DisplayFormat { + /** + * The list of built in formats + */ + public static BuiltInFormat[] builtIns = new BuiltInFormat[0x32]; + + // Populate the built ins + static { + builtIns[0x0] = new BuiltInFormat("", 0); + builtIns[0x1] = new BuiltInFormat("0", 1); + builtIns[0x2] = new BuiltInFormat("0.00", 2); + builtIns[0x3] = new BuiltInFormat("#,##0", 3); + builtIns[0x4] = new BuiltInFormat("#,##0.00", 4); + builtIns[0x5] = new BuiltInFormat("($#,##0_);($#,##0)", 5); + builtIns[0x6] = new BuiltInFormat("($#,##0_);[Red]($#,##0)", 6); + builtIns[0x7] = new BuiltInFormat("($#,##0_);[Red]($#,##0)", 7); + builtIns[0x8] = new BuiltInFormat("($#,##0.00_);[Red]($#,##0.00)", 8); + builtIns[0x9] = new BuiltInFormat("0%", 9); + builtIns[0xa] = new BuiltInFormat("0.00%", 10); + builtIns[0xb] = new BuiltInFormat("0.00E+00", 11); + builtIns[0xc] = new BuiltInFormat("# ?/?", 12); + builtIns[0xd] = new BuiltInFormat("# ??/??", 13); + builtIns[0xe] = new BuiltInFormat("dd/mm/yyyy", 14); + builtIns[0xf] = new BuiltInFormat("d-mmm-yy", 15); + builtIns[0x10] = new BuiltInFormat("d-mmm", 16); + builtIns[0x11] = new BuiltInFormat("mmm-yy", 17); + builtIns[0x12] = new BuiltInFormat("h:mm AM/PM", 18); + builtIns[0x13] = new BuiltInFormat("h:mm:ss AM/PM", 19); + builtIns[0x14] = new BuiltInFormat("h:mm", 20); + builtIns[0x15] = new BuiltInFormat("h:mm:ss", 21); + builtIns[0x16] = new BuiltInFormat("m/d/yy h:mm", 22); + builtIns[0x25] = new BuiltInFormat("(#,##0_);(#,##0)", 0x25); + builtIns[0x26] = new BuiltInFormat("(#,##0_);[Red](#,##0)", 0x26); + builtIns[0x27] = new BuiltInFormat("(#,##0.00_);(#,##0.00)", 0x27); + builtIns[0x28] = new BuiltInFormat("(#,##0.00_);[Red](#,##0.00)", 0x28); + builtIns[0x29] = new BuiltInFormat + ("_(*#,##0_);_(*(#,##0);_(*\"-\"_);(@_)", 0x29); + builtIns[0x2a] = new BuiltInFormat + ("_($*#,##0_);_($*(#,##0);_($*\"-\"_);(@_)", 0x2a); + builtIns[0x2b] = new BuiltInFormat + ("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);(@_)", 0x2b); + builtIns[0x2c] = new BuiltInFormat + ("_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);(@_)", 0x2c); + builtIns[0x2d] = new BuiltInFormat("mm:ss", 0x2d); + builtIns[0x2e] = new BuiltInFormat("[h]mm:ss", 0x2e); + builtIns[0x2f] = new BuiltInFormat("mm:ss.0", 0x2f); + builtIns[0x30] = new BuiltInFormat("##0.0E+0", 0x30); + builtIns[0x31] = new BuiltInFormat("@", 0x31); + } + + /** + * The excel format string + */ + private final String formatString; + /** + * The index + */ + private final int formatIndex; + + /** + * Constructor + * + * @param s the format string + * @param i the format index + */ + private BuiltInFormat(String s, int i) { + formatIndex = i; + formatString = s; + } + + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString() { + return formatString; + } + + /** + * Accessor for the index style of this format + * + * @return the index for this format + */ + public int getFormatIndex() { + return formatIndex; + } + + /** + * Accessor to see whether this format has been initialized + * + * @return TRUE if initialized, FALSE otherwise + */ + public boolean isInitialized() { + return true; + } + + /** + * Initializes this format with the specified index number + * + * @param pos the position of this format record in the workbook + */ + public void initialize(int pos) { + } + + /** + * Accessor to determine whether or not this format is built in + * + * @return TRUE if this format is a built in format, FALSE otherwise + */ + public boolean isBuiltIn() { + return true; + } + + /** + * Equals method + * + * @return TRUE if the two built in formats are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof BuiltInFormat)) { + return false; + } + + BuiltInFormat bif = (BuiltInFormat) o; + return (formatIndex == bif.formatIndex); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/BuiltInName.java b/datastructures-xslx/src/main/java/jxl/biff/BuiltInName.java new file mode 100755 index 0000000..5aeafb8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BuiltInName.java @@ -0,0 +1,110 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Enumeration of built in names + */ +public class BuiltInName { + // The list of built in names + public static final BuiltInName CONSOLIDATE_AREA = + new BuiltInName("Consolidate_Area", 0x0); + public static final BuiltInName AUTO_OPEN = + new BuiltInName("Auto_Open", 0x1); + public static final BuiltInName AUTO_CLOSE = + new BuiltInName("Auto_Open", 0x2); + public static final BuiltInName EXTRACT = + new BuiltInName("Extract", 0x3); + public static final BuiltInName DATABASE = + new BuiltInName("Database", 0x4); + public static final BuiltInName CRITERIA = + new BuiltInName("Criteria", 0x5); + public static final BuiltInName PRINT_AREA = + new BuiltInName("Print_Area", 0x6); + public static final BuiltInName PRINT_TITLES = + new BuiltInName("Print_Titles", 0x7); + public static final BuiltInName RECORDER = + new BuiltInName("Recorder", 0x8); + public static final BuiltInName DATA_FORM = + new BuiltInName("Data_Form", 0x9); + public static final BuiltInName AUTO_ACTIVATE = + new BuiltInName("Auto_Activate", 0xa); + public static final BuiltInName AUTO_DEACTIVATE = + new BuiltInName("Auto_Deactivate", 0xb); + public static final BuiltInName SHEET_TITLE = + new BuiltInName("Sheet_Title", 0xb); + public static final BuiltInName FILTER_DATABASE = + new BuiltInName("_FilterDatabase", 0xd); + /** + * The list of name + */ + private static BuiltInName[] builtInNames = new BuiltInName[0]; + /** + * The name + */ + private final String name; + /** + * The value + */ + private final int value; + /** + * Constructor + */ + private BuiltInName(String n, int v) { + name = n; + value = v; + + BuiltInName[] oldnames = builtInNames; + builtInNames = new BuiltInName[oldnames.length + 1]; + System.arraycopy(oldnames, 0, builtInNames, 0, oldnames.length); + builtInNames[oldnames.length] = this; + } + + /** + * Gets the built in name for the value + */ + public static BuiltInName getBuiltInName(int val) { + BuiltInName ret = FILTER_DATABASE; + for (int i = 0; i < builtInNames.length; i++) { + if (builtInNames[i].getValue() == val) { + ret = builtInNames[i]; + } + } + return ret; + } + + /** + * Accessor for the name + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Accessor for the value + * + * @return the value + */ + public int getValue() { + return value; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/BuiltInStyle.java b/datastructures-xslx/src/main/java/jxl/biff/BuiltInStyle.java new file mode 100755 index 0000000..780f8d8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BuiltInStyle.java @@ -0,0 +1,70 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Represents a built in, rather than a user defined, style. + * This class is used by the FormattingRecords class when writing out the hard* + * coded styles + */ +class BuiltInStyle extends WritableRecordData { + /** + * The XF index of this style + */ + private final int xfIndex; + /** + * The reference number of this style + */ + private final int styleNumber; + + /** + * Constructor + * + * @param xfind the xf index of this style + * @param sn the style number of this style + */ + public BuiltInStyle(int xfind, int sn) { + super(Type.STYLE); + + xfIndex = xfind; + styleNumber = sn; + } + + /** + * Abstract method implementation to get the raw byte data ready to write out + * + * @return The byte data + */ + public byte[] getData() { + byte[] data = new byte[4]; + + IntegerHelper.getTwoBytes(xfIndex, data, 0); + + // Set the built in bit + data[1] |= 0x80; + + data[2] = (byte) styleNumber; + + // Set the outline level + data[3] = (byte) 0xff; + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ByteArray.java b/datastructures-xslx/src/main/java/jxl/biff/ByteArray.java new file mode 100755 index 0000000..322e841 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ByteArray.java @@ -0,0 +1,106 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * A growable array of bytes + */ +public class ByteArray { + // The default grow size + private final static int defaultGrowSize = 1024; + /** + * The array grow size + */ + private final int growSize; + /** + * The current array + */ + private byte[] bytes; + /** + * The current position + */ + private int pos; + + /** + * Constructor + */ + public ByteArray() { + this(defaultGrowSize); + } + + /** + * Constructor + * + * @param gs + */ + public ByteArray(int gs) { + growSize = gs; + bytes = new byte[defaultGrowSize]; + pos = 0; + } + + /** + * Adds a byte onto the array + * + * @param b the byte + */ + public void add(byte b) { + checkSize(1); + bytes[pos] = b; + pos++; + } + + /** + * Adds an array of bytes onto the array + * + * @param b the array of bytes + */ + public void add(byte[] b) { + checkSize(b.length); + System.arraycopy(b, 0, bytes, pos, b.length); + pos += b.length; + } + + /** + * Gets the complete array + * + * @return the array + */ + public byte[] getBytes() { + byte[] returnArray = new byte[pos]; + System.arraycopy(bytes, 0, returnArray, 0, pos); + return returnArray; + } + + /** + * Checks to see if there is sufficient space left on the array. If not, + * then it grows the array + * + * @param sz the amount of bytes to add + */ + private void checkSize(int sz) { + while (pos + sz >= bytes.length) { + // Grow the array + byte[] newArray = new byte[bytes.length + growSize]; + System.arraycopy(bytes, 0, newArray, 0, pos); + bytes = newArray; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ByteData.java b/datastructures-xslx/src/main/java/jxl/biff/ByteData.java new file mode 100755 index 0000000..ba6cf5a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ByteData.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Interface which provides a method for transferring chunks of binary + * data from one Excel file (read in) to another (written out) + */ +public interface ByteData { + /** + * Used when writing out records + * + * @return the full data to be included in the final compound file + */ + byte[] getBytes(); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/CellFinder.java b/datastructures-xslx/src/main/java/jxl/biff/CellFinder.java new file mode 100755 index 0000000..0ee9b6d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/CellFinder.java @@ -0,0 +1,196 @@ +/********************************************************************** + * + * Copyright (C) 2008 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jxl.Cell; +import jxl.CellType; +import jxl.LabelCell; +import jxl.Sheet; + +/** + * Refactorisation to provide more sophisticated find cell by contents + * functionality + */ +public class CellFinder { + private final Sheet sheet; + + public CellFinder(Sheet s) { + sheet = s; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + Cell cell = null; + boolean found = false; + + int numCols = lastCol - firstCol; + int numRows = lastRow - firstRow; + + int row1 = reverse ? lastRow : firstRow; + int row2 = reverse ? firstRow : lastRow; + int col1 = reverse ? lastCol : firstCol; + int col2 = reverse ? firstCol : lastCol; + int inc = reverse ? -1 : 1; + + for (int i = 0; i <= numCols && found == false; i++) { + for (int j = 0; j <= numRows && found == false; j++) { + int curCol = col1 + i * inc; + int curRow = row1 + j * inc; + if (curCol < sheet.getColumns() && curRow < sheet.getRows()) { + Cell c = sheet.getCell(curCol, curRow); + if (c.getType() != CellType.EMPTY) { + if (c.getContents().equals(contents)) { + cell = c; + found = true; + } + } + } + } + } + + return cell; + } + + /** + * Finds a cell within a given range of cells + * + * @param contents the string to match + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents) { + Cell cell = null; + boolean found = false; + + for (int i = 0; i < sheet.getRows() && found == false; i++) { + Cell[] row = sheet.getRow(i); + for (int j = 0; j < row.length && found == false; j++) { + if (row[j].getContents().equals(contents)) { + cell = row[j]; + found = true; + } + } + } + + return cell; + } + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + Cell cell = null; + boolean found = false; + + int numCols = lastCol - firstCol; + int numRows = lastRow - firstRow; + + int row1 = reverse ? lastRow : firstRow; + int row2 = reverse ? firstRow : lastRow; + int col1 = reverse ? lastCol : firstCol; + int col2 = reverse ? firstCol : lastCol; + int inc = reverse ? -1 : 1; + + for (int i = 0; i <= numCols && found == false; i++) { + for (int j = 0; j <= numRows && found == false; j++) { + int curCol = col1 + i * inc; + int curRow = row1 + j * inc; + if (curCol < sheet.getColumns() && curRow < sheet.getRows()) { + Cell c = sheet.getCell(curCol, curRow); + if (c.getType() != CellType.EMPTY) { + Matcher m = pattern.matcher(c.getContents()); + if (m.matches()) { + cell = c; + found = true; + } + } + } + } + } + + return cell; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell methods in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public LabelCell findLabelCell(String contents) { + LabelCell cell = null; + boolean found = false; + + for (int i = 0; i < sheet.getRows() && !found; i++) { + Cell[] row = sheet.getRow(i); + for (int j = 0; j < row.length && !found; j++) { + if ((row[j].getType() == CellType.LABEL || + row[j].getType() == CellType.STRING_FORMULA) && + row[j].getContents().equals(contents)) { + cell = (LabelCell) row[j]; + found = true; + } + } + } + + return cell; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/CellReferenceHelper.java b/datastructures-xslx/src/main/java/jxl/biff/CellReferenceHelper.java new file mode 100755 index 0000000..001dad2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/CellReferenceHelper.java @@ -0,0 +1,313 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.biff.formula.ExternalSheet; +import jxl.common.Logger; + +/** + * A helper to transform between excel cell references and + * sheet:column:row notation + * Because this function will be called when generating a string + * representation of a formula, the cell reference will merely + * be appened to the string buffer instead of returning a full + * blooded string, for performance reasons + */ +public final class CellReferenceHelper { + /** + * The character which indicates whether a reference is fixed + */ + private static final char fixedInd = '$'; + /** + * The character which indicates the sheet name terminator + */ + private static final char sheetInd = '!'; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CellReferenceHelper.class); + + /** + * Constructor to prevent instantiation + */ + private CellReferenceHelper() { + } + + /** + * Gets the cell reference + * + * @param column + * @param row + * @param buf + */ + public static void getCellReference(int column, int row, StringBuffer buf) { + // Put the column letter into the buffer + getColumnReference(column, buf); + + // Add the row into the buffer + buf.append(row + 1); + } + + /** + * Overloaded method which prepends $ for absolute reference + * + * @param column + * @param colabs TRUE if the column reference is absolute + * @param row + * @param rowabs TRUE if the row reference is absolute + * @param buf + */ + public static void getCellReference(int column, boolean colabs, + int row, boolean rowabs, + StringBuffer buf) { + if (colabs) { + buf.append(fixedInd); + } + + // Put the column letter into the buffer + getColumnReference(column, buf); + + if (rowabs) { + buf.append(fixedInd); + } + + // Add the row into the buffer + buf.append(row + 1); + } + + /** + * Gets the column letter corresponding to the 0-based column number + * + * @param column the column number + * @return the letter for that column number + */ + public static String getColumnReference(int column) { + StringBuffer buf = new StringBuffer(); + getColumnReference(column, buf); + return buf.toString(); + } + + /** + * Gets the column letter corresponding to the 0-based column number + * + * @param column the column number + * @param buf the string buffer in which to write the column letter + */ + public static void getColumnReference(int column, StringBuffer buf) { + int v = column / 26; + int r = column % 26; + + StringBuffer tmp = new StringBuffer(); + while (v != 0) { + char col = (char) ('A' + r); + + tmp.append(col); + + r = v % 26 - 1; // subtract one because only rows >26 preceded by A + v = v / 26; + } + + char col = (char) ('A' + r); + tmp.append(col); + + // Insert into the proper string buffer in reverse order + for (int i = tmp.length() - 1; i >= 0; i--) { + buf.append(tmp.charAt(i)); + } + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param row + * @param workbook + * @param buf + */ + public static void getCellReference + (int sheet, int column, int row, + ExternalSheet workbook, StringBuffer buf) { + // Quotes are added by the WorkbookParser + String name = workbook.getExternalSheetName(sheet); + buf.append(StringHelper.replace(name, "'", "''")); + buf.append(sheetInd); + getCellReference(column, row, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param colabs TRUE if the column is an absolute reference + * @param row + * @param rowabs TRUE if the row is an absolute reference + * @param workbook + * @param buf + */ + public static void getCellReference + (int sheet, int column, boolean colabs, + int row, boolean rowabs, + ExternalSheet workbook, StringBuffer buf) { + // WorkbookParser now appends quotes and escapes apostrophes + String name = workbook.getExternalSheetName(sheet); + buf.append(name); + buf.append(sheetInd); + getCellReference(column, colabs, row, rowabs, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param row + * @param workbook + * @return the cell reference in the form 'Sheet 1'!A1 + */ + public static String getCellReference + (int sheet, int column, int row, + ExternalSheet workbook) { + StringBuffer sb = new StringBuffer(); + getCellReference(sheet, column, row, workbook, sb); + return sb.toString(); + } + + + /** + * Gets the cell reference for the specified column and row + * + * @param column + * @param row + * @return + */ + public static String getCellReference(int column, int row) { + StringBuffer buf = new StringBuffer(); + getCellReference(column, row, buf); + return buf.toString(); + } + + /** + * Gets the columnn number of the string cell reference + * + * @param s the string to parse + * @return the column portion of the cell reference + */ + public static int getColumn(String s) { + int colnum = 0; + int numindex = getNumberIndex(s); + + String s2 = s.toUpperCase(); + + int startPos = s.lastIndexOf(sheetInd) + 1; + if (s.charAt(startPos) == fixedInd) { + startPos++; + } + + int endPos = numindex; + if (s.charAt(numindex - 1) == fixedInd) { + endPos--; + } + + for (int i = startPos; i < endPos; i++) { + + if (i != startPos) { + colnum = (colnum + 1) * 26; + } + colnum += (int) s2.charAt(i) - (int) 'A'; + } + + return colnum; + } + + /** + * Gets the row number of the cell reference + */ + public static int getRow(String s) { + try { + return (Integer.parseInt(s.substring(getNumberIndex(s))) - 1); + } catch (NumberFormatException e) { + logger.warn(e, e); + return 0xffff; + } + } + + /** + * Finds the position where the first number occurs in the string + */ + private static int getNumberIndex(String s) { + // Find the position of the first number + boolean numberFound = false; + int pos = s.lastIndexOf(sheetInd) + 1; + char c = '\0'; + + while (!numberFound && pos < s.length()) { + c = s.charAt(pos); + + if (c >= '0' && c <= '9') { + numberFound = true; + } else { + pos++; + } + } + + return pos; + } + + /** + * Sees if the column component is relative or not + * + * @param s + * @return TRUE if the column is relative, FALSE otherwise + */ + public static boolean isColumnRelative(String s) { + return s.charAt(0) != fixedInd; + } + + /** + * Sees if the row component is relative or not + * + * @param s + * @return TRUE if the row is relative, FALSE otherwise + */ + public static boolean isRowRelative(String s) { + return s.charAt(getNumberIndex(s) - 1) != fixedInd; + } + + /** + * Gets the sheet name from the cell reference string + * + * @param ref + * @return the sheet reference + */ + public static String getSheet(String ref) { + int sheetPos = ref.lastIndexOf(sheetInd); + if (sheetPos == -1) { + return ""; + } + + return ref.substring(0, sheetPos); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormat.java b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormat.java new file mode 100755 index 0000000..8489d51 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormat.java @@ -0,0 +1,113 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.write.biff.File; + +/** + * Class containing the CONDFMT and CF records for conditionally formatting + * a cell + */ +public class ConditionalFormat { + /** + * The range of the format + */ + private final ConditionalFormatRangeRecord range; + + /** + * The format conditions + */ + private final ArrayList conditions; + + /** + * Constructor + */ + public ConditionalFormat(ConditionalFormatRangeRecord cfrr) { + range = cfrr; + conditions = new ArrayList(); + } + + /** + * Adds a condition + * + * @param cond the condition + */ + public void addCondition(ConditionalFormatRecord cond) { + conditions.add(cond); + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) { + range.insertColumn(col); + } + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + public void removeColumn(int col) { + range.removeColumn(col); + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) { + range.removeRow(row); + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) { + range.insertRow(row); + } + + /** + * Writes out the data validation + * + * @param outputFile the output file + * @throws IOException + */ + public void write(File outputFile) throws IOException { + outputFile.write(range); + + for (Iterator i = conditions.iterator(); i.hasNext(); ) { + ConditionalFormatRecord cfr = (ConditionalFormatRecord) i.next(); + outputFile.write(cfr); + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRangeRecord.java b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRangeRecord.java new file mode 100755 index 0000000..0ebcd89 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRangeRecord.java @@ -0,0 +1,344 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class ConditionalFormatRangeRecord extends WritableRecordData { + // The logger + private static final Logger logger = + Logger.getLogger(ConditionalFormatRangeRecord.class); + + /** + * The enclosing range + */ + private Range enclosingRange; + + /** + * The discrete ranges + */ + private Range[] ranges; + + /** + * The number of ranges + */ + private int numRanges; + + /** + * Initialized flag + */ + private boolean initialized; + + /** + * Modified flag + */ + private boolean modified; + + /** + * The data + */ + private final byte[] data; + + /** + * Constructor + */ + public ConditionalFormatRangeRecord(Record t) { + super(t); + + initialized = false; + modified = false; + data = getRecord().getData(); + } + + /** + * Initialization function + */ + private void initialize() { + enclosingRange = new Range(); + enclosingRange.firstRow = IntegerHelper.getInt(data[4], data[5]); + enclosingRange.lastRow = IntegerHelper.getInt(data[6], data[7]); + enclosingRange.firstColumn = IntegerHelper.getInt(data[8], data[9]); + enclosingRange.lastColumn = IntegerHelper.getInt(data[10], data[11]); + numRanges = IntegerHelper.getInt(data[12], data[13]); + ranges = new Range[numRanges]; + + int pos = 14; + + for (int i = 0; i < numRanges; i++) { + ranges[i] = new Range(); + ranges[i].firstRow = IntegerHelper.getInt(data[pos], data[pos + 1]); + ranges[i].lastRow = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + ranges[i].firstColumn = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + ranges[i].lastColumn = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + pos += 8; + } + + initialized = true; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) { + if (!initialized) { + initialize(); + } + + enclosingRange.insertColumn(col); + if (enclosingRange.modified) { + modified = true; + } + + for (int i = 0; i < ranges.length; i++) { + ranges[i].insertColumn(col); + + if (ranges[i].modified) { + modified = true; + } + } + + return; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void removeColumn(int col) { + if (!initialized) { + initialize(); + } + + enclosingRange.removeColumn(col); + if (enclosingRange.modified) { + modified = true; + } + + for (int i = 0; i < ranges.length; i++) { + ranges[i].removeColumn(col); + + if (ranges[i].modified) { + modified = true; + } + } + + return; + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) { + if (!initialized) { + initialize(); + } + + enclosingRange.removeRow(row); + if (enclosingRange.modified) { + modified = true; + } + + for (int i = 0; i < ranges.length; i++) { + ranges[i].removeRow(row); + + if (ranges[i].modified) { + modified = true; + } + } + + return; + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) { + if (!initialized) { + initialize(); + } + + enclosingRange.insertRow(row); + if (enclosingRange.modified) { + modified = true; + } + + for (int i = 0; i < ranges.length; i++) { + ranges[i].insertRow(row); + + if (ranges[i].modified) { + modified = true; + } + } + + return; + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + if (!modified) { + return data; + } + + byte[] d = new byte[14 + ranges.length * 8]; + + // Copy in the original information + System.arraycopy(data, 0, d, 0, 4); + + // Create the new range + IntegerHelper.getTwoBytes(enclosingRange.firstRow, d, 4); + IntegerHelper.getTwoBytes(enclosingRange.lastRow, d, 6); + IntegerHelper.getTwoBytes(enclosingRange.firstColumn, d, 8); + IntegerHelper.getTwoBytes(enclosingRange.lastColumn, d, 10); + + IntegerHelper.getTwoBytes(numRanges, d, 12); + + int pos = 14; + for (int i = 0; i < ranges.length; i++) { + IntegerHelper.getTwoBytes(ranges[i].firstRow, d, pos); + IntegerHelper.getTwoBytes(ranges[i].lastRow, d, pos + 2); + IntegerHelper.getTwoBytes(ranges[i].firstColumn, d, pos + 4); + IntegerHelper.getTwoBytes(ranges[i].lastColumn, d, pos + 6); + pos += 8; + } + + return d; + } + + private static class Range { + public int firstRow; + public int firstColumn; + public int lastRow; + public int lastColumn; + public boolean modified; + + public Range() { + modified = false; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) { + if (col > lastColumn) { + return; + } + + if (col <= firstColumn) { + firstColumn++; + modified = true; + } + + if (col <= lastColumn) { + lastColumn++; + modified = true; + } + } + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + public void removeColumn(int col) { + if (col > lastColumn) { + return; + } + + if (col < firstColumn) { + firstColumn--; + modified = true; + } + + if (col <= lastColumn) { + lastColumn--; + modified = true; + } + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) { + if (row > lastRow) { + return; + } + + if (row < firstRow) { + firstRow--; + modified = true; + } + + if (row <= lastRow) { + lastRow--; + modified = true; + } + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) { + if (row > lastRow) { + return; + } + + if (row <= firstRow) { + firstRow++; + modified = true; + } + + if (row <= lastRow) { + lastRow++; + modified = true; + } + } + + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRecord.java b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRecord.java new file mode 100755 index 0000000..d9b6dff --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRecord.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * The conditional format conditions + */ +public class ConditionalFormatRecord extends WritableRecordData { + // the logger + private static final Logger logger = + Logger.getLogger(ConditionalFormatRecord.class); + + /** + * The data + */ + private final byte[] data; + + /** + * Constructor + */ + public ConditionalFormatRecord(Record t) { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ContinueRecord.java b/datastructures-xslx/src/main/java/jxl/biff/ContinueRecord.java new file mode 100755 index 0000000..d1a074f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ContinueRecord.java @@ -0,0 +1,74 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.read.biff.Record; + +/** + * A continue record - only used explicitly in special circumstances, as + * the general continuation record is handled directly by the records + */ +public class ContinueRecord extends WritableRecordData { + /** + * The data + */ + private final byte[] data; + + /** + * Constructor + * + * @param t the raw bytes + */ + public ContinueRecord(Record t) { + super(t); + data = t.getData(); + } + + /** + * Constructor invoked when creating continue records + * + * @param d the data + */ + public ContinueRecord(byte[] d) { + super(Type.CONTINUE); + data = d; + } + + /** + * Accessor for the binary data - used when copying + * + * @return the binary data + */ + public byte[] getData() { + return data; + } + + /** + * Accessor for the record. Used when forcibly changing this record + * into another type, notably a drawing record, as sometimes Excel appears + * to switch to writing Continue records instead of MsoDrawing records + * + * @return the record + */ + public Record getRecord() { + return super.getRecord(); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/CountryCode.java b/datastructures-xslx/src/main/java/jxl/biff/CountryCode.java new file mode 100755 index 0000000..71d3cba --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/CountryCode.java @@ -0,0 +1,150 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; + +/** + * Enumeration type for the excel country codes + */ +public class CountryCode { + // The country codes + public final static CountryCode USA = new CountryCode(0x1, "US", "USA"); + public final static CountryCode CANADA = + new CountryCode(0x2, "CA", "Canada"); + public final static CountryCode GREECE = + new CountryCode(0x1e, "GR", "Greece"); + public final static CountryCode NETHERLANDS = + new CountryCode(0x1f, "NE", "Netherlands"); + public final static CountryCode BELGIUM = + new CountryCode(0x20, "BE", "Belgium"); + public final static CountryCode FRANCE = + new CountryCode(0x21, "FR", "France"); + public final static CountryCode SPAIN = new CountryCode(0x22, "ES", "Spain"); + public final static CountryCode ITALY = new CountryCode(0x27, "IT", "Italy"); + public final static CountryCode SWITZERLAND = + new CountryCode(0x29, "CH", "Switzerland"); + public final static CountryCode UK = + new CountryCode(0x2c, "UK", "United Kingdowm"); + public final static CountryCode DENMARK = + new CountryCode(0x2d, "DK", "Denmark"); + public final static CountryCode SWEDEN = + new CountryCode(0x2e, "SE", "Sweden"); + public final static CountryCode NORWAY = + new CountryCode(0x2f, "NO", "Norway"); + public final static CountryCode GERMANY = + new CountryCode(0x31, "DE", "Germany"); + public final static CountryCode PHILIPPINES = + new CountryCode(0x3f, "PH", "Philippines"); + public final static CountryCode CHINA = + new CountryCode(0x56, "CN", "China"); + public final static CountryCode INDIA = + new CountryCode(0x5b, "IN", "India"); + public final static CountryCode UNKNOWN = + new CountryCode(0xffff, "??", "Unknown"); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CountryCode.class); + /** + * The array of country codes + */ + private static CountryCode[] codes = new CountryCode[0]; + /** + * The country code + */ + private final int value; + /** + * The ISO 3166 two letter country mnemonic (as used by the Locale class) + */ + private final String code; + /** + * The long description + */ + private final String description; + /** + * Constructor + */ + private CountryCode(int v, String c, String d) { + value = v; + code = c; + description = d; + + CountryCode[] newcodes = new CountryCode[codes.length + 1]; + System.arraycopy(codes, 0, newcodes, 0, codes.length); + newcodes[codes.length] = this; + codes = newcodes; + } + /** + * Constructor used to create an arbitrary code with a specified value. + * Doesn't add the latest value to the static array + */ + private CountryCode(int v) { + value = v; + description = "Arbitrary"; + code = "??"; + } + + /** + * Gets the country code for the given two character mnemonic string + */ + public static CountryCode getCountryCode(String s) { + if (s == null || s.length() != 2) { + logger.warn("Please specify two character ISO 3166 country code"); + return USA; + } + + CountryCode code = UNKNOWN; + for (int i = 0; i < codes.length && code == UNKNOWN; i++) { + if (codes[i].code.equals(s)) { + code = codes[i]; + } + } + + return code; + } + + /** + * Creates an arbitrary country code with the specified value. Used + * when copying sheets, and the country code isn't initialized as part + * of the static data below + */ + public static CountryCode createArbitraryCode(int i) { + return new CountryCode(i); + } + + /** + * Accessor for the excel value + * + * @return the excel value + */ + public int getValue() { + return value; + } + + /** + * Accessor for the string + * + * @return the two character iso 3166 string + */ + public String getCode() { + return code; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DVParser.java b/datastructures-xslx/src/main/java/jxl/biff/DVParser.java new file mode 100755 index 0000000..1a777fa --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DVParser.java @@ -0,0 +1,906 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.text.DecimalFormat; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Iterator; +import jxl.WorkbookSettings; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.biff.formula.ParseContext; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Class which parses the binary data associated with Data Validity (DV) + * setting + */ +public class DVParser { + // The values + public static final DVType ANY = new DVType(0, "any"); + public static final DVType INTEGER = new DVType(1, "int"); + public static final DVType DECIMAL = new DVType(2, "dec"); + public static final DVType LIST = new DVType(3, "list"); + public static final DVType DATE = new DVType(4, "date"); + public static final DVType TIME = new DVType(5, "time"); + public static final DVType TEXT_LENGTH = new DVType(6, "strlen"); + public static final DVType FORMULA = new DVType(7, "form"); + // The error styles + public static final ErrorStyle STOP = new ErrorStyle(0); + public static final ErrorStyle WARNING = new ErrorStyle(1); + public static final ErrorStyle INFO = new ErrorStyle(2); + // The conditions + public static final Condition BETWEEN = new Condition(0, "{0} <= x <= {1}"); + public static final Condition NOT_BETWEEN = + new Condition(1, "!({0} <= x <= {1}"); + public static final Condition EQUAL = new Condition(2, "x == {0}"); + public static final Condition NOT_EQUAL = new Condition(3, "x != {0}"); + public static final Condition GREATER_THAN = new Condition(4, "x > {0}"); + public static final Condition LESS_THAN = new Condition(5, "x < {0}"); + public static final Condition GREATER_EQUAL = new Condition(6, "x >= {0}"); + public static final Condition LESS_EQUAL = new Condition(7, "x <= {0}"); + // The masks + private static final int STRING_LIST_GIVEN_MASK = 0x80; + private static final int EMPTY_CELLS_ALLOWED_MASK = 0x100; + private static final int SUPPRESS_ARROW_MASK = 0x200; + private static final int SHOW_PROMPT_MASK = 0x40000; + private static final int SHOW_ERROR_MASK = 0x80000; + // The maximum string length for a data validation list + private static final int MAX_VALIDATION_LIST_LENGTH = 254; + // The maximum number of rows and columns + private static final int MAX_ROWS = 0xffff; + private static final int MAX_COLUMNS = 0xff; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DVParser.class); + // The decimal format + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.#"); + /** + * The type + */ + private final DVType type; + /** + * The error style + */ + private final ErrorStyle errorStyle; + /** + * The condition + */ + private final Condition condition; + /** + * String list option + */ + private final boolean stringListGiven; + /** + * Empty cells allowed + */ + private final boolean emptyCellsAllowed; + /** + * Suppress arrow + */ + private final boolean suppressArrow; + /** + * Show prompt + */ + private final boolean showPrompt; + /** + * Show error + */ + private final boolean showError; + /** + * The title of the prompt box + */ + private String promptTitle; + /** + * The title of the error box + */ + private String errorTitle; + /** + * The text of the prompt box + */ + private String promptText; + /** + * The text of the error box + */ + private String errorText; + /** + * The first formula + */ + private FormulaParser formula1; + /** + * The first formula string + */ + private String formula1String; + /** + * The second formula + */ + private FormulaParser formula2; + /** + * The second formula string + */ + private String formula2String; + /** + * The column number of the cell at the top left of the range + */ + private int column1; + /** + * The row number of the cell at the top left of the range + */ + private int row1; + /** + * The column index of the cell at the bottom right + */ + private int column2; + /** + * The row index of the cell at the bottom right + */ + private int row2; + /** + * Flag to indicate that this DV Parser is shared amongst a group + * of cells + */ + private boolean extendedCellsValidation; + /** + * Flag indicated whether this has been copied + */ + private final boolean copied; + + /** + * Constructor + */ + public DVParser(byte[] data, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) { + Assert.verify(nt != null); + + copied = false; + int options = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); + + int typeVal = options & 0xf; + type = DVType.getType(typeVal); + + int errorStyleVal = (options & 0x70) >> 4; + errorStyle = ErrorStyle.getErrorStyle(errorStyleVal); + + int conditionVal = (options & 0xf00000) >> 20; + condition = Condition.getCondition(conditionVal); + + stringListGiven = (options & STRING_LIST_GIVEN_MASK) != 0; + emptyCellsAllowed = (options & EMPTY_CELLS_ALLOWED_MASK) != 0; + suppressArrow = (options & SUPPRESS_ARROW_MASK) != 0; + showPrompt = (options & SHOW_PROMPT_MASK) != 0; + showError = (options & SHOW_ERROR_MASK) != 0; + + int pos = 4; + int length = IntegerHelper.getInt(data[pos], data[pos + 1]); + if (length > 0 && data[pos + 2] == 0) { + promptTitle = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } else if (length > 0) { + promptTitle = StringHelper.getUnicodeString(data, length, pos + 3); + pos += length * 2 + 3; + } else { + pos += 3; + } + + length = IntegerHelper.getInt(data[pos], data[pos + 1]); + if (length > 0 && data[pos + 2] == 0) { + errorTitle = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } else if (length > 0) { + errorTitle = StringHelper.getUnicodeString(data, length, pos + 3); + pos += length * 2 + 3; + } else { + pos += 3; + } + + length = IntegerHelper.getInt(data[pos], data[pos + 1]); + if (length > 0 && data[pos + 2] == 0) { + promptText = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } else if (length > 0) { + promptText = StringHelper.getUnicodeString(data, length, pos + 3); + pos += length * 2 + 3; + } else { + pos += 3; + } + + length = IntegerHelper.getInt(data[pos], data[pos + 1]); + if (length > 0 && data[pos + 2] == 0) { + errorText = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } else if (length > 0) { + errorText = StringHelper.getUnicodeString(data, length, pos + 3); + pos += length * 2 + 3; + } else { + pos += 3; + } + + int formula1Length = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 4; + int formula1Pos = pos; + pos += formula1Length; + + int formula2Length = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 4; + int formula2Pos = pos; + pos += formula2Length; + + pos += 2; + + row1 = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + + row2 = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + + column1 = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + + column2 = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + + extendedCellsValidation = row1 != row2 || column1 != column2; + + // Do the formulas + try { + // First, create a temporary blank cell for any formula relative + // references + EmptyCell tmprt = new EmptyCell(column1, row1); + + if (formula1Length != 0) { + byte[] tokens = new byte[formula1Length]; + System.arraycopy(data, formula1Pos, tokens, 0, formula1Length); + formula1 = new FormulaParser(tokens, tmprt, es, nt, ws, + ParseContext.DATA_VALIDATION); + formula1.parse(); + } + + if (formula2Length != 0) { + byte[] tokens = new byte[formula2Length]; + System.arraycopy(data, formula2Pos, tokens, 0, formula2Length); + formula2 = new FormulaParser(tokens, tmprt, es, nt, ws, + ParseContext.DATA_VALIDATION); + formula2.parse(); + } + } catch (FormulaException e) { + logger.warn(e.getMessage() + " for cells " + + CellReferenceHelper.getCellReference(column1, row1) + "-" + + CellReferenceHelper.getCellReference(column2, row2)); + } + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(Collection strings) { + copied = false; + type = LIST; + errorStyle = STOP; + condition = BETWEEN; + extendedCellsValidation = false; + + // the options + stringListGiven = true; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + if (strings.size() == 0) { + logger.warn("no validation strings - ignoring"); + } + + Iterator i = strings.iterator(); + StringBuffer formulaString = new StringBuffer(); + + formulaString.append(i.next().toString()); + while (i.hasNext()) { + formulaString.append('\0'); + formulaString.append(' '); + formulaString.append(i.next().toString()); + } + + // If the formula string exceeds + // the maximum validation list length, then truncate and stop there + if (formulaString.length() > MAX_VALIDATION_LIST_LENGTH) { + logger.warn("Validation list exceeds maximum number of characters - " + + "truncating"); + formulaString.delete(MAX_VALIDATION_LIST_LENGTH, + formulaString.length()); + } + + // Put the string in quotes + formulaString.insert(0, '\"'); + formulaString.append('\"'); + formula1String = formulaString.toString(); + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(String namedRange) { + // Handle the case for an empty string + if (namedRange.length() == 0) { + copied = false; + type = FORMULA; + errorStyle = STOP; + condition = EQUAL; + extendedCellsValidation = false; + // the options + stringListGiven = false; + emptyCellsAllowed = false; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + formula1String = "\"\""; + return; + } + + copied = false; + type = LIST; + errorStyle = STOP; + condition = BETWEEN; + extendedCellsValidation = false; + + // the options + stringListGiven = false; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + formula1String = namedRange; + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(int c1, int r1, int c2, int r2) { + copied = false; + type = LIST; + errorStyle = STOP; + condition = BETWEEN; + extendedCellsValidation = false; + + // the options + stringListGiven = false; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + StringBuffer formulaString = new StringBuffer(); + CellReferenceHelper.getCellReference(c1, r1, formulaString); + formulaString.append(':'); + CellReferenceHelper.getCellReference(c2, r2, formulaString); + formula1String = formulaString.toString(); + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(double val1, double val2, Condition c) { + copied = false; + type = DECIMAL; + errorStyle = STOP; + condition = c; + extendedCellsValidation = false; + + // the options + stringListGiven = false; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + formula1String = DECIMAL_FORMAT.format(val1); + + if (!Double.isNaN(val2)) { + formula2String = DECIMAL_FORMAT.format(val2); + } + } + + /** + * Constructor called when doing a cell deep copy + */ + public DVParser(DVParser copy) { + copied = true; + type = copy.type; + errorStyle = copy.errorStyle; + condition = copy.condition; + stringListGiven = copy.stringListGiven; + emptyCellsAllowed = copy.emptyCellsAllowed; + suppressArrow = copy.suppressArrow; + showPrompt = copy.showPrompt; + showError = copy.showError; + promptTitle = copy.promptTitle; + promptText = copy.promptText; + errorTitle = copy.errorTitle; + errorText = copy.errorText; + extendedCellsValidation = copy.extendedCellsValidation; + + row1 = copy.row1; + row2 = copy.row2; + column1 = copy.column1; + column2 = copy.column2; + + // Don't copy the formula parsers - just take their string equivalents + if (copy.formula1String != null) { + formula1String = copy.formula1String; + formula2String = copy.formula2String; + } else { + try { + formula1String = copy.formula1.getFormula(); + formula2String = (copy.formula2 != null) ? + copy.formula2.getFormula() : null; + } catch (FormulaException e) { + logger.warn("Cannot parse validation formula: " + e.getMessage()); + } + } + // Don't copy the cell references - these will be added later + } + + /** + * Gets the data + */ + public byte[] getData() { + // Compute the length of the data + byte[] f1Bytes = formula1 != null ? formula1.getBytes() : new byte[0]; + byte[] f2Bytes = formula2 != null ? formula2.getBytes() : new byte[0]; + int dataLength = + 4 + // the options + promptTitle.length() * 2 + 3 + // the prompt title + errorTitle.length() * 2 + 3 + // the error title + promptText.length() * 2 + 3 + // the prompt text + errorText.length() * 2 + 3 + // the error text + f1Bytes.length + 2 + // first formula + f2Bytes.length + 2 + // second formula + +4 + // unused bytes + 10; // cell range + + byte[] data = new byte[dataLength]; + + // The position + int pos = 0; + + // The options + int options = 0; + options |= type.getValue(); + options |= errorStyle.getValue() << 4; + options |= condition.getValue() << 20; + + if (stringListGiven) { + options |= STRING_LIST_GIVEN_MASK; + } + + if (emptyCellsAllowed) { + options |= EMPTY_CELLS_ALLOWED_MASK; + } + + if (suppressArrow) { + options |= SUPPRESS_ARROW_MASK; + } + + if (showPrompt) { + options |= SHOW_PROMPT_MASK; + } + + if (showError) { + options |= SHOW_ERROR_MASK; + } + + // The text + IntegerHelper.getFourBytes(options, data, pos); + pos += 4; + + IntegerHelper.getTwoBytes(promptTitle.length(), data, pos); + pos += 2; + + data[pos] = (byte) 0x1; // unicode indicator + pos++; + + StringHelper.getUnicodeBytes(promptTitle, data, pos); + pos += promptTitle.length() * 2; + + IntegerHelper.getTwoBytes(errorTitle.length(), data, pos); + pos += 2; + + data[pos] = (byte) 0x1; // unicode indicator + pos++; + + StringHelper.getUnicodeBytes(errorTitle, data, pos); + pos += errorTitle.length() * 2; + + IntegerHelper.getTwoBytes(promptText.length(), data, pos); + pos += 2; + + data[pos] = (byte) 0x1; // unicode indicator + pos++; + + StringHelper.getUnicodeBytes(promptText, data, pos); + pos += promptText.length() * 2; + + IntegerHelper.getTwoBytes(errorText.length(), data, pos); + pos += 2; + + data[pos] = (byte) 0x1; // unicode indicator + pos++; + + StringHelper.getUnicodeBytes(errorText, data, pos); + pos += errorText.length() * 2; + + // Formula 1 + IntegerHelper.getTwoBytes(f1Bytes.length, data, pos); + pos += 4; + + System.arraycopy(f1Bytes, 0, data, pos, f1Bytes.length); + pos += f1Bytes.length; + + // Formula 2 + IntegerHelper.getTwoBytes(f2Bytes.length, data, pos); + pos += 4; + + System.arraycopy(f2Bytes, 0, data, pos, f2Bytes.length); + pos += f2Bytes.length; + + // The cell ranges + IntegerHelper.getTwoBytes(1, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(row1, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(row2, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(column1, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(column2, data, pos); + pos += 2; + + return data; + } + + /** + * Inserts a row + * + * @param row the row to insert + */ + public void insertRow(int row) { + if (formula1 != null) { + formula1.rowInserted(0, row, true); + } + + if (formula2 != null) { + formula2.rowInserted(0, row, true); + } + + if (row1 >= row) { + row1++; + } + + if (row2 >= row && row2 != MAX_ROWS) { + row2++; + } + } + + /** + * Inserts a column + * + * @param col the column to insert + */ + public void insertColumn(int col) { + if (formula1 != null) { + formula1.columnInserted(0, col, true); + } + + if (formula2 != null) { + formula2.columnInserted(0, col, true); + } + + if (column1 >= col) { + column1++; + } + + if (column2 >= col && column2 != MAX_COLUMNS) { + column2++; + } + } + + /** + * Removes a row + * + * @param row the row to insert + */ + public void removeRow(int row) { + if (formula1 != null) { + formula1.rowRemoved(0, row, true); + } + + if (formula2 != null) { + formula2.rowRemoved(0, row, true); + } + + if (row1 > row) { + row1--; + } + + if (row2 >= row) { + row2--; + } + } + + /** + * Removes a column + * + * @param col the row to remove + */ + public void removeColumn(int col) { + if (formula1 != null) { + formula1.columnRemoved(0, col, true); + } + + if (formula2 != null) { + formula2.columnRemoved(0, col, true); + } + + if (column1 > col) { + column1--; + } + + if (column2 >= col && column2 != MAX_COLUMNS) { + column2--; + } + } + + /** + * Accessor for first column + * + * @return the first column + */ + public int getFirstColumn() { + return column1; + } + + /** + * Accessor for the last column + * + * @return the last column + */ + public int getLastColumn() { + return column2; + } + + /** + * Accessor for first row + * + * @return the first row + */ + public int getFirstRow() { + return row1; + } + + /** + * Accessor for the last row + * + * @return the last row + */ + public int getLastRow() { + return row2; + } + + /** + * Gets the formula present in the validation + * + * @return the validation formula as a string + * @throws FormulaException + */ + String getValidationFormula() throws FormulaException { + if (type == LIST) { + return formula1.getFormula(); + } + + String s1 = formula1.getFormula(); + String s2 = formula2 != null ? formula2.getFormula() : null; + return condition.getConditionString(s1, s2) + + "; x " + type.getDescription(); + } + + /** + * Called by the cell value when the cell features are added to the sheet + */ + public void setCell(int col, + int row, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) throws FormulaException { + // If this is part of an extended cells validation, then do nothing + // as this will already have been called and parsed when the top left + // cell was added + if (extendedCellsValidation) { + return; + } + + row1 = row; + row2 = row; + column1 = col; + column2 = col; + + formula1 = new FormulaParser(formula1String, + es, nt, ws, + ParseContext.DATA_VALIDATION); + formula1.parse(); + + if (formula2String != null) { + formula2 = new FormulaParser(formula2String, + es, nt, ws, + ParseContext.DATA_VALIDATION); + formula2.parse(); + } + } + + /** + * Indicates that the data validation extends across several more cells + * + * @param cols - the number of extra columns + * @param rows - the number of extra rows + */ + public void extendCellValidation(int cols, int rows) { + row2 = row1 + rows; + column2 = column1 + cols; + extendedCellsValidation = true; + } + + /** + * Accessor which indicates whether this validation applies across + * multiple cels + */ + public boolean extendedCellsValidation() { + return extendedCellsValidation; + } + + public boolean copied() { + return copied; + } + + // DV Type + public static class DVType { + private static DVType[] types = new DVType[0]; + private final int value; + private final String desc; + + DVType(int v, String d) { + value = v; + desc = d; + DVType[] oldtypes = types; + types = new DVType[oldtypes.length + 1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + static DVType getType(int v) { + DVType found = null; + for (int i = 0; i < types.length && found == null; i++) { + if (types[i].value == v) { + found = types[i]; + } + } + return found; + } + + public int getValue() { + return value; + } + + public String getDescription() { + return desc; + } + } + + // Error Style + public static class ErrorStyle { + private static ErrorStyle[] types = new ErrorStyle[0]; + private final int value; + + ErrorStyle(int v) { + value = v; + ErrorStyle[] oldtypes = types; + types = new ErrorStyle[oldtypes.length + 1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + static ErrorStyle getErrorStyle(int v) { + ErrorStyle found = null; + for (int i = 0; i < types.length && found == null; i++) { + if (types[i].value == v) { + found = types[i]; + } + } + return found; + } + + public int getValue() { + return value; + } + } + + // Conditions + public static class Condition { + private static Condition[] types = new Condition[0]; + private final int value; + private final MessageFormat format; + + Condition(int v, String pattern) { + value = v; + format = new MessageFormat(pattern); + Condition[] oldtypes = types; + types = new Condition[oldtypes.length + 1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + static Condition getCondition(int v) { + Condition found = null; + for (int i = 0; i < types.length && found == null; i++) { + if (types[i].value == v) { + found = types[i]; + } + } + return found; + } + + public int getValue() { + return value; + } + + public String getConditionString(String s1, String s2) { + return format.format(new String[]{s1, s2}); + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DValParser.java b/datastructures-xslx/src/main/java/jxl/biff/DValParser.java new file mode 100755 index 0000000..8d8dd24 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DValParser.java @@ -0,0 +1,149 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; + +/** + * Class which parses the binary data associated with Data Validity (DVal) + * setting + */ +public class DValParser { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DValParser.class); + + // The option masks + private static final int PROMPT_BOX_VISIBLE_MASK = 0x1; + private static final int PROMPT_BOX_AT_CELL_MASK = 0x2; + private static final int VALIDITY_DATA_CACHED_MASK = 0x4; + + /** + * Prompt box visible + */ + private boolean promptBoxVisible; + + /** + * Empty cells allowed + */ + private boolean promptBoxAtCell; + + /** + * Cell validity data cached in following DV records + */ + private final boolean validityDataCached; + + /** + * The number of following DV records + */ + private int numDVRecords; + + /** + * The object id of the associated down arrow + */ + private final int objectId; + + /** + * Constructor + */ + public DValParser(byte[] data) { + int options = IntegerHelper.getInt(data[0], data[1]); + + promptBoxVisible = (options & PROMPT_BOX_VISIBLE_MASK) != 0; + promptBoxAtCell = (options & PROMPT_BOX_AT_CELL_MASK) != 0; + validityDataCached = (options & VALIDITY_DATA_CACHED_MASK) != 0; + + objectId = IntegerHelper.getInt(data[10], data[11], data[12], data[13]); + numDVRecords = IntegerHelper.getInt(data[14], data[15], + data[16], data[17]); + } + + /** + * Constructor + */ + public DValParser(int objid, int num) { + objectId = objid; + numDVRecords = num; + validityDataCached = true; + } + + /** + * Gets the data + */ + public byte[] getData() { + byte[] data = new byte[18]; + + int options = 0; + + if (promptBoxVisible) { + options |= PROMPT_BOX_VISIBLE_MASK; + } + + if (promptBoxAtCell) { + options |= PROMPT_BOX_AT_CELL_MASK; + } + + if (validityDataCached) { + options |= VALIDITY_DATA_CACHED_MASK; + } + + IntegerHelper.getTwoBytes(options, data, 0); + + IntegerHelper.getFourBytes(objectId, data, 10); + + IntegerHelper.getFourBytes(numDVRecords, data, 14); + + return data; + } + + /** + * Called when a remove row or column results in one of DV records being + * removed + */ + public void dvRemoved() { + numDVRecords--; + } + + /** + * Accessor for the number of DV records + * + * @return the number of DV records for this list + */ + public int getNumberOfDVRecords() { + return numDVRecords; + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public int getObjectId() { + return objectId; + } + + /** + * Called when adding a DV record on a copied DVal + */ + public void dvAdded() { + numDVRecords++; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DataValidation.java b/datastructures-xslx/src/main/java/jxl/biff/DataValidation.java new file mode 100755 index 0000000..f03a329 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DataValidation.java @@ -0,0 +1,306 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.WorkbookSettings; +import jxl.biff.formula.ExternalSheet; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.biff.File; + + +/** + * Class which encapsulates a data validation. This encapsulates the + * base DVAL record (DataValidityListRecord) and all the individual DV + * (DataValiditySettingsRecord) records + */ +public class DataValidation { + public static final int DEFAULT_OBJECT_ID = 0xffffffff; + private static final int MAX_NO_OF_VALIDITY_SETTINGS = 0xfffd; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DataValidation.class); + /** + * The data validity list + */ + private DataValidityListRecord validityList; + /** + * The list of data validity (DV) records + */ + private ArrayList validitySettings; + /** + * Handle to the workbook + */ + private WorkbookMethods workbook; + /** + * Handle to the external sheet + */ + private ExternalSheet externalSheet; + /** + * Handle to the workbook settings + */ + private WorkbookSettings workbookSettings; + /** + * The object id of the combo box used for drop downs + */ + private int comboBoxObjectId; + /** + * Indicates whether this was copied + */ + private final boolean copied; + + /** + * Constructor + */ + public DataValidation(DataValidityListRecord dvlr) { + validityList = dvlr; + validitySettings = new ArrayList(validityList.getNumberOfSettings()); + copied = false; + } + + /** + * Constructor used to create writable data validations + */ + public DataValidation(int objId, + ExternalSheet es, + WorkbookMethods wm, + WorkbookSettings ws) { + workbook = wm; + externalSheet = es; + workbookSettings = ws; + validitySettings = new ArrayList(); + comboBoxObjectId = objId; + copied = false; + } + + /** + * Copy constructor used to copy from read to write + */ + public DataValidation(DataValidation dv, + ExternalSheet es, + WorkbookMethods wm, + WorkbookSettings ws) { + workbook = wm; + externalSheet = es; + workbookSettings = ws; + copied = true; + validityList = new DataValidityListRecord(dv.getDataValidityList()); + + validitySettings = new ArrayList(); + DataValiditySettingsRecord[] settings = dv.getDataValiditySettings(); + + for (int i = 0; i < settings.length; i++) { + validitySettings.add(new DataValiditySettingsRecord(settings[i], + externalSheet, + workbook, + workbookSettings)); + } + } + + /** + * Adds a new settings object to this data validation + */ + public void add(DataValiditySettingsRecord dvsr) { + validitySettings.add(dvsr); + dvsr.setDataValidation(this); + + if (copied) { + // adding a writable dv record to a copied validity list + Assert.verify(validityList != null); + validityList.dvAdded(); + } + } + + /** + * Accessor for the validity list. Used when copying sheets + */ + public DataValidityListRecord getDataValidityList() { + return validityList; + } + + /** + * Accessor for the validity settings. Used when copying sheets + */ + public DataValiditySettingsRecord[] getDataValiditySettings() { + DataValiditySettingsRecord[] dvlr = new DataValiditySettingsRecord[0]; + return (DataValiditySettingsRecord[]) validitySettings.toArray(dvlr); + } + + /** + * Writes out the data validation + * + * @param outputFile the output file + * @throws IOException + */ + public void write(File outputFile) throws IOException { + if (validitySettings.size() > MAX_NO_OF_VALIDITY_SETTINGS) { + logger.warn("Maximum number of data validations exceeded - " + + "truncating..."); + validitySettings = new ArrayList + (validitySettings.subList(0, MAX_NO_OF_VALIDITY_SETTINGS - 1)); + Assert.verify(validitySettings.size() <= MAX_NO_OF_VALIDITY_SETTINGS); + } + + if (validityList == null) { + DValParser dvp = new DValParser(comboBoxObjectId, + validitySettings.size()); + validityList = new DataValidityListRecord(dvp); + } + + if (!validityList.hasDVRecords()) { + return; + } + + outputFile.write(validityList); + + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dvsr = (DataValiditySettingsRecord) i.next(); + outputFile.write(dvsr); + } + } + + /** + * Inserts a row + * + * @param row the inserted row + */ + public void insertRow(int row) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + dv.insertRow(row); + } + } + + /** + * Removes row + * + * @param row the row to be removed + */ + public void removeRow(int row) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstRow() == row && dv.getLastRow() == row) { + i.remove(); + validityList.dvRemoved(); + } else { + dv.removeRow(row); + } + } + } + + /** + * Inserts a column + * + * @param col the inserted column + */ + public void insertColumn(int col) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + dv.insertColumn(col); + } + } + + /** + * Removes a column + * + * @param col the inserted column + */ + public void removeColumn(int col) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstColumn() == col && dv.getLastColumn() == col) { + i.remove(); + validityList.dvRemoved(); + } else { + dv.removeColumn(col); + } + } + } + + /** + * Removes the data validation for a specific cell + * + * @param col the column + * @param row the row + */ + public void removeDataValidation(int col, int row) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstColumn() == col && dv.getLastColumn() == col && + dv.getFirstRow() == row && dv.getLastRow() == row) { + i.remove(); + validityList.dvRemoved(); + break; + } + } + } + + /** + * Removes the data validation for a specific cell + * + * @param col1 the first column + * @param row1 the first row + */ + public void removeSharedDataValidation(int col1, int row1, + int col2, int row2) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstColumn() == col1 && dv.getLastColumn() == col2 && + dv.getFirstRow() == row1 && dv.getLastRow() == row2) { + i.remove(); + validityList.dvRemoved(); + break; + } + } + } + + /** + * Used during the copy process to retrieve the validity settings for + * a particular cell + */ + public DataValiditySettingsRecord getDataValiditySettings(int col, int row) { + boolean found = false; + DataValiditySettingsRecord foundRecord = null; + for (Iterator i = validitySettings.iterator(); i.hasNext() && !found; ) { + DataValiditySettingsRecord dvsr = (DataValiditySettingsRecord) i.next(); + if (dvsr.getFirstColumn() == col && dvsr.getFirstRow() == row) { + found = true; + foundRecord = dvsr; + } + } + + return foundRecord; + } + + /** + * Accessor for the combo box, used when copying sheets + */ + public int getComboBoxObjectId() { + return comboBoxObjectId; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DataValidityListRecord.java b/datastructures-xslx/src/main/java/jxl/biff/DataValidityListRecord.java new file mode 100755 index 0000000..2c41f7b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DataValidityListRecord.java @@ -0,0 +1,151 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Record containing the list of data validation settings for a given sheet + */ +public class DataValidityListRecord extends WritableRecordData { + private static final Logger logger = Logger.getLogger + (DataValidityListRecord.class); + + /** + * The number of settings records associated with this list + */ + private int numSettings; + + /** + * The object id of the associated down arrow + */ + private int objectId; + + /** + * The dval parser + */ + private DValParser dvalParser; + + /** + * The data + */ + private byte[] data; + + /** + * Constructor + */ + public DataValidityListRecord(Record t) { + super(t); + + data = getRecord().getData(); + objectId = IntegerHelper.getInt(data[10], data[11], data[12], data[13]); + numSettings = IntegerHelper.getInt(data[14], data[15], data[16], data[17]); + } + + /** + * Constructor called when generating a data validity list from the API + */ + public DataValidityListRecord(DValParser dval) { + super(Type.DVAL); + + dvalParser = dval; + } + + /** + * Copy constructor + * + * @param dvlr the record copied from a read only sheet + */ + DataValidityListRecord(DataValidityListRecord dvlr) { + super(Type.DVAL); + + data = dvlr.getData(); + } + + /** + * Accessor for the number of settings records associated with this list + */ + int getNumberOfSettings() { + return numSettings; + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + if (dvalParser == null) { + return data; + } + + return dvalParser.getData(); + } + + /** + * Called when a remove row or column results in one of DV records being + * removed + */ + void dvRemoved() { + if (dvalParser == null) { + dvalParser = new DValParser(data); + } + + dvalParser.dvRemoved(); + } + + /** + * Called when a writable DV record is added to a copied validity list + */ + void dvAdded() { + if (dvalParser == null) { + dvalParser = new DValParser(data); + } + + dvalParser.dvAdded(); + } + + /** + * Accessor for the number of DV records + * + * @return the number of DV records for this list + */ + public boolean hasDVRecords() { + if (dvalParser == null) { + return true; + } + + return dvalParser.getNumberOfDVRecords() > 0; + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public int getObjectId() { + if (dvalParser == null) { + return objectId; + } + + return dvalParser.getObjectId(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DataValiditySettingsRecord.java b/datastructures-xslx/src/main/java/jxl/biff/DataValiditySettingsRecord.java new file mode 100755 index 0000000..9eeae31 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DataValiditySettingsRecord.java @@ -0,0 +1,282 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.WorkbookSettings; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Data validity settings. Contains an individual Data validation (DV). + * All the computationa work is delegated to the DVParser object + */ +public class DataValiditySettingsRecord extends WritableRecordData { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(DataValiditySettingsRecord.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The reader + */ + private DVParser dvParser; + + /** + * Handle to the workbook + */ + private WorkbookMethods workbook; + + /** + * Handle to the externalSheet + */ + private ExternalSheet externalSheet; + + /** + * Handle to the workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Handle to the data validation record + */ + private DataValidation dataValidation; + + /** + * Constructor + */ + public DataValiditySettingsRecord(Record t, + ExternalSheet es, + WorkbookMethods wm, + WorkbookSettings ws) { + super(t); + + data = t.getData(); + externalSheet = es; + workbook = wm; + workbookSettings = ws; + } + + /** + * Copy constructor + */ + DataValiditySettingsRecord(DataValiditySettingsRecord dvsr) { + super(Type.DV); + + data = dvsr.getData(); + } + + /** + * Constructor used when copying sheets + * + * @param dvsr the record copied from a writable sheet + */ + DataValiditySettingsRecord(DataValiditySettingsRecord dvsr, + ExternalSheet es, + WorkbookMethods w, + WorkbookSettings ws) { + super(Type.DV); + + workbook = w; + externalSheet = es; + workbookSettings = ws; + + Assert.verify(w != null); + Assert.verify(es != null); + + data = new byte[dvsr.data.length]; + System.arraycopy(dvsr.data, 0, data, 0, data.length); + } + + /** + * Constructor called when the API creates a writable data validation + * + * @param dvsr the record copied from a writable sheet + */ + public DataValiditySettingsRecord(DVParser dvp) { + super(Type.DV); + dvParser = dvp; + } + + /** + * Initializes the dvParser + */ + private void initialize() { + if (dvParser == null) { + dvParser = new DVParser(data, externalSheet, + workbook, workbookSettings); + } + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + if (dvParser == null) { + return data; + } + + return dvParser.getData(); + } + + /** + * Inserts a row + * + * @param row the row to insert + */ + public void insertRow(int row) { + if (dvParser == null) { + initialize(); + } + + dvParser.insertRow(row); + } + + /** + * Removes a row + * + * @param row the row to insert + */ + public void removeRow(int row) { + if (dvParser == null) { + initialize(); + } + + dvParser.removeRow(row); + } + + /** + * Inserts a row + * + * @param col the row to insert + */ + public void insertColumn(int col) { + if (dvParser == null) { + initialize(); + } + + dvParser.insertColumn(col); + } + + /** + * Removes a column + * + * @param col the row to insert + */ + public void removeColumn(int col) { + if (dvParser == null) { + initialize(); + } + + dvParser.removeColumn(col); + } + + /** + * Accessor for first column + * + * @return the first column + */ + public int getFirstColumn() { + if (dvParser == null) { + initialize(); + } + + return dvParser.getFirstColumn(); + } + + /** + * Accessor for the last column + * + * @return the last column + */ + public int getLastColumn() { + if (dvParser == null) { + initialize(); + } + + return dvParser.getLastColumn(); + } + + /** + * Accessor for first row + * + * @return the first row + */ + public int getFirstRow() { + if (dvParser == null) { + initialize(); + } + + return dvParser.getFirstRow(); + } + + /** + * Accessor for the last row + * + * @return the last row + */ + public int getLastRow() { + if (dvParser == null) { + initialize(); + } + + return dvParser.getLastRow(); + } + + /** + * Sets the handle to the data validation record + * + * @param dv the data validation + */ + void setDataValidation(DataValidation dv) { + dataValidation = dv; + } + + /** + * Gets the DVParser. This is used when doing a deep copy of cells + * on the writable side of things + */ + DVParser getDVParser() { + return dvParser; + } + + public String getValidationFormula() { + try { + if (dvParser == null) { + initialize(); + } + + return dvParser.getValidationFormula(); + } catch (FormulaException e) { + logger.warn("Cannot read drop down range " + e.getMessage()); + return ""; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DisplayFormat.java b/datastructures-xslx/src/main/java/jxl/biff/DisplayFormat.java new file mode 100755 index 0000000..e2782ad --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DisplayFormat.java @@ -0,0 +1,55 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * The interface implemented by the various number and date format styles. + * The methods on this interface are called internally when generating a + * spreadsheet + */ +public interface DisplayFormat { + /** + * Accessor for the index style of this format + * + * @return the index for this format + */ + int getFormatIndex(); + + /** + * Accessor to see whether this format has been initialized + * + * @return TRUE if initialized, FALSE otherwise + */ + boolean isInitialized(); + + /** + * Initializes this format with the specified index number + * + * @param pos the position of this format record in the workbook + */ + void initialize(int pos); + + /** + * Accessor to determine whether or not this format is built in + * + * @return TRUE if this format is a built in format, FALSE otherwise + */ + boolean isBuiltIn(); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DoubleHelper.java b/datastructures-xslx/src/main/java/jxl/biff/DoubleHelper.java new file mode 100755 index 0000000..249d94d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DoubleHelper.java @@ -0,0 +1,84 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Class to help handle doubles + */ +public class DoubleHelper { + /** + * Private constructor to prevent instantiation + */ + private DoubleHelper() { + } + + /** + * Gets the IEEE value from the byte array passed in + * + * @param pos the position in the data block which contains the double value + * @param data the data block containing the raw bytes + * @return the double value converted from the raw data + */ + public static double getIEEEDouble(byte[] data, int pos) { + int num1 = IntegerHelper.getInt(data[pos], data[pos + 1], + data[pos + 2], data[pos + 3]); + int num2 = IntegerHelper.getInt(data[pos + 4], data[pos + 5], + data[pos + 6], data[pos + 7]); + + // Long.parseLong doesn't like the sign bit, so have to extract this + // information and put it in at the end. (Acknowledgment: thanks + // to Ruben for pointing this out) + boolean negative = ((num2 & 0x80000000) != 0); + + // Thanks to Lyle for the following improved IEEE double processing + long val = ((num2 & 0x7fffffff) * 0x100000000L) + + (num1 < 0 ? 0x100000000L + num1 : num1); + double value = Double.longBitsToDouble(val); + + if (negative) { + value = -value; + } + return value; + } + + /** + * Puts the IEEE representation of the double provided into the array + * at the designated position + * + * @param target the data block into which the binary representation is to + * be placed + * @param pos the position in target in which to place the bytes + * @param d the double value to convert to raw bytes + */ + public static void getIEEEBytes(double d, byte[] target, int pos) { + long val = Double.doubleToLongBits(d); + target[pos] = (byte) (val & 0xff); + target[pos + 1] = (byte) ((val & 0xff00) >> 8); + target[pos + 2] = (byte) ((val & 0xff0000) >> 16); + target[pos + 3] = (byte) ((val & 0xff000000) >> 24); + target[pos + 4] = (byte) ((val & 0xff00000000L) >> 32); + target[pos + 5] = (byte) ((val & 0xff0000000000L) >> 40); + target[pos + 6] = (byte) ((val & 0xff000000000000L) >> 48); + target[pos + 7] = (byte) ((val & 0xff00000000000000L) >> 56); + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/EmptyCell.java b/datastructures-xslx/src/main/java/jxl/biff/EmptyCell.java new file mode 100755 index 0000000..dac8e3d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/EmptyCell.java @@ -0,0 +1,207 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.CellFeatures; +import jxl.CellType; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.VerticalAlignment; +import jxl.write.WritableCell; +import jxl.write.WritableCellFeatures; + +/** + * An empty cell. Represents an empty, as opposed to a blank cell + * in the workbook + */ +public class EmptyCell implements WritableCell { + /** + * The row of this empty cell + */ + private final int row; + /** + * The column number of this empty cell + */ + private final int col; + + /** + * Constructs an empty cell at the specified position + * + * @param c the zero based column + * @param r the zero based row + */ + public EmptyCell(int c, int r) { + row = r; + col = c; + } + + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + public int getRow() { + return row; + } + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + public int getColumn() { + return col; + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.EMPTY; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * + * @return an empty string + */ + public String getContents() { + return ""; + } + + /** + * Accessor for the format which is applied to this cell + * + * @return the format applied to this cell + */ + public CellFormat getCellFormat() { + return null; + } + + /** + * Dummy override + * + * @param cf dummy + */ + public void setCellFormat(CellFormat cf) { + } + + /** + * Dummy override + * + * @param cf dummy + * @deprecated + */ + public void setCellFormat(jxl.CellFormat cf) { + } + + /** + * Dummy override + * + * @param flag dummy + */ + public void setLocked(boolean flag) { + } + + /** + * Dummy override + * + * @param align dummy + */ + public void setAlignment(Alignment align) { + } + + /** + * Dummy override + * + * @param valign dummy + */ + public void setVerticalAlignment(VerticalAlignment valign) { + } + + /** + * Dummy override + * + * @param line dummy + * @param border dummy + */ + public void setBorder(Border border, BorderLineStyle line) { + } + + /** + * Indicates whether or not this cell is hidden, by virtue of either + * the entire row or column being collapsed + * + * @return TRUE if this cell is hidden, FALSE otherwise + */ + public boolean isHidden() { + return false; + } + + /** + * Dummy override + * + * @param flag the hidden flag + */ + public void setHidden(boolean flag) { + } + + /** + * Implementation of the deep copy function + * + * @param c the column which the new cell will occupy + * @param r the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int c, int r) { + return new EmptyCell(c, r); + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return null; + } + + /** + * Accessor for the cell features + */ + public void setCellFeatures(WritableCellFeatures wcf) { + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public WritableCellFeatures getWritableCellFeatures() { + return null; + } + +} + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/EncodedURLHelper.java b/datastructures-xslx/src/main/java/jxl/biff/EncodedURLHelper.java new file mode 100755 index 0000000..7ea12f4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/EncodedURLHelper.java @@ -0,0 +1,117 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.WorkbookSettings; +import jxl.common.Logger; + +/** + * Helper to get the Microsoft encoded URL from the given string + */ +public class EncodedURLHelper { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(EncodedURLHelper.class); + + // The control codes + private static final byte msDosDriveLetter = 0x01; + private static final byte sameDrive = 0x02; + private static final byte endOfSubdirectory = 0x03; + private static final byte parentDirectory = 0x04; + private static final byte unencodedUrl = 0x05; + + public static byte[] getEncodedURL(String s, WorkbookSettings ws) { + if (s.startsWith("http:")) { + return getURL(s, ws); + } else { + return getFile(s, ws); + } + } + + private static byte[] getFile(String s, WorkbookSettings ws) { + ByteArray byteArray = new ByteArray(); + + int pos = 0; + if (s.charAt(1) == ':') { + // we have a drive letter + byteArray.add(msDosDriveLetter); + byteArray.add((byte) s.charAt(0)); + pos = 2; + } else if (s.charAt(pos) == '\\' || + s.charAt(pos) == '/') { + byteArray.add(sameDrive); + } + + while (s.charAt(pos) == '\\' || + s.charAt(pos) == '/') { + pos++; + } + + while (pos < s.length()) { + int nextSepIndex1 = s.indexOf('/', pos); + int nextSepIndex2 = s.indexOf('\\', pos); + int nextSepIndex = 0; + String nextFileNameComponent = null; + + if (nextSepIndex1 != -1 && nextSepIndex2 != -1) { + // choose the smallest (ie. nearest) separator + nextSepIndex = Math.min(nextSepIndex1, nextSepIndex2); + } else if (nextSepIndex1 == -1 || nextSepIndex2 == -1) { + // chose the maximum separator + nextSepIndex = Math.max(nextSepIndex1, nextSepIndex2); + } + + if (nextSepIndex == -1) { + // no more separators + nextFileNameComponent = s.substring(pos); + pos = s.length(); + } else { + nextFileNameComponent = s.substring(pos, nextSepIndex); + pos = nextSepIndex + 1; + } + + if (nextFileNameComponent.equals(".")) { + // current directory - do nothing + } else if (nextFileNameComponent.equals("..")) { + // parent directory + byteArray.add(parentDirectory); + } else { + // add the filename component + byteArray.add(StringHelper.getBytes(nextFileNameComponent, + ws)); + } + + if (pos < s.length()) { + byteArray.add(endOfSubdirectory); + } + } + + return byteArray.getBytes(); + } + + private static byte[] getURL(String s, WorkbookSettings ws) { + ByteArray byteArray = new ByteArray(); + byteArray.add(unencodedUrl); + byteArray.add((byte) s.length()); + byteArray.add(StringHelper.getBytes(s, ws)); + return byteArray.getBytes(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/FilterModeRecord.java b/datastructures-xslx/src/main/java/jxl/biff/FilterModeRecord.java new file mode 100755 index 0000000..89ddcb3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FilterModeRecord.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class FilterModeRecord extends WritableRecordData { + // The logger + private static final Logger logger = Logger.getLogger(FilterModeRecord.class); + + /** + * The data + */ + private final byte[] data; + + + /** + * Constructor + */ + public FilterModeRecord(Record t) { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/FontRecord.java b/datastructures-xslx/src/main/java/jxl/biff/FontRecord.java new file mode 100755 index 0000000..4d22bd2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FontRecord.java @@ -0,0 +1,501 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.WorkbookSettings; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.Colour; +import jxl.format.Font; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; +import jxl.read.biff.Record; + +/** + * A record containing the necessary data for the font information + */ +public class FontRecord extends WritableRecordData implements Font { + public static final Biff7 biff7 = new Biff7(); + /** + * The conversion factor between microsoft internal units and point size + */ + private static final int EXCEL_UNITS_PER_POINT = 20; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FontRecord.class); + /** + * The point height of this font + */ + private int pointHeight; + /** + * The index into the colour palette + */ + private int colourIndex; + /** + * The bold weight for this font (normal or bold) + */ + private int boldWeight; + /** + * The style of the script (italic or normal) + */ + private int scriptStyle; + /** + * The underline style for this font (none, single, double etc) + */ + private int underlineStyle; + /** + * The font family + */ + private byte fontFamily; + /** + * The character set + */ + private byte characterSet; + /** + * Indicates whether or not this font is italic + */ + private boolean italic; + /** + * Indicates whether or not this font is struck out + */ + private boolean struckout; + /** + * The name of this font + */ + private final String name; + /** + * Flag to indicate whether the derived data (such as the font index) has + * been initialized or not + */ + private boolean initialized; + + /** + * The index of this font in the font list + */ + private int fontIndex; + + /** + * Constructor, used when creating a new font for writing out. + * + * @param bold the bold indicator + * @param ps the point size + * @param us the underline style + * @param fn the name + * @param it italicised indicator + * @param ss the script style + * @param ci the colour index + */ + protected FontRecord(String fn, int ps, int bold, boolean it, + int us, int ci, int ss) { + super(Type.FONT); + boldWeight = bold; + underlineStyle = us; + name = fn; + pointHeight = ps; + italic = it; + scriptStyle = ss; + colourIndex = ci; + initialized = false; + struckout = false; + } + + /** + * Constructs this object from the raw data. Used when reading in a + * format record + * + * @param t the raw data + * @param ws the workbook settings + */ + public FontRecord(Record t, WorkbookSettings ws) { + super(t); + + byte[] data = getRecord().getData(); + + pointHeight = IntegerHelper.getInt(data[0], data[1]) / + EXCEL_UNITS_PER_POINT; + colourIndex = IntegerHelper.getInt(data[4], data[5]); + boldWeight = IntegerHelper.getInt(data[6], data[7]); + scriptStyle = IntegerHelper.getInt(data[8], data[9]); + underlineStyle = data[10]; + fontFamily = data[11]; + characterSet = data[12]; + initialized = false; + + if ((data[2] & 0x02) != 0) { + italic = true; + } + + if ((data[2] & 0x08) != 0) { + struckout = true; + } + + int numChars = data[14]; + if (data[15] == 0) { + name = StringHelper.getString(data, numChars, 16, ws); + } else if (data[15] == 1) { + name = StringHelper.getUnicodeString(data, numChars, 16); + } else { + // Some font names don't have the unicode indicator + name = StringHelper.getString(data, numChars, 15, ws); + } + } + + /** + * Constructs this object from the raw data. Used when reading in a + * format record + * + * @param t the raw data + * @param ws the workbook settings + * @param dummy dummy overload + */ + public FontRecord(Record t, WorkbookSettings ws, Biff7 dummy) { + super(t); + + byte[] data = getRecord().getData(); + + pointHeight = IntegerHelper.getInt(data[0], data[1]) / + EXCEL_UNITS_PER_POINT; + colourIndex = IntegerHelper.getInt(data[4], data[5]); + boldWeight = IntegerHelper.getInt(data[6], data[7]); + scriptStyle = IntegerHelper.getInt(data[8], data[9]); + underlineStyle = data[10]; + fontFamily = data[11]; + initialized = false; + + if ((data[2] & 0x02) != 0) { + italic = true; + } + + if ((data[2] & 0x08) != 0) { + struckout = true; + } + + int numChars = data[14]; + name = StringHelper.getString(data, numChars, 15, ws); + } + + /** + * Publicly available copy constructor + * + * @param f the font to copy + */ + protected FontRecord(Font f) { + super(Type.FONT); + + Assert.verify(f != null); + + pointHeight = f.getPointSize(); + colourIndex = f.getColour().getValue(); + boldWeight = f.getBoldWeight(); + scriptStyle = f.getScriptStyle().getValue(); + underlineStyle = f.getUnderlineStyle().getValue(); + italic = f.isItalic(); + name = f.getName(); + struckout = f.isStruckout(); + initialized = false; + } + + /** + * Gets the byte data for writing out + * + * @return the raw data + */ + public byte[] getData() { + byte[] data = new byte[16 + name.length() * 2]; + + // Excel expects font heights in 1/20ths of a point + IntegerHelper.getTwoBytes(pointHeight * EXCEL_UNITS_PER_POINT, data, 0); + + // Set the font attributes to be zero for now + if (italic) { + data[2] |= 0x2; + } + + if (struckout) { + data[2] |= 0x08; + } + + // Set the index to the colour palette + IntegerHelper.getTwoBytes(colourIndex, data, 4); + + // Bold style + IntegerHelper.getTwoBytes(boldWeight, data, 6); + + // Script style + IntegerHelper.getTwoBytes(scriptStyle, data, 8); + + // Underline style + data[10] = (byte) underlineStyle; + + // Set the font family to be 0 + data[11] = fontFamily; + + // Set the character set to be zero + data[12] = characterSet; + + // Set the reserved bit to be zero + data[13] = 0; + + // Set the length of the font name + data[14] = (byte) name.length(); + + data[15] = (byte) 1; + + // Copy in the string + StringHelper.getUnicodeBytes(name, data, 16); + + return data; + } + + /** + * Accessor to see whether this object is initialized or not. + * + * @return TRUE if this font record has been initialized, FALSE otherwise + */ + public final boolean isInitialized() { + return initialized; + } + + /** + * Sets the font index of this record. Called from the FormattingRecords + * object + * + * @param pos the position of this font in the workbooks font list + */ + public final void initialize(int pos) { + fontIndex = pos; + initialized = true; + } + + /** + * Resets the initialize flag. This is called by the constructor of + * WritableWorkbookImpl to reset the statically declared fonts + */ + public final void uninitialize() { + initialized = false; + } + + /** + * Accessor for the font index + * + * @return the font index + */ + public final int getFontIndex() { + return fontIndex; + } + + /** + * Sets the point size for this font, if the font hasn't been initialized + * + * @param ps the point size + */ + protected void setFontPointSize(int ps) { + Assert.verify(!initialized); + + pointHeight = ps; + } + + /** + * Gets the point size for this font, if the font hasn't been initialized + * + * @return the point size + */ + public int getPointSize() { + return pointHeight; + } + + /** + * Sets the bold style for this font, if the font hasn't been initialized + * + * @param bs the bold style + */ + protected void setFontBoldStyle(int bs) { + Assert.verify(!initialized); + + boldWeight = bs; + } + + /** + * Gets the bold weight for this font + * + * @return the bold weight for this font + */ + public int getBoldWeight() { + return boldWeight; + } + + /** + * Sets the italic indicator for this font, if the font hasn't been + * initialized + * + * @param i the italic flag + */ + protected void setFontItalic(boolean i) { + Assert.verify(!initialized); + + italic = i; + } + + /** + * Returns the italic flag + * + * @return TRUE if this font is italic, FALSE otherwise + */ + public boolean isItalic() { + return italic; + } + + /** + * Sets the underline style for this font, if the font hasn't been + * initialized + * + * @param us the underline style + */ + protected void setFontUnderlineStyle(int us) { + Assert.verify(!initialized); + + underlineStyle = us; + } + + /** + * Gets the underline style for this font + * + * @return the underline style + */ + public UnderlineStyle getUnderlineStyle() { + return UnderlineStyle.getStyle(underlineStyle); + } + + /** + * Sets the colour for this font, if the font hasn't been + * initialized + * + * @param c the colour + */ + protected void setFontColour(int c) { + Assert.verify(!initialized); + + colourIndex = c; + } + + /** + * Gets the colour for this font + * + * @return the colour + */ + public Colour getColour() { + return Colour.getInternalColour(colourIndex); + } + + /** + * Sets the script style (eg. superscript, subscript) for this font, + * if the font hasn't been initialized + * + * @param ss the colour + */ + protected void setFontScriptStyle(int ss) { + Assert.verify(!initialized); + + scriptStyle = ss; + } + + /** + * Gets the script style + * + * @return the script style + */ + public ScriptStyle getScriptStyle() { + return ScriptStyle.getStyle(scriptStyle); + } + + /** + * Gets the name of this font + * + * @return the name of this font + */ + public String getName() { + return name; + } + + /** + * Standard hash code method + * + * @return the hash code for this object + */ + public int hashCode() { + return name.hashCode(); + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof FontRecord)) { + return false; + } + + FontRecord font = (FontRecord) o; + + return pointHeight == font.pointHeight && + colourIndex == font.colourIndex && + boldWeight == font.boldWeight && + scriptStyle == font.scriptStyle && + underlineStyle == font.underlineStyle && + italic == font.italic && + struckout == font.struckout && + fontFamily == font.fontFamily && + characterSet == font.characterSet && + name.equals(font.name); + } + + /** + * Accessor for the strike out flag + * + * @return TRUE if this font is struck out, FALSE otherwise + */ + public boolean isStruckout() { + return struckout; + } + + /** + * Sets the struck out flag + * + * @param os TRUE if the font is struck out, false otherwise + */ + protected void setFontStruckout(boolean os) { + struckout = os; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/Fonts.java b/datastructures-xslx/src/main/java/jxl/biff/Fonts.java new file mode 100755 index 0000000..a7548c8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/Fonts.java @@ -0,0 +1,162 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.common.Assert; +import jxl.write.biff.File; + +/** + * A container for the list of fonts used in this workbook + */ +public class Fonts { + /** + * The default number of fonts + */ + private static final int numDefaultFonts = 4; + /** + * The list of fonts + */ + private ArrayList fonts; + + /** + * Constructor + */ + public Fonts() { + fonts = new ArrayList(); + } + + /** + * Adds a font record to this workbook. If the FontRecord passed in has not + * been initialized, then its font index is determined based upon the size + * of the fonts list. The FontRecord's initialized method is called, and + * it is added to the list of fonts. + * + * @param f the font to add + */ + public void addFont(FontRecord f) { + if (!f.isInitialized()) { + int pos = fonts.size(); + + // Remember that the pos with index 4 is skipped + if (pos >= 4) { + pos++; + } + + f.initialize(pos); + fonts.add(f); + } + } + + /** + * Used by FormattingRecord for retrieving the fonts for the + * hardcoded styles + * + * @param index the index of the font to return + * @return the font with the specified font index + */ + public FontRecord getFont(int index) { + // remember to allow for the fact that font index 4 is not used + if (index > 4) { + index--; + } + + return (FontRecord) fonts.get(index); + } + + /** + * Writes out the list of fonts + * + * @param outputFile the compound file to write the data to + * @throws IOException + */ + public void write(File outputFile) throws IOException { + Iterator i = fonts.iterator(); + + FontRecord font = null; + while (i.hasNext()) { + font = (FontRecord) i.next(); + outputFile.write(font); + } + } + + /** + * Rationalizes all the fonts, removing any duplicates + * + * @return the mappings between new indexes and old ones + */ + IndexMapping rationalize() { + IndexMapping mapping = new IndexMapping(fonts.size() + 1); + // allow for skipping record 4 + + ArrayList newfonts = new ArrayList(); + FontRecord fr = null; + int numremoved = 0; + + // Preserve the default fonts + for (int i = 0; i < numDefaultFonts; i++) { + fr = (FontRecord) fonts.get(i); + newfonts.add(fr); + mapping.setMapping(fr.getFontIndex(), fr.getFontIndex()); + } + + // Now do the rest + Iterator it = null; + FontRecord fr2 = null; + boolean duplicate = false; + for (int i = numDefaultFonts; i < fonts.size(); i++) { + fr = (FontRecord) fonts.get(i); + + // Compare to all the fonts currently on the list + duplicate = false; + it = newfonts.iterator(); + while (it.hasNext() && !duplicate) { + fr2 = (FontRecord) it.next(); + if (fr.equals(fr2)) { + duplicate = true; + mapping.setMapping(fr.getFontIndex(), + mapping.getNewIndex(fr2.getFontIndex())); + numremoved++; + } + } + + if (!duplicate) { + // Add to the new list + newfonts.add(fr); + int newindex = fr.getFontIndex() - numremoved; + Assert.verify(newindex > 4); + mapping.setMapping(fr.getFontIndex(), newindex); + } + } + + // Iterate through the remaining fonts, updating all the font indices + it = newfonts.iterator(); + while (it.hasNext()) { + fr = (FontRecord) it.next(); + fr.initialize(mapping.getNewIndex(fr.getFontIndex())); + } + + fonts = newfonts; + + return mapping; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/FormatRecord.java b/datastructures-xslx/src/main/java/jxl/biff/FormatRecord.java new file mode 100755 index 0000000..22af783 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FormatRecord.java @@ -0,0 +1,551 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import jxl.WorkbookSettings; +import jxl.common.Logger; +import jxl.format.Format; +import jxl.read.biff.Record; + +/** + * A non-built in format record + */ +public class FormatRecord extends WritableRecordData + implements DisplayFormat, Format { + public static final BiffType biff8 = new BiffType(); + public static final BiffType biff7 = new BiffType(); + /** + * The logger + */ + public static Logger logger = Logger.getLogger(FormatRecord.class); + /** + * The date strings to look for + */ + private static final String[] dateStrings = new String[] + { + "dd", + "mm", + "yy", + "hh", + "ss", + "m/", + "/d" + }; + /** + * Initialized flag + */ + private boolean initialized; + /** + * The raw data + */ + private byte[] data; + /** + * The index code + */ + private int indexCode; + /** + * The formatting string + */ + private String formatString; + /** + * Indicates whether this is a date formatting record + */ + private boolean date; + /** + * Indicates whether this a number formatting record + */ + private boolean number; + /** + * The format object + */ + private java.text.Format format; + /** + * Constructor invoked when copying sheets + * + * @param fmt the format string + * @param refno the index code + */ + FormatRecord(String fmt, int refno) { + super(Type.FORMAT); + formatString = fmt; + indexCode = refno; + initialized = true; + } + + /** + * Constructor used by writable formats + */ + protected FormatRecord() { + super(Type.FORMAT); + initialized = false; + } + + /** + * Copy constructor - can be invoked by public access + * + * @param fr the format to copy + */ + protected FormatRecord(FormatRecord fr) { + super(Type.FORMAT); + initialized = false; + + formatString = fr.formatString; + date = fr.date; + number = fr.number; + // format = (java.text.Format) fr.format.clone(); + } + + /** + * Constructs this object from the raw data. Used when reading in a + * format record + * + * @param t the raw data + * @param ws the workbook settings + * @param biffType biff type dummy overload + */ + public FormatRecord(Record t, WorkbookSettings ws, BiffType biffType) { + super(t); + + byte[] data = getRecord().getData(); + indexCode = IntegerHelper.getInt(data[0], data[1]); + initialized = true; + + if (biffType == biff8) { + int numchars = IntegerHelper.getInt(data[2], data[3]); + if (data[4] == 0) { + formatString = StringHelper.getString(data, numchars, 5, ws); + } else { + formatString = StringHelper.getUnicodeString(data, numchars, 5); + } + } else { + int numchars = data[2]; + byte[] chars = new byte[numchars]; + System.arraycopy(data, 3, chars, 0, chars.length); + formatString = new String(chars); + } + + date = false; + number = false; + + // First see if this is a date format + for (int i = 0; i < dateStrings.length; i++) { + String dateString = dateStrings[i]; + if (formatString.indexOf(dateString) != -1 || + formatString.indexOf(dateString.toUpperCase()) != -1) { + date = true; + break; + } + } + + // See if this is number format - look for the # or 0 characters + if (!date) { + if (formatString.indexOf('#') != -1 || + formatString.indexOf('0') != -1) { + number = true; + } + } + } + + /** + * Used to get the data when writing out the format record + * + * @return the raw data + */ + public byte[] getData() { + data = new byte[formatString.length() * 2 + 3 + 2]; + + IntegerHelper.getTwoBytes(indexCode, data, 0); + IntegerHelper.getTwoBytes(formatString.length(), data, 2); + data[4] = (byte) 1; // unicode indicator + StringHelper.getUnicodeBytes(formatString, data, 5); + + return data; + } + + /** + * Gets the format index of this record + * + * @return the format index of this record + */ + public int getFormatIndex() { + return indexCode; + } + + /** + * Accessor to see whether this object is initialized or not. + * + * @return TRUE if this font record has been initialized, FALSE otherwise + */ + public boolean isInitialized() { + return initialized; + } + + /** + * Sets the index of this record. Called from the FormattingRecords + * object + * + * @param pos the position of this font in the workbooks font list + */ + + public void initialize(int pos) { + indexCode = pos; + initialized = true; + } + + /** + * Replaces all instances of search with replace in the input. Used for + * replacing microsoft number formatting characters with java equivalents + * + * @param input the format string + * @param search the Excel character to be replaced + * @param replace the java equivalent + * @return the input string with the specified substring replaced + */ + protected final String replace(String input, String search, String replace) { + String fmtstr = input; + int pos = fmtstr.indexOf(search); + while (pos != -1) { + StringBuffer tmp = new StringBuffer(fmtstr.substring(0, pos)); + tmp.append(replace); + tmp.append(fmtstr.substring(pos + search.length())); + fmtstr = tmp.toString(); + pos = fmtstr.indexOf(search); + } + return fmtstr; + } + + /** + * Sees if this format is a date format + * + * @return TRUE if this format is a date + */ + public final boolean isDate() { + return date; + } + + /** + * Sees if this format is a number format + * + * @return TRUE if this format is a number + */ + public final boolean isNumber() { + return number; + } + + /** + * Gets the java equivalent number format for the formatString + * + * @return The java equivalent of the number format for this object + */ + public final NumberFormat getNumberFormat() { + if (format != null && format instanceof NumberFormat) { + return (NumberFormat) format; + } + + try { + String fs = formatString; + + // Replace the Excel formatting characters with java equivalents + fs = replace(fs, "E+", "E"); + fs = replace(fs, "_)", ""); + fs = replace(fs, "_", ""); + fs = replace(fs, "[Red]", ""); + fs = replace(fs, "\\", ""); + + format = new DecimalFormat(fs); + } catch (IllegalArgumentException e) { + // Something went wrong with the date format - fail silently + // and return a default value + format = new DecimalFormat("#.###"); + } + + return (NumberFormat) format; + } + + /** + * Gets the java equivalent date format for the formatString + * + * @return The java equivalent of the date format for this object + */ + public final DateFormat getDateFormat() { + if (format != null && format instanceof DateFormat) { + return (DateFormat) format; + } + + String fmt = formatString; + + // Replace the AM/PM indicator with an a + int pos = fmt.indexOf("AM/PM"); + while (pos != -1) { + StringBuffer sb = new StringBuffer(fmt.substring(0, pos)); + sb.append('a'); + sb.append(fmt.substring(pos + 5)); + fmt = sb.toString(); + pos = fmt.indexOf("AM/PM"); + } + + // Replace ss.0 with ss.SSS (necessary to always specify milliseconds + // because of NT) + pos = fmt.indexOf("ss.0"); + while (pos != -1) { + StringBuffer sb = new StringBuffer(fmt.substring(0, pos)); + sb.append("ss.SSS"); + + // Keep going until we run out of zeros + pos += 4; + while (pos < fmt.length() && fmt.charAt(pos) == '0') { + pos++; + } + + sb.append(fmt.substring(pos)); + fmt = sb.toString(); + pos = fmt.indexOf("ss.0"); + } + + + // Filter out the backslashes + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < fmt.length(); i++) { + if (fmt.charAt(i) != '\\') { + sb.append(fmt.charAt(i)); + } + } + + fmt = sb.toString(); + + // If the date format starts with anything inside square brackets then + // filter tham out + if (fmt.charAt(0) == '[') { + int end = fmt.indexOf(']'); + if (end != -1) { + fmt = fmt.substring(end + 1); + } + } + + // Get rid of some spurious characters that can creep in + fmt = replace(fmt, ";@", ""); + + // We need to convert the month indicator m, to upper case when we + // are dealing with dates + char[] formatBytes = fmt.toCharArray(); + + for (int i = 0; i < formatBytes.length; i++) { + if (formatBytes[i] == 'm') { + // Firstly, see if the preceding character is also an m. If so, + // copy that + if (i > 0 && (formatBytes[i - 1] == 'm' || formatBytes[i - 1] == 'M')) { + formatBytes[i] = formatBytes[i - 1]; + } else { + // There is no easy way out. We have to deduce whether this an + // minute or a month? See which is closest out of the + // letters H d s or y + // First, h + int minuteDist = Integer.MAX_VALUE; + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 'h') { + minuteDist = i - j; + break; + } + } + + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 'h') { + minuteDist = Math.min(minuteDist, j - i); + break; + } + } + + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 'H') { + minuteDist = i - j; + break; + } + } + + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 'H') { + minuteDist = Math.min(minuteDist, j - i); + break; + } + } + + // Now repeat for s + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 's') { + minuteDist = Math.min(minuteDist, i - j); + break; + } + } + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 's') { + minuteDist = Math.min(minuteDist, j - i); + break; + } + } + // We now have the distance of the closest character which could + // indicate the the m refers to a minute + // Repeat for d and y + int monthDist = Integer.MAX_VALUE; + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 'd') { + monthDist = i - j; + break; + } + } + + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 'd') { + monthDist = Math.min(monthDist, j - i); + break; + } + } + // Now repeat for y + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 'y') { + monthDist = Math.min(monthDist, i - j); + break; + } + } + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 'y') { + monthDist = Math.min(monthDist, j - i); + break; + } + } + + if (monthDist < minuteDist) { + // The month indicator is closer, so convert to a capital M + formatBytes[i] = Character.toUpperCase(formatBytes[i]); + } else if ((monthDist == minuteDist) && + (monthDist != Integer.MAX_VALUE)) { + // They are equidistant. As a tie-breaker, take the formatting + // character which precedes the m + char ind = formatBytes[i - monthDist]; + if (ind == 'y' || ind == 'd') { + // The preceding item indicates a month measure, so convert + formatBytes[i] = Character.toUpperCase(formatBytes[i]); + } + } + } + } + } + + try { + this.format = new SimpleDateFormat(new String(formatBytes)); + } catch (IllegalArgumentException e) { + // There was a spurious character - fail silently + this.format = new SimpleDateFormat("dd MM yyyy hh:mm:ss"); + } + return (DateFormat) this.format; + } + + /** + * Gets the index code, for use as a hash value + * + * @return the ifmt code for this cell + */ + public int getIndexCode() { + return indexCode; + } + + /** + * Gets the formatting string. + * + * @return the excel format string + */ + public String getFormatString() { + return formatString; + } + + /** + * Called by the immediate subclass to set the string + * once the Java-Excel replacements have been done + * + * @param s the format string + */ + protected final void setFormatString(String s) { + formatString = s; + } + + /** + * Indicates whether this formula is a built in + * + * @return FALSE + */ + public boolean isBuiltIn() { + return false; + } + + /** + * Standard hash code method + * + * @return the hash code value for this object + */ + public int hashCode() { + return formatString.hashCode(); + } + + /** + * Standard equals method. This compares the contents of two + * format records, and not their indexCodes, which are ignored + * + * @param o the object to compare + * @return TRUE if the two objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof FormatRecord)) { + return false; + } + + FormatRecord fr = (FormatRecord) o; + + // Initialized format comparison + if (initialized && fr.initialized) { + // Must be either a number or a date format + if (date != fr.date || + number != fr.number) { + return false; + } + + return formatString.equals(fr.formatString); + } + + // Uninitialized format comparison + return formatString.equals(fr.formatString); + } + + // Type to distinguish between biff7 and biff8 + private static class BiffType { + } +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/FormattingRecords.java b/datastructures-xslx/src/main/java/jxl/biff/FormattingRecords.java new file mode 100755 index 0000000..473e182 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FormattingRecords.java @@ -0,0 +1,512 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.Colour; +import jxl.format.RGB; +import jxl.write.biff.File; + +/** + * The list of XF records and formatting records for the workbook + */ +public class FormattingRecords { + /** + * The start index number for custom format records + */ + private static final int customFormatStartIndex = 0xa4; + /** + * The maximum number of format records. This is some weird internal + * Excel constraint + */ + private static final int maxFormatRecordsIndex = 0x1b9; + /** + * The minimum number of XF records for a sheet. The rationalization + * processes commences immediately after this number + */ + private static final int minXFRecords = 21; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FormattingRecords.class); + /** + * A hash map of FormatRecords, for random access retrieval when reading + * in a spreadsheet + */ + private final HashMap formats; + /** + * A list of formats, used when writing out a spreadsheet + */ + private ArrayList formatsList; + /** + * The list of extended format records + */ + private ArrayList xfRecords; + /** + * The next available index number for custom format records + */ + private int nextCustomIndexNumber; + /** + * A handle to the available fonts + */ + private final Fonts fonts; + /** + * The colour palette + */ + private PaletteRecord palette; + + /** + * Constructor + * + * @param f the container for the fonts + */ + public FormattingRecords(Fonts f) { + xfRecords = new ArrayList(10); + formats = new HashMap(10); + formatsList = new ArrayList(10); + fonts = f; + nextCustomIndexNumber = customFormatStartIndex; + } + + /** + * Adds an extended formatting record to the list. If the XF record passed + * in has not been initialized, its index is determined based on the + * xfRecords list, and + * this position is passed to the XF records initialize method + * + * @param xf the xf record to add + * @throws NumFormatRecordsException + */ + public final void addStyle(XFRecord xf) + throws NumFormatRecordsException { + if (!xf.isInitialized()) { + int pos = xfRecords.size(); + xf.initialize(pos, this, fonts); + xfRecords.add(xf); + } else { + // The XF record has probably been read in. If the index is greater + // Than the size of the list, then it is not a preset format, + // so add it + if (xf.getXFIndex() >= xfRecords.size()) { + xfRecords.add(xf); + } + } + } + + /** + * Adds a cell format to the hash map, keyed on its index. If the format + * record is not initialized, then its index number is determined and its + * initialize method called. If the font is not a built in format, then it + * is added to the list of formats for writing out + * + * @param fr the format record + */ + public final void addFormat(DisplayFormat fr) + throws NumFormatRecordsException { + // Handle the case the where the index number in the read Excel + // file exhibits some major weirdness + if (fr.isInitialized() && + fr.getFormatIndex() >= maxFormatRecordsIndex) { + logger.warn("Format index exceeds Excel maximum - assigning custom " + + "number"); + fr.initialize(nextCustomIndexNumber); + nextCustomIndexNumber++; + } + + // Initialize the format record with a custom index number + if (!fr.isInitialized()) { + fr.initialize(nextCustomIndexNumber); + nextCustomIndexNumber++; + } + + if (nextCustomIndexNumber > maxFormatRecordsIndex) { + nextCustomIndexNumber = maxFormatRecordsIndex; + throw new NumFormatRecordsException(); + } + + if (fr.getFormatIndex() >= nextCustomIndexNumber) { + nextCustomIndexNumber = fr.getFormatIndex() + 1; + } + + if (!fr.isBuiltIn()) { + formatsList.add(fr); + formats.put(new Integer(fr.getFormatIndex()), fr); + } + } + + /** + * Sees if the extended formatting record at the specified position + * represents a date. First checks against the built in formats, and + * then checks against the hash map of FormatRecords + * + * @param pos the xf format index + * @return TRUE if this format index is formatted as a Date + */ + public final boolean isDate(int pos) { + XFRecord xfr = (XFRecord) xfRecords.get(pos); + + if (xfr.isDate()) { + return true; + } + + FormatRecord fr = (FormatRecord) + formats.get(new Integer(xfr.getFormatRecord())); + + return fr != null && fr.isDate(); + } + + /** + * Gets the DateFormat used to format the cell. + * + * @param pos the xf format index + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public final DateFormat getDateFormat(int pos) { + XFRecord xfr = (XFRecord) xfRecords.get(pos); + + if (xfr.isDate()) { + return xfr.getDateFormat(); + } + + FormatRecord fr = (FormatRecord) + formats.get(new Integer(xfr.getFormatRecord())); + + if (fr == null) { + return null; + } + + return fr.isDate() ? fr.getDateFormat() : null; + } + + /** + * Gets the NumberFormat used to format the cell. + * + * @param pos the xf format index + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public final NumberFormat getNumberFormat(int pos) { + XFRecord xfr = (XFRecord) xfRecords.get(pos); + + if (xfr.isNumber()) { + return xfr.getNumberFormat(); + } + + FormatRecord fr = (FormatRecord) + formats.get(new Integer(xfr.getFormatRecord())); + + if (fr == null) { + return null; + } + + return fr.isNumber() ? fr.getNumberFormat() : null; + } + + /** + * Gets the format record + * + * @param index the formatting record index to retrieve + * @return the format record at the specified index + */ + FormatRecord getFormatRecord(int index) { + return (FormatRecord) + formats.get(new Integer(index)); + } + + /** + * Writes out all the format records and the XF records + * + * @param outputFile the file to write to + * @throws IOException + */ + public void write(File outputFile) throws IOException { + // Write out all the formats + Iterator i = formatsList.iterator(); + FormatRecord fr = null; + while (i.hasNext()) { + fr = (FormatRecord) i.next(); + outputFile.write(fr); + } + + // Write out the styles + i = xfRecords.iterator(); + XFRecord xfr = null; + while (i.hasNext()) { + xfr = (XFRecord) i.next(); + outputFile.write(xfr); + } + + // Write out the style records + BuiltInStyle style = new BuiltInStyle(0x10, 3); + outputFile.write(style); + + style = new BuiltInStyle(0x11, 6); + outputFile.write(style); + + style = new BuiltInStyle(0x12, 4); + outputFile.write(style); + + style = new BuiltInStyle(0x13, 7); + outputFile.write(style); + + style = new BuiltInStyle(0x0, 0); + outputFile.write(style); + + style = new BuiltInStyle(0x14, 5); + outputFile.write(style); + } + + /** + * Accessor for the fonts used by this workbook + * + * @return the fonts container + */ + protected final Fonts getFonts() { + return fonts; + } + + /** + * Gets the XFRecord for the specified index. Used when copying individual + * cells + * + * @param index the XF record to retrieve + * @return the XF record at the specified index + */ + public final XFRecord getXFRecord(int index) { + return (XFRecord) xfRecords.get(index); + } + + /** + * Gets the number of formatting records on the list. This is used by the + * writable subclass because there is an upper limit on the amount of + * format records that are allowed to be present in an excel sheet + * + * @return the number of format records present + */ + protected final int getNumberOfFormatRecords() { + return formatsList.size(); + } + + /** + * Rationalizes all the fonts, removing duplicate entries + * + * @return the list of new font index number + */ + public IndexMapping rationalizeFonts() { + return fonts.rationalize(); + } + + /** + * Rationalizes the cell formats. Duplicate + * formats are removed and the format indexed of the cells + * adjusted accordingly + * + * @param fontMapping the font mapping index numbers + * @param formatMapping the format mapping index numbers + * @return the list of new font index number + */ + public IndexMapping rationalize(IndexMapping fontMapping, + IndexMapping formatMapping) { + // Update the index codes for the XF records using the format + // mapping and the font mapping + // at the same time + XFRecord xfr = null; + for (Iterator it = xfRecords.iterator(); it.hasNext(); ) { + xfr = (XFRecord) it.next(); + + if (xfr.getFormatRecord() >= customFormatStartIndex) { + xfr.setFormatIndex(formatMapping.getNewIndex(xfr.getFormatRecord())); + } + + xfr.setFontIndex(fontMapping.getNewIndex(xfr.getFontIndex())); + } + + ArrayList newrecords = new ArrayList(minXFRecords); + IndexMapping mapping = new IndexMapping(xfRecords.size()); + int numremoved = 0; + + int numXFRecords = Math.min(minXFRecords, xfRecords.size()); + // Copy across the fundamental styles + for (int i = 0; i < numXFRecords; i++) { + newrecords.add(xfRecords.get(i)); + mapping.setMapping(i, i); + } + + if (numXFRecords < minXFRecords) { + logger.warn("There are less than the expected minimum number of " + + "XF records"); + return mapping; + } + + // Iterate through the old list + for (int i = minXFRecords; i < xfRecords.size(); i++) { + XFRecord xf = (XFRecord) xfRecords.get(i); + + // Compare against formats already on the list + boolean duplicate = false; + for (Iterator it = newrecords.iterator(); + it.hasNext() && !duplicate; ) { + XFRecord xf2 = (XFRecord) it.next(); + if (xf2.equals(xf)) { + duplicate = true; + mapping.setMapping(i, mapping.getNewIndex(xf2.getXFIndex())); + numremoved++; + } + } + + // If this format is not a duplicate then add it to the new list + if (!duplicate) { + newrecords.add(xf); + mapping.setMapping(i, i - numremoved); + } + } + + // It is sufficient to merely change the xf index field on all XFRecords + // In this case, CellValues which refer to defunct format records + // will nevertheless be written out with the correct index number + for (Iterator i = xfRecords.iterator(); i.hasNext(); ) { + XFRecord xf = (XFRecord) i.next(); + xf.rationalize(mapping); + } + + // Set the new list + xfRecords = newrecords; + + return mapping; + } + + /** + * Rationalizes the display formats. Duplicate + * formats are removed and the format indices of the cells + * adjusted accordingly. It is invoked immediately prior to writing + * writing out the sheet + * + * @return the index mapping between the old display formats and the + * rationalized ones + */ + public IndexMapping rationalizeDisplayFormats() { + ArrayList newformats = new ArrayList(); + int numremoved = 0; + IndexMapping mapping = new IndexMapping(nextCustomIndexNumber); + + // Iterate through the old list + Iterator i = formatsList.iterator(); + DisplayFormat df = null; + DisplayFormat df2 = null; + boolean duplicate = false; + while (i.hasNext()) { + df = (DisplayFormat) i.next(); + + Assert.verify(!df.isBuiltIn()); + + // Compare against formats already on the list + Iterator i2 = newformats.iterator(); + duplicate = false; + while (i2.hasNext() && !duplicate) { + df2 = (DisplayFormat) i2.next(); + if (df2.equals(df)) { + duplicate = true; + mapping.setMapping(df.getFormatIndex(), + mapping.getNewIndex(df2.getFormatIndex())); + numremoved++; + } + } + + // If this format is not a duplicate then add it to the new list + if (!duplicate) { + newformats.add(df); + int indexnum = df.getFormatIndex() - numremoved; + if (indexnum > maxFormatRecordsIndex) { + logger.warn("Too many number formats - using default format."); + indexnum = 0; // the default number format index + } + mapping.setMapping(df.getFormatIndex(), + df.getFormatIndex() - numremoved); + } + } + + // Set the new list + formatsList = newformats; + + // Update the index codes for the remaining formats + i = formatsList.iterator(); + + while (i.hasNext()) { + df = (DisplayFormat) i.next(); + df.initialize(mapping.getNewIndex(df.getFormatIndex())); + } + + return mapping; + } + + /** + * Accessor for the colour palette + * + * @return the palette + */ + public PaletteRecord getPalette() { + return palette; + } + + /** + * Called from the WorkbookParser to set the colour palette + * + * @param pr the palette + */ + public void setPalette(PaletteRecord pr) { + palette = pr; + } + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public void setColourRGB(Colour c, int r, int g, int b) { + if (palette == null) { + palette = new PaletteRecord(); + } + palette.setColourRGB(c, r, g, b); + } + + /** + * Accessor for the RGB value for the specified colour + * + * @return the RGB for the specified colour + */ + public RGB getColourRGB(Colour c) { + if (palette == null) { + return c.getDefaultRGB(); + } + + return palette.getColourRGB(c); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/FormulaData.java b/datastructures-xslx/src/main/java/jxl/biff/FormulaData.java new file mode 100755 index 0000000..c373c71 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FormulaData.java @@ -0,0 +1,39 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.Cell; +import jxl.biff.formula.FormulaException; + +/** + * Interface which is used for copying formulas from a read only + * to a writable spreadsheet + */ +public interface FormulaData extends Cell { + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array EXCLUDING the standard cell information + * (row, column, xfindex) + * + * @return the raw record data + * @throws FormulaException + */ + byte[] getFormulaData() throws FormulaException; +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/HeaderFooter.java b/datastructures-xslx/src/main/java/jxl/biff/HeaderFooter.java new file mode 100755 index 0000000..766dd2a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/HeaderFooter.java @@ -0,0 +1,615 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan, Eric Jung + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; + +/** + * Class which represents an Excel header or footer. Information for this + * class came from Microsoft Knowledge Base Article 142136 + * (previously Q142136). + *

+ * This class encapsulates three internal structures representing the header + * or footer contents which appear on the left, right or central part of the + * page + */ +public abstract class HeaderFooter { + /** + * Turns bold printing on or off + */ + private static final String BOLD_TOGGLE = "&B"; + + // Codes to format text + /** + * Turns underline printing on or off + */ + private static final String UNDERLINE_TOGGLE = "&U"; + /** + * Turns italic printing on or off + */ + private static final String ITALICS_TOGGLE = "&I"; + /** + * Turns strikethrough printing on or off + */ + private static final String STRIKETHROUGH_TOGGLE = "&S"; + /** + * Turns double-underline printing on or off + */ + private static final String DOUBLE_UNDERLINE_TOGGLE = "&E"; + /** + * Turns superscript printing on or off + */ + private static final String SUPERSCRIPT_TOGGLE = "&X"; + /** + * Turns subscript printing on or off + */ + private static final String SUBSCRIPT_TOGGLE = "&Y"; + /** + * Turns outline printing on or off (Macintosh only) + */ + private static final String OUTLINE_TOGGLE = "&O"; + /** + * Turns shadow printing on or off (Macintosh only) + */ + private static final String SHADOW_TOGGLE = "&H"; + /** + * Left-aligns the characters that follow + */ + private static final String LEFT_ALIGN = "&L"; + /** + * Centres the characters that follow + */ + private static final String CENTRE = "&C"; + /** + * Right-aligns the characters that follow + */ + private static final String RIGHT_ALIGN = "&R"; + /** + * Prints the page number + */ + private static final String PAGENUM = "&P"; + + // Codes to insert specific data + /** + * Prints the total number of pages in the document + */ + private static final String TOTAL_PAGENUM = "&N"; + /** + * Prints the current date + */ + private static final String DATE = "&D"; + /** + * Prints the current time + */ + private static final String TIME = "&T"; + /** + * Prints the name of the workbook + */ + private static final String WORKBOOK_NAME = "&F"; + /** + * Prints the name of the worksheet + */ + private static final String WORKSHEET_NAME = "&A"; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(HeaderFooter.class); + /** + * The left aligned header/footer contents + */ + private Contents left; + /** + * The right aligned header/footer contents + */ + private Contents right; + /** + * The centrally aligned header/footer contents + */ + private Contents centre; + + /** + * Default constructor. + */ + protected HeaderFooter() { + left = createContents(); + right = createContents(); + centre = createContents(); + } + + /** + * Copy constructor + * + * @param c the item to copy + */ + protected HeaderFooter(HeaderFooter hf) { + left = createContents(hf.left); + right = createContents(hf.right); + centre = createContents(hf.centre); + } + + /** + * Constructor used when reading workbooks to separate the left, right + * a central part of the strings into their constituent parts + */ + protected HeaderFooter(String s) { + if (s == null || s.length() == 0) { + left = createContents(); + right = createContents(); + centre = createContents(); + return; + } + + int leftPos = s.indexOf(LEFT_ALIGN); + int rightPos = s.indexOf(RIGHT_ALIGN); + int centrePos = s.indexOf(CENTRE); + + if (leftPos == -1 && rightPos == -1 && centrePos == -1) { + // When no part is specified, it is the center part + centre = createContents(s); + } else { + // Left part? + if (leftPos != -1) { + // We have a left part, find end of left part + int endLeftPos = s.length(); + if (centrePos > leftPos) { + // Case centre part behind left part + endLeftPos = centrePos; + if (rightPos > leftPos && endLeftPos > rightPos) { + // LRC case + endLeftPos = rightPos; + } else { + // LCR case + } + } else { + // Case centre part before left part + if (rightPos > leftPos) { + // LR case + endLeftPos = rightPos; + } else { + // *L case + // Left pos is last + + + } + } + left = createContents(s.substring(leftPos + 2, endLeftPos)); + } + + // Right part? + if (rightPos != -1) { + // Find end of right part + int endRightPos = s.length(); + if (centrePos > rightPos) { + // centre part behind right part + endRightPos = centrePos; + if (leftPos > rightPos && endRightPos > leftPos) { + // RLC case + endRightPos = leftPos; + } else { + // RCL case + } + } else { + if (leftPos > rightPos) { + // RL case + endRightPos = leftPos; + } else { + // *R case + // Right pos is last + } + } + right = createContents(s.substring(rightPos + 2, endRightPos)); + } + + // Centre part? + if (centrePos != -1) { + // Find end of centre part + int endCentrePos = s.length(); + if (rightPos > centrePos) { + // right part behind centre part + endCentrePos = rightPos; + if (leftPos > centrePos && endCentrePos > leftPos) { + // CLR case + endCentrePos = leftPos; + } else { + // CRL case + } + } else { + if (leftPos > centrePos) { + // CL case + endCentrePos = leftPos; + } else { + // *C case + // Centre pos is last + } + } + centre = createContents(s.substring(centrePos + 2, endCentrePos)); + } + } + + + if (left == null) { + left = createContents(); + } + + if (centre == null) { + centre = createContents(); + } + + if (right == null) { + right = createContents(); + } + } + + /** + * Retrieves a Stringified + * version of this object + * + * @return the header string + */ + public String toString() { + StringBuffer hf = new StringBuffer(); + if (!left.empty()) { + hf.append(LEFT_ALIGN); + hf.append(left.getContents()); + } + + if (!centre.empty()) { + hf.append(CENTRE); + hf.append(centre.getContents()); + } + + if (!right.empty()) { + hf.append(RIGHT_ALIGN); + hf.append(right.getContents()); + } + + return hf.toString(); + } + + /** + * Accessor for the contents which appear on the right hand side of the page + * + * @return the right aligned contents + */ + protected Contents getRightText() { + return right; + } + + /** + * Accessor for the contents which in the centre of the page + * + * @return the centrally aligned contents + */ + protected Contents getCentreText() { + return centre; + } + + /** + * Accessor for the contents which appear on the left hand side of the page + * + * @return the left aligned contents + */ + protected Contents getLeftText() { + return left; + } + + /** + * Clears the contents of the header/footer + */ + protected void clear() { + left.clear(); + right.clear(); + centre.clear(); + } + + /** + * Creates internal class of the appropriate type + */ + protected abstract Contents createContents(); + + /** + * Creates internal class of the appropriate type + */ + protected abstract Contents createContents(String s); + + /** + * Creates internal class of the appropriate type + */ + protected abstract Contents createContents(Contents c); + + /** + * The contents - a simple wrapper around a string buffer + */ + protected static class Contents { + /** + * The buffer containing the header/footer string + */ + private StringBuffer contents; + + /** + * The constructor + */ + protected Contents() { + contents = new StringBuffer(); + } + + /** + * Constructor used when reading worksheets. The string contains all + * the formatting (but not alignment characters + * + * @param s the format string + */ + protected Contents(String s) { + contents = new StringBuffer(s); + } + + /** + * Copy constructor + * + * @param copy the contents to copy + */ + protected Contents(Contents copy) { + contents = new StringBuffer(copy.getContents()); + } + + /** + * Retrieves a Stringified + * version of this object + * + * @return the header string + */ + protected String getContents() { + return contents != null ? contents.toString() : ""; + } + + /** + * Internal method which appends the text to the string buffer + * + * @param txt + */ + private void appendInternal(String txt) { + if (contents == null) { + contents = new StringBuffer(); + } + + contents.append(txt); + } + + /** + * Internal method which appends the text to the string buffer + * + * @param ch + */ + private void appendInternal(char ch) { + if (contents == null) { + contents = new StringBuffer(); + } + + contents.append(ch); + } + + /** + * Appends the text to the string buffer + * + * @param txt + */ + protected void append(String txt) { + appendInternal(txt); + } + + /** + * Turns bold printing on or off. Bold printing + * is initially off. Text subsequently appended to + * this object will be bolded until this method is + * called again. + */ + protected void toggleBold() { + appendInternal(BOLD_TOGGLE); + } + + /** + * Turns underline printing on or off. Underline printing + * is initially off. Text subsequently appended to + * this object will be underlined until this method is + * called again. + */ + protected void toggleUnderline() { + appendInternal(UNDERLINE_TOGGLE); + } + + /** + * Turns italics printing on or off. Italics printing + * is initially off. Text subsequently appended to + * this object will be italicized until this method is + * called again. + */ + protected void toggleItalics() { + appendInternal(ITALICS_TOGGLE); + } + + /** + * Turns strikethrough printing on or off. Strikethrough printing + * is initially off. Text subsequently appended to + * this object will be striked out until this method is + * called again. + */ + protected void toggleStrikethrough() { + appendInternal(STRIKETHROUGH_TOGGLE); + } + + /** + * Turns double-underline printing on or off. Double-underline printing + * is initially off. Text subsequently appended to + * this object will be double-underlined until this method is + * called again. + */ + protected void toggleDoubleUnderline() { + appendInternal(DOUBLE_UNDERLINE_TOGGLE); + } + + /** + * Turns superscript printing on or off. Superscript printing + * is initially off. Text subsequently appended to + * this object will be superscripted until this method is + * called again. + */ + protected void toggleSuperScript() { + appendInternal(SUPERSCRIPT_TOGGLE); + } + + /** + * Turns subscript printing on or off. Subscript printing + * is initially off. Text subsequently appended to + * this object will be subscripted until this method is + * called again. + */ + protected void toggleSubScript() { + appendInternal(SUBSCRIPT_TOGGLE); + } + + /** + * Turns outline printing on or off (Macintosh only). + * Outline printing is initially off. Text subsequently appended + * to this object will be outlined until this method is + * called again. + */ + protected void toggleOutline() { + appendInternal(OUTLINE_TOGGLE); + } + + /** + * Turns shadow printing on or off (Macintosh only). + * Shadow printing is initially off. Text subsequently appended + * to this object will be shadowed until this method is + * called again. + */ + protected void toggleShadow() { + appendInternal(SHADOW_TOGGLE); + } + + /** + * Sets the font of text subsequently appended to this + * object.. Previously appended text is not affected. + *

+ * Note: no checking is performed to + * determine if fontName is a valid font. + * + * @param fontName name of the font to use + */ + protected void setFontName(String fontName) { + // Font name must be in quotations + appendInternal("&\""); + appendInternal(fontName); + appendInternal('\"'); + } + + /** + * Sets the font size of text subsequently appended to this + * object. Previously appended text is not affected. + *

+ * Valid point sizes are between 1 and 99 (inclusive). If + * size is outside this range, this method returns false + * and does not change font size. If size is within this + * range, the font size is changed and true is returned. + * + * @param size The size in points. Valid point sizes are + * between 1 and 99 (inclusive). + * @return true if the font size was changed, false if font + * size was not changed because 1 > size > 99. + */ + protected boolean setFontSize(int size) { + if (size < 1 || size > 99) { + return false; + } + + // A two digit number should be used -- even if the + // leading number is just a zero. + String fontSize; + if (size < 10) { + // single-digit -- make two digit + fontSize = "0" + size; + } else { + fontSize = Integer.toString(size); + } + + appendInternal('&'); + appendInternal(fontSize); + return true; + } + + /** + * Appends the page number + */ + protected void appendPageNumber() { + appendInternal(PAGENUM); + } + + /** + * Appends the total number of pages + */ + protected void appendTotalPages() { + appendInternal(TOTAL_PAGENUM); + } + + /** + * Appends the current date + */ + protected void appendDate() { + appendInternal(DATE); + } + + /** + * Appends the current time + */ + protected void appendTime() { + appendInternal(TIME); + } + + /** + * Appends the workbook name + */ + protected void appendWorkbookName() { + appendInternal(WORKBOOK_NAME); + } + + /** + * Appends the worksheet name + */ + protected void appendWorkSheetName() { + appendInternal(WORKSHEET_NAME); + } + + /** + * Clears the contents of this portion + */ + protected void clear() { + contents = null; + } + + /** + * Queries if the contents are empty + * + * @return TRUE if the contents are empty, FALSE otherwise + */ + protected boolean empty() { + return contents == null || contents.length() == 0; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/IndexMapping.java b/datastructures-xslx/src/main/java/jxl/biff/IndexMapping.java new file mode 100755 index 0000000..a31017f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/IndexMapping.java @@ -0,0 +1,68 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; + +/** + * This class is a wrapper for a list of mappings between indices. + * It is used when removing duplicate records and specifies the new + * index for cells which have the duplicate format + */ +public final class IndexMapping { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(IndexMapping.class); + + /** + * The array of new indexes for an old one + */ + private final int[] newIndices; + + /** + * Constructor + * + * @param size the number of index numbers to be mapped + */ + public IndexMapping(int size) { + newIndices = new int[size]; + } + + /** + * Sets a mapping + * + * @param oldIndex the old index + * @param newIndex the new index + */ + public void setMapping(int oldIndex, int newIndex) { + newIndices[oldIndex] = newIndex; + } + + /** + * Gets the new cell format index + * + * @param oldIndex the existing index number + * @return the new index number + */ + public int getNewIndex(int oldIndex) { + return newIndices[oldIndex]; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/IntegerHelper.java b/datastructures-xslx/src/main/java/jxl/biff/IntegerHelper.java new file mode 100755 index 0000000..b7b23af --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/IntegerHelper.java @@ -0,0 +1,140 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Converts excel byte representations into integers + */ +public final class IntegerHelper { + /** + * Private constructor disables the instantiation of this object + */ + private IntegerHelper() { + } + + /** + * Gets an int from two bytes + * + * @param b2 the second byte + * @param b1 the first byte + * @return The integer value + */ + public static int getInt(byte b1, byte b2) { + int i1 = b1 & 0xff; + int i2 = b2 & 0xff; + int val = i2 << 8 | i1; + return val; + } + + /** + * Gets an short from two bytes + * + * @param b2 the second byte + * @param b1 the first byte + * @return The short value + */ + public static short getShort(byte b1, byte b2) { + short i1 = (short) (b1 & 0xff); + short i2 = (short) (b2 & 0xff); + short val = (short) (i2 << 8 | i1); + return val; + } + + + /** + * Gets an int from four bytes, doing all the necessary swapping + * + * @param b1 a byte + * @param b2 a byte + * @param b3 a byte + * @param b4 a byte + * @return the integer value represented by the four bytes + */ + public static int getInt(byte b1, byte b2, byte b3, byte b4) { + int i1 = getInt(b1, b2); + int i2 = getInt(b3, b4); + + int val = i2 << 16 | i1; + return val; + } + + /** + * Gets a two byte array from an integer + * + * @param i the integer + * @return the two bytes + */ + public static byte[] getTwoBytes(int i) { + byte[] bytes = new byte[2]; + + bytes[0] = (byte) (i & 0xff); + bytes[1] = (byte) ((i & 0xff00) >> 8); + + return bytes; + } + + /** + * Gets a four byte array from an integer + * + * @param i the integer + * @return a four byte array + */ + public static byte[] getFourBytes(int i) { + byte[] bytes = new byte[4]; + + int i1 = i & 0xffff; + int i2 = (i & 0xffff0000) >> 16; + + getTwoBytes(i1, bytes, 0); + getTwoBytes(i2, bytes, 2); + + return bytes; + } + + + /** + * Converts an integer into two bytes, and places it in the array at the + * specified position + * + * @param target the array to place the byte data into + * @param pos the position at which to place the data + * @param i the integer value to convert + */ + public static void getTwoBytes(int i, byte[] target, int pos) { + target[pos] = (byte) (i & 0xff); + target[pos + 1] = (byte) ((i & 0xff00) >> 8); + } + + /** + * Converts an integer into four bytes, and places it in the array at the + * specified position + * + * @param target the array which is to contain the converted data + * @param pos the position in the array in which to place the data + * @param i the integer to convert + */ + public static void getFourBytes(int i, byte[] target, int pos) { + byte[] bytes = getFourBytes(i); + target[pos] = bytes[0]; + target[pos + 1] = bytes[1]; + target[pos + 2] = bytes[2]; + target[pos + 3] = bytes[3]; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/NameRangeException.java b/datastructures-xslx/src/main/java/jxl/biff/NameRangeException.java new file mode 100755 index 0000000..b528a1f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/NameRangeException.java @@ -0,0 +1,35 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.JXLException; + +/** + * A properly typed exception in case consumers of the API specifically + * wish to handle the case when the workbook is password protected + */ +public class NameRangeException extends JXLException { + /** + * Constructor + */ + public NameRangeException() { + super(""); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/NumFormatRecordsException.java b/datastructures-xslx/src/main/java/jxl/biff/NumFormatRecordsException.java new file mode 100755 index 0000000..42ca5c3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/NumFormatRecordsException.java @@ -0,0 +1,34 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Excel places a constraint on the number of format records that + * are allowed. This exception is thrown when that number is exceeded + * This is a static exception and should be handled internally + */ +public class NumFormatRecordsException extends Exception { + /** + * Constructor + */ + public NumFormatRecordsException() { + super("Internal error: max number of FORMAT records exceeded"); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/PaletteRecord.java b/datastructures-xslx/src/main/java/jxl/biff/PaletteRecord.java new file mode 100755 index 0000000..5154e5f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/PaletteRecord.java @@ -0,0 +1,207 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.format.Colour; +import jxl.format.RGB; +import jxl.read.biff.Record; + +/** + * A record representing the RGB colour palette + */ +public class PaletteRecord extends WritableRecordData { + /** + * The number of colours in the palette + */ + private static final int numColours = 56; + /** + * The list of bespoke rgb colours used by this sheet + */ + private final RGB[] rgbColours = new RGB[numColours]; + /** + * A dirty flag indicating that this palette has been tampered with + * in some way + */ + private boolean dirty; + /** + * Flag indicating that the palette was read in + */ + private final boolean read; + /** + * Initialized flag + */ + private boolean initialized; + + /** + * Constructor + * + * @param t the raw bytes + */ + public PaletteRecord(Record t) { + super(t); + + initialized = false; + dirty = false; + read = true; + } + + /** + * Default constructor - used when there is no palette specified + */ + public PaletteRecord() { + super(Type.PALETTE); + + initialized = true; + dirty = false; + read = false; + + // Initialize the array with all the default colours + Colour[] colours = Colour.getAllColours(); + + for (int i = 0; i < colours.length; i++) { + Colour c = colours[i]; + setColourRGB(c, + c.getDefaultRGB().getRed(), + c.getDefaultRGB().getGreen(), + c.getDefaultRGB().getBlue()); + } + } + + /** + * Accessor for the binary data - used when copying + * + * @return the binary data + */ + public byte[] getData() { + // Palette was read in, but has not been changed + if (read && !dirty) { + return getRecord().getData(); + } + + byte[] data = new byte[numColours * 4 + 2]; + int pos = 0; + + // Set the number of records + IntegerHelper.getTwoBytes(numColours, data, pos); + + // Set the rgb content + for (int i = 0; i < numColours; i++) { + pos = i * 4 + 2; + data[pos] = (byte) rgbColours[i].getRed(); + data[pos + 1] = (byte) rgbColours[i].getGreen(); + data[pos + 2] = (byte) rgbColours[i].getBlue(); + } + + return data; + } + + /** + * Initialize the record data + */ + private void initialize() { + byte[] data = getRecord().getData(); + + int numrecords = IntegerHelper.getInt(data[0], data[1]); + + for (int i = 0; i < numrecords; i++) { + int pos = i * 4 + 2; + int red = IntegerHelper.getInt(data[pos], (byte) 0); + int green = IntegerHelper.getInt(data[pos + 1], (byte) 0); + int blue = IntegerHelper.getInt(data[pos + 2], (byte) 0); + rgbColours[i] = new RGB(red, green, blue); + } + + initialized = true; + } + + /** + * Accessor for the dirty flag, which indicates if this palette has been + * modified + * + * @return TRUE if the palette has been modified, FALSE if it is the default + */ + public boolean isDirty() { + return dirty; + } + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public void setColourRGB(Colour c, int r, int g, int b) { + // Only colours on the standard palette with values 8-64 are acceptable + int pos = c.getValue() - 8; + if (pos < 0 || pos >= numColours) { + return; + } + + if (!initialized) { + initialize(); + } + + // Force the colours into the range 0-255 + r = setValueRange(r, 0, 0xff); + g = setValueRange(g, 0, 0xff); + b = setValueRange(b, 0, 0xff); + + rgbColours[pos] = new RGB(r, g, b); + + // Indicate that the palette has been modified + dirty = true; + } + + /** + * Gets the colour RGB from the palette + * + * @param c the colour + * @return an RGB structure + */ + public RGB getColourRGB(Colour c) { + // Only colours on the standard palette with values 8-64 are acceptable + int pos = c.getValue() - 8; + if (pos < 0 || pos >= numColours) { + return c.getDefaultRGB(); + } + + if (!initialized) { + initialize(); + } + + return rgbColours[pos]; + } + + /** + * Forces the value passed in to be between the range passed in + * + * @param val the value to constrain + * @param min the minimum acceptable value + * @param max the maximum acceptable value + * @return the constrained value + */ + private int setValueRange(int val, int min, int max) { + val = Math.max(val, min); + val = Math.min(val, max); + return val; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/RangeImpl.java b/datastructures-xslx/src/main/java/jxl/biff/RangeImpl.java new file mode 100755 index 0000000..ba85eaa --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/RangeImpl.java @@ -0,0 +1,157 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.Cell; +import jxl.Range; +import jxl.Sheet; +import jxl.common.Logger; + +/** + * Implementation class for the Range interface. This merely + * holds the raw range information, and when the time comes, it + * interrogates the workbook for the object. + * This does not keep handles to the objects for performance reasons, + * as this could impact garbage collection on larger spreadsheets + */ +public class RangeImpl implements Range { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(RangeImpl.class); + + /** + * A handle to the workbook + */ + private final WorkbookMethods workbook; + + /** + * The sheet index containing the column at the top left + */ + private final int sheet1; + + /** + * The column number of the cell at the top left of the range + */ + private final int column1; + + /** + * The row number of the cell at the top left of the range + */ + private final int row1; + + /** + * The sheet index of the cell at the bottom right + */ + private final int sheet2; + + /** + * The column index of the cell at the bottom right + */ + private final int column2; + + /** + * The row index of the cell at the bottom right + */ + private final int row2; + + /** + * Constructor + * + * @param w the workbook + * @param es the external sheet + * @param s1 the sheet of the top left cell of the range + * @param c1 the column number of the top left cell of the range + * @param r1 the row number of the top left cell of the range + * @param s2 the sheet of the bottom right cell + * @param c2 the column number of the bottom right cell of the range + * @param r2 the row number of the bottomr right cell of the range + */ + public RangeImpl(WorkbookMethods w, + int s1, int c1, int r1, + int s2, int c2, int r2) { + workbook = w; + sheet1 = s1; + sheet2 = s2; + row1 = r1; + row2 = r2; + column1 = c1; + column2 = c2; + } + + /** + * Gets the cell at the top left of this range + * + * @return the cell at the top left + */ + public Cell getTopLeft() { + Sheet s = workbook.getReadSheet(sheet1); + + if (column1 < s.getColumns() && + row1 < s.getRows()) { + return s.getCell(column1, row1); + } else { + return new EmptyCell(column1, row1); + } + } + + /** + * Gets the cell at the bottom right of this range + * + * @return the cell at the bottom right + */ + public Cell getBottomRight() { + Sheet s = workbook.getReadSheet(sheet2); + + if (column2 < s.getColumns() && + row2 < s.getRows()) { + return s.getCell(column2, row2); + } else { + return new EmptyCell(column2, row2); + } + } + + /** + * Gets the index of the first sheet in the range + * + * @return the index of the first sheet in the range + */ + public int getFirstSheetIndex() { + return sheet1; + } + + /** + * Gets the index of the last sheet in the range + * + * @return the index of the last sheet in the range + */ + public int getLastSheetIndex() { + return sheet2; + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/RecordData.java b/datastructures-xslx/src/main/java/jxl/biff/RecordData.java new file mode 100755 index 0000000..163e4a1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/RecordData.java @@ -0,0 +1,83 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.read.biff.Record; + +/** + * The record data within a record + */ +public abstract class RecordData { + /** + * The raw data + */ + private Record record; + + /** + * The Biff code for this record. This is set up when the record is + * used for writing + */ + private final int code; + + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + protected RecordData(Record r) { + record = r; + code = r.getCode(); + } + + /** + * Constructor used by the writable records + * + * @param t the type + */ + protected RecordData(Type t) { + code = t.value; + } + + /** + * Returns the raw data to its subclasses + * + * @return the raw data + */ + protected Record getRecord() { + return record; + } + + /** + * Accessor for the code + * + * @return the code + */ + protected final int getCode() { + return code; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/SheetRangeImpl.java b/datastructures-xslx/src/main/java/jxl/biff/SheetRangeImpl.java new file mode 100755 index 0000000..e193ee0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/SheetRangeImpl.java @@ -0,0 +1,293 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.Cell; +import jxl.Range; +import jxl.Sheet; + +/** + * Implementation class for the Range interface. This merely + * holds the raw range information. This implementation is used + * for ranges which are present on the current working sheet, so the + * getSheetIndex merely returns -1 + */ +public class SheetRangeImpl implements Range { + /** + * A handle to the sheet containing this range + */ + private final Sheet sheet; + + /** + * The column number of the cell at the top left of the range + */ + private int column1; + + /** + * The row number of the cell at the top left of the range + */ + private int row1; + + /** + * The column index of the cell at the bottom right + */ + private int column2; + + /** + * The row index of the cell at the bottom right + */ + private int row2; + + /** + * Constructor + * + * @param s the sheet containing the range + * @param c1 the column number of the top left cell of the range + * @param r1 the row number of the top left cell of the range + * @param c2 the column number of the bottom right cell of the range + * @param r2 the row number of the bottomr right cell of the range + */ + public SheetRangeImpl(Sheet s, int c1, int r1, + int c2, int r2) { + sheet = s; + row1 = r1; + row2 = r2; + column1 = c1; + column2 = c2; + } + + /** + * A copy constructor used for copying ranges between sheets + * + * @param c the range to copy from + * @param s the writable sheet + */ + public SheetRangeImpl(SheetRangeImpl c, Sheet s) { + sheet = s; + row1 = c.row1; + row2 = c.row2; + column1 = c.column1; + column2 = c.column2; + } + + /** + * Gets the cell at the top left of this range + * + * @return the cell at the top left + */ + public Cell getTopLeft() { + // If the print area exceeds the bounds of the sheet, then handle + // it here. The sheet implementation will give a NPE + if (column1 >= sheet.getColumns() || + row1 >= sheet.getRows()) { + return new EmptyCell(column1, row1); + } + + return sheet.getCell(column1, row1); + } + + /** + * Gets the cell at the bottom right of this range + * + * @return the cell at the bottom right + */ + public Cell getBottomRight() { + // If the print area exceeds the bounds of the sheet, then handle + // it here. The sheet implementation will give a NPE + if (column2 >= sheet.getColumns() || + row2 >= sheet.getRows()) { + return new EmptyCell(column2, row2); + } + + return sheet.getCell(column2, row2); + } + + /** + * Not supported. Returns -1, indicating that it refers to the current + * sheet + * + * @return -1 + */ + public int getFirstSheetIndex() { + return -1; + } + + /** + * Not supported. Returns -1, indicating that it refers to the current + * sheet + * + * @return -1 + */ + public int getLastSheetIndex() { + return -1; + } + + /** + * Sees whether there are any intersections between this range and the + * range passed in. This method is used internally by the WritableSheet to + * verify the integrity of merged cells, hyperlinks etc. Ranges are + * only ever compared for the same sheet + * + * @param range the range to compare against + * @return TRUE if the ranges intersect, FALSE otherwise + */ + public boolean intersects(SheetRangeImpl range) { + if (range == this) { + return true; + } + + return row2 >= range.row1 && + row1 <= range.row2 && + column2 >= range.column1 && + column1 <= range.column2; + } + + /** + * To string method - primarily used during debugging + * + * @return the string version of this object + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + CellReferenceHelper.getCellReference(column1, row1, sb); + sb.append('-'); + CellReferenceHelper.getCellReference(column2, row2, sb); + return sb.toString(); + } + + /** + * A row has been inserted, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + public void insertRow(int r) { + if (r > row2) { + return; + } + + if (r <= row1) { + row1++; + } + + if (r <= row2) { + row2++; + } + } + + /** + * A column has been inserted, so adjust the range objects accordingly + * + * @param c the column which has been inserted + */ + public void insertColumn(int c) { + if (c > column2) { + return; + } + + if (c <= column1) { + column1++; + } + + if (c <= column2) { + column2++; + } + } + + /** + * A row has been removed, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + public void removeRow(int r) { + if (r > row2) { + return; + } + + if (r < row1) { + row1--; + } + + if (r < row2) { + row2--; + } + } + + /** + * A column has been removed, so adjust the range objects accordingly + * + * @param c the column which has been removed + */ + public void removeColumn(int c) { + if (c > column2) { + return; + } + + if (c < column1) { + column1--; + } + + if (c < column2) { + column2--; + } + } + + /** + * Standard hash code method + * + * @return the hash code + */ + public int hashCode() { + return 0xffff ^ row1 ^ row2 ^ column1 ^ column2; + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the two objects are the same, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof SheetRangeImpl)) { + return false; + } + + SheetRangeImpl compare = (SheetRangeImpl) o; + + return (column1 == compare.column1 && + column2 == compare.column2 && + row1 == compare.row1 && + row2 == compare.row2); + } + +} + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/StringHelper.java b/datastructures-xslx/src/main/java/jxl/biff/StringHelper.java new file mode 100755 index 0000000..b3eed46 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/StringHelper.java @@ -0,0 +1,199 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.UnsupportedEncodingException; +import jxl.WorkbookSettings; +import jxl.common.Logger; + +/** + * Helper function to convert Java string objects to and from the byte + * representations + */ +public final class StringHelper { + // Due to a a Sun bug in some versions of JVM 1.4, the UnicodeLittle + // encoding doesn't always work. Making this a public static field + // enables client code access to this (but in an undocumented and + // unsupported fashion). Suggested alternative values for this + // are "UTF-16LE" or "UnicodeLittleUnmarked" + public static String UNICODE_ENCODING = "UnicodeLittle"; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(StringHelper.class); + + /** + * Private default constructor to prevent instantiation + */ + private StringHelper() { + } + + /** + * Gets the bytes of the specified string. This will simply return the ASCII + * values of the characters in the string + * + * @param s the string to convert into bytes + * @return the ASCII values of the characters in the string + * @deprecated + */ + public static byte[] getBytes(String s) { + return s.getBytes(); + } + + /** + * Gets the bytes of the specified string. This will simply return the ASCII + * values of the characters in the string + * + * @param s the string to convert into bytes + * @return the ASCII values of the characters in the string + */ + public static byte[] getBytes(String s, WorkbookSettings ws) { + try { + return s.getBytes(ws.getEncoding()); + } catch (UnsupportedEncodingException e) { + // fail silently + return null; + } + } + + /** + * Converts the string into a little-endian array of Unicode bytes + * + * @param s the string to convert + * @return the unicode values of the characters in the string + */ + public static byte[] getUnicodeBytes(String s) { + try { + byte[] b = s.getBytes(UNICODE_ENCODING); + + // Sometimes this method writes out the unicode + // identifier + if (b.length == (s.length() * 2 + 2)) { + byte[] b2 = new byte[b.length - 2]; + System.arraycopy(b, 2, b2, 0, b2.length); + b = b2; + } + return b; + } catch (UnsupportedEncodingException e) { + // Fail silently + return null; + } + } + + + /** + * Gets the ASCII bytes from the specified string and places them in the + * array at the specified position + * + * @param pos the position at which to place the converted data + * @param s the string to convert + * @param d the byte array which will contain the converted string data + */ + public static void getBytes(String s, byte[] d, int pos) { + byte[] b = getBytes(s); + System.arraycopy(b, 0, d, pos, b.length); + } + + /** + * Inserts the unicode byte representation of the specified string into the + * array passed in + * + * @param pos the position at which to insert the converted data + * @param s the string to convert + * @param d the byte array which will hold the string data + */ + public static void getUnicodeBytes(String s, byte[] d, int pos) { + byte[] b = getUnicodeBytes(s); + System.arraycopy(b, 0, d, pos, b.length); + } + + /** + * Gets a string from the data array using the character encoding for + * this workbook + * + * @param pos The start position of the string + * @param length The number of bytes to transform into a string + * @param d The byte data + * @param ws the workbook settings + * @return the string built up from the raw bytes + */ + public static String getString(byte[] d, int length, int pos, + WorkbookSettings ws) { + if (length == 0) { + return ""; // Reduces number of new Strings + } + + try { + return new String(d, pos, length, ws.getEncoding()); + // byte[] b = new byte[length]; + // System.arraycopy(d, pos, b, 0, length); + // return new String(b, ws.getEncoding()); + } catch (UnsupportedEncodingException e) { + logger.warn(e.toString()); + return ""; + } + } + + /** + * Gets a string from the data array + * + * @param pos The start position of the string + * @param length The number of characters to be converted into a string + * @param d The byte data + * @return the string built up from the unicode characters + */ + public static String getUnicodeString(byte[] d, int length, int pos) { + try { + byte[] b = new byte[length * 2]; + System.arraycopy(d, pos, b, 0, length * 2); + return new String(b, UNICODE_ENCODING); + } catch (UnsupportedEncodingException e) { + // Fail silently + return ""; + } + } + + /** + * Replaces all instances of search with replace in the input. + * Even though later versions of java can use string.replace() + * this is included Java 1.2 compatibility + * + * @param input the format string + * @param search the Excel character to be replaced + * @param replace the java equivalent + * @return the input string with the specified substring replaced + */ + public static final String replace(String input, + String search, + String replace) { + String fmtstr = input; + int pos = fmtstr.indexOf(search); + while (pos != -1) { + StringBuffer tmp = new StringBuffer(fmtstr.substring(0, pos)); + tmp.append(replace); + tmp.append(fmtstr.substring(pos + search.length())); + fmtstr = tmp.toString(); + pos = fmtstr.indexOf(search, pos + replace.length()); + } + return fmtstr; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/Type.java b/datastructures-xslx/src/main/java/jxl/biff/Type.java new file mode 100755 index 0000000..761cb66 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/Type.java @@ -0,0 +1,612 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * An enumeration class which contains the biff types + */ +public final class Type { + /** + * + */ + public static final Type BOF = new Type(0x809); + /** + * + */ + public static final Type EOF = new Type(0x0a); + /** + * + */ + public static final Type BOUNDSHEET = new Type(0x85); + /** + * + */ + public static final Type SUPBOOK = new Type(0x1ae); + + /** + * + */ + public static final Type EXTERNSHEET = new Type(0x17); + /** + * + */ + public static final Type DIMENSION = new Type(0x200); + /** + * + */ + public static final Type BLANK = new Type(0x201); + /** + * + */ + public static final Type MULBLANK = new Type(0xbe); + /** + * + */ + public static final Type ROW = new Type(0x208); + /** + * + */ + public static final Type NOTE = new Type(0x1c); + /** + * + */ + public static final Type TXO = new Type(0x1b6); + /** + * + */ + public static final Type RK = new Type(0x7e); + /** + * + */ + public static final Type RK2 = new Type(0x27e); + /** + * + */ + public static final Type MULRK = new Type(0xbd); + /** + * + */ + public static final Type INDEX = new Type(0x20b); + /** + * + */ + public static final Type DBCELL = new Type(0xd7); + /** + * + */ + public static final Type SST = new Type(0xfc); + /** + * + */ + public static final Type COLINFO = new Type(0x7d); + /** + * + */ + public static final Type EXTSST = new Type(0xff); + /** + * + */ + public static final Type CONTINUE = new Type(0x3c); + /** + * + */ + public static final Type LABEL = new Type(0x204); + /** + * + */ + public static final Type RSTRING = new Type(0xd6); + /** + * + */ + public static final Type LABELSST = new Type(0xfd); + /** + * + */ + public static final Type NUMBER = new Type(0x203); + /** + * + */ + public static final Type NAME = new Type(0x18); + /** + * + */ + public static final Type TABID = new Type(0x13d); + /** + * + */ + public static final Type ARRAY = new Type(0x221); + /** + * + */ + public static final Type STRING = new Type(0x207); + /** + * + */ + public static final Type FORMULA = new Type(0x406); + /** + * + */ + public static final Type FORMULA2 = new Type(0x6); + /** + * + */ + public static final Type SHAREDFORMULA = new Type(0x4bc); + /** + * + */ + public static final Type FORMAT = new Type(0x41e); + /** + * + */ + public static final Type XF = new Type(0xe0); + /** + * + */ + public static final Type BOOLERR = new Type(0x205); + /** + * + */ + public static final Type INTERFACEHDR = new Type(0xe1); + /** + * + */ + public static final Type SAVERECALC = new Type(0x5f); + /** + * + */ + public static final Type INTERFACEEND = new Type(0xe2); + /** + * + */ + public static final Type XCT = new Type(0x59); + /** + * + */ + public static final Type CRN = new Type(0x5a); + /** + * + */ + public static final Type DEFCOLWIDTH = new Type(0x55); + /** + * + */ + public static final Type DEFAULTROWHEIGHT = new Type(0x225); + /** + * + */ + public static final Type WRITEACCESS = new Type(0x5c); + /** + * + */ + public static final Type WSBOOL = new Type(0x81); + /** + * + */ + public static final Type CODEPAGE = new Type(0x42); + /** + * + */ + public static final Type DSF = new Type(0x161); + /** + * + */ + public static final Type FNGROUPCOUNT = new Type(0x9c); + /** + * + */ + public static final Type FILTERMODE = new Type(0x9b); + /** + * + */ + public static final Type AUTOFILTERINFO = new Type(0x9d); + /** + * + */ + public static final Type AUTOFILTER = new Type(0x9e); + /** + * + */ + public static final Type COUNTRY = new Type(0x8c); + /** + * + */ + public static final Type PROTECT = new Type(0x12); + /** + * + */ + public static final Type SCENPROTECT = new Type(0xdd); + /** + * + */ + public static final Type OBJPROTECT = new Type(0x63); + /** + * + */ + public static final Type PRINTHEADERS = new Type(0x2a); + /** + * + */ + public static final Type HEADER = new Type(0x14); + /** + * + */ + public static final Type FOOTER = new Type(0x15); + /** + * + */ + public static final Type HCENTER = new Type(0x83); + /** + * + */ + public static final Type VCENTER = new Type(0x84); + /** + * + */ + public static final Type FILEPASS = new Type(0x2f); + /** + * + */ + public static final Type SETUP = new Type(0xa1); + /** + * + */ + public static final Type PRINTGRIDLINES = new Type(0x2b); + /** + * + */ + public static final Type GRIDSET = new Type(0x82); + /** + * + */ + public static final Type GUTS = new Type(0x80); + /** + * + */ + public static final Type WINDOWPROTECT = new Type(0x19); + /** + * + */ + public static final Type PROT4REV = new Type(0x1af); + /** + * + */ + public static final Type PROT4REVPASS = new Type(0x1bc); + /** + * + */ + public static final Type PASSWORD = new Type(0x13); + /** + * + */ + public static final Type REFRESHALL = new Type(0x1b7); + /** + * + */ + public static final Type WINDOW1 = new Type(0x3d); + /** + * + */ + public static final Type WINDOW2 = new Type(0x23e); + /** + * + */ + public static final Type BACKUP = new Type(0x40); + /** + * + */ + public static final Type HIDEOBJ = new Type(0x8d); + /** + * + */ + public static final Type NINETEENFOUR = new Type(0x22); + /** + * + */ + public static final Type PRECISION = new Type(0xe); + /** + * + */ + public static final Type BOOKBOOL = new Type(0xda); + /** + * + */ + public static final Type FONT = new Type(0x31); + /** + * + */ + public static final Type MMS = new Type(0xc1); + /** + * + */ + public static final Type CALCMODE = new Type(0x0d); + /** + * + */ + public static final Type CALCCOUNT = new Type(0x0c); + /** + * + */ + public static final Type REFMODE = new Type(0x0f); + /** + * + */ + public static final Type TEMPLATE = new Type(0x60); + /** + * + */ + public static final Type OBJPROJ = new Type(0xd3); + /** + * + */ + public static final Type DELTA = new Type(0x10); + /** + * + */ + public static final Type MERGEDCELLS = new Type(0xe5); + /** + * + */ + public static final Type ITERATION = new Type(0x11); + /** + * + */ + public static final Type STYLE = new Type(0x293); + /** + * + */ + public static final Type USESELFS = new Type(0x160); + /** + * + */ + public static final Type VERTICALPAGEBREAKS = new Type(0x1a); + /** + * + */ + public static final Type HORIZONTALPAGEBREAKS = new Type(0x1b); + /** + * + */ + public static final Type SELECTION = new Type(0x1d); + /** + * + */ + public static final Type HLINK = new Type(0x1b8); + /** + * + */ + public static final Type OBJ = new Type(0x5d); + /** + * + */ + public static final Type MSODRAWING = new Type(0xec); + /** + * + */ + public static final Type MSODRAWINGGROUP = new Type(0xeb); + /** + * + */ + public static final Type LEFTMARGIN = new Type(0x26); + /** + * + */ + public static final Type RIGHTMARGIN = new Type(0x27); + /** + * + */ + public static final Type TOPMARGIN = new Type(0x28); + /** + * + */ + public static final Type BOTTOMMARGIN = new Type(0x29); + /** + * + */ + public static final Type EXTERNNAME = new Type(0x23); + /** + * + */ + public static final Type PALETTE = new Type(0x92); + /** + * + */ + public static final Type PLS = new Type(0x4d); + /** + * + */ + public static final Type SCL = new Type(0xa0); + /** + * + */ + public static final Type PANE = new Type(0x41); + /** + * + */ + public static final Type WEIRD1 = new Type(0xef); + /** + * + */ + public static final Type SORT = new Type(0x90); + /** + * + */ + public static final Type CONDFMT = new Type(0x1b0); + /** + * + */ + public static final Type CF = new Type(0x1b1); + /** + * + */ + public static final Type DV = new Type(0x1be); + /** + * + */ + public static final Type DVAL = new Type(0x1b2); + /** + * + */ + public static final Type BUTTONPROPERTYSET = new Type(0x1ba); + /** + * + */ + public static final Type EXCEL9FILE = new Type(0x1c0); + /** + * + */ + public static final Type FONTX = new Type(0x1026); + /** + * + */ + public static final Type IFMT = new Type(0x104e); + /** + * + */ + public static final Type FBI = new Type(0x1060); + /** + * + */ + public static final Type ALRUNS = new Type(0x1050); + /** + * + */ + public static final Type SERIES = new Type(0x1003); + /** + * + */ + public static final Type SERIESLIST = new Type(0x1016); + /** + * + */ + public static final Type SBASEREF = new Type(0x1048); + /** + * + */ + public static final Type UNKNOWN = new Type(0xffff); + /** + * + */ + // public static final Type R = new Type(0xffff); + + // Unknown types + public static final Type U1C0 = new Type(0x1c0); + public static final Type U1C1 = new Type(0x1c1); + + // Chart types + /** + * An array of all types + */ + private static Type[] types = new Type[0]; + private static final ArbitraryType arbitrary = new ArbitraryType(); + /** + * The biff value for this type + */ + public final int value; + /** + * Constructor + * Sets the biff value and adds this type to the array of all types + * + * @param v the biff code for the type + */ + private Type(int v) { + value = v; + + // Add to the list of available types + Type[] newTypes = new Type[types.length + 1]; + System.arraycopy(types, 0, newTypes, 0, types.length); + newTypes[types.length] = this; + types = newTypes; + } + /** + * Constructor used for the creation of arbitrary types + */ + private Type(int v, ArbitraryType arb) { + value = v; + } + + /** + * Gets the type object from its integer value + * + * @param v the internal code + * @return the type + */ + public static Type getType(int v) { + for (int i = 0; i < types.length; i++) { + if (types[i].value == v) { + return types[i]; + } + } + + return UNKNOWN; + } + + /** + * Used to create an arbitrary record type. This method is only + * used during bespoke debugging process. The creation of an + * arbitrary type does not add it to the static list of known types + */ + public static Type createType(int v) { + return new Type(v, arbitrary); + } + + /** + * Standard hash code method + * + * @return the hash code + */ + public int hashCode() { + return value; + } + + // Pivot stuff + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof Type)) { + return false; + } + + Type t = (Type) o; + + return value == t.value; + } + + private static class ArbitraryType { + } + +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/WorkbookMethods.java b/datastructures-xslx/src/main/java/jxl/biff/WorkbookMethods.java new file mode 100755 index 0000000..b1700b1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/WorkbookMethods.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.Sheet; + +/** + * An interface containing some common workbook methods. This so that + * objects which are re-used for both readable and writable workbooks + * can still make the same method calls on a workbook + */ +public interface WorkbookMethods { + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + Sheet getReadSheet(int index); + + /** + * Gets the name at the specified index + * + * @param index the index into the name table + * @return the name of the cell + * @throws NameRangeException + */ + String getName(int index) throws NameRangeException; + + /** + * Gets the index of the name record for the name + * + * @param name the name + * @return the index in the name table + */ + int getNameIndex(String name); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/WorkspaceInformationRecord.java b/datastructures-xslx/src/main/java/jxl/biff/WorkspaceInformationRecord.java new file mode 100755 index 0000000..78607a2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/WorkspaceInformationRecord.java @@ -0,0 +1,143 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * A record detailing whether the sheet is protected + */ +public class WorkspaceInformationRecord extends WritableRecordData { + // the masks + private static final int FIT_TO_PAGES = 0x100; + private static final int SHOW_ROW_OUTLINE_SYMBOLS = 0x400; + private static final int SHOW_COLUMN_OUTLINE_SYMBOLS = 0x800; + private static final int DEFAULT_OPTIONS = 0x4c1; + // the logger + private static final Logger logger = + Logger.getLogger(WorkspaceInformationRecord.class); + /** + * The options byte + */ + private int wsoptions; + /** + * The row outlines + */ + private boolean rowOutlines; + /** + * The column outlines + */ + private boolean columnOutlines; + /** + * The fit to pages flag + */ + private boolean fitToPages; + + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public WorkspaceInformationRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + + wsoptions = IntegerHelper.getInt(data[0], data[1]); + fitToPages = (wsoptions | FIT_TO_PAGES) != 0; + rowOutlines = (wsoptions | SHOW_ROW_OUTLINE_SYMBOLS) != 0; + columnOutlines = (wsoptions | SHOW_COLUMN_OUTLINE_SYMBOLS) != 0; + } + + /** + * Constructs this object from the raw data + */ + public WorkspaceInformationRecord() { + super(Type.WSBOOL); + wsoptions = DEFAULT_OPTIONS; + } + + /** + * Gets the fit to pages flag + * + * @return TRUE if fit to pages is set + */ + public boolean getFitToPages() { + return fitToPages; + } + + /** + * Sets the fit to page flag + * + * @param b fit to page indicator + */ + public void setFitToPages(boolean b) { + fitToPages = b; + } + + /** + * Sets the outlines + */ + public void setRowOutlines(boolean ro) { + rowOutlines = true; + } + + /** + * Sets the outlines + */ + public void setColumnOutlines(boolean ro) { + rowOutlines = true; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[2]; + + if (fitToPages) { + wsoptions |= FIT_TO_PAGES; + } + + if (rowOutlines) { + wsoptions |= SHOW_ROW_OUTLINE_SYMBOLS; + } + + if (columnOutlines) { + wsoptions |= SHOW_COLUMN_OUTLINE_SYMBOLS; + } + + IntegerHelper.getTwoBytes(wsoptions, data, 0); + + return data; + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/WritableRecordData.java b/datastructures-xslx/src/main/java/jxl/biff/WritableRecordData.java new file mode 100755 index 0000000..41c604b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/WritableRecordData.java @@ -0,0 +1,139 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Extension of the standard RecordData which is used to support those + * records which, once read, may also be written + */ +public abstract class WritableRecordData extends RecordData + implements ByteData { + /** + * The maximum length allowed by Excel for any record length + */ + protected static final int maxRecordLength = 8228; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(WritableRecordData.class); + + /** + * Constructor used by the writable records + * + * @param t the biff type of this record + */ + protected WritableRecordData(Type t) { + super(t); + } + + /** + * Constructor used when reading a record + * + * @param t the raw data read from the biff file + */ + protected WritableRecordData(Record t) { + super(t); + } + + /** + * Used when writing out records. This portion of the method handles the + * biff code and the length of the record and appends on the data retrieved + * from the subclasses + * + * @return the full record data to be written out to the compound file + */ + public final byte[] getBytes() { + byte[] data = getData(); + + int dataLength = data.length; + + // Don't the call the automatic continuation code for now + // Assert.verify(dataLength <= maxRecordLength - 4); + // If the bytes length is greater than the max record length + // then split out the data set into continue records + if (data.length > maxRecordLength - 4) { + dataLength = maxRecordLength - 4; + data = handleContinueRecords(data); + } + + byte[] bytes = new byte[data.length + 4]; + + System.arraycopy(data, 0, bytes, 4, data.length); + + IntegerHelper.getTwoBytes(getCode(), bytes, 0); + IntegerHelper.getTwoBytes(dataLength, bytes, 2); + + return bytes; + } + + /** + * The number of bytes for this record exceeds the maximum record + * length, so a continue is required + * + * @param data the raw data + * @return the continued data + */ + private byte[] handleContinueRecords(byte[] data) { + // Deduce the number of continue records + int continuedData = data.length - (maxRecordLength - 4); + int numContinueRecords = continuedData / (maxRecordLength - 4) + 1; + + // Create the new byte array, allowing for the continue records + // code and length + byte[] newdata = new byte[data.length + numContinueRecords * 4]; + + // Copy the bona fide record data into the beginning of the super + // record + System.arraycopy(data, 0, newdata, 0, maxRecordLength - 4); + int oldarraypos = maxRecordLength - 4; + int newarraypos = maxRecordLength - 4; + + // Now handle all the continue records + for (int i = 0; i < numContinueRecords; i++) { + // The number of bytes to add into the new array + int length = Math.min(data.length - oldarraypos, maxRecordLength - 4); + + // Add in the continue record code + IntegerHelper.getTwoBytes(Type.CONTINUE.value, newdata, newarraypos); + IntegerHelper.getTwoBytes(length, newdata, newarraypos + 2); + + // Copy in as much of the new data as possible + System.arraycopy(data, oldarraypos, newdata, newarraypos + 4, length); + + // Update the position counters + oldarraypos += length; + newarraypos += length + 4; + } + + return newdata; + } + + /** + * Abstract method called by the getBytes method. Subclasses implement + * this method to incorporate their specific binary data - excluding the + * biff code and record length, which is handled by this class + * + * @return subclass specific biff data + */ + protected abstract byte[] getData(); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/XCTRecord.java b/datastructures-xslx/src/main/java/jxl/biff/XCTRecord.java new file mode 100755 index 0000000..8298ee7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/XCTRecord.java @@ -0,0 +1,48 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.read.biff.Record; + +/** + * A record representing the XCT record + */ +public class XCTRecord extends WritableRecordData { + + /** + * Constructor + * + * @param t the raw bytes + */ + public XCTRecord(Record t) { + super(t); + } + + + /** + * Accessor for the binary data - used when copying + * + * @return the binary data + */ + public byte[] getData() { + return getRecord().getData(); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/XFRecord.java b/datastructures-xslx/src/main/java/jxl/biff/XFRecord.java new file mode 100755 index 0000000..32c0270 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/XFRecord.java @@ -0,0 +1,1501 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + + +package jxl.biff; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import jxl.WorkbookSettings; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.Font; +import jxl.format.Format; +import jxl.format.Orientation; +import jxl.format.Pattern; +import jxl.format.VerticalAlignment; +import jxl.read.biff.Record; + +/** + * Holds an extended formatting record + */ +public class XFRecord extends WritableRecordData implements CellFormat { + public static final BiffType biff8 = new BiffType(); + public static final BiffType biff7 = new BiffType(); + protected static final XFType cell = new XFType(); + protected static final XFType style = new XFType(); + /** + * Constants for the used attributes + */ + private static final int USE_FONT = 0x4; + private static final int USE_FORMAT = 0x8; + private static final int USE_ALIGNMENT = 0x10; + private static final int USE_BORDER = 0x20; + private static final int USE_BACKGROUND = 0x40; + private static final int USE_PROTECTION = 0x80; + private static final int USE_DEFAULT_VALUE = 0xf8; + /** + * The list of built in date format values + */ + private static final int[] dateFormats = new int[] + {0xe, + 0xf, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x2d, + 0x2e, + 0x2f}; + /** + * The list of java date format equivalents + */ + private static final DateFormat[] javaDateFormats = new DateFormat[] + {SimpleDateFormat.getDateInstance(DateFormat.SHORT), + SimpleDateFormat.getDateInstance(DateFormat.MEDIUM), + new SimpleDateFormat("d-MMM"), + new SimpleDateFormat("MMM-yy"), + new SimpleDateFormat("h:mm a"), + new SimpleDateFormat("h:mm:ss a"), + new SimpleDateFormat("H:mm"), + new SimpleDateFormat("H:mm:ss"), + new SimpleDateFormat("M/d/yy H:mm"), + new SimpleDateFormat("mm:ss"), + new SimpleDateFormat("H:mm:ss"), + new SimpleDateFormat("mm:ss.S")}; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(XFRecord.class); + /** + * The list of built in number format values + */ + private static final int[] numberFormats = new int[] + {0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0x9, + 0xa, + 0xb, + 0x25, + 0x26, + 0x27, + 0x28, + 0x29, + 0x2a, + 0x2b, + 0x2c, + 0x30}; + /** + * The list of java number format equivalents + */ + private static final NumberFormat[] javaNumberFormats = new NumberFormat[] + {new DecimalFormat("0"), + new DecimalFormat("0.00"), + new DecimalFormat("#,##0"), + new DecimalFormat("#,##0.00"), + new DecimalFormat("$#,##0;($#,##0)"), + new DecimalFormat("$#,##0;($#,##0)"), + new DecimalFormat("$#,##0.00;($#,##0.00)"), + new DecimalFormat("$#,##0.00;($#,##0.00)"), + new DecimalFormat("0%"), + new DecimalFormat("0.00%"), + new DecimalFormat("0.00E00"), + new DecimalFormat("#,##0;(#,##0)"), + new DecimalFormat("#,##0;(#,##0)"), + new DecimalFormat("#,##0.00;(#,##0.00)"), + new DecimalFormat("#,##0.00;(#,##0.00)"), + new DecimalFormat("#,##0;(#,##0)"), + new DecimalFormat("$#,##0;($#,##0)"), + new DecimalFormat("#,##0.00;(#,##0.00)"), + new DecimalFormat("$#,##0.00;($#,##0.00)"), + new DecimalFormat("##0.0E0")}; + /** + * The index to the format record + */ + public int formatIndex; + /** + * The index of the parent format + */ + private int parentFormat; + /** + * The format type + */ + private XFType xfFormatType; + /** + * Indicates whether this is a date formatting record + */ + private boolean date; + /** + * Indicates whether this is a number formatting record + */ + private boolean number; + /** + * The date format for this record. Deduced when the record is + * read in from a spreadsheet + */ + private DateFormat dateFormat; + /** + * The number format for this record. Deduced when the record is read in + * from a spreadsheet + */ + private NumberFormat numberFormat; + /** + * The used attribute. Needs to be preserved in order to get accurate + * rationalization + */ + private byte usedAttributes; + /** + * The index to the font record used by this XF record + */ + private int fontIndex; + /** + * Flag to indicate whether this XF record represents a locked cell + */ + private boolean locked; + /** + * Flag to indicate whether this XF record is hidden + */ + private boolean hidden; + /** + * The alignment for this cell (left, right, centre) + */ + private Alignment align; + /** + * The vertical alignment for the cell (top, bottom, centre) + */ + private VerticalAlignment valign; + /** + * The orientation of the cell + */ + private Orientation orientation; + /** + * Flag to indicates whether the data (normally text) in the cell will be + * wrapped around to fit in the cell width + */ + private boolean wrap; + /** + * Indentation of the cell text + */ + private int indentation; + /** + * Flag to indicate that this format is shrink to fit + */ + private boolean shrinkToFit; + /** + * The border indicator for the left of this cell + */ + private BorderLineStyle leftBorder; + /** + * The border indicator for the right of the cell + */ + private BorderLineStyle rightBorder; + /** + * The border indicator for the top of the cell + */ + private BorderLineStyle topBorder; + /** + * The border indicator for the bottom of the cell + */ + private BorderLineStyle bottomBorder; + /** + * The border colour for the left of the cell + */ + private Colour leftBorderColour; + /** + * The border colour for the right of the cell + */ + private Colour rightBorderColour; + /** + * The border colour for the top of the cell + */ + private Colour topBorderColour; + /** + * The border colour for the bottom of the cell + */ + private Colour bottomBorderColour; + /** + * The background colour + */ + private Colour backgroundColour; + /** + * The background pattern + */ + private Pattern pattern; + /** + * The options mask which is used to store the processed cell options (such + * as alignment, borders etc) + */ + private int options; + /** + * The index of this XF record within the workbook + */ + private int xfIndex; + /** + * The font object for this XF record + */ + private FontRecord font; + /** + * The format object for this XF record. This is used when creating + * a writable record + */ + private DisplayFormat format; + /** + * Flag to indicate whether this XF record has been initialized + */ + private boolean initialized; + /** + * Indicates whether this cell was constructed by an API or read + * from an existing Excel file + */ + private final boolean read; + /** + * The excel format for this record. This is used to display the actual + * excel format string back to the user (eg. when generating certain + * types of XML document) as opposed to the java equivalent + */ + private Format excelFormat; + + /** + * Flag to indicate whether the format information has been initialized. + * This is false if the xf record has been read in, but true if it + * has been written + */ + private boolean formatInfoInitialized; + /** + * Flag to indicate whether this cell was copied. If it was copied, then + * it can be set to uninitialized, allowing us to change certain format + * information + */ + private final boolean copied; + /** + * A handle to the formatting records. The purpose of this is + * to read the formatting information back, for the purposes of + * output eg. to some form of XML + */ + private FormattingRecords formattingRecords; + /** + * The biff type + */ + private final BiffType biffType; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param bt the biff type + */ + public XFRecord(Record t, WorkbookSettings ws, BiffType bt) { + super(t); + + biffType = bt; + + byte[] data = getRecord().getData(); + + fontIndex = IntegerHelper.getInt(data[0], data[1]); + formatIndex = IntegerHelper.getInt(data[2], data[3]); + date = false; + number = false; + + + // Compare against the date formats + for (int i = 0; i < dateFormats.length && date == false; i++) { + if (formatIndex == dateFormats[i]) { + date = true; + dateFormat = javaDateFormats[i]; + } + } + + // Compare against the number formats + for (int i = 0; i < numberFormats.length && number == false; i++) { + if (formatIndex == numberFormats[i]) { + number = true; + DecimalFormat df = (DecimalFormat) javaNumberFormats[i].clone(); + DecimalFormatSymbols symbols = + new DecimalFormatSymbols(ws.getLocale()); + df.setDecimalFormatSymbols(symbols); + numberFormat = df; + //numberFormat = javaNumberFormats[i]; + } + } + + // Initialize the parent format and the type + int cellAttributes = IntegerHelper.getInt(data[4], data[5]); + parentFormat = (cellAttributes & 0xfff0) >> 4; + + int formatType = cellAttributes & 0x4; + xfFormatType = formatType == 0 ? cell : style; + locked = ((cellAttributes & 0x1) != 0); + hidden = ((cellAttributes & 0x2) != 0); + + if (xfFormatType == cell && + (parentFormat & 0xfff) == 0xfff) { + // Something is screwy with the parent format - set to zero + parentFormat = 0; + logger.warn("Invalid parent format found - ignoring"); + } + + initialized = false; + read = true; + formatInfoInitialized = false; + copied = false; + } + /** + * A constructor used when creating a writable record + * + * @param fnt the font + * @param form the format + */ + public XFRecord(FontRecord fnt, DisplayFormat form) { + super(Type.XF); + + initialized = false; + locked = true; + hidden = false; + align = Alignment.GENERAL; + valign = VerticalAlignment.BOTTOM; + orientation = Orientation.HORIZONTAL; + wrap = false; + leftBorder = BorderLineStyle.NONE; + rightBorder = BorderLineStyle.NONE; + topBorder = BorderLineStyle.NONE; + bottomBorder = BorderLineStyle.NONE; + leftBorderColour = Colour.AUTOMATIC; + rightBorderColour = Colour.AUTOMATIC; + topBorderColour = Colour.AUTOMATIC; + bottomBorderColour = Colour.AUTOMATIC; + pattern = Pattern.NONE; + backgroundColour = Colour.DEFAULT_BACKGROUND; + indentation = 0; + shrinkToFit = false; + usedAttributes = (byte) (USE_FONT | USE_FORMAT | + USE_BACKGROUND | USE_ALIGNMENT | USE_BORDER); + + // This will be set by the initialize method and the subclass respectively + parentFormat = 0; + xfFormatType = null; + + font = fnt; + format = form; + biffType = biff8; + read = false; + copied = false; + formatInfoInitialized = true; + + Assert.verify(font != null); + Assert.verify(format != null); + } + + /** + * Copy constructor. Used for copying writable formats, typically + * when duplicating formats to handle merged cells + * + * @param fmt XFRecord + */ + protected XFRecord(XFRecord fmt) { + super(Type.XF); + + initialized = false; + locked = fmt.locked; + hidden = fmt.hidden; + align = fmt.align; + valign = fmt.valign; + orientation = fmt.orientation; + wrap = fmt.wrap; + leftBorder = fmt.leftBorder; + rightBorder = fmt.rightBorder; + topBorder = fmt.topBorder; + bottomBorder = fmt.bottomBorder; + leftBorderColour = fmt.leftBorderColour; + rightBorderColour = fmt.rightBorderColour; + topBorderColour = fmt.topBorderColour; + bottomBorderColour = fmt.bottomBorderColour; + pattern = fmt.pattern; + xfFormatType = fmt.xfFormatType; + indentation = fmt.indentation; + shrinkToFit = fmt.shrinkToFit; + parentFormat = fmt.parentFormat; + backgroundColour = fmt.backgroundColour; + + // Shallow copy is sufficient for these purposes + font = fmt.font; + format = fmt.format; + + fontIndex = fmt.fontIndex; + formatIndex = fmt.formatIndex; + + formatInfoInitialized = fmt.formatInfoInitialized; + + biffType = biff8; + read = false; + copied = true; + } + + /** + * A public copy constructor which can be used for copy formats between + * different sheets. Unlike the the other copy constructor, this + * version does a deep copy + * + * @param cellFormat the format to copy + */ + protected XFRecord(CellFormat cellFormat) { + super(Type.XF); + + Assert.verify(cellFormat != null); + Assert.verify(cellFormat instanceof XFRecord); + XFRecord fmt = (XFRecord) cellFormat; + + if (!fmt.formatInfoInitialized) { + fmt.initializeFormatInformation(); + } + + locked = fmt.locked; + hidden = fmt.hidden; + align = fmt.align; + valign = fmt.valign; + orientation = fmt.orientation; + wrap = fmt.wrap; + leftBorder = fmt.leftBorder; + rightBorder = fmt.rightBorder; + topBorder = fmt.topBorder; + bottomBorder = fmt.bottomBorder; + leftBorderColour = fmt.leftBorderColour; + rightBorderColour = fmt.rightBorderColour; + topBorderColour = fmt.topBorderColour; + bottomBorderColour = fmt.bottomBorderColour; + pattern = fmt.pattern; + xfFormatType = fmt.xfFormatType; + parentFormat = fmt.parentFormat; + indentation = fmt.indentation; + shrinkToFit = fmt.shrinkToFit; + backgroundColour = fmt.backgroundColour; + + // Deep copy of the font + font = new FontRecord(fmt.getFont()); + + // Copy the format + if (fmt.getFormat() == null) { + // format is writable + if (fmt.format.isBuiltIn()) { + format = fmt.format; + } else { + // Format is not built in, so do a deep copy + format = new FormatRecord((FormatRecord) fmt.format); + } + } else if (fmt.getFormat() instanceof BuiltInFormat) { + // read excel format is built in + excelFormat = fmt.excelFormat; + format = (BuiltInFormat) fmt.excelFormat; + } else { + // read excel format is user defined + Assert.verify(fmt.formatInfoInitialized); + + // in this case FormattingRecords should initialize the excelFormat + // field with an instance of FormatRecord + Assert.verify(fmt.excelFormat instanceof FormatRecord); + + // Format is not built in, so do a deep copy + FormatRecord fr = new FormatRecord((FormatRecord) fmt.excelFormat); + + // Set both format fields to be the same object, since + // FormatRecord implements all the necessary interfaces + excelFormat = fr; + format = fr; + } + + biffType = biff8; + + // The format info should be all OK by virtue of the deep copy + formatInfoInitialized = true; + + // This format was not read in + read = false; + + // Treat this as a new cell record, so set the copied flag to false + copied = false; + + // The font or format indexes need to be set, so set initialized to false + initialized = false; + } + + /** + * Gets the java date format for this format record + * + * @return returns the date format + */ + public DateFormat getDateFormat() { + return dateFormat; + } + + /** + * Gets the java number format for this format record + * + * @return returns the number format + */ + public NumberFormat getNumberFormat() { + return numberFormat; + } + + /** + * Gets the lookup number of the format record + * + * @return returns the lookup number of the format record + */ + public int getFormatRecord() { + return formatIndex; + } + + /** + * Sees if this format is a date format + * + * @return TRUE if this refers to a built in date format + */ + public boolean isDate() { + return date; + } + + /** + * Sees if this format is a number format + * + * @return TRUE if this refers to a built in date format + */ + public boolean isNumber() { + return number; + } + + /** + * Converts the various fields into binary data. If this object has + * been read from an Excel file rather than being requested by a user (ie. + * if the read flag is TRUE) then + * no processing takes place and the raw data is simply returned. + * + * @return the raw data for writing + */ + public byte[] getData() { + // Format rationalization process means that we always want to + // regenerate the format info - even if the spreadsheet was + // read in + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + byte[] data = new byte[20]; + + IntegerHelper.getTwoBytes(fontIndex, data, 0); + IntegerHelper.getTwoBytes(formatIndex, data, 2); + + // Do the cell attributes + int cellAttributes = 0; + + if (getLocked()) { + cellAttributes |= 0x01; + } + + if (getHidden()) { + cellAttributes |= 0x02; + } + + if (xfFormatType == style) { + cellAttributes |= 0x04; + parentFormat = 0xffff; + } + + cellAttributes |= (parentFormat << 4); + + IntegerHelper.getTwoBytes(cellAttributes, data, 4); + + int alignMask = align.getValue(); + + if (wrap) { + alignMask |= 0x08; + } + + alignMask |= (valign.getValue() << 4); + + alignMask |= (orientation.getValue() << 8); + + IntegerHelper.getTwoBytes(alignMask, data, 6); + + data[9] = (byte) 0x10; + + // Set the borders + int borderMask = leftBorder.getValue(); + borderMask |= (rightBorder.getValue() << 4); + borderMask |= (topBorder.getValue() << 8); + borderMask |= (bottomBorder.getValue() << 12); + + IntegerHelper.getTwoBytes(borderMask, data, 10); + + // Set the border palette information if border mask is non zero + // Hard code the colours to be black + if (borderMask != 0) { + byte lc = (byte) leftBorderColour.getValue(); + byte rc = (byte) rightBorderColour.getValue(); + byte tc = (byte) topBorderColour.getValue(); + byte bc = (byte) bottomBorderColour.getValue(); + + int sideColourMask = (lc & 0x7f) | ((rc & 0x7f) << 7); + int topColourMask = (tc & 0x7f) | ((bc & 0x7f) << 7); + + IntegerHelper.getTwoBytes(sideColourMask, data, 12); + IntegerHelper.getTwoBytes(topColourMask, data, 14); + } + + // Set the background pattern + int patternVal = pattern.getValue() << 10; + IntegerHelper.getTwoBytes(patternVal, data, 16); + + // Set the colour palette + int colourPaletteMask = backgroundColour.getValue(); + colourPaletteMask |= (0x40 << 7); + IntegerHelper.getTwoBytes(colourPaletteMask, data, 18); + + // Set the cell options + options |= indentation & 0x0f; + + if (shrinkToFit) { + options |= 0x10; + } else { + options &= 0xef; + } + + data[8] = (byte) options; + + if (biffType == biff8) { + data[9] = usedAttributes; + } + + return data; + } + + /** + * Accessor for the locked flag + * + * @return TRUE if this XF record locks cells, FALSE otherwise + */ + protected final boolean getLocked() { + return locked; + } + + /** + * Accessor for the hidden flag + * + * @return TRUE if this XF record hides the cell, FALSE otherwise + */ + protected final boolean getHidden() { + return hidden; + } + + /** + * Sets whether or not this XF record locks the cell + * + * @param l the locked flag + */ + protected final void setXFLocked(boolean l) { + locked = l; + usedAttributes |= USE_PROTECTION; + } + + /** + * Sets the cell options + * + * @param opt the cell options + */ + protected final void setXFCellOptions(int opt) { + options |= opt; + } + + /** + * Sets the horizontal alignment for the data in this cell. + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param a the alignment + */ + protected void setXFAlignment(Alignment a) { + Assert.verify(!initialized); + align = a; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Sets the indentation + * + * @param i the indentation + */ + protected void setXFIndentation(int i) { + Assert.verify(!initialized); + indentation = i; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Sets the shrink to fit flag + * + * @param s the shrink to fit flag + */ + protected void setXFShrinkToFit(boolean s) { + Assert.verify(!initialized); + shrinkToFit = s; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Gets the horizontal cell alignment + * + * @return the alignment + */ + public Alignment getAlignment() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return align; + } + + /** + * Gets the indentation + * + * @return the indentation + */ + public int getIndentation() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return indentation; + } + + /** + * Gets the shrink to fit flag + * + * @return TRUE if this format is shrink to fit, FALSE otherise + */ + public boolean isShrinkToFit() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return shrinkToFit; + } + + /** + * Accessor for whether a particular cell is locked + * + * @return TRUE if this cell is locked, FALSE otherwise + */ + public boolean isLocked() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return locked; + } + + /** + * Gets the vertical cell alignment + * + * @return the alignment + */ + public VerticalAlignment getVerticalAlignment() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return valign; + } + + /** + * Gets the orientation + * + * @return the orientation + */ + public Orientation getOrientation() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return orientation; + } + + /** + * Sets the horizontal alignment for the data in this cell. + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param c the background colour + * @param p the background pattern + */ + protected void setXFBackground(Colour c, Pattern p) { + Assert.verify(!initialized); + backgroundColour = c; + pattern = p; + usedAttributes |= USE_BACKGROUND; + } + + /** + * Gets the background colour used by this cell + * + * @return the foreground colour + */ + public Colour getBackgroundColour() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return backgroundColour; + } + + /** + * Gets the pattern used by this cell format + * + * @return the background pattern + */ + public Pattern getPattern() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return pattern; + } + + /** + * Sets the vertical alignment for the data in this cell + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param va the vertical alignment + */ + protected void setXFVerticalAlignment(VerticalAlignment va) { + Assert.verify(!initialized); + valign = va; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Sets the vertical alignment for the data in this cell + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param o the orientation + */ + protected void setXFOrientation(Orientation o) { + Assert.verify(!initialized); + orientation = o; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Sets whether the data in this cell is wrapped + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param w the wrap flag + */ + protected void setXFWrap(boolean w) { + Assert.verify(!initialized); + wrap = w; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Gets whether or not the contents of this cell are wrapped + * + * @return TRUE if this cell's contents are wrapped, FALSE otherwise + */ + public boolean getWrap() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return wrap; + } + + /** + * Sets the border for this cell + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param b the border + * @param ls the border line style + */ + protected void setXFBorder(Border b, BorderLineStyle ls, Colour c) { + Assert.verify(!initialized); + + if (c == Colour.BLACK || c == Colour.UNKNOWN) { + c = Colour.PALETTE_BLACK; + } + + if (b == Border.LEFT) { + leftBorder = ls; + leftBorderColour = c; + } else if (b == Border.RIGHT) { + rightBorder = ls; + rightBorderColour = c; + } else if (b == Border.TOP) { + topBorder = ls; + topBorderColour = c; + } else if (b == Border.BOTTOM) { + bottomBorder = ls; + bottomBorderColour = c; + } + + usedAttributes |= USE_BORDER; + + return; + } + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + public BorderLineStyle getBorder(Border border) { + return getBorderLine(border); + } + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + public BorderLineStyle getBorderLine(Border border) { + // Don't bother with the short cut records + if (border == Border.NONE || + border == Border.ALL) { + return BorderLineStyle.NONE; + } + + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + if (border == Border.LEFT) { + return leftBorder; + } else if (border == Border.RIGHT) { + return rightBorder; + } else if (border == Border.TOP) { + return topBorder; + } else if (border == Border.BOTTOM) { + return bottomBorder; + } + + return BorderLineStyle.NONE; + } + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + public Colour getBorderColour(Border border) { + // Don't bother with the short cut records + if (border == Border.NONE || + border == Border.ALL) { + return Colour.PALETTE_BLACK; + } + + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + if (border == Border.LEFT) { + return leftBorderColour; + } else if (border == Border.RIGHT) { + return rightBorderColour; + } else if (border == Border.TOP) { + return topBorderColour; + } else if (border == Border.BOTTOM) { + return bottomBorderColour; + } + + return Colour.BLACK; + } + + /** + * Determines if this cell format has any borders at all. Used to + * set the new borders when merging a group of cells + * + * @return TRUE if this cell has any borders, FALSE otherwise + */ + public final boolean hasBorders() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + return leftBorder != BorderLineStyle.NONE || + rightBorder != BorderLineStyle.NONE || + topBorder != BorderLineStyle.NONE || + bottomBorder != BorderLineStyle.NONE; + } + + /** + * If this cell has not been read in from an existing Excel sheet, + * then initializes this record with the XF index passed in. Calls + * initialized on the font and format record + * + * @param pos the xf index to initialize this record with + * @param fr the containing formatting records + * @param fonts the container for the fonts + * @throws NumFormatRecordsException + */ + public final void initialize(int pos, FormattingRecords fr, Fonts fonts) + throws NumFormatRecordsException { + xfIndex = pos; + formattingRecords = fr; + + // If this file has been read in or copied, + // the font and format indexes will + // already be initialized, so just set the initialized flag and + // return + if (read || copied) { + initialized = true; + return; + } + + if (!font.isInitialized()) { + fonts.addFont(font); + } + + if (!format.isInitialized()) { + fr.addFormat(format); + } + + fontIndex = font.getFontIndex(); + formatIndex = format.getFormatIndex(); + + initialized = true; + } + + /** + * Resets the initialize flag. This is called by the constructor of + * WritableWorkbookImpl to reset the statically declared fonts + */ + public final void uninitialize() { + // As the default formats are cloned internally, the initialized + // flag should never be anything other than false + if (initialized == true) { + logger.warn("A default format has been initialized"); + } + initialized = false; + } + + /** + * Accessor for the XF index + * + * @return the XF index for this cell + */ + public final int getXFIndex() { + return xfIndex; + } + + /** + * Sets the XF index. Called when rationalizing the XF records + * immediately prior to writing + * + * @param xfi the new xf index + */ + final void setXFIndex(int xfi) { + xfIndex = xfi; + } + + /** + * Accessor to see if this format is initialized + * + * @return TRUE if this format is initialized, FALSE otherwise + */ + public final boolean isInitialized() { + return initialized; + } + + /** + * Accessor to see if this format was read in. Used when checking merged + * cells + * + * @return TRUE if this XF record was read in, FALSE if it was generated by + * the user API + */ + public final boolean isRead() { + return read; + } + + /** + * Gets the format used by this format + * + * @return the format + */ + public Format getFormat() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + return excelFormat; + } + + /** + * Gets the font used by this format + * + * @return the font + */ + public Font getFont() { + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + return font; + } + + /** + * Sets the font object with a workbook specific clone. Called from + * the CellValue object when the font has been identified as a statically + * shared font + * Also called to superimpose a HyperlinkFont on an existing label cell + */ + public void setFont(FontRecord f) { + // This style cannot be initialized, otherwise it would mean it would + // have been initialized with shared font + // However, sometimes (when setting a row or column format) an initialized + // XFRecord may have its font overridden by the column/row + + font = f; + } + + /** + * Initializes the internal format information from the data read in + */ + private void initializeFormatInformation() { + // Initialize the cell format string + if (formatIndex < BuiltInFormat.builtIns.length && + BuiltInFormat.builtIns[formatIndex] != null) { + excelFormat = BuiltInFormat.builtIns[formatIndex]; + } else { + excelFormat = formattingRecords.getFormatRecord(formatIndex); + } + + // Initialize the font + font = formattingRecords.getFonts().getFont(fontIndex); + + // Initialize the cell format data from the binary record + byte[] data = getRecord().getData(); + + // Get the parent record + int cellAttributes = IntegerHelper.getInt(data[4], data[5]); + parentFormat = (cellAttributes & 0xfff0) >> 4; + int formatType = cellAttributes & 0x4; + xfFormatType = formatType == 0 ? cell : style; + locked = ((cellAttributes & 0x1) != 0); + hidden = ((cellAttributes & 0x2) != 0); + + if (xfFormatType == cell && + (parentFormat & 0xfff) == 0xfff) { + // Something is screwy with the parent format - set to zero + parentFormat = 0; + logger.warn("Invalid parent format found - ignoring"); + } + + + int alignMask = IntegerHelper.getInt(data[6], data[7]); + + // Get the wrap + if ((alignMask & 0x08) != 0) { + wrap = true; + } + + // Get the horizontal alignment + align = Alignment.getAlignment(alignMask & 0x7); + + // Get the vertical alignment + valign = VerticalAlignment.getAlignment((alignMask >> 4) & 0x7); + + // Get the orientation + orientation = Orientation.getOrientation((alignMask >> 8) & 0xff); + + int attr = IntegerHelper.getInt(data[8], data[9]); + + // Get the indentation + indentation = attr & 0x0F; + + // Get the shrink to fit flag + shrinkToFit = (attr & 0x10) != 0; + + // Get the used attribute + if (biffType == biff8) { + usedAttributes = data[9]; + } + + // Get the borders + int borderMask = IntegerHelper.getInt(data[10], data[11]); + + leftBorder = BorderLineStyle.getStyle(borderMask & 0x7); + rightBorder = BorderLineStyle.getStyle((borderMask >> 4) & 0x7); + topBorder = BorderLineStyle.getStyle((borderMask >> 8) & 0x7); + bottomBorder = BorderLineStyle.getStyle((borderMask >> 12) & 0x7); + + int borderColourMask = IntegerHelper.getInt(data[12], data[13]); + + leftBorderColour = Colour.getInternalColour(borderColourMask & 0x7f); + rightBorderColour = Colour.getInternalColour + ((borderColourMask & 0x3f80) >> 7); + + borderColourMask = IntegerHelper.getInt(data[14], data[15]); + topBorderColour = Colour.getInternalColour(borderColourMask & 0x7f); + bottomBorderColour = Colour.getInternalColour + ((borderColourMask & 0x3f80) >> 7); + + if (biffType == biff8) { + // Get the background pattern. This is the six most significant bits + int patternVal = IntegerHelper.getInt(data[16], data[17]); + patternVal = patternVal & 0xfc00; + patternVal = patternVal >> 10; + pattern = Pattern.getPattern(patternVal); + + // Get the background colour + int colourPaletteMask = IntegerHelper.getInt(data[18], data[19]); + backgroundColour = Colour.getInternalColour(colourPaletteMask & 0x3f); + + if (backgroundColour == Colour.UNKNOWN || + backgroundColour == Colour.DEFAULT_BACKGROUND1) { + backgroundColour = Colour.DEFAULT_BACKGROUND; + } + } else { + pattern = Pattern.NONE; + backgroundColour = Colour.DEFAULT_BACKGROUND; + } + + // Set the lazy initialization flag + formatInfoInitialized = true; + } + + /** + * Standard hash code implementation + * + * @return the hash code + */ + public int hashCode() { + // Must have its formats info initialized in order to compute the hash code + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + int hashValue = 17; + int oddPrimeNumber = 37; + + // The boolean fields + hashValue = oddPrimeNumber * hashValue + (hidden ? 1 : 0); + hashValue = oddPrimeNumber * hashValue + (locked ? 1 : 0); + hashValue = oddPrimeNumber * hashValue + (wrap ? 1 : 0); + hashValue = oddPrimeNumber * hashValue + (shrinkToFit ? 1 : 0); + + // The enumerations + if (xfFormatType == cell) { + hashValue = oddPrimeNumber * hashValue + 1; + } else if (xfFormatType == style) { + hashValue = oddPrimeNumber * hashValue + 2; + } + + hashValue = oddPrimeNumber * hashValue + (align.getValue() + 1); + hashValue = oddPrimeNumber * hashValue + (valign.getValue() + 1); + hashValue = oddPrimeNumber * hashValue + (orientation.getValue()); + + hashValue ^= leftBorder.getDescription().hashCode(); + hashValue ^= rightBorder.getDescription().hashCode(); + hashValue ^= topBorder.getDescription().hashCode(); + hashValue ^= bottomBorder.getDescription().hashCode(); + + hashValue = oddPrimeNumber * hashValue + (leftBorderColour.getValue()); + hashValue = oddPrimeNumber * hashValue + (rightBorderColour.getValue()); + hashValue = oddPrimeNumber * hashValue + (topBorderColour.getValue()); + hashValue = oddPrimeNumber * hashValue + (bottomBorderColour.getValue()); + hashValue = oddPrimeNumber * hashValue + (backgroundColour.getValue()); + hashValue = oddPrimeNumber * hashValue + (pattern.getValue() + 1); + + // The integer fields + hashValue = oddPrimeNumber * hashValue + usedAttributes; + hashValue = oddPrimeNumber * hashValue + parentFormat; + hashValue = oddPrimeNumber * hashValue + fontIndex; + hashValue = oddPrimeNumber * hashValue + formatIndex; + hashValue = oddPrimeNumber * hashValue + indentation; + + return hashValue; + } + + /** + * Equals method. This is called when comparing writable formats + * in order to prevent duplicate formats being added to the workbook + * + * @param o object to compare + * @return TRUE if the objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof XFRecord)) { + return false; + } + + XFRecord xfr = (XFRecord) o; + + // Both records must be writable and have their format info initialized + if (!formatInfoInitialized) { + initializeFormatInformation(); + } + + if (!xfr.formatInfoInitialized) { + xfr.initializeFormatInformation(); + } + + if (xfFormatType != xfr.xfFormatType || + parentFormat != xfr.parentFormat || + locked != xfr.locked || + hidden != xfr.hidden || + usedAttributes != xfr.usedAttributes) { + return false; + } + + if (align != xfr.align || + valign != xfr.valign || + orientation != xfr.orientation || + wrap != xfr.wrap || + shrinkToFit != xfr.shrinkToFit || + indentation != xfr.indentation) { + return false; + } + + if (leftBorder != xfr.leftBorder || + rightBorder != xfr.rightBorder || + topBorder != xfr.topBorder || + bottomBorder != xfr.bottomBorder) { + return false; + } + + if (leftBorderColour != xfr.leftBorderColour || + rightBorderColour != xfr.rightBorderColour || + topBorderColour != xfr.topBorderColour || + bottomBorderColour != xfr.bottomBorderColour) { + return false; + } + + if (backgroundColour != xfr.backgroundColour || + pattern != xfr.pattern) { + return false; + } + + if (initialized && xfr.initialized) { + // Both formats are initialized, so it is sufficient to just do + // shallow equals on font, format objects, + // since we are testing for the presence of clones anwyay + // Use indices rather than objects because of the rationalization + // process (which does not set the object on an XFRecord) + return fontIndex == xfr.fontIndex && + formatIndex == xfr.formatIndex; + } else { + // Perform a deep compare of fonts and formats + return font.equals(xfr.font) && + format.equals(xfr.format); + } + } + + /** + * Sets the format index. This is called during the rationalization process + * when some of the duplicate number formats have been removed + * + * @param newindex the new format index + */ + void setFormatIndex(int newindex) { + formatIndex = newindex; + } + + /** + * Accessor for the font index. Called by the FormattingRecords objects + * during the rationalization process + * + * @return the font index + */ + public int getFontIndex() { + return fontIndex; + } + + /** + * Sets the font index. This is called during the rationalization process + * when some of the duplicate fonts have been removed + * + * @param newindex the new index + */ + void setFontIndex(int newindex) { + fontIndex = newindex; + } + + /** + * Sets the format type and parent format from the writable subclass + * + * @param t the xf type + * @param pf the parent format + */ + protected void setXFDetails(XFType t, int pf) { + xfFormatType = t; + parentFormat = pf; + } + + /** + * Changes the appropriate indexes during the rationalization process + * + * @param xfMapping the xf index re-mappings + */ + void rationalize(IndexMapping xfMapping) { + xfIndex = xfMapping.getNewIndex(xfIndex); + + if (xfFormatType == cell) { + parentFormat = xfMapping.getNewIndex(parentFormat); + } + } + + // Type to distinguish between biff7 and biff8 + private static class BiffType { + } + + // Type to distinguish between cell and style records + private static class XFType { + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/BStoreContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/BStoreContainer.java new file mode 100755 index 0000000..da8b797 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/BStoreContainer.java @@ -0,0 +1,86 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.common.Logger; + +/** + * A BStoreContainer escher record + */ +class BStoreContainer extends EscherContainer { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(BStoreContainer.class); + + /** + * The number of blips inside this container + */ + private int numBlips; + + /** + * Constructor used to instantiate this object when reading from an + * escher stream + * + * @param erd the escher data + */ + public BStoreContainer(EscherRecordData erd) { + super(erd); + numBlips = getInstance(); + } + + /** + * Constructor used when writing out an escher record + */ + public BStoreContainer() { + super(EscherRecordType.BSTORE_CONTAINER); + } + + /** + * Accessor for the number of blips + * + * @return the number of blips + */ + public int getNumBlips() { + return numBlips; + } + + /** + * Sets the number of drawings in this container + * + * @param count the number of blips + */ + void setNumBlips(int count) { + numBlips = count; + setInstance(numBlips); + } + + /** + * Accessor for the drawing + * + * @param i the index number of the drawing to return + * @return the drawing + */ + public BlipStoreEntry getDrawing(int i) { + EscherRecord[] children = getChildren(); + BlipStoreEntry bse = (BlipStoreEntry) children[i]; + return bse; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/BlipStoreEntry.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/BlipStoreEntry.java new file mode 100755 index 0000000..3f8d898 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/BlipStoreEntry.java @@ -0,0 +1,195 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import jxl.biff.IntegerHelper; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * The data for this blip store entry. Typically this is the raw image data + */ +class BlipStoreEntry extends EscherAtom { + /** + * The start of the image data within this blip entry + */ + private static final int IMAGE_DATA_OFFSET = 61; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(BlipStoreEntry.class); + /** + * The type of the blip + */ + private final BlipType type; + /** + * The image data read in + */ + private byte[] data; + /** + * The length of the image data + */ + private int imageDataLength; + /** + * The reference count on this blip + */ + private int referenceCount; + /** + * Flag to indicate that this entry was specified by the API, and not + * read in + */ + private final boolean write; + + /** + * Constructor + * + * @param erd the escher record data + */ + public BlipStoreEntry(EscherRecordData erd) { + super(erd); + type = BlipType.getType(getInstance()); + write = false; + byte[] bytes = getBytes(); + referenceCount = IntegerHelper.getInt(bytes[24], bytes[25], + bytes[26], bytes[27]); + } + + /** + * Constructor + * + * @param d the drawing + * @throws IOException + */ + public BlipStoreEntry(Drawing d) throws IOException { + super(EscherRecordType.BSE); + type = BlipType.PNG; + setVersion(2); + setInstance(type.getValue()); + + byte[] imageData = d.getImageBytes(); + imageDataLength = imageData.length; + data = new byte[imageDataLength + IMAGE_DATA_OFFSET]; + System.arraycopy(imageData, 0, data, IMAGE_DATA_OFFSET, imageDataLength); + referenceCount = d.getReferenceCount(); + write = true; + } + + /** + * Accessor for the blip type + * + * @return the blip type + */ + public BlipType getBlipType() { + return type; + } + + /** + * Gets the data for this blip so that it can be written out + * + * @return the data for the blip + */ + public byte[] getData() { + if (write) { + // Drawing has been specified by API + + // Type on win32 + data[0] = (byte) type.getValue(); + + // Type on MacOs + data[1] = (byte) type.getValue(); + + // The blip identifier + // IntegerHelper.getTwoBytes(0xfce1, data, 2); + + // Unused tags - 18 bytes + // System.arraycopy(stuff, 0, data, 2, stuff.length); + + // The size of the file + IntegerHelper.getFourBytes(imageDataLength + 8 + 17, data, 20); + + // The reference count on the blip + IntegerHelper.getFourBytes(referenceCount, data, 24); + + // Offset in the delay stream + IntegerHelper.getFourBytes(0, data, 28); + + // Usage byte + data[32] = (byte) 0; + + // Length of the blip name + data[33] = (byte) 0; + + // Last two bytes unused + data[34] = (byte) 0x7e; + data[35] = (byte) 0x01; + + // The blip itself + data[36] = (byte) 0; + data[37] = (byte) 0x6e; + + // The blip identifier + IntegerHelper.getTwoBytes(0xf01e, data, 38); + + // The length of the blip. This is the length of the image file plus + // 16 bytes + IntegerHelper.getFourBytes(imageDataLength + 17, data, 40); + + // Unknown stuff + // System.arraycopy(stuff, 0, data, 44, stuff.length); + } else { + // drawing has been read in + data = getBytes(); + } + + return setHeaderData(data); + } + + /** + * Reduces the reference count in this blip. Called when a drawing is + * removed + */ + void dereference() { + referenceCount--; + Assert.verify(referenceCount >= 0); + } + + /** + * Accessor for the reference count on the blip + * + * @return the reference count on the blip + */ + int getReferenceCount() { + return referenceCount; + } + + /** + * Accessor for the image data. + * + * @return the image data + */ + byte[] getImageData() { + byte[] allData = getBytes(); + byte[] imageData = new byte[allData.length - IMAGE_DATA_OFFSET]; + System.arraycopy(allData, IMAGE_DATA_OFFSET, + imageData, 0, imageData.length); + return imageData; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/BlipType.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/BlipType.java new file mode 100755 index 0000000..24deaf4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/BlipType.java @@ -0,0 +1,106 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Enumeration for the BLIP type + */ +final class BlipType { + public static final BlipType ERROR = new BlipType(0, "Error"); + // An error occured during loading + public static final BlipType UNKNOWN = new BlipType(1, "Unknown"); + // An unknown blip type + public static final BlipType EMF = new BlipType(2, "EMF"); + // Windows Enhanced Metafile + public static final BlipType WMF = new BlipType(3, "WMF"); + // Windows Metafile + public static final BlipType PICT = new BlipType(4, "PICT"); + // Macintosh PICT + public static final BlipType JPEG = new BlipType(5, "JPEG"); // JFIF + public static final BlipType PNG = new BlipType(6, "PNG"); // PNG + public static final BlipType DIB = new BlipType(7, "DIB"); // Windows DIB + public static final BlipType FIRST_CLIENT = new BlipType(32, "FIRST"); + // First client defined blip type + public static final BlipType LAST_CLIENT = new BlipType(255, "LAST"); + /** + * All blip types + */ + private static BlipType[] types = new BlipType[0]; + /** + * The blip type code + */ + private final int value; + /** + * The blip type description + */ + private final String desc; + /** + * Constructor + * + * @param val the code + * @param d the description + */ + private BlipType(int val, String d) { + value = val; + desc = d; + + BlipType[] newtypes = new BlipType[types.length + 1]; + System.arraycopy(types, 0, newtypes, 0, types.length); + newtypes[types.length] = this; + types = newtypes; + } + + /** + * Gets the blip type given the value + * + * @param val get the value + * @return the blip type + */ + public static BlipType getType(int val) { + BlipType type = UNKNOWN; + for (int i = 0; i < types.length; i++) { + if (types[i].value == val) { + type = types[i]; + break; + } + } + + return type; + } + + /** + * Accessor for the description + * + * @return the description + */ + public String getDescription() { + return desc; + } + + /** + * Accessor for the value + * + * @return the value + */ + public int getValue() { + return value; + } + // Last client defined blip type +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Button.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Button.java new file mode 100755 index 0000000..dd93dd4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Button.java @@ -0,0 +1,790 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import jxl.WorkbookSettings; +import jxl.biff.ContinueRecord; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.biff.File; + +/** + * Contains the various biff records used to copy a Button (from the + * Form toolbox) between workbook + */ +public class Button implements DrawingGroupObject { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Button.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The spContainer that was generated + */ + private EscherContainer spContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private final MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the drawing + */ + private final ObjRecord objRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The shape id + */ + private int shapeId; + + /** + * The column + */ + private int column; + + /** + * The row position of the image + */ + private int row; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private final DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The drawing position on the sheet + */ + private final int drawingNumber; + + /** + * An mso drawing record, which sometimes appears + */ + private MsoDrawingRecord mso; + + /** + * The text object record + */ + private TextObjectRecord txo; + + /** + * Text data from the first continue record + */ + private ContinueRecord text; + + /** + * Formatting data from the second continue record + */ + private ContinueRecord formatting; + + /** + * The comment text + */ + private String commentText; + + /** + * The workbook settings + */ + private final WorkbookSettings workbookSettings; + + /** + * Constructor used when reading images + * + * @param msodr the drawing record + * @param obj the object record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + * @param ws the workbook settings + */ + public Button(MsoDrawingRecord msodr, + ObjRecord obj, + DrawingData dd, + DrawingGroup dg, + WorkbookSettings ws) { + drawingGroup = dg; + msoDrawingRecord = msodr; + drawingData = dd; + objRecord = obj; + initialized = false; + workbookSettings = ws; + origin = Origin.READ; + drawingData.addData(msoDrawingRecord.getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + drawingGroup.addDrawing(this); + + Assert.verify(msoDrawingRecord != null && objRecord != null); + + initialize(); + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + * @param ws the workbook settings + */ + public Button(DrawingGroupObject dgo, + DrawingGroup dg, + WorkbookSettings ws) { + Button d = (Button) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + objRecord = d.objRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + mso = d.mso; + txo = d.txo; + text = d.text; + formatting = d.formatting; + workbookSettings = ws; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() { + readSpContainer = drawingData.getSpContainer(drawingNumber); + Assert.verify(readSpContainer != null); + + EscherRecord[] children = readSpContainer.getChildren(); + + Sp sp = (Sp) readSpContainer.getChildren()[0]; + objectId = objRecord.getObjectId(); + shapeId = sp.getShapeId(); + type = ShapeType.getType(sp.getShapeType()); + + if (type == ShapeType.UNKNOWN) { + logger.warn("Unknown shape type"); + } + + ClientAnchor clientAnchor = null; + for (int i = 0; i < children.length && clientAnchor == null; i++) { + if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) { + clientAnchor = (ClientAnchor) children[i]; + } + } + + if (clientAnchor == null) { + logger.warn("Client anchor not found"); + } else { + column = (int) clientAnchor.getX1() - 1; + row = (int) clientAnchor.getY1() + 1; + } + + initialized = true; + } + + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() { + if (!initialized) { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the object id + */ + public final int getShapeId() { + if (!initialized) { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() { + if (!initialized) { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() { + if (!initialized) { + initialize(); + } + + if (origin == Origin.READ) { + return getReadSpContainer(); + } + + Assert.verify(false); + + /* + if (spContainer == null) + { + spContainer = new SpContainer(); + Sp sp = new Sp(type, shapeId, 2560); + spContainer.add(sp); + Opt opt = new Opt(); + opt.addProperty(344, false, false, 0); // ? + opt.addProperty(385, false, false, 134217808); // fill colour + opt.addProperty(387, false, false, 134217808); // background colour + opt.addProperty(959, false, false, 131074); // hide + spContainer.add(opt); + + ClientAnchor clientAnchor = new ClientAnchor(column + 1.3, + Math.max(0, row - 0.6), + column+3, row + 4); + + spContainer.add(clientAnchor); + + ClientData clientData = new ClientData(); + spContainer.add(clientData); + + ClientTextBox clientTextBox = new ClientTextBox(); + spContainer.add(clientTextBox); + } + */ + + return spContainer; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() { + return drawingGroup; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) { + drawingGroup = dg; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() { + if (!initialized) { + initialize(); + } + return column; + } + + /** + * Sets the column position of this drawing. Used when inserting/removing + * columns from the spreadsheet + * + * @param x the column + */ + public void setX(double x) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + column = (int) x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() { + if (!initialized) { + initialize(); + } + + return row; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + row = (int) y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() { + if (!initialized) { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() { + if (!initialized) { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() { + if (!initialized) { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() { + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() { + return type; + } + + /** + * Sets the text object + * + * @param t the text object record + */ + public void setTextObject(TextObjectRecord t) { + txo = t; + } + + /** + * Sets the formatting + * + * @param t continue record + */ + public void setFormatting(ContinueRecord t) { + formatting = t; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() { + Assert.verify(false); + return null; + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() { + Assert.verify(false); + return null; + } + + /** + * The drawing record + * + * @param d the drawing record + */ + public void addMso(MsoDrawingRecord d) { + mso = d; + drawingData.addRawData(mso.getData()); + } + + /** + * Writes out any additional records + * + * @param outputFile the output file + * @throws IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException { + if (origin == Origin.READ) { + outputFile.write(objRecord); + + if (mso != null) { + outputFile.write(mso); + } + outputFile.write(txo); + outputFile.write(text); + if (formatting != null) { + outputFile.write(formatting); + } + return; + } + + Assert.verify(false); + + // Create the obj record + ObjRecord objrec = new ObjRecord(objectId, + ObjRecord.EXCELNOTE); + + outputFile.write(objrec); + + // Create the mso data record. Write the text box record again, + // although it is already included in the SpContainer + ClientTextBox textBox = new ClientTextBox(); + MsoDrawingRecord msod = new MsoDrawingRecord(textBox.getData()); + outputFile.write(msod); + + TextObjectRecord tor = new TextObjectRecord(getText()); + outputFile.write(tor); + + // Data for the first continue record + byte[] textData = new byte[commentText.length() * 2 + 1]; + textData[0] = 0x1; // unicode indicator + StringHelper.getUnicodeBytes(commentText, textData, 1); + //StringHelper.getBytes(commentText, textData, 1); + ContinueRecord textContinue = new ContinueRecord(textData); + outputFile.write(textContinue); + + // Data for the formatting runs + + byte[] frData = new byte[16]; + + // First txo run (the user) + IntegerHelper.getTwoBytes(0, frData, 0); // index to the first character + IntegerHelper.getTwoBytes(0, frData, 2); // index to the font(default) + // Mandatory last txo run + IntegerHelper.getTwoBytes(commentText.length(), frData, 8); + IntegerHelper.getTwoBytes(0, frData, 10); // index to the font(default) + + ContinueRecord frContinue = new ContinueRecord(frData); + outputFile.write(frContinue); + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Writes out all the note records + * + * @param outputFile the output file + */ + public void writeTailRecords(File outputFile) { + } + + /** + * Accessor for the row. As buttons are not associated with a cell, + * does nothing here + * + * @return the row number + */ + public int getRow() { + return 0; + } + + /** + * Accessor for the column. As buttons are not associated with a cell, + * does nothing here + * + * @return the column number + */ + public int getColumn() { + return 0; + } + + /** + * Accessor for the text on the button + * + * @return the button text + */ + public String getText() { + if (commentText == null) { + Assert.verify(text != null); + + byte[] td = text.getData(); + if (td[0] == 0) { + commentText = StringHelper.getString + (td, td.length - 1, 1, workbookSettings); + } else { + commentText = StringHelper.getUnicodeString + (td, (td.length - 1) / 2, 1); + } + } + + return commentText; + } + + /** + * Sets the text data + * + * @param t continuation record + */ + public void setText(ContinueRecord t) { + text = t; + } + + /** + * Hashing algorithm + * + * @return the hash code + */ + public int hashCode() { + return commentText.hashCode(); + } + + /** + * Called when the comment text is changed during the sheet copy process + * + * @param t the new text + */ + public void setButtonText(String t) { + commentText = t; + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() { + return mso.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() { + return true; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Chart.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Chart.java new file mode 100755 index 0000000..e257050 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Chart.java @@ -0,0 +1,249 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.WorkbookSettings; +import jxl.biff.ByteData; +import jxl.biff.IndexMapping; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.read.biff.File; + +/** + * Contains the various biff records used to insert a chart into a + * worksheet + */ +public class Chart implements ByteData, EscherStream { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Chart.class); + + /** + * The MsoDrawingRecord associated with the chart + */ + private final MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the chart + */ + private final ObjRecord objRecord; + + /** + * The start pos of the chart bof stream in the data file + */ + private final int startpos; + + /** + * The start pos of the chart bof stream in the data file + */ + private final int endpos; + + /** + * A handle to the Excel file + */ + private final File file; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The drawing number + */ + private int drawingNumber; + + /** + * The chart byte data + */ + private byte[] data; + + /** + * Flag which indicates that the byte data has been initialized + */ + private boolean initialized; + + /** + * The workbook settings + */ + private final WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param mso a MsoDrawingRecord value + * @param obj an ObjRecord value + * @param dd the drawing data + * @param sp an int value + * @param ep an int value + * @param f a File value + * @param ws the workbook settings + */ + public Chart(MsoDrawingRecord mso, + ObjRecord obj, + DrawingData dd, + int sp, int ep, File f, WorkbookSettings ws) { + msoDrawingRecord = mso; + objRecord = obj; + startpos = sp; + endpos = ep; + file = f; + workbookSettings = ws; + + // msoDrawingRecord is null if the entire sheet consists of just the + // chart. In this case, as there is only one drawing on the page, + // it isn't necessary to add to the drawing data record anyway + if (msoDrawingRecord != null) { + drawingData = dd; + drawingData.addData(msoDrawingRecord.getRecord().getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + } + + initialized = false; + + // Note: mso and obj values can be null if we are creating a chart + // which takes up an entire worksheet. Check that both are null or both + // not null though + Assert.verify((mso != null && obj != null) || + (mso == null && obj == null)); + } + + /** + * Gets the entire binary record for the chart as a chunk of binary data + * + * @return the bytes + */ + public byte[] getBytes() { + if (!initialized) { + initialize(); + } + + return data; + } + + /** + * Implementation of the EscherStream method + * + * @return the data + */ + public byte[] getData() { + return msoDrawingRecord.getRecord().getData(); + } + + /** + * Initializes the charts byte data + */ + private void initialize() { + data = file.read(startpos, endpos - startpos); + initialized = true; + } + + /** + * Rationalizes the sheet's xf index mapping + * + * @param xfMapping the index mapping for XFRecords + * @param fontMapping the index mapping for fonts + * @param formatMapping the index mapping for formats + */ + public void rationalize(IndexMapping xfMapping, + IndexMapping fontMapping, + IndexMapping formatMapping) { + if (!initialized) { + initialize(); + } + + // Read through the array, looking for the data types + // This is a total hack bodge for now - it will eventually need to be + // integrated properly + int pos = 0; + int code = 0; + int length = 0; + Type type = null; + while (pos < data.length) { + code = IntegerHelper.getInt(data[pos], data[pos + 1]); + length = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + + type = Type.getType(code); + + if (type == Type.FONTX) { + int fontind = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + IntegerHelper.getTwoBytes(fontMapping.getNewIndex(fontind), + data, pos + 4); + } else if (type == Type.FBI) { + int fontind = IntegerHelper.getInt(data[pos + 12], data[pos + 13]); + IntegerHelper.getTwoBytes(fontMapping.getNewIndex(fontind), + data, pos + 12); + } else if (type == Type.IFMT) { + int formind = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + IntegerHelper.getTwoBytes(formatMapping.getNewIndex(formind), + data, pos + 4); + } else if (type == Type.ALRUNS) { + int numRuns = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + int fontPos = pos + 6; + for (int i = 0; i < numRuns; i++) { + int fontind = IntegerHelper.getInt(data[fontPos + 2], + data[fontPos + 3]); + IntegerHelper.getTwoBytes(fontMapping.getNewIndex(fontind), + data, fontPos + 2); + fontPos += 4; + } + } + + pos += length + 4; + } + } + + /** + * Gets the SpContainer containing the charts drawing information + * + * @return the spContainer + */ + EscherContainer getSpContainer() { + EscherContainer spContainer = drawingData.getSpContainer(drawingNumber); + + return spContainer; + } + + /** + * Accessor for the mso drawing record + * + * @return the drawing record + */ + MsoDrawingRecord getMsoDrawingRecord() { + return msoDrawingRecord; + } + + /** + * Accessor for the obj record + * + * @return the obj record + */ + ObjRecord getObjRecord() { + return objRecord; + } +} + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/CheckBox.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/CheckBox.java new file mode 100755 index 0000000..bb37622 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/CheckBox.java @@ -0,0 +1,716 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import jxl.WorkbookSettings; +import jxl.biff.ContinueRecord; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.biff.File; + +/** + * Contains the various biff records used to copy a CheckBox (from the + * Form toolbox) between workbook + */ +public class CheckBox implements DrawingGroupObject { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CheckBox.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The spContainer that was generated + */ + private EscherContainer spContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the drawing + */ + private ObjRecord objRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The shape id + */ + private int shapeId; + + /** + * The column + */ + private int column; + + /** + * The row position of the image + */ + private int row; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The drawing position on the sheet + */ + private int drawingNumber; + + /** + * An mso drawing record, which sometimes appears + */ + private MsoDrawingRecord mso; + + /** + * The text object record + */ + private TextObjectRecord txo; + + /** + * Text data from the first continue record + */ + private ContinueRecord text; + + /** + * Formatting data from the second continue record + */ + private ContinueRecord formatting; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor used when reading images + * + * @param mso the drawing record + * @param obj the object record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + * @param ws the workbook settings + */ + public CheckBox(MsoDrawingRecord mso, ObjRecord obj, DrawingData dd, + DrawingGroup dg, WorkbookSettings ws) { + drawingGroup = dg; + msoDrawingRecord = mso; + drawingData = dd; + objRecord = obj; + initialized = false; + workbookSettings = ws; + origin = Origin.READ; + drawingData.addData(msoDrawingRecord.getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + drawingGroup.addDrawing(this); + + Assert.verify(mso != null && obj != null); + + initialize(); + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + * @param ws the workbook settings + */ + public CheckBox(DrawingGroupObject dgo, + DrawingGroup dg, + WorkbookSettings ws) { + CheckBox d = (CheckBox) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + objRecord = d.objRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + mso = d.mso; + txo = d.txo; + text = d.text; + formatting = d.formatting; + workbookSettings = ws; + } + + /** + * Constructor invoked when writing images + */ + public CheckBox() { + initialized = true; + origin = Origin.WRITE; + referenceCount = 1; + type = ShapeType.HOST_CONTROL; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() { + readSpContainer = drawingData.getSpContainer(drawingNumber); + Assert.verify(readSpContainer != null); + + EscherRecord[] children = readSpContainer.getChildren(); + + Sp sp = (Sp) readSpContainer.getChildren()[0]; + objectId = objRecord.getObjectId(); + shapeId = sp.getShapeId(); + type = ShapeType.getType(sp.getShapeType()); + + if (type == ShapeType.UNKNOWN) { + logger.warn("Unknown shape type"); + } + + ClientAnchor clientAnchor = null; + for (int i = 0; i < children.length && clientAnchor == null; i++) { + if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) { + clientAnchor = (ClientAnchor) children[i]; + } + } + + if (clientAnchor == null) { + logger.warn("Client anchor not found"); + } else { + column = (int) clientAnchor.getX1(); + row = (int) clientAnchor.getY1(); + } + + initialized = true; + } + + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() { + if (!initialized) { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the object id + */ + public final int getShapeId() { + if (!initialized) { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() { + if (!initialized) { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() { + if (!initialized) { + initialize(); + } + + if (origin == Origin.READ) { + return getReadSpContainer(); + } + + SpContainer spc = new SpContainer(); + Sp sp = new Sp(type, shapeId, 2560); + spc.add(sp); + Opt opt = new Opt(); + opt.addProperty(127, false, false, 17039620); + opt.addProperty(191, false, false, 524296); + opt.addProperty(511, false, false, 524288); + opt.addProperty(959, false, false, 131072); + // opt.addProperty(260, true, false, blipId); + // opt.addProperty(261, false, false, 36); + spc.add(opt); + + ClientAnchor clientAnchor = new ClientAnchor(column, + row, + column + 1, + row + 1, + 0x1); + spc.add(clientAnchor); + ClientData clientData = new ClientData(); + spc.add(clientData); + + return spc; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() { + return drawingGroup; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) { + drawingGroup = dg; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() { + if (!initialized) { + initialize(); + } + return column; + } + + /** + * Sets the column position of this drawing. Used when inserting/removing + * columns from the spreadsheet + * + * @param x the column + */ + public void setX(double x) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + column = (int) x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() { + if (!initialized) { + initialize(); + } + + return row; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + row = (int) y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() { + if (!initialized) { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() { + if (!initialized) { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() { + if (!initialized) { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() { + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() { + return type; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() { + Assert.verify(false); + return null; + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() { + Assert.verify(false); + return null; + } + + /** + * Writes out the additional records for a combo box + * + * @param outputFile the output file + * @throws IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException { + if (origin == Origin.READ) { + outputFile.write(objRecord); + + if (mso != null) { + outputFile.write(mso); + } + outputFile.write(txo); + outputFile.write(text); + if (formatting != null) { + outputFile.write(formatting); + } + return; + } + + // Create the obj record + ObjRecord objrec = new ObjRecord(objectId, + ObjRecord.CHECKBOX); + + outputFile.write(objrec); + + logger.warn("Writing of additional records for checkboxes not " + + "implemented"); + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Writes out all the note records + * + * @param outputFile the output file + */ + public void writeTailRecords(File outputFile) { + } + + /** + * Accessor for the row + * + * @return the row + */ + public int getRow() { + return 0; + } + + /** + * Accessor for the column + * + * @return the column + */ + public int getColumn() { + return 0; + } + + /** + * Hashing algorithm + * + * @return the hash code + */ + public int hashCode() { + return getClass().getName().hashCode(); + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() { + return msoDrawingRecord.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() { + return false; + } + + /** + * Sets the text object + * + * @param t the text object record + */ + public void setTextObject(TextObjectRecord t) { + txo = t; + } + + /** + * Sets the text data + * + * @param t continuation record + */ + public void setText(ContinueRecord t) { + text = t; + } + + /** + * Sets the formatting + * + * @param t continue record + */ + public void setFormatting(ContinueRecord t) { + formatting = t; + } + + /** + * The drawing record + * + * @param d the drawing record + */ + public void addMso(MsoDrawingRecord d) { + mso = d; + drawingData.addRawData(mso.getData()); + } + +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Chunk.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Chunk.java new file mode 100755 index 0000000..04e5e6e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Chunk.java @@ -0,0 +1,40 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +class Chunk { + private final int pos; + private final int length; + private final ChunkType type; + private final byte[] data; + + public Chunk(int p, int l, ChunkType ct, byte[] d) { + pos = p; + length = l; + type = ct; + data = new byte[length]; + System.arraycopy(d, pos, data, 0, length); + + } + + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ChunkType.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ChunkType.java new file mode 100755 index 0000000..b0b0e03 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ChunkType.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.Arrays; + +/** + * Enumeration for the various chunk types + */ +class ChunkType { + public static ChunkType IHDR = new ChunkType(0x49, 0x48, 0x44, 0x52, "IHDR"); + public static ChunkType IEND = new ChunkType(0x49, 0x45, 0x4e, 0x44, "IEND"); + public static ChunkType PHYS = new ChunkType(0x70, 0x48, 0x59, 0x73, "pHYs"); + public static ChunkType UNKNOWN = new ChunkType(0xff, 0xff, 0xff, 0xff, "UNKNOWN"); + private static ChunkType[] chunkTypes = new ChunkType[0]; + private final byte[] id; + private final String name; + private ChunkType(int d1, int d2, int d3, int d4, String n) { + id = new byte[]{(byte) d1, (byte) d2, (byte) d3, (byte) d4}; + name = n; + + ChunkType[] ct = new ChunkType[chunkTypes.length + 1]; + System.arraycopy(chunkTypes, 0, ct, 0, chunkTypes.length); + ct[chunkTypes.length] = this; + chunkTypes = ct; + } + + public static ChunkType getChunkType(byte d1, byte d2, byte d3, byte d4) { + byte[] cmp = new byte[]{d1, d2, d3, d4}; + + boolean found = false; + ChunkType chunk = ChunkType.UNKNOWN; + + for (int i = 0; i < chunkTypes.length && !found; i++) { + if (Arrays.equals(chunkTypes[i].id, cmp)) { + chunk = chunkTypes[i]; + found = true; + } + } + + return chunk; + } + + public String getName() { + return name; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientAnchor.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientAnchor.java new file mode 100755 index 0000000..91ab320 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientAnchor.java @@ -0,0 +1,201 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * The client anchor record + */ +class ClientAnchor extends EscherAtom { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ClientAnchor.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The properties + */ + private final int properties; + + /** + * The x1 position + */ + private final double x1; + + /** + * The y1 position + */ + private final double y1; + + /** + * The x2 position + */ + private final double x2; + + /** + * The y2 position + */ + private final double y2; + + /** + * Constructor + * + * @param erd the escher record data + */ + public ClientAnchor(EscherRecordData erd) { + super(erd); + byte[] bytes = getBytes(); + + // The properties + properties = IntegerHelper.getInt(bytes[0], bytes[1]); + + // The x1 cell + int x1Cell = IntegerHelper.getInt(bytes[2], bytes[3]); + int x1Fraction = IntegerHelper.getInt(bytes[4], bytes[5]); + + x1 = x1Cell + (double) x1Fraction / (double) 1024; + + // The y1 cell + int y1Cell = IntegerHelper.getInt(bytes[6], bytes[7]); + int y1Fraction = IntegerHelper.getInt(bytes[8], bytes[9]); + + y1 = y1Cell + (double) y1Fraction / (double) 256; + + // The x2 cell + int x2Cell = IntegerHelper.getInt(bytes[10], bytes[11]); + int x2Fraction = IntegerHelper.getInt(bytes[12], bytes[13]); + + x2 = x2Cell + (double) x2Fraction / (double) 1024; + + // The y1 cell + int y2Cell = IntegerHelper.getInt(bytes[14], bytes[15]); + int y2Fraction = IntegerHelper.getInt(bytes[16], bytes[17]); + + y2 = y2Cell + (double) y2Fraction / (double) 256; + } + + /** + * Constructor + * + * @param x1 the x1 position + * @param y1 the y1 position + * @param x2 the x2 position + * @param y2 the y2 position + * @param props the anchor properties + */ + public ClientAnchor(double x1, double y1, double x2, double y2, int props) { + super(EscherRecordType.CLIENT_ANCHOR); + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + properties = props; + } + + /** + * Gets the client anchor data + * + * @return the data + */ + byte[] getData() { + data = new byte[18]; + IntegerHelper.getTwoBytes(properties, data, 0); + + // The x1 cell + IntegerHelper.getTwoBytes((int) x1, data, 2); + + // The x1 fraction into the cell 0-1024 + int x1fraction = (int) ((x1 - (int) x1) * 1024); + IntegerHelper.getTwoBytes(x1fraction, data, 4); + + // The y1 cell + IntegerHelper.getTwoBytes((int) y1, data, 6); + + // The y1 fraction into the cell 0-256 + int y1fraction = (int) ((y1 - (int) y1) * 256); + IntegerHelper.getTwoBytes(y1fraction, data, 8); + + // The x2 cell + IntegerHelper.getTwoBytes((int) x2, data, 10); + + // The x2 fraction into the cell 0-1024 + int x2fraction = (int) ((x2 - (int) x2) * 1024); + IntegerHelper.getTwoBytes(x2fraction, data, 12); + + // The y2 cell + IntegerHelper.getTwoBytes((int) y2, data, 14); + + // The y2 fraction into the cell 0-256 + int y2fraction = (int) ((y2 - (int) y2) * 256); + IntegerHelper.getTwoBytes(y2fraction, data, 16); + + return setHeaderData(data); + } + + /** + * Accessor for the x1 position + * + * @return the x1 position + */ + double getX1() { + return x1; + } + + /** + * Accessor for the y1 position + * + * @return the y1 position + */ + double getY1() { + return y1; + } + + /** + * Accessor for the x2 position + * + * @return the x2 position + */ + double getX2() { + return x2; + } + + /** + * Accessor for the y2 position + * + * @return the y2 position + */ + double getY2() { + return y2; + } + + /** + * Accessor for the anchor properties + */ + int getProperties() { + return properties; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientData.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientData.java new file mode 100755 index 0000000..13c7839 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientData.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.common.Logger; + +/** + * The client data + */ +class ClientData extends EscherAtom { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ClientData.class); + + /** + * The raw data + */ + private byte[] data; + + /** + * Constructor + * + * @param erd the record data + */ + public ClientData(EscherRecordData erd) { + super(erd); + } + + /** + * Constructor + */ + public ClientData() { + super(EscherRecordType.CLIENT_DATA); + } + + /** + * Accessor for the raw data + * + * @return the binary data + */ + byte[] getData() { + data = new byte[0]; + return setHeaderData(data); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientTextBox.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientTextBox.java new file mode 100755 index 0000000..e614461 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientTextBox.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.common.Logger; + +/** + * ??? + */ +class ClientTextBox extends EscherAtom { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ClientTextBox.class); + + /** + * The raw data + */ + private byte[] data; + + /** + * Constructor + * + * @param erd + */ + public ClientTextBox(EscherRecordData erd) { + super(erd); + } + + /** + * Constructor + */ + public ClientTextBox() { + super(EscherRecordType.CLIENT_TEXT_BOX); + } + + /** + * Accessor for the raw data + * + * @return + */ + byte[] getData() { + data = new byte[0]; + return setHeaderData(data); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ComboBox.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ComboBox.java new file mode 100755 index 0000000..a8ba810 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ComboBox.java @@ -0,0 +1,641 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import jxl.WorkbookSettings; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.biff.File; + +/** + * Contains the various biff records used to copy a ComboBox (from the + * Form toolbox) between workbook + */ +public class ComboBox implements DrawingGroupObject { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ComboBox.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The spContainer that was generated + */ + private EscherContainer spContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the drawing + */ + private ObjRecord objRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The shape id + */ + private int shapeId; + + /** + * The column + */ + private int column; + + /** + * The row position of the image + */ + private int row; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The drawing position on the sheet + */ + private int drawingNumber; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor used when reading images + * + * @param mso the drawing record + * @param obj the object record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + * @param ws the workbook settings + */ + public ComboBox(MsoDrawingRecord mso, ObjRecord obj, DrawingData dd, + DrawingGroup dg, WorkbookSettings ws) { + drawingGroup = dg; + msoDrawingRecord = mso; + drawingData = dd; + objRecord = obj; + initialized = false; + workbookSettings = ws; + origin = Origin.READ; + drawingData.addData(msoDrawingRecord.getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + drawingGroup.addDrawing(this); + + Assert.verify(mso != null && obj != null); + + initialize(); + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + * @param ws the workbook settings + */ + public ComboBox(DrawingGroupObject dgo, + DrawingGroup dg, + WorkbookSettings ws) { + ComboBox d = (ComboBox) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + objRecord = d.objRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + workbookSettings = ws; + } + + /** + * Constructor invoked when writing images + */ + public ComboBox() { + initialized = true; + origin = Origin.WRITE; + referenceCount = 1; + type = ShapeType.HOST_CONTROL; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() { + readSpContainer = drawingData.getSpContainer(drawingNumber); + Assert.verify(readSpContainer != null); + + EscherRecord[] children = readSpContainer.getChildren(); + + Sp sp = (Sp) readSpContainer.getChildren()[0]; + objectId = objRecord.getObjectId(); + shapeId = sp.getShapeId(); + type = ShapeType.getType(sp.getShapeType()); + + if (type == ShapeType.UNKNOWN) { + logger.warn("Unknown shape type"); + } + + ClientAnchor clientAnchor = null; + for (int i = 0; i < children.length && clientAnchor == null; i++) { + if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) { + clientAnchor = (ClientAnchor) children[i]; + } + } + + if (clientAnchor == null) { + logger.warn("Client anchor not found"); + } else { + column = (int) clientAnchor.getX1(); + row = (int) clientAnchor.getY1(); + } + + initialized = true; + } + + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() { + if (!initialized) { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the object id + */ + public final int getShapeId() { + if (!initialized) { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() { + if (!initialized) { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() { + if (!initialized) { + initialize(); + } + + if (origin == Origin.READ) { + return getReadSpContainer(); + } + + SpContainer spc = new SpContainer(); + Sp sp = new Sp(type, shapeId, 2560); + spc.add(sp); + Opt opt = new Opt(); + opt.addProperty(127, false, false, 17039620); + opt.addProperty(191, false, false, 524296); + opt.addProperty(511, false, false, 524288); + opt.addProperty(959, false, false, 131072); + // opt.addProperty(260, true, false, blipId); + // opt.addProperty(261, false, false, 36); + spc.add(opt); + + ClientAnchor clientAnchor = new ClientAnchor(column, + row, + column + 1, + row + 1, + 0x1); + spc.add(clientAnchor); + ClientData clientData = new ClientData(); + spc.add(clientData); + + return spc; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() { + return drawingGroup; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) { + drawingGroup = dg; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() { + if (!initialized) { + initialize(); + } + return column; + } + + /** + * Sets the column position of this drawing. Used when inserting/removing + * columns from the spreadsheet + * + * @param x the column + */ + public void setX(double x) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + column = (int) x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() { + if (!initialized) { + initialize(); + } + + return row; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + row = (int) y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() { + if (!initialized) { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() { + if (!initialized) { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() { + if (!initialized) { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() { + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() { + return type; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() { + Assert.verify(false); + return null; + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() { + Assert.verify(false); + return null; + } + + /** + * Writes out the additional records for a combo box + * + * @param outputFile the output file + * @throws IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException { + if (origin == Origin.READ) { + outputFile.write(objRecord); + return; + } + + // Create the obj record + ObjRecord objrec = new ObjRecord(objectId, + ObjRecord.COMBOBOX); + + outputFile.write(objrec); + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Writes out all the note records + * + * @param outputFile the output file + */ + public void writeTailRecords(File outputFile) { + } + + /** + * Accessor for the row + * + * @return the row + */ + public int getRow() { + return 0; + } + + /** + * Accessor for the column + * + * @return the column + */ + public int getColumn() { + return 0; + } + + /** + * Hashing algorithm + * + * @return the hash code + */ + public int hashCode() { + return getClass().getName().hashCode(); + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() { + return msoDrawingRecord.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() { + return false; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Comment.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Comment.java new file mode 100755 index 0000000..7bd5b29 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Comment.java @@ -0,0 +1,829 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import jxl.WorkbookSettings; +import jxl.biff.ContinueRecord; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.biff.File; + +/** + * Contains the various biff records used to insert a cell note into a + * worksheet + */ +public class Comment implements DrawingGroupObject { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Comment.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The spContainer that was generated + */ + private EscherContainer spContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the drawing + */ + private ObjRecord objRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The shape id + */ + private int shapeId; + + /** + * The column + */ + private int column; + + /** + * The row position of the image + */ + private int row; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The drawing position on the sheet + */ + private int drawingNumber; + + /** + * An mso drawing record, which sometimes appears + */ + private MsoDrawingRecord mso; + + /** + * The text object record + */ + private TextObjectRecord txo; + + /** + * The note record + */ + private NoteRecord note; + + /** + * Text data from the first continue record + */ + private ContinueRecord text; + + /** + * Formatting data from the second continue record + */ + private ContinueRecord formatting; + + /** + * The comment text + */ + private String commentText; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor used when reading images + * + * @param msorec the drawing record + * @param obj the object record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + * @param ws the workbook settings + */ + public Comment(MsoDrawingRecord msorec, ObjRecord obj, DrawingData dd, + DrawingGroup dg, WorkbookSettings ws) { + drawingGroup = dg; + msoDrawingRecord = msorec; + drawingData = dd; + objRecord = obj; + initialized = false; + workbookSettings = ws; + origin = Origin.READ; + drawingData.addData(msoDrawingRecord.getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + drawingGroup.addDrawing(this); + + Assert.verify(msoDrawingRecord != null && objRecord != null); + + if (!initialized) { + initialize(); + } + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + * @param ws the workbook settings + */ + /*protected*/ + public Comment(DrawingGroupObject dgo, + DrawingGroup dg, + WorkbookSettings ws) { + Comment d = (Comment) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + objRecord = d.objRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + mso = d.mso; + txo = d.txo; + text = d.text; + formatting = d.formatting; + note = d.note; + width = d.width; + height = d.height; + workbookSettings = ws; + } + + /** + * Constructor invoked when writing the images + * + * @param txt the comment text + * @param c the column + * @param r the row + */ + public Comment(String txt, int c, int r) { + initialized = true; + origin = Origin.WRITE; + column = c; + row = r; + referenceCount = 1; + type = ShapeType.TEXT_BOX; + commentText = txt; + width = 3; + height = 4; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() { + readSpContainer = drawingData.getSpContainer(drawingNumber); + Assert.verify(readSpContainer != null); + + EscherRecord[] children = readSpContainer.getChildren(); + + Sp sp = (Sp) readSpContainer.getChildren()[0]; + objectId = objRecord.getObjectId(); + shapeId = sp.getShapeId(); + type = ShapeType.getType(sp.getShapeType()); + + if (type == ShapeType.UNKNOWN) { + logger.warn("Unknown shape type"); + } + + ClientAnchor clientAnchor = null; + for (int i = 0; i < children.length && clientAnchor == null; i++) { + if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) { + clientAnchor = (ClientAnchor) children[i]; + } + } + + if (clientAnchor == null) { + logger.warn("client anchor not found"); + } else { + column = (int) clientAnchor.getX1() - 1; + row = (int) clientAnchor.getY1() + 1; + width = clientAnchor.getX2() - clientAnchor.getX1(); + height = clientAnchor.getY2() - clientAnchor.getY1(); + } + + initialized = true; + } + + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() { + if (!initialized) { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the object id + */ + public final int getShapeId() { + if (!initialized) { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() { + if (!initialized) { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() { + if (!initialized) { + initialize(); + } + + if (origin == Origin.READ) { + return getReadSpContainer(); + } + + if (spContainer == null) { + spContainer = new SpContainer(); + Sp sp = new Sp(type, shapeId, 2560); + spContainer.add(sp); + Opt opt = new Opt(); + opt.addProperty(344, false, false, 0); // ? + opt.addProperty(385, false, false, 134217808); // fill colour + opt.addProperty(387, false, false, 134217808); // background colour + opt.addProperty(959, false, false, 131074); // hide + spContainer.add(opt); + + ClientAnchor clientAnchor = new ClientAnchor(column + 1.3, + Math.max(0, row - 0.6), + column + 1.3 + width, + row + height, + 0x1); + + spContainer.add(clientAnchor); + + ClientData clientData = new ClientData(); + spContainer.add(clientData); + + ClientTextBox clientTextBox = new ClientTextBox(); + spContainer.add(clientTextBox); + } + + return spContainer; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() { + return drawingGroup; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) { + drawingGroup = dg; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() { + if (!initialized) { + initialize(); + } + return column; + } + + /** + * Sets the column position of this drawing. Used when inserting/removing + * columns from the spreadsheet + * + * @param x the column + */ + public void setX(double x) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + column = (int) x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() { + if (!initialized) { + initialize(); + } + + return row; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + row = (int) y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() { + if (!initialized) { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() { + if (!initialized) { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() { + if (!initialized) { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() { + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() { + return type; + } + + /** + * Sets the text object + * + * @param t the text object + */ + public void setTextObject(TextObjectRecord t) { + txo = t; + } + + /** + * Sets the note object + * + * @param t the note record + */ + public void setNote(NoteRecord t) { + note = t; + } + + /** + * Sets the formatting + * + * @param t the formatting record + */ + public void setFormatting(ContinueRecord t) { + formatting = t; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() { + Assert.verify(false); + return null; + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() { + Assert.verify(false); + return null; + } + + /** + * Adds an mso record to this object + * + * @param d the mso record + */ + public void addMso(MsoDrawingRecord d) { + mso = d; + drawingData.addRawData(mso.getData()); + } + + /** + * Writes out the additional comment records + * + * @param outputFile the output file + * @throws IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException { + if (origin == Origin.READ) { + outputFile.write(objRecord); + + if (mso != null) { + outputFile.write(mso); + } + outputFile.write(txo); + outputFile.write(text); + if (formatting != null) { + outputFile.write(formatting); + } + return; + } + + // Create the obj record + ObjRecord objrec = new ObjRecord(objectId, + ObjRecord.EXCELNOTE); + + outputFile.write(objrec); + + // Create the mso data record. Write the text box record again, + // although it is already included in the SpContainer + ClientTextBox textBox = new ClientTextBox(); + MsoDrawingRecord msod = new MsoDrawingRecord(textBox.getData()); + outputFile.write(msod); + + TextObjectRecord txorec = new TextObjectRecord(getText()); + outputFile.write(txorec); + + // Data for the first continue record + byte[] textData = new byte[commentText.length() * 2 + 1]; + textData[0] = 0x1; // unicode indicator + StringHelper.getUnicodeBytes(commentText, textData, 1); + //StringHelper.getBytes(commentText, textData, 1); + ContinueRecord textContinue = new ContinueRecord(textData); + outputFile.write(textContinue); + + // Data for the formatting runs + + byte[] frData = new byte[16]; + + // First txo run (the user) + IntegerHelper.getTwoBytes(0, frData, 0); // index to the first character + IntegerHelper.getTwoBytes(0, frData, 2); // index to the font(default) + // Mandatory last txo run + IntegerHelper.getTwoBytes(commentText.length(), frData, 8); + IntegerHelper.getTwoBytes(0, frData, 10); // index to the font(default) + + ContinueRecord frContinue = new ContinueRecord(frData); + outputFile.write(frContinue); + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Writes out all the note records + * + * @param outputFile the output file + * @throws IOException + */ + public void writeTailRecords(File outputFile) throws IOException { + if (origin == Origin.READ) { + outputFile.write(note); + return; + } + + // The note record + NoteRecord noteRecord = new NoteRecord(column, row, objectId); + outputFile.write(noteRecord); + } + + /** + * Accessor for the row + * + * @return the row + */ + public int getRow() { + return note.getRow(); + } + + /** + * Accessor for the column + * + * @return the column + */ + public int getColumn() { + return note.getColumn(); + } + + /** + * Accessor for the comment text + * + * @return the comment text + */ + public String getText() { + if (commentText == null) { + Assert.verify(text != null); + + byte[] td = text.getData(); + if (td[0] == 0) { + commentText = StringHelper.getString + (td, td.length - 1, 1, workbookSettings); + } else { + commentText = StringHelper.getUnicodeString + (td, (td.length - 1) / 2, 1); + } + } + + return commentText; + } + + /** + * Sets the text data + * + * @param t the text data + */ + public void setText(ContinueRecord t) { + text = t; + } + + /** + * Hashing algorithm + * + * @return the hash code + */ + public int hashCode() { + return commentText.hashCode(); + } + + /** + * Called when the comment text is changed during the sheet copy process + * + * @param t the new text + */ + public void setCommentText(String t) { + commentText = t; + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() { + return msoDrawingRecord.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over several records and require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() { + return true; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Dg.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Dg.java new file mode 100755 index 0000000..070d1a6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Dg.java @@ -0,0 +1,105 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.IntegerHelper; + +/** + * The Drawing Group + */ +class Dg extends EscherAtom { + /** + * The data + */ + private byte[] data; + + /** + * The id of this drawing + */ + private final int drawingId; + + /** + * The number of shapes + */ + private final int shapeCount; + + /** + * The seed for drawing ids + */ + private final int seed; + + /** + * Constructor invoked when reading in an escher stream + * + * @param erd the escher record + */ + public Dg(EscherRecordData erd) { + super(erd); + drawingId = getInstance(); + + byte[] bytes = getBytes(); + shapeCount = IntegerHelper.getInt(bytes[0], bytes[1], bytes[2], bytes[3]); + seed = IntegerHelper.getInt(bytes[4], bytes[5], bytes[6], bytes[7]); + } + + /** + * Constructor invoked when writing out an escher stream + * + * @param numDrawings the number of drawings + */ + public Dg(int numDrawings) { + super(EscherRecordType.DG); + drawingId = 1; + shapeCount = numDrawings + 1; + seed = 1024 + shapeCount + 1; + setInstance(drawingId); + } + + /** + * Gets the drawing id + * + * @return the drawing id + */ + public int getDrawingId() { + return drawingId; + } + + /** + * Gets the shape count + * + * @return the shape count + */ + int getShapeCount() { + return shapeCount; + } + + /** + * Used to generate the drawing data + * + * @return the data + */ + byte[] getData() { + data = new byte[8]; + IntegerHelper.getFourBytes(shapeCount, data, 0); + IntegerHelper.getFourBytes(seed, data, 4); + + return setHeaderData(data); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DgContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DgContainer.java new file mode 100755 index 0000000..1278cce --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DgContainer.java @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * A Dg Container + */ +class DgContainer extends EscherContainer { + /** + * Constructor + */ + public DgContainer() { + super(EscherRecordType.DG_CONTAINER); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Dgg.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Dgg.java new file mode 100755 index 0000000..cae1897 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Dgg.java @@ -0,0 +1,203 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.ArrayList; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * Dgg record + */ +class Dgg extends EscherAtom { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Dgg.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The number of clusters + */ + private int numClusters; + + /** + * The maximum shape id + */ + private int maxShapeId; + + /** + * The number of shapes saved + */ + private final int shapesSaved; + + /** + * The number of drawings saved + */ + private final int drawingsSaved; + + /** + * The clusters + */ + private final ArrayList clusters; + + /** + * Constructor + * + * @param erd the read in data + */ + public Dgg(EscherRecordData erd) { + super(erd); + clusters = new ArrayList(); + byte[] bytes = getBytes(); + maxShapeId = IntegerHelper.getInt + (bytes[0], bytes[1], bytes[2], bytes[3]); + numClusters = IntegerHelper.getInt + (bytes[4], bytes[5], bytes[6], bytes[7]); + shapesSaved = IntegerHelper.getInt + (bytes[8], bytes[9], bytes[10], bytes[11]); + drawingsSaved = IntegerHelper.getInt + (bytes[12], bytes[13], bytes[14], bytes[15]); + + int pos = 16; + for (int i = 0; i < numClusters; i++) { + int dgId = IntegerHelper.getInt(bytes[pos], bytes[pos + 1]); + int sids = IntegerHelper.getInt(bytes[pos + 2], bytes[pos + 3]); + Cluster c = new Cluster(dgId, sids); + clusters.add(c); + pos += 4; + } + } + + /** + * Constructor + * + * @param numShapes the number of shapes + * @param numDrawings the number of drawings + */ + public Dgg(int numShapes, int numDrawings) { + super(EscherRecordType.DGG); + shapesSaved = numShapes; + drawingsSaved = numDrawings; + clusters = new ArrayList(); + } + + /** + * Adds a cluster to this record + * + * @param dgid the id + * @param sids the sid + */ + void addCluster(int dgid, int sids) { + Cluster c = new Cluster(dgid, sids); + clusters.add(c); + } + + /** + * Gets the data for writing out + * + * @return the binary data + */ + byte[] getData() { + numClusters = clusters.size(); + data = new byte[16 + numClusters * 4]; + + // The max shape id + IntegerHelper.getFourBytes(1024 + shapesSaved, data, 0); + + // The number of clusters + IntegerHelper.getFourBytes(numClusters, data, 4); + + // The number of shapes saved + IntegerHelper.getFourBytes(shapesSaved, data, 8); + + // The number of drawings saved + // IntegerHelper.getFourBytes(drawingsSaved, data, 12); + IntegerHelper.getFourBytes(1, data, 12); + + int pos = 16; + for (int i = 0; i < numClusters; i++) { + Cluster c = (Cluster) clusters.get(i); + IntegerHelper.getTwoBytes(c.drawingGroupId, data, pos); + IntegerHelper.getTwoBytes(c.shapeIdsUsed, data, pos + 2); + pos += 4; + } + + return setHeaderData(data); + } + + /** + * Accessor for the number of shapes saved + * + * @return the number of shapes saved + */ + int getShapesSaved() { + return shapesSaved; + } + + /** + * Accessor for the number of drawings saved + * + * @return the number of drawings saved + */ + int getDrawingsSaved() { + return drawingsSaved; + } + + /** + * Accessor for a particular cluster + * + * @param i the cluster number + * @return the cluster + */ + Cluster getCluster(int i) { + return (Cluster) clusters.get(i); + } + + /** + * The cluster structure + */ + static final class Cluster { + /** + * The drawing group id + */ + int drawingGroupId; + + /** + * The something or other + */ + int shapeIdsUsed; + + /** + * Constructor + * + * @param dgId the drawing group id + * @param sids the sids + */ + Cluster(int dgId, int sids) { + drawingGroupId = dgId; + shapeIdsUsed = sids; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DggContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DggContainer.java new file mode 100755 index 0000000..82d3236 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DggContainer.java @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Container for Dgg objects + */ +class DggContainer extends EscherContainer { + /** + * Constructor + */ + public DggContainer() { + super(EscherRecordType.DGG_CONTAINER); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing.java new file mode 100755 index 0000000..625730c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing.java @@ -0,0 +1,990 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.FileInputStream; +import java.io.IOException; +import jxl.CellView; +import jxl.Image; +import jxl.Sheet; +import jxl.common.Assert; +import jxl.common.LengthConverter; +import jxl.common.LengthUnit; +import jxl.common.Logger; +import jxl.write.biff.File; + + +/** + * Contains the various biff records used to insert a drawing into a + * worksheet + */ +public class Drawing implements DrawingGroupObject, Image { + /** + * The default font size for columns + */ + private static final double DEFAULT_FONT_SIZE = 10; + // The image anchor properties + public static ImageAnchorProperties MOVE_AND_SIZE_WITH_CELLS = + new ImageAnchorProperties(1); + public static ImageAnchorProperties MOVE_WITH_CELLS = + new ImageAnchorProperties(2); + public static ImageAnchorProperties NO_MOVE_OR_SIZE_WITH_CELLS = + new ImageAnchorProperties(3); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Drawing.class); + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + /** + * The ObjRecord associated with the drawing + */ + private ObjRecord objRecord; + /** + * Initialized flag + */ + private boolean initialized = false; + /** + * The file containing the image + */ + private java.io.File imageFile; + /** + * The raw image data, used instead of an image file + */ + private byte[] imageData; + /** + * The object id, assigned by the drawing group + */ + private int objectId; + /** + * The blip id + */ + private int blipId; + /** + * The column position of the image + */ + private double x; + /** + * The row position of the image + */ + private double y; + /** + * The width of the image in cells + */ + private double width; + /** + * The height of the image in cells + */ + private double height; + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + /** + * The top level escher container + */ + private EscherContainer escherData; + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + /** + * The drawing data + */ + private DrawingData drawingData; + /** + * The type of this drawing object + */ + private ShapeType type; + /** + * The shape id + */ + private int shapeId; + /** + * The drawing position on the sheet + */ + private int drawingNumber; + /** + * A reference to the sheet containing this drawing. Used to calculate + * the drawing dimensions in pixels + */ + private Sheet sheet; + /** + * Reader for the raw image data + */ + private PNGReader pngReader; + /** + * The client anchor properties + */ + private ImageAnchorProperties imageAnchorProperties; + + /** + * Constructor used when reading images + * + * @param mso the drawing record + * @param obj the object record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + */ + public Drawing(MsoDrawingRecord mso, + ObjRecord obj, + DrawingData dd, + DrawingGroup dg, + Sheet s) { + drawingGroup = dg; + msoDrawingRecord = mso; + drawingData = dd; + objRecord = obj; + sheet = s; + initialized = false; + origin = Origin.READ; + drawingData.addData(msoDrawingRecord.getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + drawingGroup.addDrawing(this); + + Assert.verify(mso != null && obj != null); + + initialize(); + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + */ + protected Drawing(DrawingGroupObject dgo, DrawingGroup dg) { + Drawing d = (Drawing) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + objRecord = d.objRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + } + + /** + * Constructor invoked when writing the images + * + * @param x the column + * @param y the row + * @param w the width in cells + * @param h the height in cells + * @param image the image file + */ + public Drawing(double x, + double y, + double w, + double h, + java.io.File image) { + imageFile = image; + initialized = true; + origin = Origin.WRITE; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + referenceCount = 1; + imageAnchorProperties = MOVE_WITH_CELLS; + type = ShapeType.PICTURE_FRAME; + } + + /** + * Constructor invoked when writing the images + * + * @param x the column + * @param y the row + * @param w the width in cells + * @param h the height in cells + * @param image the image data + */ + public Drawing(double x, + double y, + double w, + double h, + byte[] image) { + imageData = image; + initialized = true; + origin = Origin.WRITE; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + referenceCount = 1; + imageAnchorProperties = MOVE_WITH_CELLS; + type = ShapeType.PICTURE_FRAME; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() { + readSpContainer = drawingData.getSpContainer(drawingNumber); + Assert.verify(readSpContainer != null); + + EscherRecord[] children = readSpContainer.getChildren(); + + Sp sp = (Sp) readSpContainer.getChildren()[0]; + shapeId = sp.getShapeId(); + objectId = objRecord.getObjectId(); + type = ShapeType.getType(sp.getShapeType()); + + if (type == ShapeType.UNKNOWN) { + logger.warn("Unknown shape type"); + } + + Opt opt = (Opt) readSpContainer.getChildren()[1]; + + if (opt.getProperty(260) != null) { + blipId = opt.getProperty(260).value; + } + + if (opt.getProperty(261) != null) { + imageFile = new java.io.File(opt.getProperty(261).stringValue); + } else { + if (type == ShapeType.PICTURE_FRAME) { + logger.warn("no filename property for drawing"); + imageFile = new java.io.File(Integer.toString(blipId)); + } + } + + ClientAnchor clientAnchor = null; + for (int i = 0; i < children.length && clientAnchor == null; i++) { + if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) { + clientAnchor = (ClientAnchor) children[i]; + } + } + + if (clientAnchor == null) { + logger.warn("client anchor not found"); + } else { + x = clientAnchor.getX1(); + y = clientAnchor.getY1(); + width = clientAnchor.getX2() - x; + height = clientAnchor.getY2() - y; + imageAnchorProperties = ImageAnchorProperties.getImageAnchorProperties + (clientAnchor.getProperties()); + } + + if (blipId == 0) { + logger.warn("linked drawings are not supported"); + } + + initialized = true; + } + + /** + * Accessor for the image file + * + * @return the image file + */ + public java.io.File getImageFile() { + return imageFile; + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() { + if (imageFile == null) { + // return the blip id, if it exists + return blipId != 0 ? Integer.toString(blipId) : "__new__image__"; + } + + return imageFile.getPath(); + } + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() { + if (!initialized) { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the shape id + */ + public int getShapeId() { + if (!initialized) { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() { + if (!initialized) { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() { + if (!initialized) { + initialize(); + } + + if (origin == Origin.READ) { + return getReadSpContainer(); + } + + SpContainer spContainer = new SpContainer(); + Sp sp = new Sp(type, shapeId, 2560); + spContainer.add(sp); + Opt opt = new Opt(); + opt.addProperty(260, true, false, blipId); + + if (type == ShapeType.PICTURE_FRAME) { + String filePath = imageFile != null ? imageFile.getPath() : ""; + opt.addProperty(261, true, true, filePath.length() * 2, filePath); + opt.addProperty(447, false, false, 65536); + opt.addProperty(959, false, false, 524288); + spContainer.add(opt); + } + + ClientAnchor clientAnchor = new ClientAnchor + (x, y, x + width, y + height, + imageAnchorProperties.getValue()); + spContainer.add(clientAnchor); + ClientData clientData = new ClientData(); + spContainer.add(clientData); + + return spContainer; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() { + return drawingGroup; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) { + drawingGroup = dg; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() { + if (!initialized) { + initialize(); + } + return x; + } + + /** + * Sets the column position of this drawing + * + * @param x the column + */ + public void setX(double x) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + this.x = x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() { + if (!initialized) { + initialize(); + } + + return y; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + this.y = y; + } + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() { + if (!initialized) { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() { + if (!initialized) { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() { + if (!initialized) { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() { + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() throws IOException { + if (origin == Origin.READ || origin == Origin.READ_WRITE) { + return getImageData(); + } + + Assert.verify(origin == Origin.WRITE); + + if (imageFile == null) { + Assert.verify(imageData != null); + return imageData; + } + + byte[] data = new byte[(int) imageFile.length()]; + FileInputStream fis = new FileInputStream(imageFile); + fis.read(data, 0, data.length); + fis.close(); + return data; + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() { + return type; + } + + /** + * Writes any other records associated with this drawing group object + * + * @param outputFile the output file + * @throws IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException { + if (origin == Origin.READ) { + outputFile.write(objRecord); + return; + } + + // Create the obj record + ObjRecord objrec = new ObjRecord(objectId, + ObjRecord.PICTURE); + outputFile.write(objrec); + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Does nothing here + * + * @param outputFile the output file + */ + public void writeTailRecords(File outputFile) throws IOException { + // does nothing + } + + /** + * Interface method + * + * @return the column number at which the image is positioned + */ + public double getColumn() { + return getX(); + } + + /** + * Interface method + * + * @return the row number at which the image is positions + */ + public double getRow() { + return getY(); + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() { + return msoDrawingRecord.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() { + return false; + } + + /** + * Removes a row + * + * @param r the row to be removed + */ + public void removeRow(int r) { + if (y > r) { + setY(r); + } + } + + /** + * Accessor for the image dimensions. See technotes for Bill's explanation + * of the calculation logic + * + * @return approximate drawing size in pixels + */ + private double getWidthInPoints() { + if (sheet == null) { + logger.warn("calculating image width: sheet is null"); + return 0; + } + + // The start and end row numbers + int firstCol = (int) x; + int lastCol = (int) Math.ceil(x + width) - 1; + + // **** MAGIC NUMBER ALERT *** + // multiply the point size of the font by 0.59 to give the point size + // I know of no explanation for this yet, other than that it seems to + // give the right answer + + // Get the width of the image within the first col, allowing for + // fractional offsets + CellView cellView = sheet.getColumnView(firstCol); + int firstColWidth = cellView.getSize(); + double firstColImageWidth = (1 - (x - firstCol)) * firstColWidth; + double pointSize = (cellView.getFormat() != null) ? + cellView.getFormat().getFont().getPointSize() : DEFAULT_FONT_SIZE; + double firstColWidthInPoints = firstColImageWidth * 0.59 * pointSize / 256; + + // Get the height of the image within the last row, allowing for + // fractional offsets + int lastColWidth = 0; + double lastColImageWidth = 0; + double lastColWidthInPoints = 0; + if (lastCol != firstCol) { + cellView = sheet.getColumnView(lastCol); + lastColWidth = cellView.getSize(); + lastColImageWidth = (x + width - lastCol) * lastColWidth; + pointSize = (cellView.getFormat() != null) ? + cellView.getFormat().getFont().getPointSize() : DEFAULT_FONT_SIZE; + lastColWidthInPoints = lastColImageWidth * 0.59 * pointSize / 256; + } + + // Now get all the columns in between + double width = 0; + for (int i = 0; i < lastCol - firstCol - 1; i++) { + cellView = sheet.getColumnView(firstCol + 1 + i); + pointSize = (cellView.getFormat() != null) ? + cellView.getFormat().getFont().getPointSize() : DEFAULT_FONT_SIZE; + width += cellView.getSize() * 0.59 * pointSize / 256; + } + + // Add on the first and last row contributions to get the height in twips + double widthInPoints = width + + firstColWidthInPoints + lastColWidthInPoints; + + return widthInPoints; + } + + /** + * Accessor for the image dimensions. See technotes for Bill's explanation + * of the calculation logic + * + * @return approximate drawing size in pixels + */ + private double getHeightInPoints() { + if (sheet == null) { + logger.warn("calculating image height: sheet is null"); + return 0; + } + + // The start and end row numbers + int firstRow = (int) y; + int lastRow = (int) Math.ceil(y + height) - 1; + + // Get the height of the image within the first row, allowing for + // fractional offsets + int firstRowHeight = sheet.getRowView(firstRow).getSize(); + double firstRowImageHeight = (1 - (y - firstRow)) * firstRowHeight; + + // Get the height of the image within the last row, allowing for + // fractional offsets + int lastRowHeight = 0; + double lastRowImageHeight = 0; + if (lastRow != firstRow) { + lastRowHeight = sheet.getRowView(lastRow).getSize(); + lastRowImageHeight = (y + height - lastRow) * lastRowHeight; + } + + // Now get all the rows in between + double height = 0; + for (int i = 0; i < lastRow - firstRow - 1; i++) { + height += sheet.getRowView(firstRow + 1 + i).getSize(); + } + + // Add on the first and last row contributions to get the height in twips + double heightInTwips = height + firstRowHeight + lastRowHeight; + + // Now divide by the magic number to converts twips into pixels and + // return the value + double heightInPoints = heightInTwips / 20.0; + + return heightInPoints; + } + + /** + * Get the width of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the width of the image within Excel + */ + public double getWidth(LengthUnit unit) { + double widthInPoints = getWidthInPoints(); + return widthInPoints * LengthConverter.getConversionFactor + (LengthUnit.POINTS, unit); + } + + /** + * Get the height of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the height of the image within Excel + */ + public double getHeight(LengthUnit unit) { + double heightInPoints = getHeightInPoints(); + return heightInPoints * LengthConverter.getConversionFactor + (LengthUnit.POINTS, unit); + } + + /** + * Gets the width of the image. Note that this is the width of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image width in pixels + */ + public int getImageWidth() { + return getPngReader().getWidth(); + } + + /** + * Gets the height of the image. Note that this is the height of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image width in pixels + */ + public int getImageHeight() { + return getPngReader().getHeight(); + } + + /** + * Gets the horizontal resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + public double getHorizontalResolution(LengthUnit unit) { + int res = getPngReader().getHorizontalResolution(); + return res / LengthConverter.getConversionFactor(LengthUnit.METRES, unit); + } + + /** + * Gets the vertical resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + public double getVerticalResolution(LengthUnit unit) { + int res = getPngReader().getVerticalResolution(); + return res / LengthConverter.getConversionFactor(LengthUnit.METRES, unit); + } + + private PNGReader getPngReader() { + if (pngReader != null) { + return pngReader; + } + + byte[] imdata = null; + if (origin == Origin.READ || origin == Origin.READ_WRITE) { + imdata = getImageData(); + } else { + try { + imdata = getImageBytes(); + } catch (IOException e) { + logger.warn("Could not read image file"); + imdata = new byte[0]; + } + } + + pngReader = new PNGReader(imdata); + pngReader.read(); + return pngReader; + } + + /** + * Accessor for the anchor properties + */ + protected ImageAnchorProperties getImageAnchor() { + if (!initialized) { + initialize(); + } + + return imageAnchorProperties; + } + + /** + * Accessor for the anchor properties + */ + protected void setImageAnchor(ImageAnchorProperties iap) { + imageAnchorProperties = iap; + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + } + } + + // Enumeration type for the image anchor properties + protected static class ImageAnchorProperties { + private static ImageAnchorProperties[] o = new ImageAnchorProperties[0]; + private final int value; + + ImageAnchorProperties(int v) { + value = v; + + ImageAnchorProperties[] oldArray = o; + o = new ImageAnchorProperties[oldArray.length + 1]; + System.arraycopy(oldArray, 0, o, 0, oldArray.length); + o[oldArray.length] = this; + } + + static ImageAnchorProperties getImageAnchorProperties(int val) { + ImageAnchorProperties iap = MOVE_AND_SIZE_WITH_CELLS; + int pos = 0; + while (pos < o.length) { + if (o[pos].getValue() == val) { + iap = o[pos]; + break; + } else { + pos++; + } + } + return iap; + } + + int getValue() { + return value; + } + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing2.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing2.java new file mode 100755 index 0000000..73a767f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing2.java @@ -0,0 +1,629 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.FileInputStream; +import java.io.IOException; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.biff.File; + + +/** + * Contains the various biff records used to insert a drawing into a + * worksheet. This type of image does not have an associated object + * record + */ +public class Drawing2 implements DrawingGroupObject { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Drawing.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The file containing the image + */ + private java.io.File imageFile; + + /** + * The raw image data, used instead of an image file + */ + private byte[] imageData; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The column position of the image + */ + private double x; + + /** + * The row position of the image + */ + private double y; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The shape id + */ + private int shapeId; + + /** + * The drawing position on the sheet + */ + private int drawingNumber; + + + /** + * Constructor used when reading images + * + * @param mso the drawing record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + */ + public Drawing2(MsoDrawingRecord mso, + DrawingData dd, + DrawingGroup dg) { + drawingGroup = dg; + msoDrawingRecord = mso; + drawingData = dd; + initialized = false; + origin = Origin.READ; + // there is no drawing number associated with this drawing + drawingData.addRawData(msoDrawingRecord.getData()); + drawingGroup.addDrawing(this); + + Assert.verify(mso != null); + + initialize(); + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + */ + protected Drawing2(DrawingGroupObject dgo, DrawingGroup dg) { + Drawing2 d = (Drawing2) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + } + + /** + * Constructor invoked when writing the images + * + * @param x the column + * @param y the row + * @param w the width in cells + * @param h the height in cells + * @param image the image file + */ + public Drawing2(double x, + double y, + double w, + double h, + java.io.File image) { + imageFile = image; + initialized = true; + origin = Origin.WRITE; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + referenceCount = 1; + type = ShapeType.PICTURE_FRAME; + } + + /** + * Constructor invoked when writing the images + * + * @param x the column + * @param y the row + * @param w the width in cells + * @param h the height in cells + * @param image the image data + */ + public Drawing2(double x, + double y, + double w, + double h, + byte[] image) { + imageData = image; + initialized = true; + origin = Origin.WRITE; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + referenceCount = 1; + type = ShapeType.PICTURE_FRAME; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() { + initialized = true; + } + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() { + if (!initialized) { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the shape id + */ + public int getShapeId() { + if (!initialized) { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() { + if (!initialized) { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() { + if (!initialized) { + initialize(); + } + + Assert.verify(origin == Origin.READ); + + return getReadSpContainer(); + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() { + return drawingGroup; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) { + drawingGroup = dg; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() { + if (!initialized) { + initialize(); + } + return x; + } + + /** + * Sets the column position of this drawing + * + * @param x the column + */ + public void setX(double x) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + this.x = x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() { + if (!initialized) { + initialize(); + } + + return y; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + this.y = y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() { + if (!initialized) { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() { + if (!initialized) { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) { + if (origin == Origin.READ) { + if (!initialized) { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() { + if (!initialized) { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() { + Assert.verify(false); + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() throws IOException { + Assert.verify(false); + if (origin == Origin.READ || origin == Origin.READ_WRITE) { + return getImageData(); + } + + Assert.verify(origin == Origin.WRITE); + + if (imageFile == null) { + Assert.verify(imageData != null); + return imageData; + } + + byte[] data = new byte[(int) imageFile.length()]; + FileInputStream fis = new FileInputStream(imageFile); + fis.read(data, 0, data.length); + fis.close(); + return data; + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() { + return type; + } + + /** + * Writes any other records associated with this drawing group object + * + * @param outputFile the output file + * @throws IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException { + // no records to write + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Does nothing here + * + * @param outputFile the output file + * @throws IOException + */ + public void writeTailRecords(File outputFile) throws IOException { + // does nothing + } + + /** + * Interface method + * + * @return the column number at which the image is positioned + */ + public double getColumn() { + return getX(); + } + + /** + * Interface method + * + * @return the row number at which the image is positions + */ + public double getRow() { + return getY(); + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() { + return msoDrawingRecord.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() { + return false; + } + + /** + * Removes a row + * + * @param r the row to be removed + */ + public void removeRow(int r) { + if (y > r) { + setY(r); + } + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() { + Assert.verify(false); + return null; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingData.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingData.java new file mode 100755 index 0000000..fa3b05c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingData.java @@ -0,0 +1,206 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.ArrayList; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Class used to concatenate all the data for the various drawing objects + * into one continuous stream + */ +public class DrawingData implements EscherStream { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DrawingData.class); + + /** + * The drawing data + */ + private byte[] drawingData; + + /** + * The number of drawings + */ + private int numDrawings; + + /** + * Initialized flag + */ + private boolean initialized; + + /** + * The spgr container. The contains the SpContainer for each drawing + */ + private EscherRecord[] spContainers; + + /** + * Constructor + */ + public DrawingData() { + numDrawings = 0; + drawingData = null; + initialized = false; + } + + /** + * Initialization + */ + private void initialize() { + EscherRecordData er = new EscherRecordData(this, 0); + Assert.verify(er.isContainer()); + + EscherContainer dgContainer = new EscherContainer(er); + EscherRecord[] children = dgContainer.getChildren(); + + children = dgContainer.getChildren(); + // Dg dg = (Dg) children[0]; + + EscherContainer spgrContainer = null; + + for (int i = 0; i < children.length && spgrContainer == null; i++) { + EscherRecord child = children[i]; + if (child.getType() == EscherRecordType.SPGR_CONTAINER) { + spgrContainer = (EscherContainer) child; + } + } + Assert.verify(spgrContainer != null); + + EscherRecord[] spgrChildren = spgrContainer.getChildren(); + + // See if any of the spgrChildren are SpgrContainer + boolean nestedContainers = false; + for (int i = 0; i < spgrChildren.length && !nestedContainers; i++) { + if (spgrChildren[i].getType() == EscherRecordType.SPGR_CONTAINER) { + nestedContainers = true; + } + } + + // If there are no nested containers, simply set the spContainer list + // to be the list of children + if (!nestedContainers) { + spContainers = spgrChildren; + } else { + // Go through the hierarchy and dig out all the Sp containers + ArrayList sps = new ArrayList(); + getSpContainers(spgrContainer, sps); + spContainers = new EscherRecord[sps.size()]; + spContainers = (EscherRecord[]) sps.toArray(spContainers); + } + + initialized = true; + } + + /** + * Gets the sp container from the internal data + * + * @param spgrContainer the spgr container + * @param sps the list of sp records + */ + private void getSpContainers(EscherContainer spgrContainer, ArrayList sps) { + EscherRecord[] spgrChildren = spgrContainer.getChildren(); + for (int i = 0; i < spgrChildren.length; i++) { + if (spgrChildren[i].getType() == EscherRecordType.SP_CONTAINER) { + sps.add(spgrChildren[i]); + } else if (spgrChildren[i].getType() == EscherRecordType.SPGR_CONTAINER) { + getSpContainers((EscherContainer) spgrChildren[i], sps); + } else { + logger.warn("Spgr Containers contains a record other than Sp/Spgr " + + "containers"); + } + } + } + + /** + * Adds the byte stream to the drawing data + * + * @param data the data to add + */ + public void addData(byte[] data) { + addRawData(data); + numDrawings++; + } + + /** + * Adds the data to the array without incrementing the drawing number. + * This is used by comments, which for some bizarre and inexplicable + * reason split out the data + * + * @param data the data to add + */ + public void addRawData(byte[] data) { + if (drawingData == null) { + drawingData = data; + return; + } + + // Resize the array + byte[] newArray = new byte[drawingData.length + data.length]; + System.arraycopy(drawingData, 0, newArray, 0, drawingData.length); + System.arraycopy(data, 0, newArray, drawingData.length, data.length); + drawingData = newArray; + + // Dirty up this object + initialized = false; + } + + /** + * Accessor for the number of drawings + * + * @return the current count of drawings + */ + final int getNumDrawings() { + return numDrawings; + } + + /** + * Gets the sp container for the specified drawing number + * + * @param drawingNum the drawing number for which to return the spContainer + * @return the spcontainer + */ + EscherContainer getSpContainer(int drawingNum) { + if (!initialized) { + initialize(); + } + + if ((drawingNum + 1) >= spContainers.length) { + throw new DrawingDataException(); + } + + EscherContainer spContainer = + (EscherContainer) spContainers[drawingNum + 1]; + + Assert.verify(spContainer != null); + + return spContainer; + } + + /** + * Gets the data which was read in for the drawings + * + * @return the drawing data + */ + public byte[] getData() { + return drawingData; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingDataException.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingDataException.java new file mode 100755 index 0000000..8ee2358 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingDataException.java @@ -0,0 +1,35 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Checked exception thrown when the drawing data is corrupt eg. when + * the drawing number exceeds the number of SpContainers. This exception + * is handled within the drawing package, and usually causes drawings to be + * disabled for the remainder of the workbook + */ +public class DrawingDataException extends RuntimeException { + private static final String message = + "Drawing number exceeds available SpContainers"; + + DrawingDataException() { + super(message); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroup.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroup.java new file mode 100755 index 0000000..8a91a33 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroup.java @@ -0,0 +1,534 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.read.biff.Record; +import jxl.write.biff.File; + +/** + * This class contains the Excel picture data in Escher format for the + * entire workbook + */ +public class DrawingGroup implements EscherStream { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DrawingGroup.class); + + /** + * The escher data read in from file + */ + private byte[] drawingData; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * The Bstore container, which contains all the drawing data + */ + private BStoreContainer bstoreContainer; + + /** + * The initialized flag + */ + private boolean initialized; + + /** + * The list of user added drawings + */ + private final ArrayList drawings; + + /** + * The number of blips + */ + private int numBlips; + + /** + * The number of charts + */ + private int numCharts; + + /** + * The number of shape ids used on the second Dgg cluster + */ + private int drawingGroupId; + + /** + * Flag which indicates that at least one of the drawings has been omitted + * from the worksheet + */ + private boolean drawingsOmitted; + + /** + * The origin of this drawing group + */ + private Origin origin; + + /** + * A hash map of images keyed on the file path, containing the + * reference count + */ + private final HashMap imageFiles; + + /** + * A count of the next available object id + */ + private int maxObjectId; + + /** + * The maximum shape id so far encountered + */ + private int maxShapeId; + + /** + * Constructor + * + * @param o the origin of this drawing group + */ + public DrawingGroup(Origin o) { + origin = o; + initialized = o == Origin.WRITE; + drawings = new ArrayList(); + imageFiles = new HashMap(); + drawingsOmitted = false; + maxObjectId = 1; + maxShapeId = 1024; + } + + /** + * Copy constructor + * Uses a shallow copy for most things, since as soon as anything + * is changed, the drawing group is invalidated and all the data blocks + * regenerated + * + * @param dg the drawing group to copy + */ + public DrawingGroup(DrawingGroup dg) { + drawingData = dg.drawingData; + escherData = dg.escherData; + bstoreContainer = dg.bstoreContainer; + initialized = dg.initialized; + drawingData = dg.drawingData; + escherData = dg.escherData; + bstoreContainer = dg.bstoreContainer; + numBlips = dg.numBlips; + numCharts = dg.numCharts; + drawingGroupId = dg.drawingGroupId; + drawingsOmitted = dg.drawingsOmitted; + origin = dg.origin; + imageFiles = (HashMap) dg.imageFiles.clone(); + maxObjectId = dg.maxObjectId; + maxShapeId = dg.maxShapeId; + + // Create this as empty, because all drawings will get added later + // as part of the sheet copy process + drawings = new ArrayList(); + } + + /** + * /** + * Adds in a drawing group record to this drawing group. The binary + * data is extracted from the drawing group and added to a single + * byte array + * + * @param mso the drawing group record to add + */ + public void add(MsoDrawingGroupRecord mso) { + addData(mso.getData()); + } + + /** + * Adds a continue record to this drawing group. the binary data is + * extracted and appended to the byte array + * + * @param cont the continue record + */ + public void add(Record cont) { + addData(cont.getData()); + } + + /** + * Adds the mso record data to the drawing data + * + * @param msodata the raw mso data + */ + private void addData(byte[] msodata) { + if (drawingData == null) { + drawingData = new byte[msodata.length]; + System.arraycopy(msodata, 0, drawingData, 0, msodata.length); + return; + } + + // Grow the array + byte[] newdata = new byte[drawingData.length + msodata.length]; + System.arraycopy(drawingData, 0, newdata, 0, drawingData.length); + System.arraycopy(msodata, 0, newdata, drawingData.length, msodata.length); + drawingData = newdata; + } + + /** + * Adds a drawing to the drawing group + * + * @param d the drawing to add + */ + final void addDrawing(DrawingGroupObject d) { + drawings.add(d); + maxObjectId = Math.max(maxObjectId, d.getObjectId()); + maxShapeId = Math.max(maxShapeId, d.getShapeId()); + } + + /** + * Adds a chart to the drawing group + * + * @param c the chart + */ + public void add(Chart c) { + numCharts++; + } + + /** + * Adds a drawing from the public, writable interface + * + * @param d the drawing to add + */ + public void add(DrawingGroupObject d) { + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + BStoreContainer bsc = getBStoreContainer(); // force initialization + Dgg dgg = (Dgg) escherData.getChildren()[0]; + drawingGroupId = dgg.getCluster(1).drawingGroupId - numBlips - 1; + numBlips = bsc != null ? bsc.getNumBlips() : 0; + + if (bsc != null) { + Assert.verify(numBlips == bsc.getNumBlips()); + } + } + + if (!(d instanceof Drawing)) { + // Assign a new object id and add it to the list + // drawings.add(d); + maxObjectId++; + maxShapeId++; + d.setDrawingGroup(this); + d.setObjectId(maxObjectId, numBlips + 1, maxShapeId); + if (drawings.size() > maxObjectId) { + logger.warn("drawings length " + drawings.size() + + " exceeds the max object id " + maxObjectId); + } + // numBlips++; + return; + } + + Drawing drawing = (Drawing) d; + + // See if this is referenced elsewhere + Drawing refImage = + (Drawing) imageFiles.get(d.getImageFilePath()); + + if (refImage == null) { + // There are no other references to this drawing, so assign + // a new object id and put it on the hash map + maxObjectId++; + maxShapeId++; + drawings.add(drawing); + drawing.setDrawingGroup(this); + drawing.setObjectId(maxObjectId, numBlips + 1, maxShapeId); + numBlips++; + imageFiles.put(drawing.getImageFilePath(), drawing); + } else { + // This drawing is used elsewhere in the workbook. Increment the + // reference count on the drawing, and set the object id of the drawing + // passed in + refImage.setReferenceCount(refImage.getReferenceCount() + 1); + drawing.setDrawingGroup(this); + drawing.setObjectId(refImage.getObjectId(), + refImage.getBlipId(), + refImage.getShapeId()); + } + } + + /** + * Interface method to remove a drawing from the group + * + * @param d the drawing to remove + */ + public void remove(DrawingGroupObject d) { + // Unless there are real images or some such, it is possible that + // a BStoreContainer will not be present. In that case simply return + if (getBStoreContainer() == null) { + return; + } + + if (origin == Origin.READ) { + origin = Origin.READ_WRITE; + numBlips = getBStoreContainer().getNumBlips(); + Dgg dgg = (Dgg) escherData.getChildren()[0]; + drawingGroupId = dgg.getCluster(1).drawingGroupId - numBlips - 1; + } + + // Get the blip + EscherRecord[] children = getBStoreContainer().getChildren(); + BlipStoreEntry bse = (BlipStoreEntry) children[d.getBlipId() - 1]; + + bse.dereference(); + + if (bse.getReferenceCount() == 0) { + // Remove the blip + getBStoreContainer().remove(bse); + + // Adjust blipId on the other blips + for (Iterator i = drawings.iterator(); i.hasNext(); ) { + DrawingGroupObject drawing = (DrawingGroupObject) i.next(); + + if (drawing.getBlipId() > d.getBlipId()) { + drawing.setObjectId(drawing.getObjectId(), + drawing.getBlipId() - 1, + drawing.getShapeId()); + } + } + + numBlips--; + } + } + + + /** + * Initializes the drawing data from the escher record read in + */ + private void initialize() { + EscherRecordData er = new EscherRecordData(this, 0); + + Assert.verify(er.isContainer()); + + escherData = new EscherContainer(er); + + Assert.verify(escherData.getLength() == drawingData.length); + Assert.verify(escherData.getType() == EscherRecordType.DGG_CONTAINER); + + initialized = true; + } + + /** + * Gets hold of the BStore container from the Escher data + * + * @return the BStore container + */ + private BStoreContainer getBStoreContainer() { + if (bstoreContainer == null) { + if (!initialized) { + initialize(); + } + + EscherRecord[] children = escherData.getChildren(); + if (children.length > 1 && + children[1].getType() == EscherRecordType.BSTORE_CONTAINER) { + bstoreContainer = (BStoreContainer) children[1]; + } + } + + return bstoreContainer; + } + + /** + * Gets hold of the binary data + * + * @return the data + */ + public byte[] getData() { + return drawingData; + } + + /** + * Writes the drawing group to the output file + * + * @param outputFile the file to write to + * @throws IOException + */ + public void write(File outputFile) throws IOException { + if (origin == Origin.WRITE) { + DggContainer dggContainer = new DggContainer(); + + Dgg dgg = new Dgg(numBlips + numCharts + 1, numBlips); + + dgg.addCluster(1, 0); + dgg.addCluster(numBlips + 1, 0); + + dggContainer.add(dgg); + + int drawingsAdded = 0; + BStoreContainer bstoreCont = new BStoreContainer(); + + // Create a blip entry for each drawing + for (Iterator i = drawings.iterator(); i.hasNext(); ) { + Object o = i.next(); + if (o instanceof Drawing) { + Drawing d = (Drawing) o; + BlipStoreEntry bse = new BlipStoreEntry(d); + + bstoreCont.add(bse); + drawingsAdded++; + } + } + if (drawingsAdded > 0) { + bstoreCont.setNumBlips(drawingsAdded); + dggContainer.add(bstoreCont); + } + + Opt opt = new Opt(); + + dggContainer.add(opt); + + SplitMenuColors splitMenuColors = new SplitMenuColors(); + dggContainer.add(splitMenuColors); + + drawingData = dggContainer.getData(); + } else if (origin == Origin.READ_WRITE) { + DggContainer dggContainer = new DggContainer(); + + Dgg dgg = new Dgg(numBlips + numCharts + 1, numBlips); + + dgg.addCluster(1, 0); + dgg.addCluster(drawingGroupId + numBlips + 1, 0); + + dggContainer.add(dgg); + + BStoreContainer bstoreCont = new BStoreContainer(); + bstoreCont.setNumBlips(numBlips); + + // Create a blip entry for each drawing that was read in + BStoreContainer readBStoreContainer = getBStoreContainer(); + + if (readBStoreContainer != null) { + EscherRecord[] children = readBStoreContainer.getChildren(); + for (int i = 0; i < children.length; i++) { + BlipStoreEntry bse = (BlipStoreEntry) children[i]; + bstoreCont.add(bse); + } + } + + // Create a blip entry for each drawing that has been added + for (Iterator i = drawings.iterator(); i.hasNext(); ) { + DrawingGroupObject dgo = (DrawingGroupObject) i.next(); + if (dgo instanceof Drawing) { + Drawing d = (Drawing) dgo; + if (d.getOrigin() == Origin.WRITE) { + BlipStoreEntry bse = new BlipStoreEntry(d); + bstoreCont.add(bse); + } + } + } + + dggContainer.add(bstoreCont); + + Opt opt = new Opt(); + + opt.addProperty(191, false, false, 524296); + opt.addProperty(385, false, false, 134217737); + opt.addProperty(448, false, false, 134217792); + + dggContainer.add(opt); + + SplitMenuColors splitMenuColors = new SplitMenuColors(); + dggContainer.add(splitMenuColors); + + drawingData = dggContainer.getData(); + } + + MsoDrawingGroupRecord msodg = new MsoDrawingGroupRecord(drawingData); + outputFile.write(msodg); + } + + /** + * Accessor for the number of blips in the drawing group + * + * @return the number of blips + */ + final int getNumberOfBlips() { + return numBlips; + } + + /** + * Gets the drawing data for the given blip id. Called by the Drawing + * object + * + * @param blipId the blipId + * @return the drawing data + */ + byte[] getImageData(int blipId) { + numBlips = getBStoreContainer().getNumBlips(); + + Assert.verify(blipId <= numBlips); + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + // Get the blip + EscherRecord[] children = getBStoreContainer().getChildren(); + BlipStoreEntry bse = (BlipStoreEntry) children[blipId - 1]; + + return bse.getImageData(); + } + + /** + * Indicates that at least one of the drawings has been omitted from + * the worksheet + * + * @param mso the mso record + * @param obj the obj record + */ + public void setDrawingsOmitted(MsoDrawingRecord mso, ObjRecord obj) { + drawingsOmitted = true; + + if (obj != null) { + maxObjectId = Math.max(maxObjectId, obj.getObjectId()); + } + } + + /** + * Accessor for the drawingsOmitted flag + * + * @return TRUE if a drawing has been omitted, FALSE otherwise + */ + public boolean hasDrawingsOmitted() { + return drawingsOmitted; + } + + /** + * Updates this with the appropriate data from the drawing group passed in + * This is called during the copy process: this is first initialised as + * an empty object, but during the copy, the source DrawingGroup may + * change. After the copy process, this method is then called to update + * the relevant fields. Unfortunately, the copy process required the + * presence of a drawing group + * + * @param dg the drawing group containing the updated data + */ + public void updateData(DrawingGroup dg) { + drawingsOmitted = dg.drawingsOmitted; + maxObjectId = dg.maxObjectId; + maxShapeId = dg.maxShapeId; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroupObject.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroupObject.java new file mode 100755 index 0000000..10ede04 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroupObject.java @@ -0,0 +1,231 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import jxl.write.biff.File; + +/** + * Interface for the various object types that can be added to a drawing + * group + */ +public interface DrawingGroupObject { + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + void setObjectId(int objid, int bip, int sid); + + /** + * Accessor for the object id + * + * @return the object id + */ + int getObjectId(); + + /** + * Accessor for the blip id + * + * @return the blip id + */ + int getBlipId(); + + /** + * Accessor for the shape id + * + * @return the shape id + */ + int getShapeId(); + + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + MsoDrawingRecord getMsoDrawingRecord(); + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + EscherContainer getSpContainer(); + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + DrawingGroup getDrawingGroup(); + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + void setDrawingGroup(DrawingGroup dg); + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + Origin getOrigin(); + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + int getReferenceCount(); + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + void setReferenceCount(int r); + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + double getX(); + + /** + * Sets the column position of this drawing + * + * @param x the column + */ + void setX(double x); + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + double getY(); + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + void setY(double y); + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + double getWidth(); + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + void setWidth(double w); + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + double getHeight(); + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + void setHeight(double h); + + + /** + * Accessor for the type + * + * @return the type + */ + ShapeType getType(); + + /** + * Accessor for the image data + * + * @return the image data + */ + byte[] getImageData(); + + /** + * Accessor for the image data + * + * @return the image data + */ + byte[] getImageBytes() throws IOException; + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + String getImageFilePath(); + + /** + * Writes any other records associated with this drawing group object + */ + void writeAdditionalRecords(File outputFile) throws IOException; + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + */ + void writeTailRecords(File outputFile) throws IOException; + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + boolean isFirst(); + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + boolean isFormObject(); + +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherAtom.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherAtom.java new file mode 100755 index 0000000..611a444 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherAtom.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.common.Logger; + +/** + * Class for atoms. This may be instantiated as is for unknown/uncared about + * atoms, or subclassed if we have some semantic interest in the contents + */ +class EscherAtom extends EscherRecord { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(EscherAtom.class); + + /** + * Constructor + * + * @param erd the escher record data + */ + public EscherAtom(EscherRecordData erd) { + super(erd); + } + + /** + * Constructor + * + * @param type the type + */ + protected EscherAtom(EscherRecordType type) { + super(type); + } + + /** + * Gets the data for writing + * + * @return the data + */ + byte[] getData() { + logger.warn("escher atom getData called on object of type " + + getClass().getName() + " code " + + Integer.toString(getType().getValue(), 16)); + return null; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherContainer.java new file mode 100755 index 0000000..2f9a9cc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherContainer.java @@ -0,0 +1,177 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.common.Logger; + +/** + * An escher container. This record may contain other escher containers or + * atoms + */ +class EscherContainer extends EscherRecord { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(EscherContainer.class); + + /** + * Initialized flag + */ + private boolean initialized; + + + /** + * The children of this container + */ + private final ArrayList children; + + /** + * Constructor + * + * @param erd the raw data + */ + public EscherContainer(EscherRecordData erd) { + super(erd); + initialized = false; + children = new ArrayList(); + } + + /** + * Constructor used when writing out escher data + * + * @param type the type + */ + protected EscherContainer(EscherRecordType type) { + super(type); + setContainer(true); + children = new ArrayList(); + } + + /** + * Accessor for the children of this container + * + * @return the children + */ + public EscherRecord[] getChildren() { + if (!initialized) { + initialize(); + } + + Object[] ca = children.toArray(new EscherRecord[children.size()]); + + return (EscherRecord[]) ca; + } + + /** + * Adds a child to this container + * + * @param child the item to add + */ + public void add(EscherRecord child) { + children.add(child); + } + + /** + * Removes a child from this container + * + * @param child the item to remove + */ + public void remove(EscherRecord child) { + boolean result = children.remove(child); + } + + /** + * Initialization + */ + private void initialize() { + int curpos = getPos() + HEADER_LENGTH; + int endpos = Math.min(getPos() + getLength(), getStreamLength()); + + EscherRecord newRecord = null; + + while (curpos < endpos) { + EscherRecordData erd = new EscherRecordData(getEscherStream(), curpos); + + EscherRecordType type = erd.getType(); + if (type == EscherRecordType.DGG) { + newRecord = new Dgg(erd); + } else if (type == EscherRecordType.DG) { + newRecord = new Dg(erd); + } else if (type == EscherRecordType.BSTORE_CONTAINER) { + newRecord = new BStoreContainer(erd); + } else if (type == EscherRecordType.SPGR_CONTAINER) { + newRecord = new SpgrContainer(erd); + } else if (type == EscherRecordType.SP_CONTAINER) { + newRecord = new SpContainer(erd); + } else if (type == EscherRecordType.SPGR) { + newRecord = new Spgr(erd); + } else if (type == EscherRecordType.SP) { + newRecord = new Sp(erd); + } else if (type == EscherRecordType.CLIENT_ANCHOR) { + newRecord = new ClientAnchor(erd); + } else if (type == EscherRecordType.CLIENT_DATA) { + newRecord = new ClientData(erd); + } else if (type == EscherRecordType.BSE) { + newRecord = new BlipStoreEntry(erd); + } else if (type == EscherRecordType.OPT) { + newRecord = new Opt(erd); + } else if (type == EscherRecordType.SPLIT_MENU_COLORS) { + newRecord = new SplitMenuColors(erd); + } else if (type == EscherRecordType.CLIENT_TEXT_BOX) { + newRecord = new ClientTextBox(erd); + } else { + newRecord = new EscherAtom(erd); + } + + children.add(newRecord); + curpos += newRecord.getLength(); + } + + initialized = true; + } + + /** + * Gets the data for this container (and all of its children recursively + * + * @return the binary data + */ + byte[] getData() { + if (!initialized) { + initialize(); + } + + byte[] data = new byte[0]; + for (Iterator i = children.iterator(); i.hasNext(); ) { + EscherRecord er = (EscherRecord) i.next(); + byte[] childData = er.getData(); + + if (childData != null) { + byte[] newData = new byte[data.length + childData.length]; + System.arraycopy(data, 0, newData, 0, data.length); + System.arraycopy(childData, 0, newData, data.length, childData.length); + data = newData; + } + } + + return setHeaderData(data); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherDisplay.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherDisplay.java new file mode 100755 index 0000000..dfcb7dd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherDisplay.java @@ -0,0 +1,189 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.BufferedWriter; +import java.io.IOException; + +/** + * Class used to display a complete hierarchically organized Escher stream + * The whole thing is dumped to System.out + *

+ * This class is only used as a debugging tool + */ +public class EscherDisplay { + /** + * The escher stream + */ + private final EscherStream stream; + + /** + * The writer + */ + private final BufferedWriter writer; + + /** + * Constructor + * + * @param s the stream + * @param bw the writer + */ + public EscherDisplay(EscherStream s, BufferedWriter bw) { + stream = s; + writer = bw; + } + + /** + * Display the formatted escher stream + * + * @throws IOException + */ + public void display() throws IOException { + EscherRecordData er = new EscherRecordData(stream, 0); + EscherContainer ec = new EscherContainer(er); + displayContainer(ec, 0); + } + + /** + * Displays the escher container as text + * + * @param ec the escher container + * @param level the indent level + * @throws IOException + */ + private void displayContainer(EscherContainer ec, int level) + throws IOException { + displayRecord(ec, level); + + // Display the contents of the container + level++; + + EscherRecord[] children = ec.getChildren(); + + for (int i = 0; i < children.length; i++) { + EscherRecord er = children[i]; + if (er.getEscherData().isContainer()) { + displayContainer((EscherContainer) er, level); + } else { + displayRecord(er, level); + } + } + } + + /** + * Displays an escher record + * + * @param er the record to display + * @param level the amount of indentation + * @throws IOException + */ + private void displayRecord(EscherRecord er, int level) + throws IOException { + indent(level); + + EscherRecordType type = er.getType(); + + // Display the code + writer.write(Integer.toString(type.getValue(), 16)); + writer.write(" - "); + + // Display the name + if (type == EscherRecordType.DGG_CONTAINER) { + writer.write("Dgg Container"); + writer.newLine(); + } else if (type == EscherRecordType.BSTORE_CONTAINER) { + writer.write("BStore Container"); + writer.newLine(); + } else if (type == EscherRecordType.DG_CONTAINER) { + writer.write("Dg Container"); + writer.newLine(); + } else if (type == EscherRecordType.SPGR_CONTAINER) { + writer.write("Spgr Container"); + writer.newLine(); + } else if (type == EscherRecordType.SP_CONTAINER) { + writer.write("Sp Container"); + writer.newLine(); + } else if (type == EscherRecordType.DGG) { + writer.write("Dgg"); + writer.newLine(); + } else if (type == EscherRecordType.BSE) { + writer.write("Bse"); + writer.newLine(); + } else if (type == EscherRecordType.DG) { + Dg dg = new Dg(er.getEscherData()); + writer.write("Dg: drawing id " + dg.getDrawingId() + + " shape count " + dg.getShapeCount()); + writer.newLine(); + } else if (type == EscherRecordType.SPGR) { + writer.write("Spgr"); + writer.newLine(); + } else if (type == EscherRecordType.SP) { + Sp sp = new Sp(er.getEscherData()); + writer.write("Sp: shape id " + sp.getShapeId() + + " shape type " + sp.getShapeType()); + writer.newLine(); + } else if (type == EscherRecordType.OPT) { + Opt opt = new Opt(er.getEscherData()); + Opt.Property p260 = opt.getProperty(260); + Opt.Property p261 = opt.getProperty(261); + writer.write("Opt (value, stringValue): "); + if (p260 != null) { + writer.write("260: " + + p260.value + ", " + + p260.stringValue + + ";"); + } + if (p261 != null) { + writer.write("261: " + + p261.value + ", " + + p261.stringValue + + ";"); + } + writer.newLine(); + } else if (type == EscherRecordType.CLIENT_ANCHOR) { + writer.write("Client Anchor"); + writer.newLine(); + } else if (type == EscherRecordType.CLIENT_DATA) { + writer.write("Client Data"); + writer.newLine(); + } else if (type == EscherRecordType.CLIENT_TEXT_BOX) { + writer.write("Client Text Box"); + writer.newLine(); + } else if (type == EscherRecordType.SPLIT_MENU_COLORS) { + writer.write("Split Menu Colors"); + writer.newLine(); + } else { + writer.write("???"); + writer.newLine(); + } + } + + /** + * Indents to the amount specified by the level + * + * @param level the level + * @throws IOException + */ + private void indent(int level) throws IOException { + for (int i = 0; i < level * 2; i++) { + writer.write(' '); + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecord.java new file mode 100755 index 0000000..8961dec --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecord.java @@ -0,0 +1,179 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.common.Logger; + +/** + * The base class for all escher records. This class contains + * the jxl.common.header data and is basically a wrapper for the EscherRecordData + * object + */ +abstract class EscherRecord { + /** + * The length of the escher header on all records + */ + protected static final int HEADER_LENGTH = 8; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(EscherRecord.class); + //protected EscherRecordData data; + /** + * The escher data + */ + private final EscherRecordData data; + + /** + * Constructor + * + * @param erd the data + */ + protected EscherRecord(EscherRecordData erd) { + data = erd; + } + + /** + * Constructor + * + * @param type the type + */ + protected EscherRecord(EscherRecordType type) { + data = new EscherRecordData(type); + } + + /** + * Identifies whether this item is a container + * + * @param cont TRUE if this is a container, FALSE otherwise + */ + protected void setContainer(boolean cont) { + data.setContainer(cont); + } + + /** + * Gets the entire length of the record, including the header + * + * @return the length of the record, including the header data + */ + public int getLength() { + return data.getLength() + HEADER_LENGTH; + } + + /** + * Accessor for the escher stream + * + * @return the escher stream + */ + protected final EscherStream getEscherStream() { + return data.getEscherStream(); + } + + /** + * The position of this escher record in the stream + * + * @return the position + */ + protected final int getPos() { + return data.getPos(); + } + + /** + * Accessor for the instance + * + * @return the instance + */ + protected final int getInstance() { + return data.getInstance(); + } + + /** + * Sets the instance number when writing out the escher data + * + * @param i the instance + */ + protected final void setInstance(int i) { + data.setInstance(i); + } + + /** + * Sets the version when writing out the escher data + * + * @param v the version + */ + protected final void setVersion(int v) { + data.setVersion(v); + } + + /** + * Accessor for the escher type + * + * @return the type + */ + public EscherRecordType getType() { + return data.getType(); + } + + /** + * Abstract method used to retrieve the generated escher data when writing + * out image information + * + * @return the escher data + */ + abstract byte[] getData(); + + /** + * Prepends the standard header data to the first eight bytes of the array + * and returns it + * + * @param d the data + * @return the binary data + */ + final byte[] setHeaderData(byte[] d) { + return data.setHeaderData(d); + } + + /** + * Gets the data that was read in, excluding the header data + * + * @return the bytes read in, excluding the header data + */ + byte[] getBytes() { + return data.getBytes(); + } + + /** + * Accessor for the stream length + * + * @return the stream length + */ + protected int getStreamLength() { + return data.getStreamLength(); + } + + /** + * Used by the EscherDisplay class to retrieve the data + * + * @return the data + */ + protected EscherRecordData getEscherData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordData.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordData.java new file mode 100755 index 0000000..cf9adcb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordData.java @@ -0,0 +1,285 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + + +/** + * A single record from an Escher stream. Basically this a container for + * the header data for each Escher record + */ +final class EscherRecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(EscherRecordData.class); + + /** + * The byte position of this record in the escher stream + */ + private int pos; + + /** + * The instance value + */ + private int instance; + + /** + * The version value + */ + private int version; + + /** + * The record id + */ + private final int recordId; + + /** + * The length of the record, excluding the 8 byte header + */ + private int length; + + /** + * The length of the stream + */ + private int streamLength; + + /** + * Indicates whether this record is a container + */ + private boolean container; + + /** + * The type of this record + */ + private EscherRecordType type; + + /** + * A handle back to the drawing group, which contains the entire escher + * stream byte data + */ + private EscherStream escherStream; + + /** + * Constructor + * + * @param dg the escher stream data + * @param p the current position in the stream + */ + public EscherRecordData(EscherStream dg, int p) { + escherStream = dg; + pos = p; + byte[] data = escherStream.getData(); + + streamLength = data.length; + + // First two bytes contain instance and version + int value = IntegerHelper.getInt(data[pos], data[pos + 1]); + + // Instance value is the first 12 bits + instance = (value & 0xfff0) >> 4; + + // Version is the last four bits + version = value & 0xf; + + // Bytes 2 and 3 are the record id + recordId = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + + // Length is bytes 4,5,6 and 7 + length = IntegerHelper.getInt(data[pos + 4], data[pos + 5], + data[pos + 6], data[pos + 7]); + + container = version == 0x0f; + } + + /** + * Constructor + * + * @param t the type of the escher record + */ + public EscherRecordData(EscherRecordType t) { + type = t; + recordId = type.getValue(); + } + + /** + * Determines whether this record is a container + * + * @return TRUE if this is a container, FALSE otherwise + */ + public boolean isContainer() { + return container; + } + + /** + * Sets whether or not this is a container - called when writing + * out an escher stream + * + * @param c TRUE if this is a container, FALSE otherwise + */ + void setContainer(boolean c) { + container = c; + } + + /** + * Accessor for the length, excluding the 8 byte header + * + * @return the length excluding the 8 byte header + */ + public int getLength() { + return length; + } + + /** + * Called when writing to set the length of this record + * + * @param l the length + */ + void setLength(int l) { + length = l; + } + + /** + * Accessor for the record id + * + * @return the record id + */ + public int getRecordId() { + return recordId; + } + + /** + * Accessor for the drawing group stream + * + * @return the drawing group stream + */ + EscherStream getDrawingGroup() { + return escherStream; + } + + /** + * Gets the position in the stream + * + * @return the position in the stream + */ + int getPos() { + return pos; + } + + /** + * Gets the escher type of this record + * + * @return the escher type + */ + EscherRecordType getType() { + if (type == null) { + type = EscherRecordType.getType(recordId); + } + + return type; + } + + /** + * Gets the instance value + * + * @return the instance value + */ + int getInstance() { + return instance; + } + + /** + * Called from the subclass when writing to set the instance value + * + * @param inst the instance + */ + void setInstance(int inst) { + instance = inst; + } + + /** + * Called when writing to set the version of this record + * + * @param v the version + */ + void setVersion(int v) { + version = v; + } + + /** + * Adds the 8 byte header data on the value data passed in, returning + * the modified data + * + * @param d the value data + * @return the value data with the header information + */ + byte[] setHeaderData(byte[] d) { + byte[] data = new byte[d.length + 8]; + System.arraycopy(d, 0, data, 8, d.length); + + if (container) { + version = 0x0f; + } + + // First two bytes contain instance and version + int value = instance << 4; + value |= version; + IntegerHelper.getTwoBytes(value, data, 0); + + // Bytes 2 and 3 are the record id + IntegerHelper.getTwoBytes(recordId, data, 2); + + // Length is bytes 4,5,6 and 7 + IntegerHelper.getFourBytes(d.length, data, 4); + + return data; + } + + /** + * Accessor for the header stream + * + * @return the escher stream + */ + EscherStream getEscherStream() { + return escherStream; + } + + /** + * Gets the data that was read in, excluding the header data + * + * @return the value data that was read in + */ + byte[] getBytes() { + byte[] d = new byte[length]; + System.arraycopy(escherStream.getData(), pos + 8, d, 0, length); + return d; + } + + /** + * Accessor for the stream length + * + * @return the stream length + */ + int getStreamLength() { + return streamLength; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordType.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordType.java new file mode 100755 index 0000000..d230edd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordType.java @@ -0,0 +1,101 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Enumeration class for Escher record types + */ +final class EscherRecordType { + public static final EscherRecordType UNKNOWN = new EscherRecordType(0x0); + public static final EscherRecordType DGG_CONTAINER = + new EscherRecordType(0xf000); + public static final EscherRecordType BSTORE_CONTAINER = + new EscherRecordType(0xf001); + public static final EscherRecordType DG_CONTAINER = + new EscherRecordType(0xf002); + public static final EscherRecordType SPGR_CONTAINER = + new EscherRecordType(0xf003); + public static final EscherRecordType SP_CONTAINER = + new EscherRecordType(0xf004); + public static final EscherRecordType DGG = new EscherRecordType(0xf006); + public static final EscherRecordType BSE = new EscherRecordType(0xf007); + public static final EscherRecordType DG = new EscherRecordType(0xf008); + public static final EscherRecordType SPGR = new EscherRecordType(0xf009); + public static final EscherRecordType SP = new EscherRecordType(0xf00a); + public static final EscherRecordType OPT = new EscherRecordType(0xf00b); + public static final EscherRecordType CLIENT_ANCHOR = + new EscherRecordType(0xf010); + public static final EscherRecordType CLIENT_DATA = + new EscherRecordType(0xf011); + public static final EscherRecordType CLIENT_TEXT_BOX = + new EscherRecordType(0xf00d); + public static final EscherRecordType SPLIT_MENU_COLORS = + new EscherRecordType(0xf11e); + /** + * All escher types + */ + private static EscherRecordType[] types = new EscherRecordType[0]; + /** + * The code of the item within the escher stream + */ + private final int value; + /** + * Constructor + * + * @param val the escher record value + */ + private EscherRecordType(int val) { + value = val; + + EscherRecordType[] newtypes = new EscherRecordType[types.length + 1]; + System.arraycopy(types, 0, newtypes, 0, types.length); + newtypes[types.length] = this; + types = newtypes; + } + + /** + * Accessor to get the item from a particular value + * + * @param val the escher record value + * @return the type corresponding to val, or UNKNOWN if a match could not + * be found + */ + public static EscherRecordType getType(int val) { + EscherRecordType type = UNKNOWN; + + for (int i = 0; i < types.length; i++) { + if (val == types[i].value) { + type = types[i]; + break; + } + } + + return type; + } + + /** + * Accessor for the escher record value + * + * @return the escher record value + */ + public int getValue() { + return value; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherStream.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherStream.java new file mode 100755 index 0000000..6e7a13e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherStream.java @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Interface implemented by records which contain escher byte streams + */ +interface EscherStream { + /** + * Method to access the escher data + * + * @return the escher data + */ + byte[] getData(); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingGroupRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingGroupRecord.java new file mode 100755 index 0000000..06835a1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingGroupRecord.java @@ -0,0 +1,68 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.read.biff.Record; + +/** + * A record which merely holds the MSODRAWINGGROUP data. Used when copying + * files which contain images + */ +public class MsoDrawingGroupRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public MsoDrawingGroupRecord(Record t) { + super(t); + data = t.getData(); + } + + /** + * Constructor + * + * @param d the data + */ + MsoDrawingGroupRecord(byte[] d) { + super(Type.MSODRAWINGGROUP); + data = d; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public byte[] getData() { + return data; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingRecord.java new file mode 100755 index 0000000..8fc1390 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingRecord.java @@ -0,0 +1,108 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * A record which merely holds the MSODRAWING data. Used when copying files + * which contain images + */ +public class MsoDrawingRecord extends WritableRecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(MsoDrawingRecord.class); + + /** + * Flag to indicate whether this is the first drawing on the sheet + * - needed for copying + */ + private boolean first; + /** + * The raw drawing data which was read in + */ + private final byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public MsoDrawingRecord(Record t) { + super(t); + data = getRecord().getData(); + first = false; + } + + /** + * Constructor + * + * @param d the drawing data + */ + public MsoDrawingRecord(byte[] d) { + super(Type.MSODRAWING); + data = d; + first = false; + } + + /** + * Expose the protected function + * + * @return the raw record data + */ + public byte[] getData() { + return data; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public Record getRecord() { + return super.getRecord(); + } + + /** + * Sets the flag to indicate that this is the first drawing on the sheet + */ + public void setFirst() { + first = true; + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() { + return first; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/NoteRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/NoteRecord.java new file mode 100755 index 0000000..6faff5d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/NoteRecord.java @@ -0,0 +1,157 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * A Note (TXO) record which contains the information for comments + */ +public class NoteRecord extends WritableRecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NoteRecord.class); + + /** + * The raw drawing data which was read in + */ + private byte[] data; + + /** + * The row + */ + private int row; + + /** + * The column + */ + private int column; + + /** + * The object id + */ + private int objectId; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public NoteRecord(Record t) { + super(t); + data = getRecord().getData(); + row = IntegerHelper.getInt(data[0], data[1]); + column = IntegerHelper.getInt(data[2], data[3]); + objectId = IntegerHelper.getInt(data[6], data[7]); + } + + /** + * Constructor + * + * @param d the drawing data + */ + public NoteRecord(byte[] d) { + super(Type.NOTE); + data = d; + } + + /** + * Constructor used when writing a Note + * + * @param c the column + * @param r the row + * @param id the object id + */ + public NoteRecord(int c, int r, int id) { + super(Type.NOTE); + row = r; + column = c; + objectId = id; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public byte[] getData() { + if (data != null) { + return data; + } + + String author = ""; + data = new byte[8 + author.length() + 4]; + + // the row + IntegerHelper.getTwoBytes(row, data, 0); + + // the column + IntegerHelper.getTwoBytes(column, data, 2); + + // the object id + IntegerHelper.getTwoBytes(objectId, data, 6); + + // the length of the string + IntegerHelper.getTwoBytes(author.length(), data, 8); + + // the string + // StringHelper.getBytes(author, data, 11); + + // data[data.length-1]=(byte)0x24; + + return data; + } + + /** + * Accessor for the row + * + * @return the row + */ + int getRow() { + return row; + } + + /** + * Accessor for the column + * + * @return the column + */ + int getColumn() { + return column; + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public int getObjectId() { + return objectId; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ObjRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ObjRecord.java new file mode 100755 index 0000000..9b4168d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ObjRecord.java @@ -0,0 +1,382 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * A record which merely holds the OBJ data. Used when copying files which + * contain images + */ +public class ObjRecord extends WritableRecordData { + // The object types + public static final ObjType GROUP = new ObjType(0x0, "Group"); + public static final ObjType LINE = new ObjType(0x01, "Line"); + public static final ObjType RECTANGLE = new ObjType(0x02, "Rectangle"); + public static final ObjType OVAL = new ObjType(0x03, "Oval"); + public static final ObjType ARC = new ObjType(0x04, "Arc"); + public static final ObjType CHART = new ObjType(0x05, "Chart"); + public static final ObjType TEXT = new ObjType(0x06, "Text"); + public static final ObjType BUTTON = new ObjType(0x07, "Button"); + public static final ObjType PICTURE = new ObjType(0x08, "Picture"); + public static final ObjType POLYGON = new ObjType(0x09, "Polygon"); + public static final ObjType CHECKBOX = new ObjType(0x0b, "Checkbox"); + public static final ObjType OPTION = new ObjType(0x0c, "Option"); + public static final ObjType EDITBOX = new ObjType(0x0d, "Edit Box"); + public static final ObjType LABEL = new ObjType(0x0e, "Label"); + public static final ObjType DIALOGUEBOX = new ObjType(0x0f, "Dialogue Box"); + public static final ObjType SPINBOX = new ObjType(0x10, "Spin Box"); + public static final ObjType SCROLLBAR = new ObjType(0x11, "Scrollbar"); + public static final ObjType LISTBOX = new ObjType(0x12, "List Box"); + public static final ObjType GROUPBOX = new ObjType(0x13, "Group Box"); + public static final ObjType COMBOBOX = new ObjType(0x14, "Combo Box"); + public static final ObjType MSOFFICEDRAWING = new ObjType + (0x1e, "MS Office Drawing"); + public static final ObjType FORMCONTROL = + new ObjType(0x14, "Form Combo Box"); + public static final ObjType EXCELNOTE = + new ObjType(0x19, "Excel Note"); + public static final ObjType UNKNOWN = new ObjType(0xff, "Unknown"); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ObjRecord.class); + // Field sub records + private static final int COMMON_DATA_LENGTH = 22; + private static final int CLIPBOARD_FORMAT_LENGTH = 6; + private static final int PICTURE_OPTION_LENGTH = 6; + private static final int NOTE_STRUCTURE_LENGTH = 26; + private static final int COMBOBOX_STRUCTURE_LENGTH = 44; + private static final int END_LENGTH = 4; + /** + * The object type + */ + private final ObjType type; + /** + * Indicates whether this record was read in + */ + private boolean read; + /** + * The object id + */ + private final int objectId; + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public ObjRecord(Record t) { + super(t); + byte[] data = t.getData(); + int objtype = IntegerHelper.getInt(data[4], data[5]); + read = true; + type = ObjType.getType(objtype); + + if (type == UNKNOWN) { + logger.warn("unknown object type code " + objtype); + } + + objectId = IntegerHelper.getInt(data[6], data[7]); + } + + /** + * Constructor + * + * @param objId the object id + * @param t the object type + */ + ObjRecord(int objId, ObjType t) { + super(Type.OBJ); + objectId = objId; + type = t; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public byte[] getData() { + if (read) { + return getRecord().getData(); + } + + if (type == PICTURE || type == CHART) { + return getPictureData(); + } else if (type == EXCELNOTE) { + return getNoteData(); + } else if (type == COMBOBOX) { + return getComboBoxData(); + } else { + Assert.verify(false); + } + return null; + } + + /** + * Gets the ObjRecord subrecords for a picture + * + * @return the binary data for the picture + */ + private byte[] getPictureData() { + int dataLength = COMMON_DATA_LENGTH + + CLIPBOARD_FORMAT_LENGTH + + PICTURE_OPTION_LENGTH + + END_LENGTH; + int pos = 0; + byte[] data = new byte[dataLength]; + + // The jxl.common.data + // record id + IntegerHelper.getTwoBytes(0x15, data, pos); + + // record length + IntegerHelper.getTwoBytes(COMMON_DATA_LENGTH - 4, data, pos + 2); + + // object type + IntegerHelper.getTwoBytes(type.value, data, pos + 4); + + // object id + IntegerHelper.getTwoBytes(objectId, data, pos + 6); + + // the options + IntegerHelper.getTwoBytes(0x6011, data, pos + 8); + pos += COMMON_DATA_LENGTH; + + // The clipboard format + // record id + IntegerHelper.getTwoBytes(0x7, data, pos); + + // record length + IntegerHelper.getTwoBytes(CLIPBOARD_FORMAT_LENGTH - 4, data, pos + 2); + + // the data + IntegerHelper.getTwoBytes(0xffff, data, pos + 4); + pos += CLIPBOARD_FORMAT_LENGTH; + + // Picture option flags + // record id + IntegerHelper.getTwoBytes(0x8, data, pos); + + // record length + IntegerHelper.getTwoBytes(PICTURE_OPTION_LENGTH - 4, data, pos + 2); + + // the data + IntegerHelper.getTwoBytes(0x1, data, pos + 4); + pos += CLIPBOARD_FORMAT_LENGTH; + + // End record id + IntegerHelper.getTwoBytes(0x0, data, pos); + + // record length + IntegerHelper.getTwoBytes(END_LENGTH - 4, data, pos + 2); + + // the data + pos += END_LENGTH; + + return data; + } + + /** + * Gets the ObjRecord subrecords for a note + * + * @return the note data + */ + private byte[] getNoteData() { + int dataLength = COMMON_DATA_LENGTH + + NOTE_STRUCTURE_LENGTH + + END_LENGTH; + int pos = 0; + byte[] data = new byte[dataLength]; + + // The jxl.common.data + // record id + IntegerHelper.getTwoBytes(0x15, data, pos); + + // record length + IntegerHelper.getTwoBytes(COMMON_DATA_LENGTH - 4, data, pos + 2); + + // object type + IntegerHelper.getTwoBytes(type.value, data, pos + 4); + + // object id + IntegerHelper.getTwoBytes(objectId, data, pos + 6); + + // the options + IntegerHelper.getTwoBytes(0x4011, data, pos + 8); + pos += COMMON_DATA_LENGTH; + + // The note structure + // record id + IntegerHelper.getTwoBytes(0xd, data, pos); + + // record length + IntegerHelper.getTwoBytes(NOTE_STRUCTURE_LENGTH - 4, data, pos + 2); + + // the data + pos += NOTE_STRUCTURE_LENGTH; + + // End + // record id + IntegerHelper.getTwoBytes(0x0, data, pos); + + // record length + IntegerHelper.getTwoBytes(END_LENGTH - 4, data, pos + 2); + + // the data + pos += END_LENGTH; + + return data; + } + + /** + * Gets the ObjRecord subrecords for a combo box + * + * @return returns the binary data for a combo box + */ + private byte[] getComboBoxData() { + int dataLength = COMMON_DATA_LENGTH + + COMBOBOX_STRUCTURE_LENGTH + + END_LENGTH; + int pos = 0; + byte[] data = new byte[dataLength]; + + // The jxl.common.data + // record id + IntegerHelper.getTwoBytes(0x15, data, pos); + + // record length + IntegerHelper.getTwoBytes(COMMON_DATA_LENGTH - 4, data, pos + 2); + + // object type + IntegerHelper.getTwoBytes(type.value, data, pos + 4); + + // object id + IntegerHelper.getTwoBytes(objectId, data, pos + 6); + + // the options + IntegerHelper.getTwoBytes(0x0, data, pos + 8); + pos += COMMON_DATA_LENGTH; + + // The combo box structure + // record id + IntegerHelper.getTwoBytes(0xc, data, pos); + + // record length + IntegerHelper.getTwoBytes(0x14, data, pos + 2); + + // the data + data[pos + 14] = 0x01; + data[pos + 16] = 0x04; + data[pos + 20] = 0x10; + data[pos + 24] = 0x13; + data[pos + 26] = (byte) 0xee; + data[pos + 27] = 0x1f; + data[pos + 30] = 0x04; + data[pos + 34] = 0x01; + data[pos + 35] = 0x06; + data[pos + 38] = 0x02; + data[pos + 40] = 0x08; + data[pos + 42] = 0x40; + + pos += COMBOBOX_STRUCTURE_LENGTH; + + // End + // record id + IntegerHelper.getTwoBytes(0x0, data, pos); + + // record length + IntegerHelper.getTwoBytes(END_LENGTH - 4, data, pos + 2); + + // the data + pos += END_LENGTH; + + return data; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public Record getRecord() { + return super.getRecord(); + } + + /** + * Accessor for the object type + * + * @return the object type + */ + public ObjType getType() { + return type; + } + + /** + * Accessor for the object id + * + * @return accessor for the object id + */ + public int getObjectId() { + return objectId; + } + + /** + * Object type enumeration + */ + private static final class ObjType { + private static ObjType[] types = new ObjType[0]; + public int value; + public String desc; + + ObjType(int v, String d) { + value = v; + desc = d; + + ObjType[] oldtypes = types; + types = new ObjType[types.length + 1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + public static ObjType getType(int val) { + ObjType retval = UNKNOWN; + for (int i = 0; i < types.length && retval == UNKNOWN; i++) { + if (types[i].value == val) { + retval = types[i]; + } + } + return retval; + } + + public String toString() { + return desc; + } + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Opt.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Opt.java new file mode 100755 index 0000000..da22f30 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Opt.java @@ -0,0 +1,236 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.common.Logger; + +/** + * An options record in the escher stream + */ +class Opt extends EscherAtom { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Opt.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The number of properties + */ + private int numProperties; + + /** + * The list of properties + */ + private ArrayList properties; + + /** + * Constructor + * + * @param erd the escher record data + */ + public Opt(EscherRecordData erd) { + super(erd); + numProperties = getInstance(); + readProperties(); + } + + /** + * Constructor + */ + public Opt() { + super(EscherRecordType.OPT); + properties = new ArrayList(); + setVersion(3); + } + + /** + * Reads the properties + */ + private void readProperties() { + properties = new ArrayList(); + int pos = 0; + byte[] bytes = getBytes(); + + for (int i = 0; i < numProperties; i++) { + int val = IntegerHelper.getInt(bytes[pos], bytes[pos + 1]); + int id = val & 0x3fff; + int value = IntegerHelper.getInt(bytes[pos + 2], bytes[pos + 3], + bytes[pos + 4], bytes[pos + 5]); + Property p = new Property(id, + (val & 0x4000) != 0, + (val & 0x8000) != 0, + value); + pos += 6; + properties.add(p); + } + + for (Iterator i = properties.iterator(); i.hasNext(); ) { + Property p = (Property) i.next(); + if (p.complex) { + p.stringValue = StringHelper.getUnicodeString(bytes, p.value / 2, + pos); + pos += p.value; + } + } + } + + /** + * Accessor for the binary data + * + * @return the binary data + */ + byte[] getData() { + numProperties = properties.size(); + setInstance(numProperties); + + data = new byte[numProperties * 6]; + int pos = 0; + + // Add in the root data + for (Iterator i = properties.iterator(); i.hasNext(); ) { + Property p = (Property) i.next(); + int val = p.id & 0x3fff; + + if (p.blipId) { + val |= 0x4000; + } + + if (p.complex) { + val |= 0x8000; + } + + IntegerHelper.getTwoBytes(val, data, pos); + IntegerHelper.getFourBytes(p.value, data, pos + 2); + pos += 6; + } + + // Add in any complex data + for (Iterator i = properties.iterator(); i.hasNext(); ) { + Property p = (Property) i.next(); + + if (p.complex && p.stringValue != null) { + byte[] newData = + new byte[data.length + p.stringValue.length() * 2]; + System.arraycopy(data, 0, newData, 0, data.length); + StringHelper.getUnicodeBytes(p.stringValue, newData, data.length); + data = newData; + } + } + + return setHeaderData(data); + } + + /** + * Adds a property into the options + * + * @param id the property id + * @param blip the blip id + * @param complex whether it's a complex property + * @param val the value + */ + void addProperty(int id, boolean blip, boolean complex, int val) { + Property p = new Property(id, blip, complex, val); + properties.add(p); + } + + /** + * Adds a property into the options + * + * @param id the property id + * @param blip the blip id + * @param complex whether it's a complex property + * @param val the value + * @param s the value string + */ + void addProperty(int id, boolean blip, boolean complex, int val, String s) { + Property p = new Property(id, blip, complex, val, s); + properties.add(p); + } + + /** + * Accessor for the property + * + * @param id the property id + * @return the property + */ + Property getProperty(int id) { + boolean found = false; + Property p = null; + for (Iterator i = properties.iterator(); i.hasNext() && !found; ) { + p = (Property) i.next(); + if (p.id == id) { + found = true; + } + } + return found ? p : null; + } + + /** + * Properties enumeration inner class + */ + final static class Property { + int id; + boolean blipId; + boolean complex; + int value; + String stringValue; + + /** + * Constructor + * + * @param i the property id + * @param bl the blip id + * @param co complex flag + * @param v the value + */ + public Property(int i, boolean bl, boolean co, int v) { + id = i; + blipId = bl; + complex = co; + value = v; + } + + /** + * Constructor + * + * @param i the property id + * @param bl the blip id + * @param co complex flag + * @param v the value + * @param s the property string + */ + public Property(int i, boolean bl, boolean co, int v, String s) { + id = i; + blipId = bl; + complex = co; + value = v; + stringValue = s; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Origin.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Origin.java new file mode 100755 index 0000000..2777aa3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Origin.java @@ -0,0 +1,34 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Enumeration type for the origin of a drawing object + */ +public final class Origin { + public static final Origin READ = new Origin(); + public static final Origin WRITE = new Origin(); + public static final Origin READ_WRITE = new Origin(); + /** + * Constructor + */ + private Origin() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/PNGReader.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/PNGReader.java new file mode 100755 index 0000000..d83ec68 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/PNGReader.java @@ -0,0 +1,136 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Arrays; + +public class PNGReader { + private static final byte[] PNG_MAGIC_NUMBER = new byte[] + {(byte) 0x89, (byte) 0x50, (byte) 0x4e, (byte) 0x47, + (byte) 0x0d, (byte) 0x0a, (byte) 0x1a, (byte) 0x0a}; + private final byte[] pngData; + private Chunk ihdr; + private Chunk phys; + private int pixelWidth; + private int pixelHeight; + private int verticalResolution; + private int horizontalResolution; + private int resolutionUnit; + + public PNGReader(byte[] data) { + pngData = data; + } + + public static void main(String[] args) { + try { + File f = new File(args[0]); + int size = (int) f.length(); + + byte[] data = new byte[size]; + + FileInputStream fis = new FileInputStream(f); + fis.read(data); + fis.close(); + PNGReader reader = new PNGReader(data); + reader.read(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + void read() { + // Verify the magic data + byte[] header = new byte[PNG_MAGIC_NUMBER.length]; + System.arraycopy(pngData, 0, header, 0, header.length); + boolean pngFile = Arrays.equals(PNG_MAGIC_NUMBER, header); + if (!pngFile) { + return; + } + + int pos = 8; + while (pos < pngData.length) { + int length = getInt(pngData[pos], + pngData[pos + 1], + pngData[pos + 2], + pngData[pos + 3]); + ChunkType chunkType = ChunkType.getChunkType(pngData[pos + 4], + pngData[pos + 5], + pngData[pos + 6], + pngData[pos + 7]); + + if (chunkType == ChunkType.IHDR) { + ihdr = new Chunk(pos + 8, length, chunkType, pngData); + } else if (chunkType == ChunkType.PHYS) { + phys = new Chunk(pos + 8, length, chunkType, pngData); + } + + pos += length + 12; + } + + // Get the width and height from the ihdr + byte[] ihdrData = ihdr.getData(); + pixelWidth = getInt(ihdrData[0], ihdrData[1], ihdrData[2], ihdrData[3]); + pixelHeight = getInt(ihdrData[4], ihdrData[5], ihdrData[6], ihdrData[7]); + + if (phys != null) { + byte[] physData = phys.getData(); + resolutionUnit = physData[8]; + horizontalResolution = getInt(physData[0], physData[1], + physData[2], physData[3]); + verticalResolution = getInt(physData[4], physData[5], + physData[6], physData[7]); + } + } + + // Gets the big-Endian integer + private int getInt(byte d1, byte d2, byte d3, byte d4) { + int i1 = d1 & 0xff; + int i2 = d2 & 0xff; + int i3 = d3 & 0xff; + int i4 = d4 & 0xff; + + int val = i1 << 24 | + i2 << 16 | + i3 << 8 | + i4; + + return val; + } + + public int getHeight() { + return pixelHeight; + } + + public int getWidth() { + return pixelWidth; + } + + public int getHorizontalResolution() { + // only return if the resolution unit is in metres + return resolutionUnit == 1 ? horizontalResolution : 0; + } + + public int getVerticalResolution() { + // only return if the resolution unit is in metres + return resolutionUnit == 1 ? verticalResolution : 0; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ShapeType.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ShapeType.java new file mode 100755 index 0000000..42a2494 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ShapeType.java @@ -0,0 +1,79 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Enumerations for the shape type + */ +final class ShapeType { + public static final ShapeType MIN = new ShapeType(0); + public static final ShapeType PICTURE_FRAME = new ShapeType(75); + public static final ShapeType HOST_CONTROL = new ShapeType(201); + public static final ShapeType TEXT_BOX = new ShapeType(202); + public static final ShapeType UNKNOWN = new ShapeType(-1); + /** + * The list of shape types + */ + private static ShapeType[] types = new ShapeType[0]; + /** + * The value + */ + private final int value; + /** + * Constructor + * + * @param v the value + */ + ShapeType(int v) { + value = v; + + ShapeType[] old = types; + types = new ShapeType[types.length + 1]; + System.arraycopy(old, 0, types, 0, old.length); + types[old.length] = this; + } + + /** + * Gets the shape type given the value + * + * @param v the value + * @return the shape type for the value + */ + static ShapeType getType(int v) { + ShapeType st = UNKNOWN; + boolean found = false; + for (int i = 0; i < types.length && !found; i++) { + if (types[i].value == v) { + found = true; + st = types[i]; + } + } + return st; + } + + /** + * Accessor for the value + * + * @return the value + */ + public int getValue() { + return value; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/SheetDrawingWriter.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/SheetDrawingWriter.java new file mode 100755 index 0000000..7779a49 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/SheetDrawingWriter.java @@ -0,0 +1,432 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; +import jxl.write.biff.File; + +/** + * Handles the writing out of the different charts and images on a sheet. + * Called by the SheetWriter object + */ +public class SheetDrawingWriter { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SheetDrawingWriter.class); + + /** + * The drawings on the sheet + */ + private ArrayList drawings; + + /** + * Flag indicating whether the drawings on the sheet were modified + */ + private boolean drawingsModified; + + /** + * The charts on the sheet + */ + private Chart[] charts; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param ws the workbook settings + */ + public SheetDrawingWriter(WorkbookSettings ws) { + charts = new Chart[0]; + } + + /** + * The drawings on the sheet + * + * @param dr the list of drawings + * @param mod flag indicating whether the drawings have been tampered with + */ + public void setDrawings(ArrayList dr, boolean mod) { + drawings = dr; + drawingsModified = mod; + } + + /** + * Writes out the MsoDrawing records and Obj records for each image + * and chart on the sheet + * + * @param outputFile the output file + * @throws IOException + */ + public void write(File outputFile) throws IOException { + // If there are no drawings or charts on this sheet then exit + if (drawings.size() == 0 && charts.length == 0) { + return; + } + + // See if any drawing has been modified + boolean modified = drawingsModified; + int numImages = drawings.size(); + + for (Iterator i = drawings.iterator(); i.hasNext() && !modified; ) { + DrawingGroupObject d = (DrawingGroupObject) i.next(); + if (d.getOrigin() != Origin.READ) { + modified = true; + } + } + + // If the drawing order has been muddled at all, then we'll need + // to regenerate the Escher drawing data + if (numImages > 0 && !modified) { + DrawingGroupObject d2 = (DrawingGroupObject) drawings.get(0); + if (!d2.isFirst()) { + modified = true; + } + } + + // Check to see if this sheet consists of just a single chart. If so + // there is no MsoDrawingRecord, so write out the data and exit + if (numImages == 0 && + charts.length == 1 && + charts[0].getMsoDrawingRecord() == null) { + modified = false; // this sheet has not been modified + } + + // If no drawing has been modified, then simply write them straight out + // again and exit + if (!modified) { + writeUnmodified(outputFile); + return; + } + + Object[] spContainerData = new Object[numImages + charts.length]; + int length = 0; + EscherContainer firstSpContainer = null; + + // Get all the spContainer byte data from the drawings + // and store in an array + for (int i = 0; i < numImages; i++) { + DrawingGroupObject drawing = (DrawingGroupObject) drawings.get(i); + + EscherContainer spc = drawing.getSpContainer(); + + if (spc != null) { + byte[] data = spc.getData(); + spContainerData[i] = data; + + if (i == 0) { + firstSpContainer = spc; + } else { + length += data.length; + } + } + } + + // Get all the spContainer bytes from the charts and add to the array + for (int i = 0; i < charts.length; i++) { + EscherContainer spContainer = charts[i].getSpContainer(); + byte[] data = spContainer.getBytes(); //use getBytes instead of getData + data = spContainer.setHeaderData(data); + spContainerData[i + numImages] = data; + + if (i == 0 && numImages == 0) { + firstSpContainer = spContainer; + } else { + length += data.length; + } + } + + // Put the generalised stuff around the first item + DgContainer dgContainer = new DgContainer(); + Dg dg = new Dg(numImages + charts.length); + dgContainer.add(dg); + + SpgrContainer spgrContainer = new SpgrContainer(); + + SpContainer spContainer = new SpContainer(); + Spgr spgr = new Spgr(); + spContainer.add(spgr); + Sp sp = new Sp(ShapeType.MIN, 1024, 5); + spContainer.add(sp); + spgrContainer.add(spContainer); + + spgrContainer.add(firstSpContainer); + + dgContainer.add(spgrContainer); + + byte[] firstMsoData = dgContainer.getData(); + + // Adjust the length of the DgContainer + int len = IntegerHelper.getInt(firstMsoData[4], + firstMsoData[5], + firstMsoData[6], + firstMsoData[7]); + IntegerHelper.getFourBytes(len + length, firstMsoData, 4); + + // Adjust the length of the SpgrContainer + len = IntegerHelper.getInt(firstMsoData[28], + firstMsoData[29], + firstMsoData[30], + firstMsoData[31]); + IntegerHelper.getFourBytes(len + length, firstMsoData, 28); + + // Now write out each MsoDrawing record + + // First MsoRecord + // test hack for form objects, to remove the ClientTextBox record + // from the end of the SpContainer + if (numImages > 0 && + ((DrawingGroupObject) drawings.get(0)).isFormObject()) { + byte[] msodata2 = new byte[firstMsoData.length - 8]; + System.arraycopy(firstMsoData, 0, msodata2, 0, msodata2.length); + firstMsoData = msodata2; + } + + MsoDrawingRecord msoDrawingRecord = new MsoDrawingRecord(firstMsoData); + outputFile.write(msoDrawingRecord); + + if (numImages > 0) { + DrawingGroupObject firstDrawing = (DrawingGroupObject) drawings.get(0); + firstDrawing.writeAdditionalRecords(outputFile); + } else { + // first image is a chart + Chart chart = charts[0]; + ObjRecord objRecord = chart.getObjRecord(); + outputFile.write(objRecord); + outputFile.write(chart); + } + + // Now do all the others + for (int i = 1; i < spContainerData.length; i++) { + byte[] bytes = (byte[]) spContainerData[i]; + + // test hack for form objects, to remove the ClientTextBox record + // from the end of the SpContainer + if (i < numImages && + ((DrawingGroupObject) drawings.get(i)).isFormObject()) { + byte[] bytes2 = new byte[bytes.length - 8]; + System.arraycopy(bytes, 0, bytes2, 0, bytes2.length); + bytes = bytes2; + } + + msoDrawingRecord = new MsoDrawingRecord(bytes); + outputFile.write(msoDrawingRecord); + + if (i < numImages) { + // Write anything else the object needs + DrawingGroupObject d = (DrawingGroupObject) drawings.get(i); + d.writeAdditionalRecords(outputFile); + } else { + Chart chart = charts[i - numImages]; + ObjRecord objRecord = chart.getObjRecord(); + outputFile.write(objRecord); + outputFile.write(chart); + } + } + + // Write any tail records that need to be written + for (Iterator i = drawings.iterator(); i.hasNext(); ) { + DrawingGroupObject dgo2 = (DrawingGroupObject) i.next(); + dgo2.writeTailRecords(outputFile); + } + } + + /** + * Writes out the drawings and the charts if nothing has been modified + * + * @param outputFile the output file + * @throws IOException + */ + private void writeUnmodified(File outputFile) throws IOException { + if (charts.length == 0 && drawings.size() == 0) { + // No drawings or charts + return; + } else if (charts.length == 0 && drawings.size() != 0) { + // If there are no charts, then write out the drawings and return + for (Iterator i = drawings.iterator(); i.hasNext(); ) { + DrawingGroupObject d = (DrawingGroupObject) i.next(); + outputFile.write(d.getMsoDrawingRecord()); + d.writeAdditionalRecords(outputFile); + } + + for (Iterator i = drawings.iterator(); i.hasNext(); ) { + DrawingGroupObject d = (DrawingGroupObject) i.next(); + d.writeTailRecords(outputFile); + } + return; + } else if (drawings.size() == 0 && charts.length != 0) { + // If there are no drawings, then write out the charts and return + Chart curChart = null; + for (int i = 0; i < charts.length; i++) { + curChart = charts[i]; + if (curChart.getMsoDrawingRecord() != null) { + outputFile.write(curChart.getMsoDrawingRecord()); + } + + if (curChart.getObjRecord() != null) { + outputFile.write(curChart.getObjRecord()); + } + + outputFile.write(curChart); + } + + return; + } + + // There are both charts and drawings - the output + // drawing group records will need + // to be re-jigged in order to write the drawings out first, then the + // charts + int numDrawings = drawings.size(); + int length = 0; + EscherContainer[] spContainers = + new EscherContainer[numDrawings + charts.length]; + boolean[] isFormObject = new boolean[numDrawings + charts.length]; + + for (int i = 0; i < numDrawings; i++) { + DrawingGroupObject d = (DrawingGroupObject) drawings.get(i); + spContainers[i] = d.getSpContainer(); + + if (i > 0) { + length += spContainers[i].getLength(); + } + + if (d.isFormObject()) { + isFormObject[i] = true; + } + } + + for (int i = 0; i < charts.length; i++) { + spContainers[i + numDrawings] = charts[i].getSpContainer(); + length += spContainers[i + numDrawings].getLength(); + } + + // Put the generalised stuff around the first item + DgContainer dgContainer = new DgContainer(); + Dg dg = new Dg(numDrawings + charts.length); + dgContainer.add(dg); + + SpgrContainer spgrContainer = new SpgrContainer(); + + SpContainer spContainer = new SpContainer(); + Spgr spgr = new Spgr(); + spContainer.add(spgr); + Sp sp = new Sp(ShapeType.MIN, 1024, 5); + spContainer.add(sp); + spgrContainer.add(spContainer); + + spgrContainer.add(spContainers[0]); + + dgContainer.add(spgrContainer); + + byte[] firstMsoData = dgContainer.getData(); + + // Adjust the length of the DgContainer + int len = IntegerHelper.getInt(firstMsoData[4], + firstMsoData[5], + firstMsoData[6], + firstMsoData[7]); + IntegerHelper.getFourBytes(len + length, firstMsoData, 4); + + // Adjust the length of the SpgrContainer + len = IntegerHelper.getInt(firstMsoData[28], + firstMsoData[29], + firstMsoData[30], + firstMsoData[31]); + IntegerHelper.getFourBytes(len + length, firstMsoData, 28); + + // Now write out each MsoDrawing record and object record + + // Hack to remove the last eight bytes (text box escher record) + // from the container + if (isFormObject[0] == true) { + byte[] cbytes = new byte[firstMsoData.length - 8]; + System.arraycopy(firstMsoData, 0, cbytes, 0, cbytes.length); + firstMsoData = cbytes; + } + + // First MsoRecord + MsoDrawingRecord msoDrawingRecord = new MsoDrawingRecord(firstMsoData); + outputFile.write(msoDrawingRecord); + + DrawingGroupObject dgo = (DrawingGroupObject) drawings.get(0); + dgo.writeAdditionalRecords(outputFile); + + // Now do all the others + for (int i = 1; i < spContainers.length; i++) { + byte[] bytes = spContainers[i].getBytes(); + byte[] bytes2 = spContainers[i].setHeaderData(bytes); + + // Hack to remove the last eight bytes (text box escher record) + // from the container + if (isFormObject[i] == true) { + byte[] cbytes = new byte[bytes2.length - 8]; + System.arraycopy(bytes2, 0, cbytes, 0, cbytes.length); + bytes2 = cbytes; + } + + msoDrawingRecord = new MsoDrawingRecord(bytes2); + outputFile.write(msoDrawingRecord); + + if (i < numDrawings) { + dgo = (DrawingGroupObject) drawings.get(i); + dgo.writeAdditionalRecords(outputFile); + } else { + Chart chart = charts[i - numDrawings]; + ObjRecord objRecord = chart.getObjRecord(); + outputFile.write(objRecord); + outputFile.write(chart); + } + } + + // Write any tail records that need to be written + for (Iterator i = drawings.iterator(); i.hasNext(); ) { + DrawingGroupObject dgo2 = (DrawingGroupObject) i.next(); + dgo2.writeTailRecords(outputFile); + } + } + + /** + * Accessor for the charts on the sheet + * + * @return the charts + */ + public Chart[] getCharts() { + return charts; + } + + /** + * Sets the charts on the sheet + * + * @param ch the charts + */ + public void setCharts(Chart[] ch) { + charts = ch; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Sp.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Sp.java new file mode 100755 index 0000000..de311e8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Sp.java @@ -0,0 +1,113 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * The Sp escher atom + */ +class Sp extends EscherAtom { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Sp.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The shape type + */ + private final int shapeType; + + /** + * The shape id + */ + private final int shapeId; + + /** + * The Sp persistence flags + */ + private final int persistenceFlags; + + /** + * Constructor + * + * @param erd the entity record data + */ + public Sp(EscherRecordData erd) { + super(erd); + shapeType = getInstance(); + byte[] bytes = getBytes(); + shapeId = IntegerHelper.getInt(bytes[0], bytes[1], bytes[2], bytes[3]); + persistenceFlags = IntegerHelper.getInt(bytes[4], bytes[5], + bytes[6], bytes[7]); + } + + /** + * Constructor - used when writing + * + * @param st the shape type + * @param sid the shape id + * @param p persistence flags + */ + public Sp(ShapeType st, int sid, int p) { + super(EscherRecordType.SP); + setVersion(2); + shapeType = st.getValue(); + shapeId = sid; + persistenceFlags = p; + setInstance(shapeType); + } + + /** + * Accessor for the shape id + * + * @return the shape id + */ + int getShapeId() { + return shapeId; + } + + /** + * Accessor for the shape type + * + * @return the shape type + */ + int getShapeType() { + return shapeType; + } + + /** + * Gets the data + * + * @return the binary data + */ + byte[] getData() { + data = new byte[8]; + IntegerHelper.getFourBytes(shapeId, data, 0); + IntegerHelper.getFourBytes(persistenceFlags, data, 4); + return setHeaderData(data); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/SpContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/SpContainer.java new file mode 100755 index 0000000..7a01f0d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/SpContainer.java @@ -0,0 +1,41 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Shape contianer - Contains the data for this particular shape + */ +class SpContainer extends EscherContainer { + /** + * Constructor + */ + public SpContainer() { + super(EscherRecordType.SP_CONTAINER); + } + + /** + * Constructor + * + * @param erd the escher record data + */ + public SpContainer(EscherRecordData erd) { + super(erd); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Spgr.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Spgr.java new file mode 100755 index 0000000..6a74121 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Spgr.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * The SpGr escher atom + */ +class Spgr extends EscherAtom { + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param erd the raw escher record data + */ + public Spgr(EscherRecordData erd) { + super(erd); + } + + /** + * Constructor + */ + public Spgr() { + super(EscherRecordType.SPGR); + setVersion(1); + data = new byte[16]; + } + + /** + * Gets the binary data + * + * @return the binary data + */ + byte[] getData() { + return setHeaderData(data); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/SpgrContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/SpgrContainer.java new file mode 100755 index 0000000..bc680b3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/SpgrContainer.java @@ -0,0 +1,48 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.common.Logger; + +/** + * An Spgr container record in an escher stream + */ +class SpgrContainer extends EscherContainer { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SpgrContainer.class); + + /** + * Constructor used when writing + */ + public SpgrContainer() { + super(EscherRecordType.SPGR_CONTAINER); + } + + /** + * Constructor + * + * @param erd the escher record data read in + */ + public SpgrContainer(EscherRecordData erd) { + super(erd); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/SplitMenuColors.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/SplitMenuColors.java new file mode 100755 index 0000000..d780ca3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/SplitMenuColors.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Split menu colours escher record + */ +class SplitMenuColors extends EscherAtom { + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param erd escher record data + */ + public SplitMenuColors(EscherRecordData erd) { + super(erd); + } + + /** + * Constructor + */ + public SplitMenuColors() { + super(EscherRecordType.SPLIT_MENU_COLORS); + setVersion(0); + setInstance(4); + + data = new byte[] + {(byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x08, + (byte) 0x0c, (byte) 0x00, (byte) 0x00, (byte) 0x08, + (byte) 0x17, (byte) 0x00, (byte) 0x00, (byte) 0x08, + (byte) 0xf7, (byte) 0x00, (byte) 0x00, (byte) 0x10}; + } + + /** + * The binary data + * + * @return the binary data + */ + byte[] getData() { + return setHeaderData(data); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/TextObjectRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/TextObjectRecord.java new file mode 100755 index 0000000..61bf58b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/TextObjectRecord.java @@ -0,0 +1,123 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * A TextObject (TXO) record which contains the information for comments + */ +public class TextObjectRecord extends WritableRecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(TextObjectRecord.class); + + /** + * The raw drawing data which was read in + */ + private byte[] data; + + /** + * The text + */ + private int textLength; + + /** + * Constructor invoked when writing out this object + * + * @param t the text string + */ + TextObjectRecord(String t) { + super(Type.TXO); + + textLength = t.length(); + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public TextObjectRecord(Record t) { + super(t); + data = getRecord().getData(); + textLength = IntegerHelper.getInt(data[10], data[11]); + } + + /** + * Constructor + * + * @param d the drawing data + */ + public TextObjectRecord(byte[] d) { + super(Type.TXO); + data = d; + } + + /** + * Gets the text length. Used to determine if there is any data held + * in the following continue records + * + * @return the length of the text + */ + public int getTextLength() { + return textLength; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public byte[] getData() { + if (data != null) { + return data; + } + + data = new byte[18]; + + // the options + int options = 0; + options |= (0x1 << 1); // horizontal alignment - left + options |= (0x1 << 4); // vertical alignment - top + options |= (0x1 << 9); // lock text + IntegerHelper.getTwoBytes(options, data, 0); + + // the rotation + // no rotation + + // Length of text + IntegerHelper.getTwoBytes(textLength, data, 10); + + // Length of formatting runs + IntegerHelper.getTwoBytes(0x10, data, 12); + + return data; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Add.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Add.java new file mode 100755 index 0000000..df142a8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Add.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Add extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public Add() { + } + + /** + * Gets the symbol for string displays + * + * @return the symbol + */ + public String getSymbol() { + return "+"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.ADD; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 4; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Area.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Area.java new file mode 100755 index 0000000..e98ee9b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Area.java @@ -0,0 +1,383 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A nested class to hold range information + */ +class Area extends Operand implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Area.class); + + /** + * The first column + */ + private int columnFirst; + + /** + * The first row + */ + private int rowFirst; + + /** + * The last column + */ + private int columnLast; + + /** + * The last row + */ + private int rowLast; + + /** + * Indicates whether the first column is a relative or absolute reference + */ + private boolean columnFirstRelative; + + /** + * Indicates whether the first row is a relative or absolute reference + */ + private boolean rowFirstRelative; + + /** + * Indicates whether the last column is a relative or absolute reference + */ + private boolean columnLastRelative; + + /** + * Indicates whether the last row is a relative or absolute reference + */ + private boolean rowLastRelative; + + /** + * Constructor + */ + Area() { + } + + /** + * Constructor invoked when parsing a string formula + * + * @param s the string to parse + */ + Area(String s) { + int seppos = s.indexOf(":"); + Assert.verify(seppos != -1); + String startcell = s.substring(0, seppos); + String endcell = s.substring(seppos + 1); + + columnFirst = CellReferenceHelper.getColumn(startcell); + rowFirst = CellReferenceHelper.getRow(startcell); + columnLast = CellReferenceHelper.getColumn(endcell); + rowLast = CellReferenceHelper.getRow(endcell); + + columnFirstRelative = CellReferenceHelper.isColumnRelative(startcell); + rowFirstRelative = CellReferenceHelper.isRowRelative(startcell); + columnLastRelative = CellReferenceHelper.isColumnRelative(endcell); + rowLastRelative = CellReferenceHelper.isRowRelative(endcell); + } + + /** + * Accessor for the first column + * + * @return the first column + */ + int getFirstColumn() { + return columnFirst; + } + + /** + * Accessor for the first row + * + * @return the first row + */ + int getFirstRow() { + return rowFirst; + } + + /** + * Accessor for the last column + * + * @return the last column + */ + int getLastColumn() { + return columnLast; + } + + /** + * Accessor for the last row + * + * @return the last row + */ + int getLastRow() { + return rowLast; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + rowFirst = IntegerHelper.getInt(data[pos], data[pos + 1]); + rowLast = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + int columnMask = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + columnFirst = columnMask & 0x00ff; + columnFirstRelative = ((columnMask & 0x4000) != 0); + rowFirstRelative = ((columnMask & 0x8000) != 0); + columnMask = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + columnLast = columnMask & 0x00ff; + columnLastRelative = ((columnMask & 0x4000) != 0); + rowLastRelative = ((columnMask & 0x8000) != 0); + + return 8; + } + + /** + * Gets the string representation of this item + * + * @param buf the string buffer + */ + public void getString(StringBuffer buf) { + CellReferenceHelper.getCellReference(columnFirst, rowFirst, buf); + buf.append(':'); + CellReferenceHelper.getCellReference(columnLast, rowLast, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[9]; + data[0] = !useAlternateCode() ? Token.AREA.getCode() : + Token.AREA.getCode2(); + + IntegerHelper.getTwoBytes(rowFirst, data, 1); + IntegerHelper.getTwoBytes(rowLast, data, 3); + + int grcol = columnFirst; + + // Set the row/column relative bits if applicable + if (rowFirstRelative) { + grcol |= 0x8000; + } + + if (columnFirstRelative) { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 5); + + grcol = columnLast; + + // Set the row/column relative bits if applicable + if (rowLastRelative) { + grcol |= 0x8000; + } + + if (columnLastRelative) { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 7); + + return data; + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + if (columnFirstRelative) { + columnFirst += colAdjust; + } + + if (columnLastRelative) { + columnLast += colAdjust; + } + + if (rowFirstRelative) { + rowFirst += rowAdjust; + } + + if (rowLastRelative) { + rowLast += rowAdjust; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + if (!currentSheet) { + return; + } + + if (col <= columnFirst) { + columnFirst++; + } + + if (col <= columnLast) { + columnLast++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + if (!currentSheet) { + return; + } + + if (col < columnFirst) { + columnFirst--; + } + + if (col <= columnLast) { + columnLast--; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + if (!currentSheet) { + return; + } + + if (rowLast == 0xffff) { + // area applies to the whole column, so nothing to do + return; + } + + if (row <= rowFirst) { + rowFirst++; + } + + if (row <= rowLast) { + rowLast++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + if (!currentSheet) { + return; + } + + if (rowLast == 0xffff) { + // area applies to the whole column, so nothing to do + return; + } + + if (row < rowFirst) { + rowFirst--; + } + + if (row <= rowLast) { + rowLast--; + } + } + + /** + * Used by subclasses columns/row range to set the range information + * + * @param colFirst the first column + * @param colLast the last column + * @param rwFirst the first row + * @param rwLast the last row + * @param colFirstRel flag indicating whether the first column is relative + * @param colLastRel flag indicating whether the last column is relative + * @param rowFirstRel flag indicating whether the first row is relative + * @param rowLastRel flag indicating whether the last row is relative + */ + protected void setRangeData(int colFirst, + int colLast, + int rwFirst, + int rwLast, + boolean colFirstRel, + boolean colLastRel, + boolean rowFirstRel, + boolean rowLastRel) { + columnFirst = colFirst; + columnLast = colLast; + rowFirst = rwFirst; + rowLast = rwLast; + columnFirstRelative = colFirstRel; + columnLastRelative = colLastRel; + rowFirstRelative = rowFirstRel; + rowLastRelative = rowLastRel; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing here + */ + void handleImportedCellReferences() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Area3d.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Area3d.java new file mode 100755 index 0000000..219a4c1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Area3d.java @@ -0,0 +1,433 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A nested class to hold range information + */ +class Area3d extends Operand implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Area3d.class); + + /** + * The sheet + */ + private int sheet; + + /** + * The first column + */ + private int columnFirst; + + /** + * The first row + */ + private int rowFirst; + + /** + * The last column + */ + private int columnLast; + + /** + * The last row + */ + private int rowLast; + + /** + * Indicates whether the first column is relative + */ + private boolean columnFirstRelative; + + /** + * Indicates whether the first row is relative + */ + private boolean rowFirstRelative; + + /** + * Indicates whether the last column is relative + */ + private boolean columnLastRelative; + + /** + * Indicates whether the last row is relative + */ + private boolean rowLastRelative; + + /** + * A handle to the workbook + */ + private final ExternalSheet workbook; + + /** + * Constructor + * + * @param es the external sheet + */ + Area3d(ExternalSheet es) { + workbook = es; + } + + /** + * Constructor invoked when parsing a string formula + * + * @param s the string to parse + * @param es the external sheet + * @throws FormulaException + */ + Area3d(String s, ExternalSheet es) throws FormulaException { + workbook = es; + int seppos = s.lastIndexOf(":"); + Assert.verify(seppos != -1); + String endcell = s.substring(seppos + 1); + + // Get the the start cell details + int sep = s.indexOf('!'); + String cellString = s.substring(sep + 1, seppos); + columnFirst = CellReferenceHelper.getColumn(cellString); + rowFirst = CellReferenceHelper.getRow(cellString); + + // Get the sheet index + String sheetName = s.substring(0, sep); + + // Remove single quotes, if they exist + if (sheetName.charAt(0) == '\'' && + sheetName.charAt(sheetName.length() - 1) == '\'') { + sheetName = sheetName.substring(1, sheetName.length() - 1); + } + + sheet = es.getExternalSheetIndex(sheetName); + + if (sheet < 0) { + throw new FormulaException(FormulaException.SHEET_REF_NOT_FOUND, + sheetName); + } + + // Get the last cell index + columnLast = CellReferenceHelper.getColumn(endcell); + rowLast = CellReferenceHelper.getRow(endcell); + + columnFirstRelative = true; + rowFirstRelative = true; + columnLastRelative = true; + rowLastRelative = true; + } + + /** + * Accessor for the first column + * + * @return the first column + */ + int getFirstColumn() { + return columnFirst; + } + + /** + * Accessor for the first row + * + * @return the first row + */ + int getFirstRow() { + return rowFirst; + } + + /** + * Accessor for the last column + * + * @return the last column + */ + int getLastColumn() { + return columnLast; + } + + /** + * Accessor for the last row + * + * @return the last row + */ + int getLastRow() { + return rowLast; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + sheet = IntegerHelper.getInt(data[pos], data[pos + 1]); + rowFirst = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + rowLast = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + int columnMask = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + columnFirst = columnMask & 0x00ff; + columnFirstRelative = ((columnMask & 0x4000) != 0); + rowFirstRelative = ((columnMask & 0x8000) != 0); + columnMask = IntegerHelper.getInt(data[pos + 8], data[pos + 9]); + columnLast = columnMask & 0x00ff; + columnLastRelative = ((columnMask & 0x4000) != 0); + rowLastRelative = ((columnMask & 0x8000) != 0); + + return 10; + } + + /** + * Gets the string version of this area + * + * @param buf the area to populate + */ + public void getString(StringBuffer buf) { + CellReferenceHelper.getCellReference + (sheet, columnFirst, rowFirst, workbook, buf); + buf.append(':'); + CellReferenceHelper.getCellReference(columnLast, rowLast, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[11]; + data[0] = Token.AREA3D.getCode(); + + IntegerHelper.getTwoBytes(sheet, data, 1); + + IntegerHelper.getTwoBytes(rowFirst, data, 3); + IntegerHelper.getTwoBytes(rowLast, data, 5); + + int grcol = columnFirst; + + // Set the row/column relative bits if applicable + if (rowFirstRelative) { + grcol |= 0x8000; + } + + if (columnFirstRelative) { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 7); + + grcol = columnLast; + + // Set the row/column relative bits if applicable + if (rowLastRelative) { + grcol |= 0x8000; + } + + if (columnLastRelative) { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 9); + + return data; + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + if (columnFirstRelative) { + columnFirst += colAdjust; + } + + if (columnLastRelative) { + columnLast += colAdjust; + } + + if (rowFirstRelative) { + rowFirst += rowAdjust; + } + + if (rowLastRelative) { + rowLast += rowAdjust; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) { + if (sheetIndex != sheet) { + return; + } + + if (columnFirst >= col) { + columnFirst++; + } + + if (columnLast >= col) { + columnLast++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + if (sheetIndex != sheet) { + return; + } + + if (col < columnFirst) { + columnFirst--; + } + + if (col <= columnLast) { + columnLast--; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + if (sheetIndex != sheet) { + return; + } + + if (rowLast == 0xffff) { + // area applies to the whole column, so nothing to do + return; + } + + if (row <= rowFirst) { + rowFirst++; + } + + if (row <= rowLast) { + rowLast++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + if (sheetIndex != sheet) { + return; + } + + if (rowLast == 0xffff) { + // area applies to the whole column, so nothing to do + return; + } + + if (row < rowFirst) { + rowFirst--; + } + + if (row <= rowLast) { + rowLast--; + } + } + + /** + * Used by subclasses columns/row range to set the range information + * + * @param sht the sheet containing the area + * @param colFirst the first column + * @param colLast the last column + * @param rwFirst the first row + * @param rwLast the last row + * @param colFirstRel flag indicating whether the first column is relative + * @param colLastRel flag indicating whether the last column is relative + * @param rowFirstRel flag indicating whether the first row is relative + * @param rowLastRel flag indicating whether the last row is relative + */ + protected void setRangeData(int sht, + int colFirst, + int colLast, + int rwFirst, + int rwLast, + boolean colFirstRel, + boolean colLastRel, + boolean rowFirstRel, + boolean rowLastRel) { + sheet = sht; + columnFirst = colFirst; + columnLast = colLast; + rowFirst = rwFirst; + rowLast = rwLast; + columnFirstRelative = colFirstRel; + columnLastRelative = colLastRel; + rowFirstRelative = rowFirstRel; + rowLastRelative = rowLastRel; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + */ + void handleImportedCellReferences() { + setInvalid(); + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ArgumentSeparator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ArgumentSeparator.java new file mode 100755 index 0000000..636e741 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ArgumentSeparator.java @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A dummy token used when parsing strings in order to indicate the + * separation of parameters + */ +class ArgumentSeparator extends StringParseItem { + /** + * Constructor + */ + public ArgumentSeparator() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Attribute.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Attribute.java new file mode 100755 index 0000000..bde9c23 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Attribute.java @@ -0,0 +1,478 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * A special attribute control token - typically either a SUM function + * or an IF function + */ +class Attribute extends Operator implements ParsedThing { + private static final int SUM_MASK = 0x10; + private static final int IF_MASK = 0x02; + private static final int CHOOSE_MASK = 0x04; + private static final int GOTO_MASK = 0x08; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Attribute.class); + /** + * The options used by the attribute + */ + private int options; + /** + * The word contained in this attribute + */ + private int word; + /** + * The workbook settings + */ + private final WorkbookSettings settings; + /** + * If this attribute is an IF functions, sets the associated if conditions + */ + private VariableArgFunction ifConditions; + + /** + * Constructor + * + * @param ws the workbook settings + */ + public Attribute(WorkbookSettings ws) { + settings = ws; + } + + /** + * Constructor for use when this is called when parsing a string + * + * @param sf the built in function + * @param ws the workbook settings + */ + public Attribute(StringFunction sf, WorkbookSettings ws) { + settings = ws; + + if (sf.getFunction(settings) == Function.SUM) { + options |= SUM_MASK; + } else if (sf.getFunction(settings) == Function.IF) { + options |= IF_MASK; + } + } + + /** + * Sets the if conditions for this attribute, if it represents an IF function + * + * @param vaf a VariableArgFunction value + */ + void setIfConditions(VariableArgFunction vaf) { + ifConditions = vaf; + + // Sometimes there is not Attribute token, so we need to create + // an attribute out of thin air. In that case, make sure the if mask + options |= IF_MASK; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + options = data[pos]; + word = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + + if (!isChoose()) { + return 3; + } + + // word contains the number of jumps by index. + // and there is an additional final jump to the choose function itself. + return 3 + (word + 1) * 2; + } + + /** + * Queries whether this attribute is a function + * + * @return TRUE if this is a function, FALSE otherwise + */ + public boolean isFunction() { + return (options & (SUM_MASK | IF_MASK)) != 0; + } + + /** + * Queries whether this attribute is a sum + * + * @return TRUE if this is SUM, FALSE otherwise + */ + public boolean isSum() { + return (options & SUM_MASK) != 0; + } + + /** + * Queries whether this attribute is an IF + * + * @return TRUE if this is an IF, FALSE otherwise + */ + public boolean isIf() { + return (options & IF_MASK) != 0; + } + + /** + * Queries whether this attribute is a goto + * + * @return TRUE if this is a goto, FALSE otherwise + */ + public boolean isGoto() { + return (options & GOTO_MASK) != 0; + } + + /** + * Queries whether this attribute is a CHOOSE + * + * @return TRUE if this is a CHOOSE, FALSE otherwise + */ + public boolean isChoose() { + return (options & CHOOSE_MASK) != 0; + } + + /** + * Gets the operands for this operator from the stack + * + * @param s the token stack + */ + public void getOperands(Stack s) { + if ((options & SUM_MASK) != 0) { + ParseItem o1 = (ParseItem) s.pop(); + add(o1); + } else if ((options & IF_MASK) != 0) { + ParseItem o1 = (ParseItem) s.pop(); + add(o1); + } + } + + /** + * Gets the string version of the attribute + * + * @param buf the buffer to populate + */ + public void getString(StringBuffer buf) { + if ((options & SUM_MASK) != 0) { + ParseItem[] operands = getOperands(); + buf.append(Function.SUM.getName(settings)); + buf.append('('); + operands[0].getString(buf); + buf.append(')'); + } else if ((options & IF_MASK) != 0) { + buf.append(Function.IF.getName(settings)); + buf.append('('); + + ParseItem[] operands = ifConditions.getOperands(); + + // Operands are in the correct order for IFs + for (int i = 0; i < operands.length - 1; i++) { + operands[i].getString(buf); + buf.append(','); + } + operands[operands.length - 1].getString(buf); + buf.append(')'); + } + } + + /** + * Gets the token representation of this item in RPN. The Attribute + * token is a special case, which overrides anything useful we could do + * in the base class + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[0]; + if (isSum()) { + // Get the data for the operands + ParseItem[] operands = getOperands(); + + // Get the operands in reverse order to get the RPN + for (int i = operands.length - 1; i >= 0; i--) { + byte[] opdata = operands[i].getBytes(); + + // Grow the array + byte[] newdata = new byte[data.length + opdata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(opdata, 0, newdata, data.length, opdata.length); + data = newdata; + } + + // Add on the operator byte + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = Token.ATTRIBUTE.getCode(); + newdata[data.length + 1] = SUM_MASK; + data = newdata; + } else if (isIf()) { + return getIf(); + } + + return data; + } + + /** + * Gets the associated if conditions with this attribute + * + * @return the associated if conditions + */ + private byte[] getIf() { + ParseItem[] operands = ifConditions.getOperands(); + + // The position of the offset to the false portion of the expression + int falseOffsetPos = 0; + int gotoEndPos = 0; + int numArgs = operands.length; + + // First, write out the conditions + byte[] data = operands[0].getBytes(); + + // Grow the array by three and write out the optimized if attribute + int pos = data.length; + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + data[pos] = Token.ATTRIBUTE.getCode(); + data[pos + 1] = 0x2; + falseOffsetPos = pos + 2; + + // Get the true portion of the expression and add it to the array + byte[] truedata = operands[1].getBytes(); + newdata = new byte[data.length + truedata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(truedata, 0, newdata, data.length, truedata.length); + data = newdata; + + // Grow the array by three and write out the goto end attribute + pos = data.length; + newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + data[pos] = Token.ATTRIBUTE.getCode(); + data[pos + 1] = 0x8; + gotoEndPos = pos + 2; + + // If the false condition exists, then add that to the array + if (numArgs > 2) { + // Set the offset to the false expression to be the current position + IntegerHelper.getTwoBytes(data.length - falseOffsetPos - 2, + data, falseOffsetPos); + + // Copy in the false expression + byte[] falsedata = operands[numArgs - 1].getBytes(); + newdata = new byte[data.length + falsedata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(falsedata, 0, newdata, data.length, falsedata.length); + data = newdata; + + // Write the goto to skip over the varargs token + pos = data.length; + newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + data[pos] = Token.ATTRIBUTE.getCode(); + data[pos + 1] = 0x8; + data[pos + 2] = 0x3; + } + + // Grow the array and write out the varargs function + pos = data.length; + newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + data[pos] = Token.FUNCTIONVARARG.getCode(); + data[pos + 1] = (byte) numArgs; + data[pos + 2] = 1; + data[pos + 3] = 0; // indicates the end of the expression + + // Position the final offsets + int endPos = data.length - 1; + + if (numArgs < 3) { + // Set the offset to the false expression to be the current position + IntegerHelper.getTwoBytes(endPos - falseOffsetPos - 5, + data, falseOffsetPos); + } + + // Set the offset after the true expression + IntegerHelper.getTwoBytes(endPos - gotoEndPos - 2, + data, gotoEndPos); + + return data; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 3; + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + ParseItem[] operands = null; + + if (isIf()) { + operands = ifConditions.getOperands(); + } else { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) { + operands[i].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = null; + + if (isIf()) { + operands = ifConditions.getOperands(); + } else { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) { + operands[i].columnInserted(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = null; + + if (isIf()) { + operands = ifConditions.getOperands(); + } else { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) { + operands[i].columnRemoved(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted1 + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = null; + + if (isIf()) { + operands = ifConditions.getOperands(); + } else { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) { + operands[i].rowInserted(sheetIndex, row, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = null; + + if (isIf()) { + operands = ifConditions.getOperands(); + } else { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) { + operands[i].rowRemoved(sheetIndex, row, currentSheet); + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + ParseItem[] operands = null; + + if (isIf()) { + operands = ifConditions.getOperands(); + } else { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) { + operands[i].handleImportedCellReferences(); + } + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/BinaryOperator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/BinaryOperator.java new file mode 100755 index 0000000..0f1edb2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/BinaryOperator.java @@ -0,0 +1,210 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; +import jxl.common.Logger; + +/** + * A cell reference in a formula + */ +abstract class BinaryOperator extends Operator implements ParsedThing { + // The logger + private static final Logger logger = Logger.getLogger(BinaryOperator.class); + + /** + * Constructor + */ + public BinaryOperator() { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + return 0; + } + + /** + * Gets the operands for this operator from the stack + * + * @param s the token stack + */ + public void getOperands(Stack s) { + ParseItem o1 = (ParseItem) s.pop(); + ParseItem o2 = (ParseItem) s.pop(); + + add(o1); + add(o2); + } + + /** + * Gets the string version of this binary operator + * + * @param buf a the string buffer + */ + public void getString(StringBuffer buf) { + ParseItem[] operands = getOperands(); + operands[1].getString(buf); + buf.append(getSymbol()); + operands[0].getString(buf); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + ParseItem[] operands = getOperands(); + operands[1].adjustRelativeCellReferences(colAdjust, rowAdjust); + operands[0].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[1].columnInserted(sheetIndex, col, currentSheet); + operands[0].columnInserted(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[1].columnRemoved(sheetIndex, col, currentSheet); + operands[0].columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[1].rowInserted(sheetIndex, row, currentSheet); + operands[0].rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[1].rowRemoved(sheetIndex, row, currentSheet); + operands[0].rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + // Get the data for the operands + ParseItem[] operands = getOperands(); + byte[] data = new byte[0]; + + // Get the operands in reverse order to get the RPN + for (int i = operands.length - 1; i >= 0; i--) { + byte[] opdata = operands[i].getBytes(); + + // Grow the array + byte[] newdata = new byte[data.length + opdata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(opdata, 0, newdata, data.length, opdata.length); + data = newdata; + } + + // Add on the operator byte + byte[] newdata = new byte[data.length + 1]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = getToken().getCode(); + + return newdata; + } + + /** + * Abstract method which gets the binary operator string symbol + * + * @return the string symbol for this token + */ + abstract String getSymbol(); + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + abstract Token getToken(); + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + ParseItem[] operands = getOperands(); + operands[0].handleImportedCellReferences(); + operands[1].handleImportedCellReferences(); + } + +} + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/BooleanValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/BooleanValue.java new file mode 100755 index 0000000..b6abddc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/BooleanValue.java @@ -0,0 +1,90 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A boolean operand in a formula + */ +class BooleanValue extends Operand implements ParsedThing { + /** + * The boolean value + */ + private boolean value; + + /** + * Constructor + */ + public BooleanValue() { + } + + /** + * Constructor used when parsing a string formula + * + * @param s the string token, including quote marks + */ + public BooleanValue(String s) { + // remove the quotes + value = Boolean.valueOf(s).booleanValue(); + } + + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + value = data[pos] == 1; + return 1; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[2]; + data[0] = Token.BOOL.getCode(); + data[1] = (byte) (value == true ? 1 : 0); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) { + buf.append((new Boolean(value))); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/BuiltInFunction.java b/datastructures-xslx/src/main/java/jxl/biff/formula/BuiltInFunction.java new file mode 100755 index 0000000..5d20c76 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/BuiltInFunction.java @@ -0,0 +1,263 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A built in function in a formula + */ +class BuiltInFunction extends Operator implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(BuiltInFunction.class); + + /** + * The function + */ + private Function function; + + /** + * The workbook settings + */ + private final WorkbookSettings settings; + + /** + * Constructor + * + * @param ws the workbook settings + */ + public BuiltInFunction(WorkbookSettings ws) { + settings = ws; + } + + /** + * Constructor used when parsing a formula from a string + * + * @param f the function + * @param ws the workbook settings + */ + public BuiltInFunction(Function f, WorkbookSettings ws) { + function = f; + settings = ws; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + int index = IntegerHelper.getInt(data[pos], data[pos + 1]); + function = Function.getFunction(index); + Assert.verify(function != Function.UNKNOWN, "function code " + index); + return 2; + } + + /** + * Gets the operands for this operator from the stack + * + * @param s the token stack + */ + public void getOperands(Stack s) { + // parameters are in the correct order, god damn them + ParseItem[] items = new ParseItem[function.getNumArgs()]; + // modified in 2.4.3 + for (int i = function.getNumArgs() - 1; i >= 0; i--) { + ParseItem pi = (ParseItem) s.pop(); + + items[i] = pi; + } + + for (int i = 0; i < function.getNumArgs(); i++) { + add(items[i]); + } + } + + /** + * Gets the string for this functions + * + * @param buf the buffer to append + */ + public void getString(StringBuffer buf) { + buf.append(function.getName(settings)); + buf.append('('); + + int numArgs = function.getNumArgs(); + + if (numArgs > 0) { + ParseItem[] operands = getOperands(); + + // arguments are in the same order they were specified + operands[0].getString(buf); + + for (int i = 1; i < numArgs; i++) { + buf.append(','); + operands[i].getString(buf); + } + } + + buf.append(')'); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + ParseItem[] operands = getOperands(); + + for (int i = 0; i < operands.length; i++) { + operands[i].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].columnInserted(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].columnRemoved(sheetIndex, col, currentSheet); + } + } + + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].rowInserted(sheetIndex, row, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].rowRemoved(sheetIndex, row, currentSheet); + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].handleImportedCellReferences(); + } + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + // Get the data for the operands + ParseItem[] operands = getOperands(); + byte[] data = new byte[0]; + + for (int i = 0; i < operands.length; i++) { + byte[] opdata = operands[i].getBytes(); + + // Grow the array + byte[] newdata = new byte[data.length + opdata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(opdata, 0, newdata, data.length, opdata.length); + data = newdata; + } + + // Add on the operator byte + byte[] newdata = new byte[data.length + 3]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = !useAlternateCode() ? Token.FUNCTION.getCode() : + Token.FUNCTION.getCode2(); + IntegerHelper.getTwoBytes(function.getCode(), newdata, data.length + 1); + + return newdata; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 3; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference.java b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference.java new file mode 100755 index 0000000..5baf15a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference.java @@ -0,0 +1,268 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.Cell; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * A cell reference in a formula + */ +class CellReference extends Operand implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CellReference.class); + + /** + * Indicates whether the column reference is relative or absolute + */ + private boolean columnRelative; + + /** + * Indicates whether the row reference is relative or absolute + */ + private boolean rowRelative; + + /** + * The column reference + */ + private int column; + + /** + * The row reference + */ + private int row; + + /** + * The cell containing the formula. Stored in order to determine + * relative cell values + */ + private Cell relativeTo; + + /** + * Constructor + * + * @param rt the cell containing the formula + */ + public CellReference(Cell rt) { + relativeTo = rt; + } + + /** + * Constructor + */ + public CellReference() { + } + + /** + * Constructor invoked when parsing a text string + * + * @param s the string being parsed + */ + public CellReference(String s) { + column = CellReferenceHelper.getColumn(s); + row = CellReferenceHelper.getRow(s); + columnRelative = CellReferenceHelper.isColumnRelative(s); + rowRelative = CellReferenceHelper.isRowRelative(s); + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + row = IntegerHelper.getInt(data[pos], data[pos + 1]); + int columnMask = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + column = columnMask & 0x00ff; + columnRelative = ((columnMask & 0x4000) != 0); + rowRelative = ((columnMask & 0x8000) != 0); + + return 4; + } + + /** + * Accessor for the column + * + * @return the column + */ + public int getColumn() { + return column; + } + + /** + * Accessor for the row + * + * @return the row + */ + public int getRow() { + return row; + } + + /** + * Gets the cell reference as a string for this item + * + * @param buf the string buffer to populate + */ + public void getString(StringBuffer buf) { + CellReferenceHelper.getCellReference(column, !columnRelative, + row, !rowRelative, + buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[5]; + data[0] = !useAlternateCode() ? Token.REF.getCode() : + Token.REF.getCode2(); + + IntegerHelper.getTwoBytes(row, data, 1); + + int grcol = column; + + // Set the row/column relative bits if applicable + if (rowRelative) { + grcol |= 0x8000; + } + + if (columnRelative) { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 3); + + return data; + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + if (columnRelative) { + column += colAdjust; + } + + if (rowRelative) { + row += rowAdjust; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) { + if (!currentSheet) { + return; + } + + if (column >= col) { + column++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + if (!currentSheet) { + return; + } + + if (column >= col) { + column--; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param r the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int r, boolean currentSheet) { + if (!currentSheet) { + return; + } + + if (row >= r) { + row++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param r the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int r, boolean currentSheet) { + if (!currentSheet) { + return; + } + + if (row >= r) { + row--; + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Flags the formula as invalid + * Does nothing here + */ + void handleImportedCellReferences() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference3d.java b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference3d.java new file mode 100755 index 0000000..0815fad --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference3d.java @@ -0,0 +1,299 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.Cell; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * A 3d cell reference in a formula + */ +class CellReference3d extends Operand implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CellReference3d.class); + + /** + * Indicates whether the column reference is relative or absolute + */ + private boolean columnRelative; + + /** + * Indicates whether the row reference is relative or absolute + */ + private boolean rowRelative; + + /** + * The column reference + */ + private int column; + + /** + * The row reference + */ + private int row; + + /** + * The cell containing the formula. Stored in order to determine + * relative cell values + */ + private Cell relativeTo; + + /** + * The sheet which the reference is present on + */ + private int sheet; + + /** + * A handle to the container of the external sheets ie. the workbook + */ + private final ExternalSheet workbook; + + /** + * Constructor + * + * @param rt the cell containing the formula + * @param w the list of external sheets + */ + public CellReference3d(Cell rt, ExternalSheet w) { + relativeTo = rt; + workbook = w; + } + + /** + * Constructs this object from a string + * + * @param s the string + * @param w the external sheet + * @throws FormulaException + */ + public CellReference3d(String s, ExternalSheet w) throws FormulaException { + workbook = w; + columnRelative = true; + rowRelative = true; + + // Get the cell details + int sep = s.indexOf('!'); + String cellString = s.substring(sep + 1); + column = CellReferenceHelper.getColumn(cellString); + row = CellReferenceHelper.getRow(cellString); + + // Get the sheet index + String sheetName = s.substring(0, sep); + + // Remove single quotes, if they exist + if (sheetName.charAt(0) == '\'' && + sheetName.charAt(sheetName.length() - 1) == '\'') { + sheetName = sheetName.substring(1, sheetName.length() - 1); + } + sheet = w.getExternalSheetIndex(sheetName); + + if (sheet < 0) { + throw new FormulaException(FormulaException.SHEET_REF_NOT_FOUND, + sheetName); + } + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + sheet = IntegerHelper.getInt(data[pos], data[pos + 1]); + row = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + int columnMask = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + column = columnMask & 0x00ff; + columnRelative = ((columnMask & 0x4000) != 0); + rowRelative = ((columnMask & 0x8000) != 0); + + return 6; + } + + /** + * Accessor for the column + * + * @return the column number + */ + public int getColumn() { + return column; + } + + /** + * Accessor for the row + * + * @return the row number + */ + public int getRow() { + return row; + } + + /** + * Gets the string version of this cell reference + * + * @param buf the buffer to append to + */ + public void getString(StringBuffer buf) { + CellReferenceHelper.getCellReference(sheet, column, !columnRelative, + row, !rowRelative, + workbook, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[7]; + data[0] = Token.REF3D.getCode(); + + IntegerHelper.getTwoBytes(sheet, data, 1); + IntegerHelper.getTwoBytes(row, data, 3); + + int grcol = column; + + // Set the row/column relative bits if applicable + if (rowRelative) { + grcol |= 0x8000; + } + + if (columnRelative) { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 5); + + return data; + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + if (columnRelative) { + column += colAdjust; + } + + if (rowRelative) { + row += rowAdjust; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) { + if (sheetIndex != sheet) { + return; + } + + if (column >= col) { + column++; + } + } + + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + if (sheetIndex != sheet) { + return; + } + + if (column >= col) { + column--; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param r the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int r, boolean currentSheet) { + if (sheetIndex != sheet) { + return; + } + + if (row >= r) { + row++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param r the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int r, boolean currentSheet) { + if (sheetIndex != sheet) { + return; + } + + if (row >= r) { + row--; + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Flags the formula as invalid + */ + void handleImportedCellReferences() { + setInvalid(); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/CellReferenceError.java b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReferenceError.java new file mode 100755 index 0000000..5bed91f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReferenceError.java @@ -0,0 +1,82 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.common.Logger; + +/** + * An cell reference error which occurs in a formula + */ +class CellReferenceError extends Operand implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CellReferenceError.class); + + /** + * Constructor + */ + public CellReferenceError() { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + // the data is unused - just return the four bytes + + return 4; + } + + /** + * Gets the cell reference as a string for this item + * + * @param buf the string buffer to populate + */ + public void getString(StringBuffer buf) { + buf.append(FormulaErrorCode.REF.getDescription()); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[5]; + data[0] = Token.REFERR.getCode(); + + // bytes 1-5 are unused + + return data; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/CloseParentheses.java b/datastructures-xslx/src/main/java/jxl/biff/formula/CloseParentheses.java new file mode 100755 index 0000000..b4b3b37 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/CloseParentheses.java @@ -0,0 +1,31 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * The close of parentheses token + */ +class CloseParentheses extends StringParseItem { + /** + * Constructor + */ + public CloseParentheses() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange.java new file mode 100755 index 0000000..317201e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange.java @@ -0,0 +1,80 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.CellReferenceHelper; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A class to hold range information across two entire columns + */ +class ColumnRange extends Area { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ColumnRange.class); + + /** + * Constructor + */ + ColumnRange() { + super(); + } + + /** + * Constructor invoked when parsing a string formula + * + * @param s the string to parse + */ + ColumnRange(String s) { + int seppos = s.indexOf(":"); + Assert.verify(seppos != -1); + String startcell = s.substring(0, seppos); + String endcell = s.substring(seppos + 1); + + int columnFirst = CellReferenceHelper.getColumn(startcell); + int rowFirst = 0; + int columnLast = CellReferenceHelper.getColumn(endcell); + int rowLast = 0xffff; + + boolean columnFirstRelative = + CellReferenceHelper.isColumnRelative(startcell); + boolean rowFirstRelative = false; + boolean columnLastRelative = CellReferenceHelper.isColumnRelative(endcell); + boolean rowLastRelative = false; + + setRangeData(columnFirst, columnLast, + rowFirst, rowLast, + columnFirstRelative, columnLastRelative, + rowFirstRelative, rowLastRelative); + } + + /** + * Gets the string representation of this item + * + * @param buf the string buffer + */ + public void getString(StringBuffer buf) { + CellReferenceHelper.getColumnReference(getFirstColumn(), buf); + buf.append(':'); + CellReferenceHelper.getColumnReference(getLastColumn(), buf); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange3d.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange3d.java new file mode 100755 index 0000000..85fa32e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange3d.java @@ -0,0 +1,122 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.CellReferenceHelper; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A nested class to hold range information + */ +class ColumnRange3d extends Area3d { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ColumnRange3d.class); + + /** + * A handle to the workbook + */ + private final ExternalSheet workbook; + + /** + * The sheet number + */ + private int sheet; + + /** + * Constructor + * + * @param es the external sheet + */ + ColumnRange3d(ExternalSheet es) { + super(es); + workbook = es; + } + + /** + * Constructor invoked when parsing a string formula + * + * @param s the string to parse + * @param es the external sheet + * @throws FormulaException + */ + ColumnRange3d(String s, ExternalSheet es) throws FormulaException { + super(es); + workbook = es; + int seppos = s.lastIndexOf(":"); + Assert.verify(seppos != -1); + String startcell = s.substring(0, seppos); + String endcell = s.substring(seppos + 1); + + // Get the the start cell details + int sep = s.indexOf('!'); + String cellString = s.substring(sep + 1, seppos); + int columnFirst = CellReferenceHelper.getColumn(cellString); + int rowFirst = 0; + + // Get the sheet index + String sheetName = s.substring(0, sep); + int sheetNamePos = sheetName.lastIndexOf(']'); + + // Remove single quotes, if they exist + if (sheetName.charAt(0) == '\'' && + sheetName.charAt(sheetName.length() - 1) == '\'') { + sheetName = sheetName.substring(1, sheetName.length() - 1); + } + + sheet = es.getExternalSheetIndex(sheetName); + + if (sheet < 0) { + throw new FormulaException(FormulaException.SHEET_REF_NOT_FOUND, + sheetName); + } + + // Get the last cell index + int columnLast = CellReferenceHelper.getColumn(endcell); + int rowLast = 0xffff; + + boolean columnFirstRelative = true; + boolean rowFirstRelative = true; + boolean columnLastRelative = true; + boolean rowLastRelative = true; + + setRangeData(sheet, columnFirst, columnLast, rowFirst, rowLast, + columnFirstRelative, rowFirstRelative, + columnLastRelative, rowLastRelative); + } + + /** + * Gets the string representation of this column range + * + * @param buf the string buffer to append to + */ + public void getString(StringBuffer buf) { + buf.append('\''); + buf.append(workbook.getExternalSheetName(sheet)); + buf.append('\''); + buf.append('!'); + + CellReferenceHelper.getColumnReference(getFirstColumn(), buf); + buf.append(':'); + CellReferenceHelper.getColumnReference(getLastColumn(), buf); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Concatenate.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Concatenate.java new file mode 100755 index 0000000..fdcd748 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Concatenate.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Concatenate extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public Concatenate() { + } + + /** + * Gets the string representation of this symbol + * + * @return the concatenation symbol + */ + public String getSymbol() { + return "&"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.CONCAT; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 3; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Divide.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Divide.java new file mode 100755 index 0000000..ecfc299 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Divide.java @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Divide extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public Divide() { + } + + /** + * Gets the string representation of this symbol + * + * @return the symbol + */ + public String getSymbol() { + return "/"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.DIVIDE; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 3; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/DoubleValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/DoubleValue.java new file mode 100755 index 0000000..c3e0cb3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/DoubleValue.java @@ -0,0 +1,112 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.DoubleHelper; +import jxl.common.Logger; + +/** + * A cell reference in a formula + */ +class DoubleValue extends NumberValue implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DoubleValue.class); + + /** + * The value of this double in the formula + */ + private double value; + + /** + * Constructor + */ + public DoubleValue() { + } + + /** + * Constructor - invoked when writing an integer value that's out + * of range for a short + * + * @param v the double value + */ + DoubleValue(double v) { + value = v; + } + + /** + * Constructor for a double value when reading from a string + * + * @param s the string representation of this token + */ + public DoubleValue(String s) { + try { + value = Double.parseDouble(s); + } catch (NumberFormatException e) { + logger.warn(e, e); + value = 0; + } + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + value = DoubleHelper.getIEEEDouble(data, pos); + + return 8; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[9]; + data[0] = Token.DOUBLE.getCode(); + + DoubleHelper.getIEEEBytes(value, data, 1); + + return data; + } + + /** + * The double value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Equal.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Equal.java new file mode 100755 index 0000000..407a516 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Equal.java @@ -0,0 +1,61 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Equal extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public Equal() { + } + + /** + * Gets the string for this symbol + * + * @return the symbol string + */ + public String getSymbol() { + return "="; + } + + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.EQUAL; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 5; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ErrorConstant.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ErrorConstant.java new file mode 100755 index 0000000..f90f55b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ErrorConstant.java @@ -0,0 +1,89 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * An error constant + */ +class ErrorConstant extends Operand implements ParsedThing { + /** + * The error + */ + private FormulaErrorCode error; + + /** + * Constructor + */ + public ErrorConstant() { + } + + /** + * Constructor + * + * @param s the error constant + */ + public ErrorConstant(String s) { + error = FormulaErrorCode.getErrorCode(s); + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + int code = data[pos]; + error = FormulaErrorCode.getErrorCode(code); + return 1; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[2]; + data[0] = Token.ERR.getCode(); + data[1] = (byte) error.getCode(); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) { + buf.append(error.getDescription()); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ExternalSheet.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ExternalSheet.java new file mode 100755 index 0000000..f0838a0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ExternalSheet.java @@ -0,0 +1,76 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.read.biff.BOFRecord; + +/** + * Interface which exposes the methods needed by formulas + * to access external sheet records + */ +public interface ExternalSheet { + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + String getExternalSheetName(int index); + + /** + * Gets the index of the first external sheet for the name + * + * @param sheetName the name of the external sheet + * @return the index of the external sheet with the specified name + */ + int getExternalSheetIndex(String sheetName); + + /** + * Gets the index of the first external sheet for the name + * + * @param index the external sheet index + * @return the sheet index of the external sheet index + */ + int getExternalSheetIndex(int index); + + /** + * Gets the index of the last external sheet for the name + * + * @param sheetName the name of the external sheet + * @return the index of the external sheet with the specified name + */ + int getLastExternalSheetIndex(String sheetName); + + /** + * Gets the index of the first external sheet for the name + * + * @param index the external sheet index + * @return the sheet index of the external sheet index + */ + int getLastExternalSheetIndex(int index); + + /** + * Parsing of formulas is only supported for a subset of the available + * biff version, so we need to test to see if this version is acceptable + * + * @return the BOF record + */ + BOFRecord getWorkbookBof(); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaErrorCode.java b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaErrorCode.java new file mode 100755 index 0000000..4500bef --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaErrorCode.java @@ -0,0 +1,127 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * Enumeration for formula error codes + */ +public class FormulaErrorCode { + public static final FormulaErrorCode UNKNOWN = + new FormulaErrorCode(0xff, "?"); + public static final FormulaErrorCode NULL = + new FormulaErrorCode(0x0, "#NULL!"); + public static final FormulaErrorCode DIV0 = + new FormulaErrorCode(0x7, "#DIV/0!"); + public static final FormulaErrorCode VALUE = + new FormulaErrorCode(0xf, "#VALUE!"); + public static final FormulaErrorCode REF = + new FormulaErrorCode(0x17, "#REF!"); + public static final FormulaErrorCode NAME = + new FormulaErrorCode(0x1d, "#NAME?"); + public static final FormulaErrorCode NUM = new FormulaErrorCode(0x24, + "#NUM!"); + public static final FormulaErrorCode NA = new FormulaErrorCode(0x2a, + "#N/A!"); + /** + * The list of error codes + */ + private static FormulaErrorCode[] codes = new FormulaErrorCode[0]; + /** + * The error code + */ + private final int errorCode; + /** + * The description + */ + private final String description; + /** + * Constructor + * + * @param code the code + * @param desc the description + */ + FormulaErrorCode(int code, String desc) { + errorCode = code; + description = desc; + FormulaErrorCode[] newcodes = new FormulaErrorCode[codes.length + 1]; + System.arraycopy(codes, 0, newcodes, 0, codes.length); + newcodes[codes.length] = this; + codes = newcodes; + } + + /** + * Gets the error type given just the code + * + * @param code the code to lookup + * @return the error type + */ + public static FormulaErrorCode getErrorCode(int code) { + boolean found = false; + FormulaErrorCode ec = UNKNOWN; + for (int i = 0; i < codes.length && !found; i++) { + if (codes[i].errorCode == code) { + found = true; + ec = codes[i]; + } + } + return ec; + } + + /** + * Gets the error type given the string value + * + * @param code the code to lookup + * @return the error type + */ + public static FormulaErrorCode getErrorCode(String code) { + boolean found = false; + FormulaErrorCode ec = UNKNOWN; + + if (code == null || code.length() == 0) { + return ec; + } + + for (int i = 0; i < codes.length && !found; i++) { + if (codes[i].description.equals(code)) { + found = true; + ec = codes[i]; + } + } + return ec; + } + + /** + * Accessor for the code + * + * @return the code + */ + public int getCode() { + return errorCode; + } + + /** + * Accessor for the description + * + * @return the description + */ + public String getDescription() { + return description; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaException.java b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaException.java new file mode 100755 index 0000000..3597d80 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaException.java @@ -0,0 +1,122 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.JXLException; + +/** + * Exception thrown when parsing a formula + */ +public class FormulaException extends JXLException { + /** + * + */ + public static final FormulaMessage BIFF8_SUPPORTED = + new FormulaMessage("Only biff8 formulas are supported"); + /** + * + */ + static final FormulaMessage UNRECOGNIZED_TOKEN = + new FormulaMessage("Unrecognized token"); + + /** + * + */ + static final FormulaMessage UNRECOGNIZED_FUNCTION = + new FormulaMessage("Unrecognized function"); + /** + * + */ + static final FormulaMessage LEXICAL_ERROR = + new FormulaMessage("Lexical error: "); + /** + * + */ + static final FormulaMessage INCORRECT_ARGUMENTS = + new FormulaMessage("Incorrect arguments supplied to function"); + /** + * + */ + static final FormulaMessage SHEET_REF_NOT_FOUND = + new FormulaMessage("Could not find sheet"); + /** + * + */ + static final FormulaMessage CELL_NAME_NOT_FOUND = + new FormulaMessage("Could not find named cell"); + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + public FormulaException(FormulaMessage m) { + super(m.message); + } + + + /** + * Constructs this exception with the specified message + * + * @param m the message + * @param val the value + */ + public FormulaException(FormulaMessage m, int val) { + super(m.message + " " + val); + } + + /** + * Constructs this exception with the specified message + * + * @param m the message + * @param val the value + */ + public FormulaException(FormulaMessage m, String val) { + super(m.message + " " + val); + } + + /** + * Inner class containing the message + */ + private static class FormulaMessage { + /** + * The message + */ + private final String message; + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + FormulaMessage(String m) { + message = m; + } + + /** + * Accessor for the message + * + * @return the message + */ + public String getMessage() { + return message; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaParser.java b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaParser.java new file mode 100755 index 0000000..59749ae --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaParser.java @@ -0,0 +1,239 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the7 Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.Cell; +import jxl.WorkbookSettings; +import jxl.biff.WorkbookMethods; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Parses the formula passed in (either as parsed strings or as a string) + * into a tree of operators and operands + */ +public class FormulaParser { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FormulaParser.class); + + /** + * The formula parser. The object implementing this interface will either + * parse tokens or strings + */ + private final Parser parser; + + /** + * Constructor which creates the parse tree out of tokens + * + * @param tokens the list of parsed tokens + * @param rt the cell containing the formula + * @param es a handle to the external sheet + * @param nt a handle to the name table + * @param ws the workbook settings + * @param pc the parse context + * @throws FormulaException + */ + public FormulaParser(byte[] tokens, + Cell rt, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) + throws FormulaException { + // A null workbook bof means that it is a writable workbook and therefore + // must be biff8 + if (es.getWorkbookBof() != null && + !es.getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + Assert.verify(nt != null); + parser = new TokenFormulaParser(tokens, rt, es, nt, ws, + ParseContext.DEFAULT); + } + + /** + * Constructor which creates the parse tree out of tokens + * + * @param tokens the list of parsed tokens + * @param rt the cell containing the formula + * @param es a handle to the external sheet + * @param nt a handle to the name table + * @param ws the workbook settings + * @param pc the parse context + * @throws FormulaException + */ + public FormulaParser(byte[] tokens, + Cell rt, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws, + ParseContext pc) + throws FormulaException { + // A null workbook bof means that it is a writable workbook and therefore + // must be biff8 + if (es.getWorkbookBof() != null && + !es.getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + Assert.verify(nt != null); + parser = new TokenFormulaParser(tokens, rt, es, nt, ws, pc); + } + + /** + * Constructor which creates the parse tree out of the string + * + * @param form the formula string + * @param es the external sheet handle + * @param nt the name table + * @param ws the workbook settings + */ + public FormulaParser(String form, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) { + parser = new StringFormulaParser(form, es, nt, ws, + ParseContext.DEFAULT); + } + + /** + * Constructor which creates the parse tree out of the string + * + * @param form the formula string + * @param es the external sheet handle + * @param nt the name table + * @param ws the workbook settings + * @param pc the context of the parse + */ + public FormulaParser(String form, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws, + ParseContext pc) { + parser = new StringFormulaParser(form, es, nt, ws, pc); + } + + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + parser.adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Parses the formula into a parse tree + * + * @throws FormulaException + */ + public void parse() throws FormulaException { + parser.parse(); + } + + /** + * Gets the formula as a string + * + * @return the formula as a string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + return parser.getFormula(); + } + + /** + * Gets the bytes for the formula. This takes into account any + * token mapping necessary because of shared formulas + * + * @return the bytes in RPN + */ + public byte[] getBytes() { + return parser.getBytes(); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) { + parser.columnInserted(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + parser.columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowInserted(int sheetIndex, int row, boolean currentSheet) { + parser.rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + parser.rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if the formula is valid import, FALSE otherwise + */ + public boolean handleImportedCellReferences() { + return parser.handleImportedCellReferences(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Function.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Function.java new file mode 100755 index 0000000..8d220d7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Function.java @@ -0,0 +1,694 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.WorkbookSettings; +import jxl.common.Logger; + +/** + * An enumeration detailing the Excel function codes + */ +final class Function { + public static final Function COUNT = + new Function(0x0, "count", 0xff); + public static final Function ATTRIBUTE = new Function(0x1, "", 0xff); + public static final Function ISNA = + new Function(0x2, "isna", 1); + public static final Function ISERROR = + new Function(0x3, "iserror", 1); + public static final Function SUM = + new Function(0x4, "sum", 0xff); + public static final Function AVERAGE = + new Function(0x5, "average", 0xff); + public static final Function MIN = + new Function(0x6, "min", 0xff); + public static final Function MAX = + new Function(0x7, "max", 0xff); + public static final Function ROW = + new Function(0x8, "row", 0xff); + public static final Function COLUMN = + new Function(0x9, "column", 0xff); + public static final Function NA = + new Function(0xa, "na", 0); + public static final Function NPV = + new Function(0xb, "npv", 0xff); + public static final Function STDEV = + new Function(0xc, "stdev", 0xff); + public static final Function DOLLAR = + new Function(0xd, "dollar", 2); + + // The functions + public static final Function FIXED = + new Function(0xe, "fixed", 0xff); + public static final Function SIN = + new Function(0xf, "sin", 1); + public static final Function COS = + new Function(0x10, "cos", 1); + public static final Function TAN = + new Function(0x11, "tan", 1); + public static final Function ATAN = + new Function(0x12, "atan", 1); + public static final Function PI = + new Function(0x13, "pi", 0); + public static final Function SQRT = + new Function(0x14, "sqrt", 1); + public static final Function EXP = + new Function(0x15, "exp", 1); + public static final Function LN = + new Function(0x16, "ln", 1); + public static final Function LOG10 = + new Function(0x17, "log10", 1); + public static final Function ABS = + new Function(0x18, "abs", 1); + public static final Function INT = + new Function(0x19, "int", 1); + public static final Function SIGN = + new Function(0x1a, "sign", 1); + public static final Function ROUND = + new Function(0x1b, "round", 2); + public static final Function LOOKUP = + new Function(0x1c, "lookup", 2); + public static final Function INDEX = + new Function(0x1d, "index", 3); + public static final Function REPT = new Function(0x1e, "rept", 2); + public static final Function MID = + new Function(0x1f, "mid", 3); + public static final Function LEN = + new Function(0x20, "len", 1); + public static final Function VALUE = + new Function(0x21, "value", 1); + public static final Function TRUE = + new Function(0x22, "true", 0); + public static final Function FALSE = + new Function(0x23, "false", 0); + public static final Function AND = + new Function(0x24, "and", 0xff); + public static final Function OR = + new Function(0x25, "or", 0xff); + public static final Function NOT = + new Function(0x26, "not", 1); + public static final Function MOD = + new Function(0x27, "mod", 2); + public static final Function DCOUNT = + new Function(0x28, "dcount", 3); + public static final Function DSUM = + new Function(0x29, "dsum", 3); + public static final Function DAVERAGE = + new Function(0x2a, "daverage", 3); + public static final Function DMIN = + new Function(0x2b, "dmin", 3); + public static final Function DMAX = + new Function(0x2c, "dmax", 3); + public static final Function DSTDEV = + new Function(0x2d, "dstdev", 3); + public static final Function VAR = + new Function(0x2e, "var", 0xff); + public static final Function DVAR = + new Function(0x2f, "dvar", 3); + public static final Function TEXT = + new Function(0x30, "text", 2); + public static final Function LINEST = + new Function(0x31, "linest", 0xff); + public static final Function TREND = + new Function(0x32, "trend", 0xff); + public static final Function LOGEST = + new Function(0x33, "logest", 0xff); + public static final Function GROWTH = + new Function(0x34, "growth", 0xff); + //public static final Function GOTO = new Function(0x35, "GOTO",); + //public static final Function HALT = new Function(0x36, "HALT",); + public static final Function PV = + new Function(0x38, "pv", 0xff); + public static final Function FV = + new Function(0x39, "fv", 0xff); + public static final Function NPER = + new Function(0x3a, "nper", 0xff); + public static final Function PMT = + new Function(0x3b, "pmt", 0xff); + public static final Function RATE = + new Function(0x3c, "rate", 0xff); + //public static final Function MIRR = new Function(0x3d, "MIRR",); + //public static final Function IRR = new Function(0x3e, "IRR",); + public static final Function RAND = + new Function(0x3f, "rand", 0); + public static final Function MATCH = + new Function(0x40, "match", 3); + public static final Function DATE = + new Function(0x41, "date", 3); + public static final Function TIME = + new Function(0x42, "time", 3); + public static final Function DAY = + new Function(0x43, "day", 1); + public static final Function MONTH = + new Function(0x44, "month", 1); + public static final Function YEAR = + new Function(0x45, "year", 1); + public static final Function WEEKDAY = + new Function(0x46, "weekday", 2); + public static final Function HOUR = + new Function(0x47, "hour", 1); + public static final Function MINUTE = + new Function(0x48, "minute", 1); + public static final Function SECOND = + new Function(0x49, "second", 1); + public static final Function NOW = + new Function(0x4a, "now", 0); + public static final Function AREAS = + new Function(0x4b, "areas", 0xff); + public static final Function ROWS = + new Function(0x4c, "rows", 1); + public static final Function COLUMNS = + new Function(0x4d, "columns", 0xff); + public static final Function OFFSET = + new Function(0x4e, "offset", 0xff); + //public static final Function ABSREF = new Function(0x4f, "ABSREF",); + //public static final Function RELREF = new Function(0x50, "RELREF",); + //public static final Function ARGUMENT = new Function(0x51,"ARGUMENT",); + public static final Function SEARCH = new Function(0x52, "search", 0xff); + public static final Function TRANSPOSE = + new Function(0x53, "transpose", 0xff); + public static final Function ERROR = + new Function(0x54, "error", 1); + //public static final Function STEP = new Function(0x55, "STEP",); + public static final Function TYPE = + new Function(0x56, "type", 1); + //public static final Function ECHO = new Function(0x57, "ECHO",); + //public static final Function SETNAME = new Function(0x58, "SETNAME",); + //public static final Function CALLER = new Function(0x59, "CALLER",); + //public static final Function DEREF = new Function(0x5a, "DEREF",); + //public static final Function WINDOWS = new Function(0x5b, "WINDOWS",); + //public static final Function SERIES = new Function(0x5c, "SERIES",); + //public static final Function DOCUMENTS = new Function(0x5d,"DOCUMENTS",); + //public static final Function ACTIVECELL = new Function(0x5e,"ACTIVECELL",); + //public static final Function SELECTION = new Function(0x5f,"SELECTION",); + //public static final Function RESULT = new Function(0x60, "RESULT",); + public static final Function ATAN2 = + new Function(0x61, "atan2", 1); + public static final Function ASIN = + new Function(0x62, "asin", 1); + public static final Function ACOS = + new Function(0x63, "acos", 1); + public static final Function CHOOSE = + new Function(0x64, "choose", 0xff); + public static final Function HLOOKUP = + new Function(0x65, "hlookup", 0xff); + public static final Function VLOOKUP = + new Function(0x66, "vlookup", 0xff); + //public static final Function LINKS = new Function(0x67, "LINKS",); + //public static final Function INPUT = new Function(0x68, "INPUT",); + public static final Function ISREF = + new Function(0x69, "isref", 1); + //public static final Function GETFORMULA = new Function(0x6a,"GETFORMULA",); + //public static final Function GETNAME = new Function(0x6b, "GETNAME",); + //public static final Function SETVALUE = new Function(0x6c,"SETVALUE",); + public static final Function LOG = + new Function(0x6d, "log", 0xff); + //public static final Function EXEC = new Function(0x6e, "EXEC",); + public static final Function CHAR = + new Function(0x6f, "char", 1); + public static final Function LOWER = + new Function(0x70, "lower", 1); + public static final Function UPPER = + new Function(0x71, "upper", 1); + public static final Function PROPER = + new Function(0x72, "proper", 1); + public static final Function LEFT = + new Function(0x73, "left", 0xff); + public static final Function RIGHT = + new Function(0x74, "right", 0xff); + public static final Function EXACT = + new Function(0x75, "exact", 2); + public static final Function TRIM = + new Function(0x76, "trim", 1); + public static final Function REPLACE = + new Function(0x77, "replace", 4); + public static final Function SUBSTITUTE = + new Function(0x78, "substitute", 0xff); + public static final Function CODE = + new Function(0x79, "code", 1); + //public static final Function NAMES = new Function(0x7a, "NAMES",); + //public static final Function DIRECTORY = new Function(0x7b,"DIRECTORY",); + public static final Function FIND = + new Function(0x7c, "find", 0xff); + public static final Function CELL = + new Function(0x7d, "cell", 2); + public static final Function ISERR = + new Function(0x7e, "iserr", 1); + public static final Function ISTEXT = + new Function(0x7f, "istext", 1); + public static final Function ISNUMBER = + new Function(0x80, "isnumber", 1); + public static final Function ISBLANK = + new Function(0x81, "isblank", 1); + public static final Function T = + new Function(0x82, "t", 1); + public static final Function N = + new Function(0x83, "n", 1); + //public static final Function FOPEN = new Function(0x84, "FOPEN",); + //public static final Function FCLOSE = new Function(0x85, "FCLOSE",); + //public static final Function FSIZE = new Function(0x86, "FSIZE",); + //public static final Function FREADLN = new Function(0x87, "FREADLN",); + //public static final Function FREAD = new Function(0x88, "FREAD",); + //public static final Function FWRITELN = new Function(0x89,"FWRITELN",); + //public static final Function FWRITE = new Function(0x8a, "FWRITE",); + //public static final Function FPOS = new Function(0x8b, "FPOS",); + public static final Function DATEVALUE = + new Function(0x8c, "datevalue", 1); + public static final Function TIMEVALUE = + new Function(0x8d, "timevalue", 1); + public static final Function SLN = + new Function(0x8e, "sln", 3); + public static final Function SYD = + new Function(0x8f, "syd", 3); + public static final Function DDB = + new Function(0x90, "ddb", 0xff); + //public static final Function GETDEF = new Function(0x91, "GETDEF",); + //public static final Function REFTEXT = new Function(0x92, "REFTEXT",); + //public static final Function TEXTREF = new Function(0x93, "TEXTREF",); + public static final Function INDIRECT = + new Function(0x94, "indirect", 0xff); + //public static final Function REGISTER = new Function(0x95,"REGISTER",); + //public static final Function CALL = new Function(0x96, "CALL",); + //public static final Function ADDBAR = new Function(0x97, "ADDBAR",); + //public static final Function ADDMENU = new Function(0x98, "ADDMENU",); + //public static final Function ADDCOMMAND = + // new Function(0x99,"ADDCOMMAND",); + //public static final Function ENABLECOMMAND = + // new Function(0x9a,"ENABLECOMMAND",); + //public static final Function CHECKCOMMAND = + // new Function(0x9b,"CHECKCOMMAND",); + //public static final Function RENAMECOMMAND = + // new Function(0x9c,"RENAMECOMMAND",); + //public static final Function SHOWBAR = new Function(0x9d, "SHOWBAR",); + //public static final Function DELETEMENU = + // new Function(0x9e,"DELETEMENU",); + //public static final Function DELETECOMMAND = + // new Function(0x9f,"DELETECOMMAND",); + //public static final Function GETCHARTITEM = + // new Function(0xa0,"GETCHARTITEM",); + //public static final Function DIALOGBOX = new Function(0xa1,"DIALOGBOX",); + public static final Function CLEAN = + new Function(0xa2, "clean", 1); + public static final Function MDETERM = + new Function(0xa3, "mdeterm", 0xff); + public static final Function MINVERSE = + new Function(0xa4, "minverse", 0xff); + public static final Function MMULT = + new Function(0xa5, "mmult", 0xff); + public static final Function IPMT = + new Function(0xa7, "ipmt", 0xff); + public static final Function PPMT = + new Function(0xa8, "ppmt", 0xff); + public static final Function COUNTA = + new Function(0xa9, "counta", 0xff); + public static final Function PRODUCT = + new Function(0xb7, "product", 0xff); + public static final Function FACT = + new Function(0xb8, "fact", 1); + //public static final Function GETCELL = new Function(0xb9, "GETCELL",); + //public static final Function GETWORKSPACE = + // new Function(0xba,"GETWORKSPACE",); + //public static final Function GETWINDOW = new Function(0xbb,"GETWINDOW",); + //public static final Function GETDOCUMENT = + // new Function(0xbc,"GETDOCUMENT",); + public static final Function DPRODUCT = + new Function(0xbd, "dproduct", 3); + public static final Function ISNONTEXT = + new Function(0xbe, "isnontext", 1); + //public static final Function GETNOTE = new Function(0xbf, "GETNOTE",); + //public static final Function NOTE = new Function(0xc0, "NOTE",); + public static final Function STDEVP = + new Function(0xc1, "stdevp", 0xff); + public static final Function VARP = + new Function(0xc2, "varp", 0xff); + public static final Function DSTDEVP = + new Function(0xc3, "dstdevp", 0xff); + public static final Function DVARP = + new Function(0xc4, "dvarp", 0xff); + public static final Function TRUNC = + new Function(0xc5, "trunc", 0xff); + public static final Function ISLOGICAL = + new Function(0xc6, "islogical", 1); + public static final Function DCOUNTA = + new Function(0xc7, "dcounta", 0xff); + //public static final Function FILES = new Function(0xa6, "FILES", + public static final Function FINDB = + new Function(0xcd, "findb", 0xff); + public static final Function SEARCHB = + new Function(0xce, "searchb", 3); + public static final Function REPLACEB = + new Function(0xcf, "replaceb", 4); + public static final Function LEFTB = + new Function(0xd0, "leftb", 0xff); + public static final Function RIGHTB = + new Function(0xd1, "rightb", 0xff); + public static final Function MIDB = + new Function(0xd2, "midb", 3); + public static final Function LENB = + new Function(0xd3, "lenb", 1); + public static final Function ROUNDUP = + new Function(0xd4, "roundup", 2); + public static final Function ROUNDDOWN = + new Function(0xd5, "rounddown", 2); + public static final Function RANK = + new Function(0xd8, "rank", 0xff); + public static final Function ADDRESS = + new Function(0xdb, "address", 0xff); + public static final Function AYS360 = + new Function(0xdc, "days360", 0xff); + public static final Function ODAY = + new Function(0xdd, "today", 0); + public static final Function VDB = + new Function(0xde, "vdb", 0xff); + public static final Function MEDIAN = + new Function(0xe3, "median", 0xff); + public static final Function SUMPRODUCT = + new Function(0xe4, "sumproduct", 0xff); + public static final Function SINH = + new Function(0xe5, "sinh", 1); + public static final Function COSH = + new Function(0xe6, "cosh", 1); + public static final Function TANH = + new Function(0xe7, "tanh", 1); + public static final Function ASINH = + new Function(0xe8, "asinh", 1); + public static final Function ACOSH = + new Function(0xe9, "acosh", 1); + public static final Function ATANH = + new Function(0xea, "atanh", 1); + public static final Function INFO = + new Function(0xf4, "info", 1); + public static final Function AVEDEV = + new Function(0x10d, "avedev", 0XFF); + public static final Function BETADIST = + new Function(0x10e, "betadist", 0XFF); + public static final Function GAMMALN = + new Function(0x10f, "gammaln", 1); + public static final Function BETAINV = + new Function(0x110, "betainv", 0XFF); + public static final Function BINOMDIST = + new Function(0x111, "binomdist", 4); + public static final Function CHIDIST = + new Function(0x112, "chidist", 2); + public static final Function CHIINV = + new Function(0x113, "chiinv", 2); + public static final Function COMBIN = + new Function(0x114, "combin", 2); + public static final Function CONFIDENCE = + new Function(0x115, "confidence", 3); + public static final Function CRITBINOM = + new Function(0x116, "critbinom", 3); + public static final Function EVEN = + new Function(0x117, "even", 1); + public static final Function EXPONDIST = + new Function(0x118, "expondist", 3); + public static final Function FDIST = + new Function(0x119, "fdist", 3); + public static final Function FINV = + new Function(0x11a, "finv", 3); + public static final Function FISHER = + new Function(0x11b, "fisher", 1); + public static final Function FISHERINV = + new Function(0x11c, "fisherinv", 1); + public static final Function FLOOR = + new Function(0x11d, "floor", 2); + public static final Function GAMMADIST = + new Function(0x11e, "gammadist", 4); + public static final Function GAMMAINV = + new Function(0x11f, "gammainv", 3); + public static final Function CEILING = + new Function(0x120, "ceiling", 2); + public static final Function HYPGEOMDIST = + new Function(0x121, "hypgeomdist", 4); + public static final Function LOGNORMDIST = + new Function(0x122, "lognormdist", 3); + public static final Function LOGINV = + new Function(0x123, "loginv", 3); + public static final Function NEGBINOMDIST = + new Function(0x124, "negbinomdist", 3); + public static final Function NORMDIST = + new Function(0x125, "normdist", 4); + public static final Function NORMSDIST = + new Function(0x126, "normsdist", 1); + public static final Function NORMINV = + new Function(0x127, "norminv", 3); + public static final Function NORMSINV = + new Function(0x128, "normsinv", 1); + public static final Function STANDARDIZE = + new Function(0x129, "standardize", 3); + public static final Function ODD = + new Function(0x12a, "odd", 1); + public static final Function PERMUT = + new Function(0x12b, "permut", 2); + public static final Function POISSON = + new Function(0x12c, "poisson", 3); + public static final Function TDIST = + new Function(0x12d, "tdist", 3); + public static final Function WEIBULL = + new Function(0x12e, "weibull", 4); + public static final Function SUMXMY2 = + new Function(303, "sumxmy2", 0xff); + public static final Function SUMX2MY2 = + new Function(304, "sumx2my2", 0xff); + public static final Function SUMX2PY2 = + new Function(305, "sumx2py2", 0xff); + public static final Function CHITEST = + new Function(0x132, "chitest", 0xff); + public static final Function CORREL = + new Function(0x133, "correl", 0xff); + public static final Function COVAR = + new Function(0x134, "covar", 0xff); + public static final Function FORECAST = + new Function(0x135, "forecast", 0xff); + public static final Function FTEST = + new Function(0x136, "ftest", 0xff); + public static final Function INTERCEPT = + new Function(0x137, "intercept", 0xff); + public static final Function PEARSON = + new Function(0x138, "pearson", 0xff); + public static final Function RSQ = + new Function(0x139, "rsq", 0xff); + public static final Function STEYX = + new Function(0x13a, "steyx", 0xff); + public static final Function SLOPE = + new Function(0x13b, "slope", 2); + public static final Function TTEST = + new Function(0x13c, "ttest", 0xff); + public static final Function PROB = + new Function(0x13d, "prob", 0xff); + public static final Function DEVSQ = + new Function(0x13e, "devsq", 0xff); + public static final Function GEOMEAN = + new Function(0x13f, "geomean", 0xff); + public static final Function HARMEAN = + new Function(0x140, "harmean", 0xff); + public static final Function SUMSQ = + new Function(0x141, "sumsq", 0xff); + public static final Function KURT = + new Function(0x142, "kurt", 0xff); + public static final Function SKEW = + new Function(0x143, "skew", 0xff); + public static final Function ZTEST = + new Function(0x144, "ztest", 0xff); + public static final Function LARGE = + new Function(0x145, "large", 0xff); + public static final Function SMALL = + new Function(0x146, "small", 0xff); + public static final Function QUARTILE = + new Function(0x147, "quartile", 0xff); + public static final Function PERCENTILE = + new Function(0x148, "percentile", 0xff); + public static final Function PERCENTRANK = + new Function(0x149, "percentrank", 0xff); + public static final Function MODE = + new Function(0x14a, "mode", 0xff); + public static final Function TRIMMEAN = + new Function(0x14b, "trimmean", 0xff); + public static final Function TINV = + new Function(0x14c, "tinv", 2); + public static final Function CONCATENATE = + new Function(0x150, "concatenate", 0xff); + public static final Function POWER = + new Function(0x151, "power", 2); + public static final Function RADIANS = + new Function(0x156, "radians", 1); + public static final Function DEGREES = + new Function(0x157, "degrees", 1); + public static final Function SUBTOTAL = + new Function(0x158, "subtotal", 0xff); + public static final Function SUMIF = + new Function(0x159, "sumif", 0xff); + public static final Function COUNTIF = + new Function(0x15a, "countif", 2); + public static final Function COUNTBLANK = + new Function(0x15b, "countblank", 1); + public static final Function HYPERLINK = + new Function(0x167, "hyperlink", 2); + public static final Function AVERAGEA = + new Function(0x169, "averagea", 0xff); + public static final Function MAXA = + new Function(0x16a, "maxa", 0xff); + public static final Function MINA = + new Function(0x16b, "mina", 0xff); + public static final Function STDEVPA = + new Function(0x16c, "stdevpa", 0xff); + public static final Function VARPA = + new Function(0x16d, "varpa", 0xff); + public static final Function STDEVA = + new Function(0x16e, "stdeva", 0xff); + public static final Function VARA = + new Function(0x16f, "vara", 0xff); + // If token. This is not an excel assigned number, but one made up + // in order that the if command may be recognized + public static final Function IF = + new Function(0xfffe, "if", 0xff); + // Unknown token + public static final Function UNKNOWN = new Function(0xffff, "", 0); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Function.class); + /** + * All available functions. This attribute is package protected in order + * to enable the FunctionNames to initialize + */ + private static Function[] functions = new Function[0]; + /** + * The code which applies to this function + */ + private final int code; + /** + * The property name of this function + */ + private final String name; + /** + * The number of args this function expects + */ + private final int numArgs; + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + * @param s the string + * @param a the number of arguments + */ + private Function(int v, String s, int a) { + code = v; + name = s; + numArgs = a; + + // Grow the array + Function[] newarray = new Function[functions.length + 1]; + System.arraycopy(functions, 0, newarray, 0, functions.length); + newarray[functions.length] = this; + functions = newarray; + } + + /** + * Gets the type object from its integer value + * + * @param v the function value + * @return the function + */ + public static Function getFunction(int v) { + Function f = null; + + for (int i = 0; i < functions.length; i++) { + if (functions[i].code == v) { + f = functions[i]; + break; + } + } + + return f != null ? f : UNKNOWN; + } + + /** + * Gets the type object from its string value. Used when parsing strings + * + * @param v the function name + * @param ws the workbook settings + * @return the function + */ + public static Function getFunction(String v, WorkbookSettings ws) { + FunctionNames fn = ws.getFunctionNames(); + Function f = fn.getFunction(v); + return f != null ? f : UNKNOWN; + } + + /** + * Accessor for all the functions, used by the internationalization + * work around + * + * @return all the functions + */ + static Function[] getFunctions() { + return functions; + } + + /** + * Standard hash code method + * + * @return the hash code + */ + public int hashCode() { + return code; + } + + /** + * Gets the function code - used when generating token data + * + * @return the code + */ + int getCode() { + return code; + } + + /** + * Gets the property name. Used by the FunctionNames object when initializing + * the locale specific names + * + * @return the property name for this function + */ + String getPropertyName() { + return name; + } + + /** + * Gets the function name + * + * @param ws the workbook settings + * @return the function name + */ + String getName(WorkbookSettings ws) { + FunctionNames fn = ws.getFunctionNames(); + return fn.getName(this); + } + + /** + * Gets the number of arguments for this function + * + * @return the number of arguments + */ + int getNumArgs() { + return numArgs; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/FunctionNames.java b/datastructures-xslx/src/main/java/jxl/biff/formula/FunctionNames.java new file mode 100755 index 0000000..d648b4d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/FunctionNames.java @@ -0,0 +1,97 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.HashMap; +import java.util.Locale; +import java.util.ResourceBundle; +import jxl.common.Logger; + +/** + * A class which contains the function names for the current workbook. The + * function names can potentially vary from workbook to workbook depending + * on the locale + */ +public class FunctionNames { + /** + * The logger class + */ + private static final Logger logger = Logger.getLogger(FunctionNames.class); + + /** + * A hash mapping keyed on the function and returning its locale specific + * name + */ + private final HashMap names; + + /** + * A hash mapping keyed on the locale specific name and returning the + * function + */ + private final HashMap functions; + + /** + * Constructor + * + * @param l the locale + */ + public FunctionNames(Locale l) { + ResourceBundle rb = ResourceBundle.getBundle("functions", l); + Function[] allfunctions = Function.getFunctions(); + names = new HashMap(allfunctions.length); + functions = new HashMap(allfunctions.length); + + // Iterate through all the functions, adding them to the hash maps + Function f = null; + String n = null; + String propname = null; + for (int i = 0; i < allfunctions.length; i++) { + f = allfunctions[i]; + propname = f.getPropertyName(); + + n = propname.length() != 0 ? rb.getString(propname) : null; + + if (n != null) { + names.put(f, n); + functions.put(n, f); + } + } + } + + /** + * Gets the function for the specified name + * + * @param s the string + * @return the function + */ + Function getFunction(String s) { + return (Function) functions.get(s); + } + + /** + * Gets the name for the function + * + * @param f the function + * @return the string + */ + String getName(Function f) { + return (String) names.get(f); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterEqual.java b/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterEqual.java new file mode 100755 index 0000000..798f3d2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterEqual.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class GreaterEqual extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public GreaterEqual() { + } + + public String getSymbol() { + return ">="; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.GREATER_EQUAL; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 5; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterThan.java b/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterThan.java new file mode 100755 index 0000000..0cadd98 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterThan.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class GreaterThan extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public GreaterThan() { + } + + public String getSymbol() { + return ">"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.GREATER_THAN; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 5; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/IntegerValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/IntegerValue.java new file mode 100755 index 0000000..e8e5e20 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/IntegerValue.java @@ -0,0 +1,118 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * A cell reference in a formula + */ +class IntegerValue extends NumberValue implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(IntegerValue.class); + + /** + * The value of this integer + */ + private double value; + + /** + * Flag which indicates whether or not this integer is out range + */ + private final boolean outOfRange; + + /** + * Constructor + */ + public IntegerValue() { + outOfRange = false; + } + + /** + * Constructor for an integer value being read from a string + */ + public IntegerValue(String s) { + try { + value = Integer.parseInt(s); + } catch (NumberFormatException e) { + logger.warn(e, e); + value = 0; + } + + short v = (short) value; + outOfRange = (value != v); + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + value = IntegerHelper.getInt(data[pos], data[pos + 1]); + + return 2; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[3]; + data[0] = Token.INTEGER.getCode(); + + IntegerHelper.getTwoBytes((int) value, data, 1); + + return data; + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Accessor for the out of range flag + * + * @return TRUE if the value is out of range for an excel integer + */ + boolean isOutOfRange() { + return outOfRange; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/LessEqual.java b/datastructures-xslx/src/main/java/jxl/biff/formula/LessEqual.java new file mode 100755 index 0000000..10dea8c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/LessEqual.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class LessEqual extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public LessEqual() { + } + + public String getSymbol() { + return "<="; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.LESS_EQUAL; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 5; + } +} + + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/LessThan.java b/datastructures-xslx/src/main/java/jxl/biff/formula/LessThan.java new file mode 100755 index 0000000..5e11973 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/LessThan.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class LessThan extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public LessThan() { + } + + public String getSymbol() { + return "<"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.LESS_THAN; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 5; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/MemArea.java b/datastructures-xslx/src/main/java/jxl/biff/formula/MemArea.java new file mode 100755 index 0000000..67b9ed0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/MemArea.java @@ -0,0 +1,67 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.IntegerHelper; + +/** + * Indicates that the function doesn't evaluate to a constant reference + */ +class MemArea extends SubExpression { + /** + * Constructor + */ + public MemArea() { + } + + public void getString(StringBuffer buf) { + ParseItem[] subExpression = getSubExpression(); + + if (subExpression.length == 1) { + subExpression[0].getString(buf); + } else if (subExpression.length == 2) { + subExpression[1].getString(buf); + buf.append(':'); + subExpression[0].getString(buf); + } + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + // For mem areas, the first four bytes are not used + setLength(IntegerHelper.getInt(data[pos + 4], data[pos + 5])); + return 6; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/MemFunc.java b/datastructures-xslx/src/main/java/jxl/biff/formula/MemFunc.java new file mode 100755 index 0000000..12c1842 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/MemFunc.java @@ -0,0 +1,46 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * Indicates that the function doesn't evaluate to a constant reference + */ +class MemFunc extends SubExpression { + /** + * Constructor + */ + public MemFunc() { + } + + public void getString(StringBuffer sb) { + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Minus.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Minus.java new file mode 100755 index 0000000..a741512 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Minus.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.common.Logger; + +/** + * Ambiguously defined minus operator, used as a place holder when parsing + * string formulas. At this stage it could be either + * a unary or binary operator - the string parser will deduce which and + * create the appropriate type + */ +class Minus extends StringOperator { + // The logger + private static final Logger logger = Logger.getLogger(StringOperator.class); + + /** + * Constructor + */ + public Minus() { + super(); + } + + /** + * Abstract method which gets the binary version of this operator + */ + Operator getBinaryOperator() { + return new Subtract(); + } + + /** + * Abstract method which gets the unary version of this operator + */ + Operator getUnaryOperator() { + return new UnaryMinus(); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/MissingArg.java b/datastructures-xslx/src/main/java/jxl/biff/formula/MissingArg.java new file mode 100755 index 0000000..8f61caf --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/MissingArg.java @@ -0,0 +1,73 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * Represents a missing argument in an argument list + */ +class MissingArg extends Operand implements ParsedThing { + /** + * Constructor + */ + public MissingArg() { + } + + /** + * Reads the ptg data from the array starting at the specified position. + * A missing argument contains no associated data + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + return 0; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[1]; + data[0] = Token.MISSING_ARG.getCode(); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) { + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Multiply.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Multiply.java new file mode 100755 index 0000000..12a4362 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Multiply.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Multiply extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public Multiply() { + } + + public String getSymbol() { + return "*"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.MULTIPLY; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 3; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Name.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Name.java new file mode 100755 index 0000000..082c3d9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Name.java @@ -0,0 +1,72 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A name operand + */ +class Name extends Operand implements ParsedThing { + /** + * Constructor + */ + public Name() { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + return 6; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[6]; + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) { + buf.append("[Name record not implemented]"); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Sets this formula to invalid + */ + void handleImportedCellReferences() { + setInvalid(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/NameRange.java b/datastructures-xslx/src/main/java/jxl/biff/formula/NameRange.java new file mode 100755 index 0000000..1d8de91 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/NameRange.java @@ -0,0 +1,137 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.IntegerHelper; +import jxl.biff.NameRangeException; +import jxl.biff.WorkbookMethods; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A name operand + */ +class NameRange extends Operand implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NameRange.class); + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The string name + */ + private String name; + + /** + * The index into the name table + */ + private int index; + + /** + * Constructor + */ + public NameRange(WorkbookMethods nt) { + nameTable = nt; + Assert.verify(nameTable != null); + } + + /** + * Constructor when parsing a string via the api + * + * @param nm the name string + * @param nt the name table + */ + public NameRange(String nm, WorkbookMethods nt) throws FormulaException { + name = nm; + nameTable = nt; + + index = nameTable.getNameIndex(name); + + if (index < 0) { + throw new FormulaException(FormulaException.CELL_NAME_NOT_FOUND, name); + } + + index += 1; // indexes are 1-based + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) throws FormulaException { + try { + index = IntegerHelper.getInt(data[pos], data[pos + 1]); + + name = nameTable.getName(index - 1); // ilbl is 1-based + + return 4; + } catch (NameRangeException e) { + throw new FormulaException(FormulaException.CELL_NAME_NOT_FOUND, ""); + } + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[5]; + + data[0] = Token.NAMED_RANGE.getValueCode(); + + if (getParseContext() == ParseContext.DATA_VALIDATION) { + data[0] = Token.NAMED_RANGE.getReferenceCode(); + } + + IntegerHelper.getTwoBytes(index, data, 1); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) { + buf.append(name); + } + + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Flags the formula as invalid + */ + void handleImportedCellReferences() { + setInvalid(); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/NotEqual.java b/datastructures-xslx/src/main/java/jxl/biff/formula/NotEqual.java new file mode 100755 index 0000000..8750c36 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/NotEqual.java @@ -0,0 +1,55 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class NotEqual extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public NotEqual() { + } + + public String getSymbol() { + return "<>"; + } + + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.NOT_EQUAL; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 5; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/NumberValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/NumberValue.java new file mode 100755 index 0000000..4ebfa5d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/NumberValue.java @@ -0,0 +1,34 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A constant numerical value in a formula + */ +abstract class NumberValue extends Operand implements ParsedThing { + protected NumberValue() { + } + + public abstract double getValue(); + + public void getString(StringBuffer buf) { + buf.append(getValue()); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/OpenParentheses.java b/datastructures-xslx/src/main/java/jxl/biff/formula/OpenParentheses.java new file mode 100755 index 0000000..6e709b1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/OpenParentheses.java @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A dummy token used when parsing strings in order to indicate the + * opening of some parenthesees + */ +class OpenParentheses extends StringParseItem { + /** + * Constructor + */ + public OpenParentheses() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Operand.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Operand.java new file mode 100755 index 0000000..a3198c5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Operand.java @@ -0,0 +1,102 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * An operand in the parse tree. Operands represent leaf nodes in the + * tree, and may not have children + * Operands include numerical values, cell references and ranges + */ +abstract class Operand extends ParseItem { + /** + * Constructor + */ + public Operand() { + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + } + + /** + * Default behaviour is to do nothing + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + } +} + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Operator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Operator.java new file mode 100755 index 0000000..77faf2b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Operator.java @@ -0,0 +1,84 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +/** + * An operator is a node in a parse tree. Its children can be other + * operators or operands + * Arithmetic operators and functions are all considered operators + */ +abstract class Operator extends ParseItem { + /** + * The items which this operator manipulates. There will be at most two + */ + private ParseItem[] operands; + + /** + * Constructor + */ + public Operator() { + operands = new ParseItem[0]; + } + + /** + * Tells the operands to use the alternate code + */ + protected void setOperandAlternateCode() { + for (int i = 0; i < operands.length; i++) { + operands[i].setAlternateCode(); + } + } + + /** + * Adds operands to this item + */ + protected void add(ParseItem n) { + n.setParent(this); + + // Grow the array + ParseItem[] newOperands = new ParseItem[operands.length + 1]; + System.arraycopy(operands, 0, newOperands, 0, operands.length); + newOperands[operands.length] = n; + operands = newOperands; + } + + /** + * Gets the operands for this operator from the stack + */ + public abstract void getOperands(Stack s); + + /** + * Gets the operands ie. the children of the node + */ + protected ParseItem[] getOperands() { + return operands; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + abstract int getPrecedence(); + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Parenthesis.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Parenthesis.java new file mode 100755 index 0000000..8083dfb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Parenthesis.java @@ -0,0 +1,179 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +/** + * A cell reference in a formula + */ +class Parenthesis extends Operator implements ParsedThing { + /** + * Constructor + */ + public Parenthesis() { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + return 0; + } + + /** + * Gets the operands for this operator from the stack + */ + public void getOperands(Stack s) { + ParseItem pi = (ParseItem) s.pop(); + + add(pi); + } + + public void getString(StringBuffer buf) { + ParseItem[] operands = getOperands(); + buf.append('('); + operands[0].getString(buf); + buf.append(')'); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + ParseItem[] operands = getOperands(); + operands[0].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[0].columnInserted(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[0].columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[0].rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[0].rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + ParseItem[] operands = getOperands(); + operands[0].handleImportedCellReferences(); + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.PARENTHESIS; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + // Get the data for the operands + ParseItem[] operands = getOperands(); + byte[] data = operands[0].getBytes(); + + // Add on the operator byte + byte[] newdata = new byte[data.length + 1]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = getToken().getCode(); + + return newdata; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 4; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ParseContext.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ParseContext.java new file mode 100755 index 0000000..7f75326 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ParseContext.java @@ -0,0 +1,34 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * Enumeration type for the context in which this formula is being parsed + */ +public class ParseContext { + public static ParseContext DEFAULT = new ParseContext(); + public static ParseContext DATA_VALIDATION = new ParseContext(); + + /** + * Private constructor + */ + private ParseContext() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ParseItem.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ParseItem.java new file mode 100755 index 0000000..a560ecf --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ParseItem.java @@ -0,0 +1,231 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.common.Logger; + +/** + * Abstract base class for an item in a formula parse tree + */ +abstract class ParseItem { + // The logger + private static final Logger logger = Logger.getLogger(ParseItem.class); + + /** + * The parent of this parse item + */ + private ParseItem parent; + + /** + * Volatile flag + */ + private boolean volatileFunction; + + /** + * Indicates that the alternative token code should be used + * + * @deprecated - use the ParseContext now + */ + private boolean alternateCode; + + /** + * Indicates that an alternative token code should be used + */ + private ParseContext parseContext; + + /** + * Indicates whether this tree represents a valid formula or not. If not + * the parser replaces it with a valid one + */ + private boolean valid; + + /** + * Constructor + */ + public ParseItem() { + volatileFunction = false; + alternateCode = false; + valid = true; + parseContext = ParseContext.DEFAULT; + } + + /** + * Called by this class to initialize the parent + */ + protected void setParent(ParseItem p) { + parent = p; + } + + /** + * Sets the volatile flag and ripples all the way up the parse tree + */ + protected void setVolatile() { + volatileFunction = true; + if (parent != null && !parent.isVolatile()) { + parent.setVolatile(); + } + } + + /** + * Sets the invalid flag and ripples all the way up the parse tree + */ + protected final void setInvalid() { + valid = false; + if (parent != null) { + parent.setInvalid(); + } + } + + /** + * Accessor for the volatile function + * + * @return TRUE if the formula is volatile, FALSE otherwise + */ + final boolean isVolatile() { + return volatileFunction; + } + + /** + * Accessor for the volatile function + * + * @return TRUE if the formula is volatile, FALSE otherwise + */ + final boolean isValid() { + return valid; + } + + /** + * Gets the string representation of this item + * + * @param ws the workbook settings + */ + abstract void getString(StringBuffer buf); + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + abstract byte[] getBytes(); + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + abstract void adjustRelativeCellReferences(int colAdjust, int rowAdjust); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + abstract void columnInserted(int sheetIndex, int col, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + abstract void columnRemoved(int sheetIndex, int col, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + abstract void rowInserted(int sheetIndex, int row, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + abstract void rowRemoved(int sheetIndex, int row, boolean currentSheet); + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + */ + abstract void handleImportedCellReferences(); + + /** + * Tells the operands to use the alternate code + * + * @deprecated - use setParseContext now + */ + protected void setAlternateCode() { + alternateCode = true; + } + + /** + * Accessor for the alternate code flag + * + * @return TRUE to use the alternate code, FALSE otherwise + * @deprecated - use setParseContext now + */ + protected final boolean useAlternateCode() { + return alternateCode; + } + + /** + * Accessor for the alternate code flag + * + * @return the parse context + */ + protected final ParseContext getParseContext() { + return parseContext; + } + + /** + * Tells the operands to use the alternate code + * + * @pc the parse context + */ + protected void setParseContext(ParseContext pc) { + parseContext = pc; + } + +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ParsedThing.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ParsedThing.java new file mode 100755 index 0000000..30cba98 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ParsedThing.java @@ -0,0 +1,34 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * An interface for an excel ptg + */ +interface ParsedThing { + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + int read(byte[] data, int pos) throws FormulaException; +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Parser.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Parser.java new file mode 100755 index 0000000..da9ce6e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Parser.java @@ -0,0 +1,113 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * Interface used by the two different types of formula parser + */ +interface Parser { + /** + * Parses the formula + * + * @throws FormulaException if an error occurs + */ + void parse() throws FormulaException; + + /** + * Gets the string version of the formula + * + * @return the formula as a string + */ + String getFormula(); + + /** + * Gets the bytes for the formula. This takes into account any + * token mapping necessary because of shared formulas + * + * @return the bytes in RPN + */ + byte[] getBytes(); + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. + * + * @param colAdjust + * @param rowAdjust + */ + void adjustRelativeCellReferences(int colAdjust, int rowAdjust); + + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param row the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet); + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if the formula is valid import, FALSE otherwise + */ + boolean handleImportedCellReferences(); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Percent.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Percent.java new file mode 100755 index 0000000..da9aecb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Percent.java @@ -0,0 +1,70 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Percent extends UnaryOperator implements ParsedThing { + /** + * Constructor + */ + public Percent() { + } + + public String getSymbol() { + return "%"; + } + + public void getString(StringBuffer buf) { + ParseItem[] operands = getOperands(); + operands[0].getString(buf); + buf.append(getSymbol()); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + ParseItem[] operands = getOperands(); + operands[0].handleImportedCellReferences(); + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.PERCENT; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 5; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Plus.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Plus.java new file mode 100755 index 0000000..f32c781 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Plus.java @@ -0,0 +1,58 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * Ambiguously defined plus operator, used as a place holder when parsing + * string formulas. At this stage it could be either + * a unary or binary operator - the string parser will deduce which and + * create the appropriate type + */ +class Plus extends StringOperator { + /** + * Constructor + */ + public Plus() { + super(); + } + + /** + * Abstract method which gets the binary version of this operator + */ + Operator getBinaryOperator() { + return new Add(); + } + + /** + * Abstract method which gets the unary version of this operator + */ + Operator getUnaryOperator() { + return new UnaryPlus(); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Power.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Power.java new file mode 100755 index 0000000..ef7c01a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Power.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Power extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public Power() { + } + + public String getSymbol() { + return "^"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.POWER; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 1; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/RangeSeparator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/RangeSeparator.java new file mode 100755 index 0000000..5514862 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/RangeSeparator.java @@ -0,0 +1,81 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.IntegerHelper; + +/** + * A "holding" token for a range separator. This token gets instantiated + * when the lexical analyzer can't distinguish a range cleanly, eg in the + * case where where one of the identifiers of the range is a formula + */ +class RangeSeparator extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public RangeSeparator() { + } + + public String getSymbol() { + return ":"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.RANGE; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 1; + } + + /** + * Overrides the getBytes() method in the base class and prepends the + * memFunc token + * + * @return the bytes + */ + byte[] getBytes() { + setVolatile(); + setOperandAlternateCode(); + + byte[] funcBytes = super.getBytes(); + + byte[] bytes = new byte[funcBytes.length + 3]; + System.arraycopy(funcBytes, 0, bytes, 3, funcBytes.length); + + // Indicate the mem func + bytes[0] = Token.MEM_FUNC.getCode(); + IntegerHelper.getTwoBytes(funcBytes.length, bytes, 1); + + return bytes; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaArea.java b/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaArea.java new file mode 100755 index 0000000..cfe9bf6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaArea.java @@ -0,0 +1,152 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.Cell; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; + +/** + * A cell reference in a formula + */ +class SharedFormulaArea extends Operand implements ParsedThing { + private int columnFirst; + private int rowFirst; + private int columnLast; + private int rowLast; + + private boolean columnFirstRelative; + private boolean rowFirstRelative; + private boolean columnLastRelative; + private boolean rowLastRelative; + + /** + * The cell containing the formula. Stored in order to determine + * relative cell values + */ + private final Cell relativeTo; + + /** + * Constructor + * + * @param the cell the formula is relative to + */ + public SharedFormulaArea(Cell rt) { + relativeTo = rt; + } + + int getFirstColumn() { + return columnFirst; + } + + int getFirstRow() { + return rowFirst; + } + + int getLastColumn() { + return columnLast; + } + + int getLastRow() { + return rowLast; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + // Preserve signage on column and row values, because they will + // probably be relative + + rowFirst = IntegerHelper.getShort(data[pos], data[pos + 1]); + rowLast = IntegerHelper.getShort(data[pos + 2], data[pos + 3]); + + int columnMask = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + columnFirst = columnMask & 0x00ff; + columnFirstRelative = ((columnMask & 0x4000) != 0); + rowFirstRelative = ((columnMask & 0x8000) != 0); + + if (columnFirstRelative) { + columnFirst = relativeTo.getColumn() + columnFirst; + } + + if (rowFirstRelative) { + rowFirst = relativeTo.getRow() + rowFirst; + } + + columnMask = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + columnLast = columnMask & 0x00ff; + + columnLastRelative = ((columnMask & 0x4000) != 0); + rowLastRelative = ((columnMask & 0x8000) != 0); + + if (columnLastRelative) { + columnLast = relativeTo.getColumn() + columnLast; + } + + if (rowLastRelative) { + rowLast = relativeTo.getRow() + rowLast; + } + + + return 8; + } + + public void getString(StringBuffer buf) { + CellReferenceHelper.getCellReference(columnFirst, rowFirst, buf); + buf.append(':'); + CellReferenceHelper.getCellReference(columnLast, rowLast, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[9]; + data[0] = Token.AREA.getCode(); + + // Use absolute references for columns, so don't bother about + // the col relative/row relative bits + IntegerHelper.getTwoBytes(rowFirst, data, 1); + IntegerHelper.getTwoBytes(rowLast, data, 3); + IntegerHelper.getTwoBytes(columnFirst, data, 5); + IntegerHelper.getTwoBytes(columnLast, data, 7); + + return data; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } + +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaCellReference.java b/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaCellReference.java new file mode 100755 index 0000000..b6f2755 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaCellReference.java @@ -0,0 +1,155 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.Cell; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + + +/** + * A cell reference in a formula + */ +class SharedFormulaCellReference extends Operand implements ParsedThing { + // The logger + private static final Logger logger = + Logger.getLogger(SharedFormulaCellReference.class); + + /** + * Indicates whether the column reference is relative or absolute + */ + private boolean columnRelative; + + /** + * Indicates whether the row reference is relative or absolute + */ + private boolean rowRelative; + + /** + * The column reference + */ + private int column; + + /** + * The row reference + */ + private int row; + + /** + * The cell containing the formula. Stored in order to determine + * relative cell values + */ + private final Cell relativeTo; + + /** + * Constructor + * + * @param the cell the formula is relative to + */ + public SharedFormulaCellReference(Cell rt) { + relativeTo = rt; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + // Preserve signage on column and row values, because they will + // probably be relative + row = IntegerHelper.getShort(data[pos], data[pos + 1]); + + int columnMask = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + + column = (byte) (columnMask & 0xff); + columnRelative = ((columnMask & 0x4000) != 0); + rowRelative = ((columnMask & 0x8000) != 0); + + if (columnRelative && relativeTo != null) { + column = relativeTo.getColumn() + column; + } + + if (rowRelative && relativeTo != null) { + row = relativeTo.getRow() + row; + } + + return 4; + } + + public int getColumn() { + return column; + } + + public int getRow() { + return row; + } + + public void getString(StringBuffer buf) { + CellReferenceHelper.getCellReference(column, row, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[5]; + data[0] = Token.REF.getCode(); + + IntegerHelper.getTwoBytes(row, data, 1); + + int columnMask = column; + + if (columnRelative) { + columnMask |= 0x4000; + } + + if (rowRelative) { + columnMask |= 0x8000; + } + + IntegerHelper.getTwoBytes(columnMask, data, 3); + + return data; + } + + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringFormulaParser.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringFormulaParser.java new file mode 100755 index 0000000..10e3116 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringFormulaParser.java @@ -0,0 +1,501 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Stack; +import jxl.WorkbookSettings; +import jxl.biff.WorkbookMethods; +import jxl.common.Logger; + +/** + * Parses a string formula into a parse tree + */ +class StringFormulaParser implements Parser { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(StringFormulaParser.class); + + /** + * The formula string passed to this object + */ + private final String formula; + + /** + * The parsed formula string, as retrieved from the parse tree + */ + private String parsedFormula; + + /** + * The parse tree + */ + private ParseItem root; + + /** + * The stack argument used when parsing a function in order to + * pass multiple arguments back to the calling method + */ + private Stack arguments; + + /** + * The workbook settings + */ + private final WorkbookSettings settings; + + /** + * A handle to the external sheet + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The parse context + */ + private final ParseContext parseContext; + + /** + * Constructor + * + * @param f + * @param ws + */ + public StringFormulaParser(String f, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws, + ParseContext pc) { + formula = f; + settings = ws; + externalSheet = es; + nameTable = nt; + parseContext = pc; + } + + /** + * Parses the list of tokens + * + * @throws FormulaException + */ + public void parse() throws FormulaException { + ArrayList tokens = getTokens(); + + Iterator i = tokens.iterator(); + + root = parseCurrent(i); + } + + /** + * Recursively parses the token array. Recursion is used in order + * to evaluate parentheses and function arguments + * + * @param i an iterator of tokens + * @return the root node of the current parse stack + * @throws FormulaException if an error occurs + */ + private ParseItem parseCurrent(Iterator i) throws FormulaException { + Stack stack = new Stack(); + Stack operators = new Stack(); + Stack args = null; // we usually don't need this + + boolean parenthesesClosed = false; + ParseItem lastParseItem = null; + + while (i.hasNext() && !parenthesesClosed) { + ParseItem pi = (ParseItem) i.next(); + pi.setParseContext(parseContext); + + if (pi instanceof Operand) { + handleOperand((Operand) pi, stack); + } else if (pi instanceof StringFunction) { + handleFunction((StringFunction) pi, i, stack); + } else if (pi instanceof Operator) { + Operator op = (Operator) pi; + + // See if the operator is a binary or unary operator + // It is a unary operator either if the stack is empty, or if + // the last thing off the stack was another operator + if (op instanceof StringOperator) { + StringOperator sop = (StringOperator) op; + if (stack.isEmpty() || lastParseItem instanceof Operator) { + op = sop.getUnaryOperator(); + } else { + op = sop.getBinaryOperator(); + } + } + + if (operators.empty()) { + // nothing much going on, so do nothing for the time being + operators.push(op); + } else { + Operator operator = (Operator) operators.peek(); + + // If the last operator has a higher precedence then add this to + // the operator stack and wait + if (op.getPrecedence() < operator.getPrecedence()) { + operators.push(op); + } else if (op.getPrecedence() == operator.getPrecedence() && + op instanceof UnaryOperator) { + // The operators are of equal precedence, but because it is a + // unary operator the operand isn't available yet, so put it on + // the stack + operators.push(op); + } else { + // The operator is of a lower precedence so we can sort out + // some of the items on the stack + operators.pop(); // remove the operator from the stack + operator.getOperands(stack); + stack.push(operator); + operators.push(op); + } + } + } else if (pi instanceof ArgumentSeparator) { + // Clean up any remaining items on this stack + while (!operators.isEmpty()) { + Operator o = (Operator) operators.pop(); + o.getOperands(stack); + stack.push(o); + } + + // Add it to the argument stack. Create the argument stack + // if necessary. Items will be stored on the argument stack in + // reverse order + if (args == null) { + args = new Stack(); + } + + args.push(stack.pop()); + stack.clear(); + } else if (pi instanceof OpenParentheses) { + ParseItem pi2 = parseCurrent(i); + Parenthesis p = new Parenthesis(); + pi2.setParent(p); + p.add(pi2); + stack.push(p); + } else if (pi instanceof CloseParentheses) { + parenthesesClosed = true; + } + + lastParseItem = pi; + } + + while (!operators.isEmpty()) { + Operator o = (Operator) operators.pop(); + o.getOperands(stack); + stack.push(o); + } + + ParseItem rt = !stack.empty() ? (ParseItem) stack.pop() : null; + + // if the argument stack is not null, then add it to that stack + // as well for good measure + if (args != null && rt != null) { + args.push(rt); + } + + arguments = args; + + if (!stack.empty() || !operators.empty()) { + logger.warn("Formula " + formula + + " has a non-empty parse stack"); + } + + return rt; + } + + /** + * Gets the list of lexical tokens using the generated lexical analyzer + * + * @return the list of tokens + * @throws FormulaException if an error occurs + */ + private ArrayList getTokens() throws FormulaException { + ArrayList tokens = new ArrayList(); + + StringReader sr = new StringReader(formula); + Yylex lex = new Yylex(sr); + lex.setExternalSheet(externalSheet); + lex.setNameTable(nameTable); + try { + ParseItem pi = lex.yylex(); + while (pi != null) { + tokens.add(pi); + pi = lex.yylex(); + } + } catch (IOException e) { + logger.warn(e.toString()); + } catch (Error e) { + throw new FormulaException(FormulaException.LEXICAL_ERROR, + formula + " at char " + lex.getPos()); + } + + return tokens; + } + + /** + * Gets the formula as a string. Uses the parse tree to do this, and + * does not simply return whatever string was passed in + */ + public String getFormula() { + if (parsedFormula == null) { + StringBuffer sb = new StringBuffer(); + root.getString(sb); + parsedFormula = sb.toString(); + } + + return parsedFormula; + } + + /** + * Gets the bytes for the formula + * + * @return the bytes in RPN + */ + public byte[] getBytes() { + byte[] bytes = root.getBytes(); + + if (root.isVolatile()) { + byte[] newBytes = new byte[bytes.length + 4]; + System.arraycopy(bytes, 0, newBytes, 4, bytes.length); + newBytes[0] = Token.ATTRIBUTE.getCode(); + newBytes[1] = (byte) 0x1; + bytes = newBytes; + } + + return bytes; + } + + /** + * Handles the case when parsing a string when a token is a function + * + * @param sf the string function + * @param i the token iterator + * @param stack the parse tree stack + * @throws FormulaException if an error occurs + */ + private void handleFunction(StringFunction sf, Iterator i, + Stack stack) + throws FormulaException { + ParseItem pi2 = parseCurrent(i); + + // If the function is unknown, then throw an error + if (sf.getFunction(settings) == Function.UNKNOWN) { + throw new FormulaException(FormulaException.UNRECOGNIZED_FUNCTION); + } + + // First check for possible optimized functions and possible + // use of the Attribute token + if (sf.getFunction(settings) == Function.SUM && arguments == null) { + // this is handled by an attribute + Attribute a = new Attribute(sf, settings); + a.add(pi2); + stack.push(a); + return; + } + + if (sf.getFunction(settings) == Function.IF) { + // this is handled by an attribute + Attribute a = new Attribute(sf, settings); + + // Add in the if conditions as a var arg function in + // the correct order + VariableArgFunction vaf = new VariableArgFunction(settings); + int numargs = arguments.size(); + for (int j = 0; j < numargs; j++) { + ParseItem pi3 = (ParseItem) arguments.get(j); + vaf.add(pi3); + } + + a.setIfConditions(vaf); + stack.push(a); + return; + } + + // Function cannot be optimized. See if it is a variable argument + // function or not + if (sf.getFunction(settings).getNumArgs() == 0xff) { + // If the arg stack has not been initialized, it means + // that there was only one argument, which is the + // returned parse item + if (arguments == null) { + int numArgs = pi2 != null ? 1 : 0; + VariableArgFunction vaf = new VariableArgFunction + (sf.getFunction(settings), numArgs, settings); + + if (pi2 != null) { + vaf.add(pi2); + } + + stack.push(vaf); + } else { + // Add the args to the function in the correct order + int numargs = arguments.size(); + VariableArgFunction vaf = new VariableArgFunction + (sf.getFunction(settings), numargs, settings); + + ParseItem[] args = new ParseItem[numargs]; + for (int j = 0; j < numargs; j++) { + ParseItem pi3 = (ParseItem) arguments.pop(); + args[numargs - j - 1] = pi3; + } + + for (int j = 0; j < args.length; j++) { + vaf.add(args[j]); + } + stack.push(vaf); + arguments.clear(); + arguments = null; + } + return; + } + + // Function is a standard built in function + BuiltInFunction bif = new BuiltInFunction(sf.getFunction(settings), + settings); + + int numargs = sf.getFunction(settings).getNumArgs(); + if (numargs == 1) { + // only one item which is the returned ParseItem + bif.add(pi2); + } else { + if ((arguments == null && numargs != 0) || + (arguments != null && numargs != arguments.size())) { + throw new FormulaException(FormulaException.INCORRECT_ARGUMENTS); + } + // multiple arguments so go to the arguments stack. + // Unlike the variable argument function, the args are + // stored in reverse order + for (int j = 0; j < numargs; j++) { + ParseItem pi3 = (ParseItem) arguments.get(j); + bif.add(pi3); + } + } + stack.push(bif); + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + root.adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) { + root.columnInserted(sheetIndex, col, currentSheet); + } + + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + root.columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowInserted(int sheetIndex, int row, boolean currentSheet) { + root.rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param row the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + root.rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * Handles operands by pushing them onto the stack + * + * @param o operand + * @param stack stack + */ + private void handleOperand(Operand o, Stack stack) { + if (!(o instanceof IntegerValue)) { + stack.push(o); + return; + } + + if (o instanceof IntegerValue) { + IntegerValue iv = (IntegerValue) o; + if (!iv.isOutOfRange()) { + stack.push(iv); + } else { + // convert to a double + DoubleValue dv = new DoubleValue(iv.getValue()); + stack.push(dv); + } + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if the formula is valid import, FALSE otherwise + */ + public boolean handleImportedCellReferences() { + root.handleImportedCellReferences(); + return root.isValid(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringFunction.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringFunction.java new file mode 100755 index 0000000..1eb2feb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringFunction.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.WorkbookSettings; +import jxl.common.Logger; + +/** + * Class used to hold a function when reading it in from a string. At this + * stage it is unknown whether it is a BuiltInFunction or a VariableArgFunction + */ +class StringFunction extends StringParseItem { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(StringFunction.class); + + /** + * The function + */ + private Function function; + + /** + * The function string + */ + private final String functionString; + + /** + * Constructor + * + * @param s the lexically parsed stirng + */ + StringFunction(String s) { + functionString = s.substring(0, s.length() - 1); + } + + /** + * Accessor for the function + * + * @param ws the workbook settings + * @return the function + */ + Function getFunction(WorkbookSettings ws) { + if (function == null) { + function = Function.getFunction(functionString, ws); + } + return function; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringOperator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringOperator.java new file mode 100755 index 0000000..f2da274 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringOperator.java @@ -0,0 +1,148 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; +import jxl.common.Assert; + +/** + * Ambiguously defined operator, used as a place holder when parsing + * string formulas. At this stage it could be either + * a unary or binary operator - the string parser will deduce which and + * create the appropriate type + */ +abstract class StringOperator extends Operator { + /** + * Constructor + */ + protected StringOperator() { + super(); + } + + /** + * Gets the operands for this operator from the stack. Does nothing + * here + */ + public void getOperands(Stack s) { + Assert.verify(false); + } + + /** + * Gets the precedence for this operator. Does nothing here + * + * @return the operator precedence + */ + int getPrecedence() { + Assert.verify(false); + return 0; + } + + /** + * Gets the token representation of this item in RPN. Does nothing here + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + Assert.verify(false); + return null; + } + + /** + * Gets the string representation of this item + */ + void getString(StringBuffer buf) { + Assert.verify(false); + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + Assert.verify(false); + } + + + /** + * Default behaviour is to do nothing + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + Assert.verify(false); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + Assert.verify(false); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + Assert.verify(false); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + Assert.verify(false); + } + + /** + * Abstract method which gets the binary version of this operator + */ + abstract Operator getBinaryOperator(); + + /** + * Abstract method which gets the unary version of this operator + */ + abstract Operator getUnaryOperator(); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringParseItem.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringParseItem.java new file mode 100755 index 0000000..a64cb8d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringParseItem.java @@ -0,0 +1,118 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A dummy implementation used for typing information when tokens + * are read when parsing strings. These are then stored by the parser before + * being re-stored as the appropriate RPN syntactic equivalent + */ +class StringParseItem extends ParseItem { + /** + * Constructor + */ + protected StringParseItem() { + } + + /** + * Gets the string representation of this item. Does nothing here + * + * @param buf + */ + void getString(StringBuffer buf) { + } + + /** + * Gets the token representation of this item in RPN. Does nothing here + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + return new byte[0]; + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + } + + /** + * Default behaviour is to do nothing + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringValue.java new file mode 100755 index 0000000..61edacc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringValue.java @@ -0,0 +1,121 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import jxl.WorkbookSettings; +import jxl.biff.StringHelper; +import jxl.common.Logger; + + +/** + * A string constant operand in a formula + */ +class StringValue extends Operand implements ParsedThing { + /** + * The logger + */ + private final static Logger logger = Logger.getLogger(StringValue.class); + + /** + * The string value + */ + private String value; + + /** + * The workbook settings + */ + private WorkbookSettings settings; + + /** + * Constructor + */ + public StringValue(WorkbookSettings ws) { + settings = ws; + } + + /** + * Constructor used when parsing a string formula + * + * @param s the string token, including quote marks + */ + public StringValue(String s) { + // remove the quotes + value = s; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + int length = data[pos] & 0xff; + int consumed = 2; + + if ((data[pos + 1] & 0x01) == 0) { + value = StringHelper.getString(data, length, pos + 2, settings); + consumed += length; + } else { + value = StringHelper.getUnicodeString(data, length, pos + 2); + consumed += length * 2; + } + + return consumed; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + byte[] data = new byte[value.length() * 2 + 3]; + data[0] = Token.STRING.getCode(); + data[1] = (byte) (value.length()); + data[2] = 0x01; + StringHelper.getUnicodeBytes(value, data, 3); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) { + buf.append("\""); + buf.append(value); + buf.append("\""); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() { + } + +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/SubExpression.java b/datastructures-xslx/src/main/java/jxl/biff/formula/SubExpression.java new file mode 100755 index 0000000..4260dac --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/SubExpression.java @@ -0,0 +1,105 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; +import jxl.biff.IntegerHelper; + +/** + * Base class for those tokens which encapsulate a subexpression + */ +abstract class SubExpression extends Operand implements ParsedThing { + /** + * The number of bytes in the subexpression + */ + private int length; + + /** + * The sub expression + */ + private ParseItem[] subExpression; + + /** + * Constructor + */ + protected SubExpression() { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + length = IntegerHelper.getInt(data[pos], data[pos + 1]); + return 2; + } + + /** + * Gets the operands for this operator from the stack + */ + public void getOperands(Stack s) { + } + + /** + * Gets the token representation of this item in RPN. The Attribute + * token is a special case, which overrides anything useful we could do + * in the base class + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + return null; + } + + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 5; + } + + /** + * Accessor for the length + * + * @return the length of the subexpression + */ + public int getLength() { + return length; + } + + protected final void setLength(int l) { + length = l; + } + + protected ParseItem[] getSubExpression() { + return subExpression; + } + + public void setSubExpression(ParseItem[] pi) { + subExpression = pi; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Subtract.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Subtract.java new file mode 100755 index 0000000..0d60f9c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Subtract.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Subtract extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public Subtract() { + } + + public String getSymbol() { + return "-"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.SUBTRACT; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 4; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Token.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Token.java new file mode 100755 index 0000000..a87dfb2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Token.java @@ -0,0 +1,197 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.HashMap; + +/** + * An enumeration detailing the Excel parsed tokens + * A particular token may be associated with more than one token code + */ +class Token { + // Operands + public static final Token REF = new Token(0x44, 0x24, 0x64); + public static final Token REF3D = new Token(0x5a, 0x3a, 0x7a); + public static final Token MISSING_ARG = new Token(0x16); + public static final Token STRING = new Token(0x17); + public static final Token ERR = new Token(0x1c); + public static final Token BOOL = new Token(0x1d); + public static final Token INTEGER = new Token(0x1e); + public static final Token DOUBLE = new Token(0x1f); + public static final Token REFERR = new Token(0x2a, 0x4a, 0x6a); + public static final Token REFV = new Token(0x2c, 0x4c, 0x6c); + public static final Token AREAV = new Token(0x2d, 0x4d, 0x6d); + public static final Token MEM_AREA = new Token(0x26, 0x46, 0x66); + public static final Token AREA = new Token(0x25, 0x65, 0x45); + public static final Token NAMED_RANGE = new Token(0x23, 0x43, 0x63); + //need 0x23 for data validation references + public static final Token NAME = new Token(0x39, 0x59); + public static final Token AREA3D = new Token(0x3b, 0x5b); + // Unary Operators + public static final Token UNARY_PLUS = new Token(0x12); + public static final Token UNARY_MINUS = new Token(0x13); + public static final Token PERCENT = new Token(0x14); + public static final Token PARENTHESIS = new Token(0x15); + // Binary Operators + public static final Token ADD = new Token(0x3); + public static final Token SUBTRACT = new Token(0x4); + public static final Token MULTIPLY = new Token(0x5); + public static final Token DIVIDE = new Token(0x6); + public static final Token POWER = new Token(0x7); + public static final Token CONCAT = new Token(0x8); + public static final Token LESS_THAN = new Token(0x9); + public static final Token LESS_EQUAL = new Token(0xa); + public static final Token EQUAL = new Token(0xb); + public static final Token GREATER_EQUAL = new Token(0xc); + public static final Token GREATER_THAN = new Token(0xd); + public static final Token NOT_EQUAL = new Token(0xe); + public static final Token UNION = new Token(0x10); + public static final Token RANGE = new Token(0x11); + // Functions + public static final Token FUNCTION = new Token(0x41, 0x21, 0x61); + public static final Token FUNCTIONVARARG = new Token(0x42, 0x22, 0x62); + // Control + public static final Token ATTRIBUTE = new Token(0x19); + public static final Token MEM_FUNC = new Token(0x29, 0x49, 0x69); + // Unknown token + public static final Token UNKNOWN = new Token(0xffff); + /** + * All available tokens, keyed on value + */ + private static final HashMap tokens = new HashMap(20); + /** + * The array of values which apply to this token + */ + public final int[] value; + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v) { + value = new int[]{v}; + + tokens.put(new Integer(v), this); + } + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v1, int v2) { + value = new int[]{v1, v2}; + + tokens.put(new Integer(v1), this); + tokens.put(new Integer(v2), this); + } + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v1, int v2, int v3) { + value = new int[]{v1, v2, v3}; + + tokens.put(new Integer(v1), this); + tokens.put(new Integer(v2), this); + tokens.put(new Integer(v3), this); + } + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v1, int v2, int v3, int v4) { + value = new int[]{v1, v2, v3, v4}; + + tokens.put(new Integer(v1), this); + tokens.put(new Integer(v2), this); + tokens.put(new Integer(v3), this); + tokens.put(new Integer(v4), this); + } + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v1, int v2, int v3, int v4, int v5) { + value = new int[]{v1, v2, v3, v4, v5}; + + tokens.put(new Integer(v1), this); + tokens.put(new Integer(v2), this); + tokens.put(new Integer(v3), this); + tokens.put(new Integer(v4), this); + tokens.put(new Integer(v5), this); + } + + /** + * Gets the type object from its integer value + */ + public static Token getToken(int v) { + Token t = (Token) tokens.get(new Integer(v)); + + return t != null ? t : UNKNOWN; + } + + /** + * Gets the token code for the specified token + * + * @return the token code. This is the first item in the array + */ + public byte getCode() { + return (byte) value[0]; + } + + /** + * Gets the reference token code for the specified token. This is always + * the first on the list + * + * @return the token code. This is the first item in the array + */ + public byte getReferenceCode() { + return (byte) value[0]; + } + + /** + * Gets the an alternative token code for the specified token + * Used for certain types of volatile function + * + * @return the token code + */ + public byte getCode2() { + return (byte) (value.length > 0 ? value[1] : value[0]); + } + + /** + * Gets the value token code for the specified token. This is always + * the second item on the list + * + * @return the token code + */ + public byte getValueCode() { + return (byte) (value.length > 0 ? value[1] : value[0]); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/TokenFormulaParser.java b/datastructures-xslx/src/main/java/jxl/biff/formula/TokenFormulaParser.java new file mode 100755 index 0000000..03e09a0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/TokenFormulaParser.java @@ -0,0 +1,474 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; +import jxl.Cell; +import jxl.WorkbookSettings; +import jxl.biff.WorkbookMethods; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Parses the excel ptgs into a parse tree + */ +class TokenFormulaParser implements Parser { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(TokenFormulaParser.class); + + /** + * The Excel ptgs + */ + private final byte[] tokenData; + + /** + * The cell containing the formula. This is used in order to determine + * relative cell values + */ + private final Cell relativeTo; + + /** + * The current position within the array + */ + private int pos; + + /** + * The parse tree + */ + private ParseItem root; + + /** + * The hash table of items that have been parsed + */ + private Stack tokenStack; + + /** + * A reference to the workbook which holds the external sheet + * information + */ + private final ExternalSheet workbook; + + /** + * A reference to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The workbook settings + */ + private final WorkbookSettings settings; + + /** + * The parse context + */ + private final ParseContext parseContext; + + /** + * Constructor + */ + public TokenFormulaParser(byte[] data, + Cell c, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws, + ParseContext pc) { + tokenData = data; + pos = 0; + relativeTo = c; + workbook = es; + nameTable = nt; + tokenStack = new Stack(); + settings = ws; + parseContext = pc; + + Assert.verify(nameTable != null); + } + + /** + * Parses the sublist of tokens. In most cases this will equate to + * the full list + * + * @throws FormulaException + */ + public void parse() throws FormulaException { + parseSubExpression(tokenData.length); + + // Finally, there should be one thing left on the stack. Get that + // and add it to the root node + root = (ParseItem) tokenStack.pop(); + + Assert.verify(tokenStack.empty()); + + } + + /** + * Parses the sublist of tokens. In most cases this will equate to + * the full list + * + * @param len the length of the subexpression to parse + * @throws FormulaException + */ + private void parseSubExpression(int len) throws FormulaException { + int tokenVal = 0; + Token t = null; + + // Indicates that we are parsing the incredibly complicated and + // hacky if construct that MS saw fit to include, the gits + Stack ifStack = new Stack(); + + // The end position of the sub-expression + int endpos = pos + len; + + while (pos < endpos) { + tokenVal = tokenData[pos]; + pos++; + + t = Token.getToken(tokenVal); + + if (t == Token.UNKNOWN) { + throw new FormulaException + (FormulaException.UNRECOGNIZED_TOKEN, tokenVal); + } + + Assert.verify(t != Token.UNKNOWN); + + // Operands + if (t == Token.REF) { + CellReference cr = new CellReference(relativeTo); + pos += cr.read(tokenData, pos); + tokenStack.push(cr); + } else if (t == Token.REFERR) { + CellReferenceError cr = new CellReferenceError(); + pos += cr.read(tokenData, pos); + tokenStack.push(cr); + } else if (t == Token.ERR) { + ErrorConstant ec = new ErrorConstant(); + pos += ec.read(tokenData, pos); + tokenStack.push(ec); + } else if (t == Token.REFV) { + SharedFormulaCellReference cr = + new SharedFormulaCellReference(relativeTo); + pos += cr.read(tokenData, pos); + tokenStack.push(cr); + } else if (t == Token.REF3D) { + CellReference3d cr = new CellReference3d(relativeTo, workbook); + pos += cr.read(tokenData, pos); + tokenStack.push(cr); + } else if (t == Token.AREA) { + Area a = new Area(); + pos += a.read(tokenData, pos); + tokenStack.push(a); + } else if (t == Token.AREAV) { + SharedFormulaArea a = new SharedFormulaArea(relativeTo); + pos += a.read(tokenData, pos); + tokenStack.push(a); + } else if (t == Token.AREA3D) { + Area3d a = new Area3d(workbook); + pos += a.read(tokenData, pos); + tokenStack.push(a); + } else if (t == Token.NAME) { + Name n = new Name(); + pos += n.read(tokenData, pos); + n.setParseContext(parseContext); + tokenStack.push(n); + } else if (t == Token.NAMED_RANGE) { + NameRange nr = new NameRange(nameTable); + pos += nr.read(tokenData, pos); + nr.setParseContext(parseContext); + tokenStack.push(nr); + } else if (t == Token.INTEGER) { + IntegerValue i = new IntegerValue(); + pos += i.read(tokenData, pos); + tokenStack.push(i); + } else if (t == Token.DOUBLE) { + DoubleValue d = new DoubleValue(); + pos += d.read(tokenData, pos); + tokenStack.push(d); + } else if (t == Token.BOOL) { + BooleanValue bv = new BooleanValue(); + pos += bv.read(tokenData, pos); + tokenStack.push(bv); + } else if (t == Token.STRING) { + StringValue sv = new StringValue(settings); + pos += sv.read(tokenData, pos); + tokenStack.push(sv); + } else if (t == Token.MISSING_ARG) { + MissingArg ma = new MissingArg(); + pos += ma.read(tokenData, pos); + tokenStack.push(ma); + } + + // Unary Operators + else if (t == Token.UNARY_PLUS) { + UnaryPlus up = new UnaryPlus(); + pos += up.read(tokenData, pos); + addOperator(up); + } else if (t == Token.UNARY_MINUS) { + UnaryMinus um = new UnaryMinus(); + pos += um.read(tokenData, pos); + addOperator(um); + } else if (t == Token.PERCENT) { + Percent p = new Percent(); + pos += p.read(tokenData, pos); + addOperator(p); + } + + // Binary Operators + else if (t == Token.SUBTRACT) { + Subtract s = new Subtract(); + pos += s.read(tokenData, pos); + addOperator(s); + } else if (t == Token.ADD) { + Add s = new Add(); + pos += s.read(tokenData, pos); + addOperator(s); + } else if (t == Token.MULTIPLY) { + Multiply s = new Multiply(); + pos += s.read(tokenData, pos); + addOperator(s); + } else if (t == Token.DIVIDE) { + Divide s = new Divide(); + pos += s.read(tokenData, pos); + addOperator(s); + } else if (t == Token.CONCAT) { + Concatenate c = new Concatenate(); + pos += c.read(tokenData, pos); + addOperator(c); + } else if (t == Token.POWER) { + Power p = new Power(); + pos += p.read(tokenData, pos); + addOperator(p); + } else if (t == Token.LESS_THAN) { + LessThan lt = new LessThan(); + pos += lt.read(tokenData, pos); + addOperator(lt); + } else if (t == Token.LESS_EQUAL) { + LessEqual lte = new LessEqual(); + pos += lte.read(tokenData, pos); + addOperator(lte); + } else if (t == Token.GREATER_THAN) { + GreaterThan gt = new GreaterThan(); + pos += gt.read(tokenData, pos); + addOperator(gt); + } else if (t == Token.GREATER_EQUAL) { + GreaterEqual gte = new GreaterEqual(); + pos += gte.read(tokenData, pos); + addOperator(gte); + } else if (t == Token.NOT_EQUAL) { + NotEqual ne = new NotEqual(); + pos += ne.read(tokenData, pos); + addOperator(ne); + } else if (t == Token.EQUAL) { + Equal e = new Equal(); + pos += e.read(tokenData, pos); + addOperator(e); + } else if (t == Token.PARENTHESIS) { + Parenthesis p = new Parenthesis(); + pos += p.read(tokenData, pos); + addOperator(p); + } + + // Functions + else if (t == Token.ATTRIBUTE) { + Attribute a = new Attribute(settings); + pos += a.read(tokenData, pos); + + if (a.isSum()) { + addOperator(a); + } else if (a.isIf()) { + // Add it to a special stack for ifs + ifStack.push(a); + } + } else if (t == Token.FUNCTION) { + BuiltInFunction bif = new BuiltInFunction(settings); + pos += bif.read(tokenData, pos); + + addOperator(bif); + } else if (t == Token.FUNCTIONVARARG) { + VariableArgFunction vaf = new VariableArgFunction(settings); + pos += vaf.read(tokenData, pos); + + if (vaf.getFunction() != Function.ATTRIBUTE) { + addOperator(vaf); + } else { + // This is part of an IF function. Get the operands, but then + // add it to the top of the if stack + vaf.getOperands(tokenStack); + + Attribute ifattr = null; + if (ifStack.empty()) { + ifattr = new Attribute(settings); + } else { + ifattr = (Attribute) ifStack.pop(); + } + + ifattr.setIfConditions(vaf); + tokenStack.push(ifattr); + } + } + + // Other things + else if (t == Token.MEM_FUNC) { + MemFunc memFunc = new MemFunc(); + handleMemoryFunction(memFunc); + } else if (t == Token.MEM_AREA) { + MemArea memArea = new MemArea(); + handleMemoryFunction(memArea); + } + } + } + + /** + * Handles a memory function + */ + private void handleMemoryFunction(SubExpression subxp) + throws FormulaException { + pos += subxp.read(tokenData, pos); + + // Create new tokenStack for the sub expression + Stack oldStack = tokenStack; + tokenStack = new Stack(); + + parseSubExpression(subxp.getLength()); + + ParseItem[] subexpr = new ParseItem[tokenStack.size()]; + int i = 0; + while (!tokenStack.isEmpty()) { + subexpr[i] = (ParseItem) tokenStack.pop(); + i++; + } + + subxp.setSubExpression(subexpr); + + tokenStack = oldStack; + tokenStack.push(subxp); + } + + /** + * Adds the specified operator to the parse tree, taking operands off + * the stack as appropriate + */ + private void addOperator(Operator o) { + // Get the operands off the stack + o.getOperands(tokenStack); + + // Add this operator onto the stack + tokenStack.push(o); + } + + /** + * Gets the formula as a string + */ + public String getFormula() { + StringBuffer sb = new StringBuffer(); + root.getString(sb); + return sb.toString(); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + root.adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Gets the bytes for the formula. This takes into account any + * token mapping necessary because of shared formulas + * + * @return the bytes in RPN + */ + public byte[] getBytes() { + return root.getBytes(); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) { + root.columnInserted(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + root.columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowInserted(int sheetIndex, int row, boolean currentSheet) { + root.rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param row the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + root.rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if the formula is valid import, FALSE otherwise + */ + public boolean handleImportedCellReferences() { + root.handleImportedCellReferences(); + return root.isValid(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryMinus.java b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryMinus.java new file mode 100755 index 0000000..c57f628 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryMinus.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class UnaryMinus extends UnaryOperator implements ParsedThing { + /** + * Constructor + */ + public UnaryMinus() { + } + + public String getSymbol() { + return "-"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.UNARY_MINUS; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 2; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryOperator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryOperator.java new file mode 100755 index 0000000..9e2d144 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryOperator.java @@ -0,0 +1,180 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +/** + * A cell reference in a formula + */ +abstract class UnaryOperator extends Operator implements ParsedThing { + /** + * Constructor + */ + public UnaryOperator() { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) { + return 0; + } + + /** + * Gets the operands for this operator from the stack + */ + public void getOperands(Stack s) { + ParseItem o1 = (ParseItem) s.pop(); + + add(o1); + } + + /** + * Gets the string + * + * @param buf + */ + public void getString(StringBuffer buf) { + ParseItem[] operands = getOperands(); + buf.append(getSymbol()); + operands[0].getString(buf); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + ParseItem[] operands = getOperands(); + operands[0].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[0].columnInserted(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[0].columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[0].rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + operands[0].rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + // Get the data for the operands + ParseItem[] operands = getOperands(); + byte[] data = operands[0].getBytes(); + + // Add on the operator byte + byte[] newdata = new byte[data.length + 1]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = getToken().getCode(); + + return newdata; + } + + /** + * Abstract method which gets the binary operator string symbol + * + * @return the string symbol for this token + */ + abstract String getSymbol(); + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + abstract Token getToken(); + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + ParseItem[] operands = getOperands(); + operands[0].handleImportedCellReferences(); + } + + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryPlus.java b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryPlus.java new file mode 100755 index 0000000..0c3ff91 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryPlus.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class UnaryPlus extends UnaryOperator implements ParsedThing { + /** + * Constructor + */ + public UnaryPlus() { + } + + public String getSymbol() { + return "+"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.UNARY_PLUS; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 2; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Union.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Union.java new file mode 100755 index 0000000..63665af --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Union.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * A union of two cell ranges - the "," operator + */ +class Union extends BinaryOperator implements ParsedThing { + /** + * Constructor + */ + public Union() { + } + + public String getSymbol() { + return ","; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() { + return Token.UNION; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 4; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/VariableArgFunction.java b/datastructures-xslx/src/main/java/jxl/biff/formula/VariableArgFunction.java new file mode 100755 index 0000000..21134b8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/VariableArgFunction.java @@ -0,0 +1,311 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * A built in function in a formula. These functions take a variable + * number of arguments, such as a range (eg. SUM etc) + */ +class VariableArgFunction extends Operator implements ParsedThing { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(VariableArgFunction.class); + + /** + * The function + */ + private Function function; + + /** + * The number of arguments + */ + private int arguments; + + /** + * Flag which indicates whether this was initialized from the client + * api or from an excel sheet + */ + private final boolean readFromSheet; + + /** + * The workbooks settings + */ + private final WorkbookSettings settings; + + /** + * Constructor + */ + public VariableArgFunction(WorkbookSettings ws) { + readFromSheet = true; + settings = ws; + } + + /** + * Constructor used when parsing a function from a string + * + * @param f the function + * @param a the number of arguments + */ + public VariableArgFunction(Function f, int a, WorkbookSettings ws) { + function = f; + arguments = a; + readFromSheet = false; + settings = ws; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + * @throws FormulaException + */ + public int read(byte[] data, int pos) throws FormulaException { + arguments = data[pos]; + int index = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + function = Function.getFunction(index); + + if (function == Function.UNKNOWN) { + throw new FormulaException(FormulaException.UNRECOGNIZED_FUNCTION, + index); + } + + return 3; + } + + /** + * Gets the operands for this operator from the stack + */ + public void getOperands(Stack s) { + // parameters are in the correct order, god damn them + ParseItem[] items = new ParseItem[arguments]; + + for (int i = arguments - 1; i >= 0; i--) { + ParseItem pi = (ParseItem) s.pop(); + + items[i] = pi; + } + + for (int i = 0; i < arguments; i++) { + add(items[i]); + } + } + + public void getString(StringBuffer buf) { + buf.append(function.getName(settings)); + buf.append('('); + + if (arguments > 0) { + ParseItem[] operands = getOperands(); + if (readFromSheet) { + // arguments are in the same order they were specified + operands[0].getString(buf); + + for (int i = 1; i < arguments; i++) { + buf.append(','); + operands[i].getString(buf); + } + } else { + // arguments are stored in the reverse order to which they + // were specified, so iterate through them backwards + operands[arguments - 1].getString(buf); + + for (int i = arguments - 2; i >= 0; i--) { + buf.append(','); + operands[i].getString(buf); + } + } + } + + buf.append(')'); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { + ParseItem[] operands = getOperands(); + + for (int i = 0; i < operands.length; i++) { + operands[i].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].columnInserted(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].columnRemoved(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].rowInserted(sheetIndex, row, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].rowRemoved(sheetIndex, row, currentSheet); + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) { + operands[i].handleImportedCellReferences(); + } + } + + /** + * Gets the underlying function + */ + Function getFunction() { + return function; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() { + handleSpecialCases(); + + // Get the data for the operands - in the correct order + ParseItem[] operands = getOperands(); + byte[] data = new byte[0]; + + for (int i = 0; i < operands.length; i++) { + byte[] opdata = operands[i].getBytes(); + + // Grow the array + byte[] newdata = new byte[data.length + opdata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(opdata, 0, newdata, data.length, opdata.length); + data = newdata; + } + + // Add on the operator byte + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = !useAlternateCode() ? + Token.FUNCTIONVARARG.getCode() : Token.FUNCTIONVARARG.getCode2(); + newdata[data.length + 1] = (byte) arguments; + IntegerHelper.getTwoBytes(function.getCode(), newdata, data.length + 2); + + return newdata; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() { + return 3; + } + + /** + * Handles functions which form a special case + */ + private void handleSpecialCases() { + // Handle the array functions. Tell all the Area records to + // use their alternative token code + if (function == Function.SUMPRODUCT) { + // Get the data for the operands - in reverse order + ParseItem[] operands = getOperands(); + + for (int i = operands.length - 1; i >= 0; i--) { + if (operands[i] instanceof Area) { + operands[i].setAlternateCode(); + } + } + } + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Yylex.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Yylex.java new file mode 100755 index 0000000..13b2113 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Yylex.java @@ -0,0 +1,837 @@ +/* The following code was generated by JFlex 1.4.1 on 24/10/09 14:10 */ + +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +/** + * This file is generated by JLex. Do not alter the contents of this file + * because changes will be overridden + */ + +import jxl.biff.WorkbookMethods; + + +/** + * This class is a scanner generated by + * JFlex 1.4.1 + * on 24/10/09 14:10 from the specification file + * xlformula.flex + */ +class Yylex { + + /** + * This character denotes the end of file + */ + public static final int YYEOF = -1; + /** + * lexical states + */ + public static final int YYSTRING = 1; + public static final int YYINITIAL = 0; + /** + * initial size of the lookahead buffer + */ + private static final int ZZ_BUFFERSIZE = 16384; + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\10\0\3\25\25\0\1\25\1\24\1\21\1\26\1\10\2\0\1\22" + + "\1\5\1\6\1\41\1\37\1\4\1\40\1\7\1\33\1\34\11\2" + + "\1\3\1\0\1\44\1\43\1\42\1\36\1\0\1\16\2\1\1\30" + + "\1\14\1\15\2\1\1\31\2\1\1\17\1\35\1\27\3\1\1\12" + + "\1\20\1\11\1\13\1\32\4\1\4\0\1\23\1\0\32\1\uff85\0"; + + /** + * Translates characters to character classes + */ + private static final char[] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + private static final String ZZ_ACTION_PACKED_0 = + "\1\0\1\1\1\2\1\3\1\4\1\5\1\6\1\7" + + "\1\0\2\2\1\10\1\0\1\11\1\0\1\12\1\13" + + "\1\14\1\15\1\16\1\17\1\20\1\1\1\21\1\2" + + "\1\22\1\0\1\23\1\0\1\2\3\0\2\2\5\0" + + "\1\24\1\25\1\26\1\2\1\0\1\27\1\0\1\22" + + "\2\0\1\30\1\0\2\2\10\0\1\27\1\0\1\31" + + "\1\0\1\32\10\0\1\33\2\0\1\31\2\0\1\34" + + "\4\0\1\35\3\0\1\35\1\0\1\36\1\0"; + /** + * Translates DFA states to action switch labels. + */ + private static final int[] ZZ_ACTION = zzUnpackAction(); + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\45\0\112\0\157\0\224\0\224\0\224\0\224" + + "\0\271\0\336\0\u0103\0\224\0\u0128\0\224\0\u014d\0\224" + + "\0\224\0\224\0\224\0\u0172\0\224\0\u0197\0\u01bc\0\224" + + "\0\u01e1\0\u0206\0\u022b\0\224\0\u0250\0\u0275\0\u029a\0\u02bf" + + "\0\u02e4\0\u0309\0\u032e\0\u0353\0\u0378\0\u039d\0\u03c2\0\u03e7" + + "\0\224\0\224\0\224\0\u040c\0\u0431\0\u0456\0\u047b\0\u04a0" + + "\0\u04c5\0\u04ea\0\u02bf\0\u050f\0\u0534\0\u0559\0\u057e\0\u05a3" + + "\0\u05c8\0\u05ed\0\u0612\0\u0637\0\u065c\0\u0681\0\224\0\u06a6" + + "\0\u06cb\0\u06cb\0\u040c\0\u06f0\0\u0715\0\u073a\0\u075f\0\u0784" + + "\0\u07a9\0\u07ce\0\u07f3\0\u0818\0\u0818\0\u083d\0\u0862\0\u0887" + + "\0\u08ac\0\224\0\u08d1\0\u08f6\0\u091b\0\u0940\0\u0965\0\u098a" + + "\0\u09af\0\u09d4\0\224\0\u09f9\0\u0a1e\0\u0a1e"; + /** + * Translates a state to a row index in the transition table + */ + private static final int[] ZZ_ROWMAP = zzUnpackRowMap(); + private static final String ZZ_TRANS_PACKED_0 = + "\1\0\1\3\1\4\1\5\1\6\1\7\1\10\1\0" + + "\1\11\1\12\3\3\1\13\3\3\1\14\1\15\2\0" + + "\1\16\1\17\4\3\1\20\1\4\1\3\1\0\1\21" + + "\1\22\1\23\1\24\1\25\1\26\21\27\1\30\23\27" + + "\1\0\1\31\1\32\1\33\1\0\1\34\2\0\1\35" + + "\10\31\2\0\1\36\1\37\2\0\4\31\1\0\1\32" + + "\1\31\11\0\1\4\4\0\1\40\24\0\1\4\56\0" + + "\1\41\7\0\10\41\6\0\4\41\2\0\1\41\10\0" + + "\1\31\1\32\1\33\1\0\1\34\2\0\1\35\1\31" + + "\1\42\6\31\2\0\1\36\1\37\2\0\4\31\1\0" + + "\1\32\1\31\10\0\1\31\1\32\1\33\1\0\1\34" + + "\2\0\1\35\5\31\1\43\2\31\2\0\1\36\1\37" + + "\2\0\4\31\1\0\1\32\1\31\7\0\22\15\1\44" + + "\22\15\12\0\1\45\14\0\1\46\1\47\1\0\1\50" + + "\55\0\1\51\43\0\1\52\1\53\1\0\21\27\1\0" + + "\23\27\1\0\1\54\1\32\1\33\1\0\1\34\2\0" + + "\1\35\10\54\2\0\1\36\1\37\2\0\4\54\1\0" + + "\1\32\1\54\10\0\1\36\1\32\1\55\5\0\10\36" + + "\2\0\1\36\3\0\4\36\1\0\1\32\1\36\10\0" + + "\1\56\6\0\1\57\10\56\6\0\4\56\2\0\1\56" + + "\11\0\1\60\31\0\1\60\11\0\2\36\6\0\10\36" + + "\2\0\1\36\3\0\4\36\1\0\2\36\10\0\1\61" + + "\6\0\1\62\10\61\6\0\4\61\2\0\1\61\11\0" + + "\1\63\31\0\1\63\11\0\1\64\1\60\1\33\4\0" + + "\1\35\10\64\6\0\4\64\1\0\1\60\1\64\10\0" + + "\1\54\1\32\1\33\1\0\1\34\2\0\1\35\2\54" + + "\1\65\5\54\2\0\1\36\1\37\2\0\4\54\1\0" + + "\1\32\1\54\10\0\1\54\1\32\1\33\1\0\1\34" + + "\2\0\1\35\6\54\1\66\1\54\2\0\1\36\1\37" + + "\2\0\4\54\1\0\1\32\1\54\33\0\1\67\34\0" + + "\1\70\43\0\1\71\2\0\1\72\57\0\1\73\31\0" + + "\1\74\27\0\1\54\1\36\2\0\1\34\3\0\10\54" + + "\2\0\1\36\1\37\2\0\4\54\1\0\1\36\1\54" + + "\10\0\1\75\6\0\1\76\10\75\6\0\4\75\2\0" + + "\1\75\10\0\1\77\7\0\10\77\6\0\4\77\2\0" + + "\1\77\10\0\1\56\7\0\10\56\6\0\4\56\2\0" + + "\1\56\11\0\1\60\1\55\30\0\1\60\11\0\1\100" + + "\1\101\5\0\1\102\10\100\6\0\4\100\1\0\1\101" + + "\1\100\10\0\1\61\7\0\10\61\6\0\4\61\2\0" + + "\1\61\11\0\1\60\1\33\4\0\1\35\23\0\1\60" + + "\11\0\1\54\1\36\2\0\1\34\3\0\3\54\1\103" + + "\4\54\2\0\1\36\1\37\2\0\4\54\1\0\1\36" + + "\1\54\10\0\1\54\1\36\2\0\1\34\3\0\7\54" + + "\1\65\2\0\1\36\1\37\2\0\4\54\1\0\1\36" + + "\1\54\10\0\1\104\6\0\1\105\10\104\6\0\4\104" + + "\2\0\1\104\24\0\1\106\46\0\1\107\15\0\1\106" + + "\44\0\1\110\41\0\1\111\31\0\1\112\26\0\1\113" + + "\1\114\5\0\1\115\10\113\6\0\4\113\1\0\1\114" + + "\1\113\10\0\1\75\7\0\10\75\6\0\4\75\2\0" + + "\1\75\11\0\1\101\5\0\1\102\23\0\1\101\12\0" + + "\1\101\31\0\1\101\11\0\1\116\1\117\1\120\4\0" + + "\1\121\10\116\6\0\4\116\1\0\1\117\1\116\10\0" + + "\1\104\7\0\10\104\6\0\4\104\2\0\1\104\33\0" + + "\1\122\37\0\1\106\41\0\1\123\63\0\1\124\24\0" + + "\1\125\33\0\1\114\5\0\1\115\23\0\1\114\12\0" + + "\1\114\31\0\1\114\12\0\1\117\1\120\4\0\1\121" + + "\23\0\1\117\12\0\1\117\1\126\30\0\1\117\11\0" + + "\1\127\6\0\1\130\10\127\6\0\4\127\2\0\1\127" + + "\11\0\1\117\31\0\1\117\46\0\1\122\42\0\1\106" + + "\24\0\1\106\31\0\1\131\6\0\1\132\10\131\6\0" + + "\4\131\2\0\1\131\10\0\1\133\7\0\10\133\6\0" + + "\4\133\2\0\1\133\10\0\1\127\7\0\10\127\6\0" + + "\4\127\2\0\1\127\10\0\1\134\1\135\5\0\1\136" + + "\10\134\6\0\4\134\1\0\1\135\1\134\10\0\1\131" + + "\7\0\10\131\6\0\4\131\2\0\1\131\11\0\1\135" + + "\5\0\1\136\23\0\1\135\12\0\1\135\31\0\1\135" + + "\10\0"; + /** + * The transition table of the DFA + */ + private static final int[] ZZ_TRANS = zzUnpackTrans(); + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + /* error messages for the codes above */ + private static final String[] ZZ_ERROR_MSG = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\1\0\3\1\4\11\1\0\2\1\1\11\1\0\1\11" + + "\1\0\4\11\1\1\1\11\2\1\1\11\2\1\1\0" + + "\1\11\1\0\1\1\3\0\2\1\5\0\3\11\1\1" + + "\1\0\1\1\1\0\1\1\2\0\1\1\1\0\2\1" + + "\10\0\1\11\1\0\1\1\1\0\1\1\10\0\1\1" + + "\2\0\1\1\2\0\1\11\4\0\1\1\3\0\1\11" + + "\1\0\1\1\1\0"; + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state aState + */ + private static final int[] ZZ_ATTRIBUTE = zzUnpackAttribute(); + /** + * the input device + */ + private java.io.Reader zzReader; + /** + * the current state of the DFA + */ + private int zzState; + /** + * the current lexical state + */ + private int zzLexicalState = YYINITIAL; + /** + * this buffer contains the current text to be matched and is + * the source of the yytext() string + */ + private char[] zzBuffer = new char[ZZ_BUFFERSIZE]; + /** + * the textposition at the last accepting state + */ + private int zzMarkedPos; + /** + * the textposition at the last state to be included in yytext + */ + private int zzPushbackPos; + /** + * the current text position in the buffer + */ + private int zzCurrentPos; + /** + * startRead marks the beginning of the yytext() string in the buffer + */ + private int zzStartRead; + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + private int zzEndRead; + /** + * number of newlines encountered up to the start of the matched text + */ + private int yyline; + /** + * the number of characters up to the start of the matched text + */ + private int yychar; + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + /** + * zzAtEOF == true <=> the scanner is at the EOF + */ + private boolean zzAtEOF; + private boolean emptyString; + private ExternalSheet externalSheet; + private WorkbookMethods nameTable; + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + Yylex(java.io.Reader in) { + this.zzReader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + Yylex(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + private static int[] zzUnpackAction() { + int[] result = new int[94]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + private static int[] zzUnpackRowMap() { + int[] result = new int[94]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + private static int[] zzUnpackTrans() { + int[] result = new int[2627]; + int offset = 0; + offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackTrans(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do result[j++] = value; while (--count > 0); + } + return j; + } + + private static int[] zzUnpackAttribute() { + int[] result = new int[94]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char[] zzUnpackCMap(String packed) { + char[] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 100) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + /* user code: */ + int getPos() { + return yychar; + } + + void setExternalSheet(ExternalSheet es) { + externalSheet = es; + } + + void setNameTable(WorkbookMethods nt) { + nameTable = nt; + } + + /** + * Refills the input buffer. + * + * @return false, iff there was new input. + * @throws java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + System.arraycopy(zzBuffer, zzStartRead, + zzBuffer, 0, + zzEndRead - zzStartRead); + + /* translate stored positions */ + zzEndRead -= zzStartRead; + zzCurrentPos -= zzStartRead; + zzMarkedPos -= zzStartRead; + zzPushbackPos -= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length) { + /* if not: blow it up */ + char[] newBuffer = new char[zzCurrentPos * 2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = zzReader.read(zzBuffer, zzEndRead, + zzBuffer.length - zzEndRead); + + if (numRead < 0) { + return true; + } else { + zzEndRead += numRead; + return false; + } + } + + + /** + * Closes the input stream. + */ + public final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) + zzReader.close(); + } + + + /** + * Resets the scanner to read from a new input stream. + * Does not close the old reader. + *

+ * All internal variables are reset, the old input stream + * cannot be reused (internal buffer is discarded and lost). + * Lexical state is set to ZZ_INITIAL. + * + * @param reader the new input stream + */ + public final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = zzPushbackPos = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final String yytext() { + return new String(zzBuffer, zzStartRead, zzMarkedPos - zzStartRead); + } + + + /** + * Returns the character at position pos from the + * matched text. + *

+ * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer[zzStartRead + pos]; + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos - zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + *

+ * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + *

+ * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + *

+ * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if (number > yylength()) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @throws java.io.IOException if any I/O-Error occurs + */ + public ParseItem yylex() throws java.io.IOException, FormulaException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char[] zzBufferL = zzBuffer; + char[] zzCMapL = ZZ_CMAP; + + int[] zzTransL = ZZ_TRANS; + int[] zzRowMapL = ZZ_ROWMAP; + int[] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + yychar += zzMarkedPosL - zzStartRead; + + boolean zzR = false; + for (zzCurrentPosL = zzStartRead; zzCurrentPosL < zzMarkedPosL; + zzCurrentPosL++) { + switch (zzBufferL[zzCurrentPosL]) { + case '\u000B': + case '\u000C': + case '\u0085': + case '\u2028': + case '\u2029': + yyline++; + zzR = false; + break; + case '\r': + yyline++; + zzR = true; + break; + case '\n': + if (zzR) + zzR = false; + else { + yyline++; + } + break; + default: + zzR = false; + } + } + + if (zzR) { + // peek one character ahead if it is \n (if we have counted one line too much) + boolean zzPeek; + if (zzMarkedPosL < zzEndReadL) + zzPeek = zzBufferL[zzMarkedPosL] == '\n'; + else if (zzAtEOF) + zzPeek = false; + else { + boolean eof = zzRefill(); + zzEndReadL = zzEndRead; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + if (eof) + zzPeek = false; + else + zzPeek = zzBufferL[zzMarkedPosL] == '\n'; + } + if (zzPeek) yyline--; + } + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = zzLexicalState; + + + zzForAction: + { + while (true) { + + if (zzCurrentPosL < zzEndReadL) + zzInput = zzBufferL[zzCurrentPosL++]; + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } else { + zzInput = zzBufferL[zzCurrentPosL++]; + } + } + int zzNext = zzTransL[zzRowMapL[zzState] + zzCMapL[zzInput]]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + int zzAttributes = zzAttrL[zzState]; + if ((zzAttributes & 1) == 1) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ((zzAttributes & 8) == 8) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 12: { + return new Minus(); + } + case 31: + break; + case 7: { + return new CloseParentheses(); + } + case 32: + break; + case 3: { + return new IntegerValue(yytext()); + } + case 33: + break; + case 24: { + return new DoubleValue(yytext()); + } + case 34: + break; + case 29: { + return new ColumnRange3d(yytext(), externalSheet); + } + case 35: + break; + case 4: { + return new RangeSeparator(); + } + case 36: + break; + case 10: { + return new Divide(); + } + case 37: + break; + case 25: { + return new CellReference3d(yytext(), externalSheet); + } + case 38: + break; + case 26: { + return new BooleanValue(yytext()); + } + case 39: + break; + case 15: { + return new Equal(); + } + case 40: + break; + case 17: { + yybegin(YYINITIAL); + if (emptyString) return new StringValue(""); + } + case 41: + break; + case 8: { + emptyString = true; + yybegin(YYSTRING); + } + case 42: + break; + case 21: { + return new NotEqual(); + } + case 43: + break; + case 22: { + return new LessEqual(); + } + case 44: + break; + case 16: { + return new LessThan(); + } + case 45: + break; + case 5: { + return new ArgumentSeparator(); + } + case 46: + break; + case 30: { + return new Area3d(yytext(), externalSheet); + } + case 47: + break; + case 14: { + return new GreaterThan(); + } + case 48: + break; + case 18: { + return new CellReference(yytext()); + } + case 49: + break; + case 20: { + return new GreaterEqual(); + } + case 50: + break; + case 27: { + return new Area(yytext()); + } + case 51: + break; + case 23: { + return new ColumnRange(yytext()); + } + case 52: + break; + case 1: { + emptyString = false; + return new StringValue(yytext()); + } + case 53: + break; + case 2: { + return new NameRange(yytext(), nameTable); + } + case 54: + break; + case 19: { + return new StringFunction(yytext()); + } + case 55: + break; + case 11: { + return new Plus(); + } + case 56: + break; + case 28: { + return new ErrorConstant(yytext()); + } + case 57: + break; + case 9: { + } + case 58: + break; + case 13: { + return new Multiply(); + } + case 59: + break; + case 6: { + return new OpenParentheses(); + } + case 60: + break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + return null; + } else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/datastructures-xslx/src/main/java/jxl/common/Assert.java b/datastructures-xslx/src/main/java/jxl/common/Assert.java new file mode 100755 index 0000000..82a47b7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/Assert.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + +/** + * Simple assertion mechanism for use during development + */ +public final class Assert { + /** + * Throws an AssertionFailed exception if the specified condition is + * false + * + * @param condition The assertion condition which must be true + */ + public static void verify(boolean condition) { + if (!condition) { + throw new AssertionFailed(); + } + } + + /** + * If the condition evaluates to false, an AssertionFailed is thrown + * + * @param message A message thrown with the failed assertion + * @param condition If this evaluates to false, an AssertionFailed is thrown + */ + public static void verify(boolean condition, String message) { + if (!condition) { + throw new AssertionFailed(message); + } + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/common/AssertionFailed.java b/datastructures-xslx/src/main/java/jxl/common/AssertionFailed.java new file mode 100755 index 0000000..cb0b88e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/AssertionFailed.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + + +package jxl.common; + +/** + * An exception thrown when an assert (from the Assert class) fails + */ +public class AssertionFailed extends RuntimeException { + /** + * Default constructor + * Prints the stack trace + */ + public AssertionFailed() { + super(); + printStackTrace(); + } + + /** + * Constructor with message + * Prints the stack trace + * + * @param s Message thrown with the assertion + */ + public AssertionFailed(String s) { + super(s); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/BaseUnit.java b/datastructures-xslx/src/main/java/jxl/common/BaseUnit.java new file mode 100755 index 0000000..b9032ea --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/BaseUnit.java @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + +public class BaseUnit { + private final int index; + + protected BaseUnit(int ind) { + index = ind; + } + + protected int getIndex() { + return index; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/LengthConverter.java b/datastructures-xslx/src/main/java/jxl/common/LengthConverter.java new file mode 100755 index 0000000..72773b1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/LengthConverter.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + +public class LengthConverter { + private static final double[][] factors = + new double[LengthUnit.getCount()][LengthUnit.getCount()]; + + static { + // The identity factors + factors[LengthUnit.POINTS.getIndex()][LengthUnit.POINTS.getIndex()] = 1; + factors[LengthUnit.METRES.getIndex()][LengthUnit.METRES.getIndex()] = 1; + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 1; + factors[LengthUnit.INCHES.getIndex()][LengthUnit.INCHES.getIndex()] = 1; + + // The points conversion factors + factors[LengthUnit.POINTS.getIndex()][LengthUnit.METRES.getIndex()] = 0.00035277777778; + factors[LengthUnit.POINTS.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 0.035277777778; + factors[LengthUnit.POINTS.getIndex()][LengthUnit.INCHES.getIndex()] = 0.013888888889; + + // The metres conversion factors + factors[LengthUnit.METRES.getIndex()][LengthUnit.POINTS.getIndex()] = 2877.84; + factors[LengthUnit.METRES.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 100; + factors[LengthUnit.METRES.getIndex()][LengthUnit.INCHES.getIndex()] = 39.37; + + // The centimetres conversion factors + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.POINTS.getIndex()] = 28.34643; + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.METRES.getIndex()] = 0.01; + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.INCHES.getIndex()] = 0.3937; + + // The inches conversion factors + factors[LengthUnit.INCHES.getIndex()][LengthUnit.POINTS.getIndex()] = 72; + factors[LengthUnit.INCHES.getIndex()][LengthUnit.METRES.getIndex()] = 0.0254; + factors[LengthUnit.INCHES.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 2.54; + } + + public static double getConversionFactor(LengthUnit from, LengthUnit to) { + return factors[from.getIndex()][to.getIndex()]; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/LengthUnit.java b/datastructures-xslx/src/main/java/jxl/common/LengthUnit.java new file mode 100755 index 0000000..8fd29e8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/LengthUnit.java @@ -0,0 +1,39 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + + +/** + * Enumeration for units + */ +public class LengthUnit extends BaseUnit { + public static LengthUnit POINTS = new LengthUnit(); + public static LengthUnit METRES = new LengthUnit(); + public static LengthUnit CENTIMETRES = new LengthUnit(); + public static LengthUnit INCHES = new LengthUnit(); + private static int count = 0; + private LengthUnit() { + super(count++); + } + + public static int getCount() { + return count; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/Logger.java b/datastructures-xslx/src/main/java/jxl/common/Logger.java new file mode 100755 index 0000000..562dde1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/Logger.java @@ -0,0 +1,156 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + +import java.security.AccessControlException; + +/** + * Abstract wrapper class for the logging interface of choice. + * The methods declared here are the same as those for the log4j + */ +public abstract class Logger { + /** + * The singleton logger + */ + private static Logger logger = null; + + /** + * Constructor + */ + protected Logger() { + } + + /** + * Factory method to return the logger + */ + public static final Logger getLogger(Class cl) { + if (logger == null) { + initializeLogger(); + } + + return logger.getLoggerImpl(cl); + } + + /** + * Initializes the logger in a thread safe manner + */ + private synchronized static void initializeLogger() { + if (logger != null) { + return; + } + + String loggerName = jxl.common.log.LoggerName.NAME; + + try { + // First see if there was anything defined at run time + loggerName = System.getProperty("logger"); + + if (loggerName == null) { + // Get the logger name from the compiled in logger + loggerName = jxl.common.log.LoggerName.NAME; + } + + logger = (Logger) Class.forName(loggerName).newInstance(); + } catch (IllegalAccessException e) { + logger = new jxl.common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } catch (InstantiationException e) { + logger = new jxl.common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } catch (AccessControlException e) { + logger = new jxl.common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } catch (ClassNotFoundException e) { + logger = new jxl.common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } + } + + /** + * Log a debug message + */ + public abstract void debug(Object message); + + /** + * Log a debug message and exception + */ + public abstract void debug(Object message, Throwable t); + + /** + * Log an error message + */ + public abstract void error(Object message); + + /** + * Log an error message object and exception + */ + public abstract void error(Object message, Throwable t); + + /** + * Log a fatal message + */ + public abstract void fatal(Object message); + + /** + * Log a fatal message and exception + */ + public abstract void fatal(Object message, Throwable t); + + /** + * Log an information message + */ + public abstract void info(Object message); + + /** + * Logs an information message and an exception + */ + public abstract void info(Object message, Throwable t); + + /** + * Log a warning message object + */ + public abstract void warn(Object message); + + /** + * Log a warning message with exception + */ + public abstract void warn(Object message, Throwable t); + + /** + * Accessor to the logger implementation + */ + protected abstract Logger getLoggerImpl(Class cl); + + /** + * Empty implementation of the suppressWarnings. Subclasses may + * or may not override this method. This method is included + * primarily for backwards support of the jxl.nowarnings property, and + * is used only by the SimpleLogger + * + * @param w suppression flag + */ + public void setSuppressWarnings(boolean w) { + // default implementation does nothing + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/log/LoggerName.java b/datastructures-xslx/src/main/java/jxl/common/log/LoggerName.java new file mode 100755 index 0000000..b594936 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/log/LoggerName.java @@ -0,0 +1,29 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common.log; + +/** + * Static structure containing the class name of the logger. This may + * be overwritten at build time if loggers other than the default, + * no-dependency logger are required + */ +public class LoggerName { + public final static String NAME = LoggerName.class.getName(); +} diff --git a/datastructures-xslx/src/main/java/jxl/common/log/SimpleLogger.java b/datastructures-xslx/src/main/java/jxl/common/log/SimpleLogger.java new file mode 100755 index 0000000..0599470 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/log/SimpleLogger.java @@ -0,0 +1,156 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common.log; + +import jxl.common.Logger; + +/** + * The default logger. Simple writes everything out to stdout or stderr + */ +public class SimpleLogger extends Logger { + /** + * Flag to indicate whether or not warnings should be suppressed + */ + private boolean suppressWarnings; + + /** + * Constructor + */ + public SimpleLogger() { + suppressWarnings = false; + } + + /** + * Log a debug message + */ + public void debug(Object message) { + if (!suppressWarnings) { + System.out.print("Debug: "); + System.out.println(message); + } + } + + /** + * Log a debug message and exception + */ + public void debug(Object message, Throwable t) { + if (!suppressWarnings) { + System.out.print("Debug: "); + System.out.println(message); + t.printStackTrace(); + } + } + + /** + * Log an error message + */ + public void error(Object message) { + System.err.print("Error: "); + System.err.println(message); + } + + /** + * Log an error message object and exception + */ + public void error(Object message, Throwable t) { + System.err.print("Error: "); + System.err.println(message); + t.printStackTrace(); + } + + /** + * Log a fatal message + */ + public void fatal(Object message) { + System.err.print("Fatal: "); + System.err.println(message); + } + + /** + * Log a fatal message and exception + */ + public void fatal(Object message, Throwable t) { + System.err.print("Fatal: "); + System.err.println(message); + t.printStackTrace(); + } + + /** + * Log an information message + */ + public void info(Object message) { + if (!suppressWarnings) { + System.out.println(message); + } + } + + /** + * Logs an information message and an exception + */ + + public void info(Object message, Throwable t) { + if (!suppressWarnings) { + System.out.println(message); + t.printStackTrace(); + } + } + + /** + * Log a warning message object + */ + public void warn(Object message) { + if (!suppressWarnings) { + System.err.print("Warning: "); + System.err.println(message); + } + } + + /** + * Log a warning message with exception + */ + public void warn(Object message, Throwable t) { + if (!suppressWarnings) { + System.err.print("Warning: "); + System.err.println(message); + t.printStackTrace(); + } + } + + /** + * Accessor to the logger implementation + */ + protected Logger getLoggerImpl(Class c) { + return this; + } + + /** + * Overrides the method in the base class to suppress warnings - it can + * be set using the system property jxl.nowarnings. + * This method was originally present in the WorkbookSettings bean, + * but has been moved to the logger class. This means it is now present + * when the JVM is initialized, and subsequent to change it on + * a Workbook by Workbook basis will prove fruitless + * + * @param w suppression flag + */ + public void setSuppressWarnings(boolean w) { + suppressWarnings = w; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/log/SimpleLoggerName.java b/datastructures-xslx/src/main/java/jxl/common/log/SimpleLoggerName.java new file mode 100755 index 0000000..0f3ce0b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/log/SimpleLoggerName.java @@ -0,0 +1,29 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common.log; + +/** + * Static structure containing the class name of the logger. This may + * be overwritten at build time if loggers other than the default, + * no-dependency logger are required + */ +public class SimpleLoggerName { + public final static String NAME = SimpleLoggerName.class.getName(); +} diff --git a/datastructures-xslx/src/main/java/jxl/format/Alignment.java b/datastructures-xslx/src/main/java/jxl/format/Alignment.java new file mode 100755 index 0000000..6c5cb5b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Alignment.java @@ -0,0 +1,115 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various alignments for data within a + * cell + */ +public /*final*/ class Alignment { + /** + * The standard alignment + */ + public static Alignment GENERAL = new Alignment(0, "general"); + /** + * Data cells with this alignment will appear at the left hand edge of the + * cell + */ + public static Alignment LEFT = new Alignment(1, "left"); + /** + * Data in cells with this alignment will be centred + */ + public static Alignment CENTRE = new Alignment(2, "centre"); + /** + * Data in cells with this alignment will be right aligned + */ + public static Alignment RIGHT = new Alignment(3, "right"); + /** + * Data in cells with this alignment will fill the cell + */ + public static Alignment FILL = new Alignment(4, "fill"); + /** + * Data in cells with this alignment will be justified + */ + public static Alignment JUSTIFY = new Alignment(5, "justify"); + /** + * The list of alignments + */ + private static Alignment[] alignments = new Alignment[0]; + /** + * The internal numerical repreentation of the alignment + */ + private final int value; + /** + * The string description of this alignment + */ + private final String string; + /** + * Private constructor + * + * @param val + * @param string + */ + protected Alignment(int val, String s) { + value = val; + string = s; + + Alignment[] oldaligns = alignments; + alignments = new Alignment[oldaligns.length + 1]; + System.arraycopy(oldaligns, 0, alignments, 0, oldaligns.length); + alignments[oldaligns.length] = this; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static Alignment getAlignment(int val) { + for (int i = 0; i < alignments.length; i++) { + if (alignments[i].getValue() == val) { + return alignments[i]; + } + } + + return GENERAL; + } + + /** + * Gets the value of this alignment. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the string description of this alignment + * + * @return the string description + */ + public String getDescription() { + return string; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/format/BoldStyle.java b/datastructures-xslx/src/main/java/jxl/format/BoldStyle.java new file mode 100755 index 0000000..f4bbe6d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/BoldStyle.java @@ -0,0 +1,75 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class containing the various bold styles for data + */ +public /*final*/ class BoldStyle { + /** + * Normal style + */ + public static final BoldStyle NORMAL = new BoldStyle(0x190, "Normal"); + /** + * Emboldened style + */ + public static final BoldStyle BOLD = new BoldStyle(0x2bc, "Bold"); + /** + * The bold weight + */ + private final int value; + /** + * The description + */ + private final String string; + + /** + * Constructor + * + * @param val + */ + protected BoldStyle(int val, String s) { + value = val; + string = s; + } + + /** + * Gets the value of the bold weight. This is the value that will be + * written to the generated Excel file. + * + * @return the bold weight + */ + public int getValue() { + return value; + } + + /** + * Gets the string description of the bold style + */ + public String getDescription() { + return string; + } +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/Border.java b/datastructures-xslx/src/main/java/jxl/format/Border.java new file mode 100755 index 0000000..ad63acf --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Border.java @@ -0,0 +1,50 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * The location of a border + */ +public /*final*/ class Border { + public final static Border NONE = new Border("none"); + public final static Border ALL = new Border("all"); + public final static Border TOP = new Border("top"); + public final static Border BOTTOM = new Border("bottom"); + public final static Border LEFT = new Border("left"); + public final static Border RIGHT = new Border("right"); + /** + * The string description + */ + private final String string; + /** + * Constructor + */ + protected Border(String s) { + string = s; + } + + /** + * Gets the description + */ + public String getDescription() { + return string; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/format/BorderLineStyle.java b/datastructures-xslx/src/main/java/jxl/format/BorderLineStyle.java new file mode 100755 index 0000000..23b81d5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/BorderLineStyle.java @@ -0,0 +1,110 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * The border line style + */ +public /*final*/ class BorderLineStyle { + public final static BorderLineStyle NONE + = new BorderLineStyle(0, "none"); + public final static BorderLineStyle THIN + = new BorderLineStyle(1, "thin"); + public final static BorderLineStyle MEDIUM + = new BorderLineStyle(2, "medium"); + public final static BorderLineStyle DASHED + = new BorderLineStyle(3, "dashed"); + public final static BorderLineStyle DOTTED + = new BorderLineStyle(4, "dotted"); + public final static BorderLineStyle THICK + = new BorderLineStyle(5, "thick"); + public final static BorderLineStyle DOUBLE + = new BorderLineStyle(6, "double"); + public final static BorderLineStyle HAIR + = new BorderLineStyle(7, "hair"); + public final static BorderLineStyle MEDIUM_DASHED + = new BorderLineStyle(8, "medium dashed"); + public final static BorderLineStyle DASH_DOT + = new BorderLineStyle(9, "dash dot"); + public final static BorderLineStyle MEDIUM_DASH_DOT + = new BorderLineStyle(0xa, "medium dash dot"); + public final static BorderLineStyle DASH_DOT_DOT + = new BorderLineStyle(0xb, "Dash dot dot"); + public final static BorderLineStyle MEDIUM_DASH_DOT_DOT + = new BorderLineStyle(0xc, "Medium dash dot dot"); + public final static BorderLineStyle SLANTED_DASH_DOT + = new BorderLineStyle(0xd, "Slanted dash dot"); + /** + * The list of alignments + */ + private static BorderLineStyle[] styles = new BorderLineStyle[0]; + /** + * The value + */ + private final int value; + /** + * The string description + */ + private final String string; + /** + * Constructor + */ + protected BorderLineStyle(int val, String s) { + value = val; + string = s; + + BorderLineStyle[] oldstyles = styles; + styles = new BorderLineStyle[oldstyles.length + 1]; + System.arraycopy(oldstyles, 0, styles, 0, oldstyles.length); + styles[oldstyles.length] = this; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static BorderLineStyle getStyle(int val) { + for (int i = 0; i < styles.length; i++) { + if (styles[i].getValue() == val) { + return styles[i]; + } + } + + return NONE; + } + + /** + * Gets the value for this line style + * + * @return the value + */ + public int getValue() { + return value; + } + + /** + * Gets the textual description + */ + public String getDescription() { + return string; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/format/CellFormat.java b/datastructures-xslx/src/main/java/jxl/format/CellFormat.java new file mode 100755 index 0000000..da96fa9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/CellFormat.java @@ -0,0 +1,142 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Interface for cell formats + */ +public interface CellFormat { + /** + * Gets the format used by this format + * + * @return the format + */ + Format getFormat(); + + /** + * Gets the font information used by this format + * + * @return the font + */ + Font getFont(); + + /** + * Gets whether or not the contents of this cell are wrapped + * + * @return TRUE if this cell's contents are wrapped, FALSE otherwise + */ + boolean getWrap(); + + /** + * Gets the horizontal cell alignment + * + * @return the alignment + */ + Alignment getAlignment(); + + /** + * Gets the vertical cell alignment + * + * @return the alignment + */ + VerticalAlignment getVerticalAlignment(); + + /** + * Gets the orientation + * + * @return the orientation + */ + Orientation getOrientation(); + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + BorderLineStyle getBorder(Border border); + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + BorderLineStyle getBorderLine(Border border); + + /** + * Gets the colour for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * If the specified cell does not have an associated line style, then + * the colour the line would be is still returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + Colour getBorderColour(Border border); + + /** + * Determines if this cell format has any borders at all. Used to + * set the new borders when merging a group of cells + * + * @return TRUE if this cell has any borders, FALSE otherwise + */ + boolean hasBorders(); + + /** + * Gets the background colour used by this cell + * + * @return the foreground colour + */ + Colour getBackgroundColour(); + + /** + * Gets the pattern used by this cell format + * + * @return the background pattern + */ + Pattern getPattern(); + + /** + * Gets the indentation of the cell text + * + * @return the indentation + */ + int getIndentation(); + + /** + * Gets the shrink to fit flag + * + * @return TRUE if this format is shrink to fit, FALSE otherise + */ + boolean isShrinkToFit(); + + /** + * Accessor for whether a particular cell is locked + * + * @return TRUE if this cell is locked, FALSE otherwise + */ + boolean isLocked(); +} diff --git a/datastructures-xslx/src/main/java/jxl/format/Colour.java b/datastructures-xslx/src/main/java/jxl/format/Colour.java new file mode 100755 index 0000000..b38934d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Colour.java @@ -0,0 +1,237 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various colours available within + * the standard Excel colour palette + */ +public /*final*/ class Colour { + // Major colours + public final static Colour UNKNOWN = + new Colour(0x7fee, "unknown", 0, 0, 0); + public final static Colour BLACK = + new Colour(0x7fff, "black", 0, 0, 0); + public final static Colour WHITE = + new Colour(0x9, "white", 0xff, 0xff, 0xff); + public final static Colour DEFAULT_BACKGROUND1 + = new Colour(0x0, "default background", 0xff, 0xff, 0xff); + public final static Colour DEFAULT_BACKGROUND + = new Colour(0xc0, "default background", 0xff, 0xff, 0xff); + public final static Colour PALETTE_BLACK = + new Colour(0x8, "black", 0x1, 0, 0); + // Other colours + public final static Colour RED = new Colour(0xa, "red", 0xff, 0, 0); + public final static Colour BRIGHT_GREEN = new Colour(0xb, "bright green", 0, 0xff, 0); + public final static Colour BLUE = new Colour(0xc, "blue", 0, 0, 0xff); + public final static Colour YELLOW = new Colour(0xd, "yellow", 0xff, 0xff, 0); + public final static Colour PINK = new Colour(0xe, "pink", 0xff, 0, 0xff); + public final static Colour TURQUOISE = new Colour(0xf, "turquoise", 0, 0xff, 0xff); + public final static Colour DARK_RED = new Colour(0x10, "dark red", 0x80, 0, 0); + public final static Colour GREEN = new Colour(0x11, "green", 0, 0x80, 0); + public final static Colour DARK_BLUE = new Colour(0x12, "dark blue", 0, 0, 0x80); + public final static Colour DARK_YELLOW = new Colour(0x13, "dark yellow", 0x80, 0x80, 0); + public final static Colour VIOLET = new Colour(0x14, "violet", 0x80, 0x80, 0); + public final static Colour TEAL = new Colour(0x15, "teal", 0, 0x80, 0x80); + public final static Colour GREY_25_PERCENT = new Colour(0x16, "grey 25%", 0xc0, 0xc0, 0xc0); + // the first item in the colour palette + public final static Colour GREY_50_PERCENT = new Colour(0x17, "grey 50%", 0x80, 0x80, 0x80); + public final static Colour PERIWINKLE = new Colour(0x18, "periwinkle%", 0x99, 0x99, 0xff); + public final static Colour PLUM2 = new Colour(0x19, "plum", 0x99, 0x33, 0x66); + public final static Colour IVORY = new Colour(0x1a, "ivory", 0xff, 0xff, 0xcc); + public final static Colour LIGHT_TURQUOISE2 = new Colour(0x1b, "light turquoise", 0xcc, 0xff, 0xff); + public final static Colour DARK_PURPLE = new Colour(0x1c, "dark purple", 0x66, 0x0, 0x66); + public final static Colour CORAL = new Colour(0x1d, "coral", 0xff, 0x80, 0x80); + public final static Colour OCEAN_BLUE = new Colour(0x1e, "ocean blue", 0x0, 0x66, 0xcc); + public final static Colour ICE_BLUE = new Colour(0x1f, "ice blue", 0xcc, 0xcc, 0xff); + public final static Colour DARK_BLUE2 = new Colour(0x20, "dark blue", 0, 0, 0x80); + public final static Colour PINK2 = new Colour(0x21, "pink", 0xff, 0, 0xff); + public final static Colour YELLOW2 = new Colour(0x22, "yellow", 0xff, 0xff, 0x0); + public final static Colour TURQOISE2 = new Colour(0x23, "turqoise", 0x0, 0xff, 0xff); + public final static Colour VIOLET2 = new Colour(0x24, "violet", 0x80, 0x0, 0x80); + public final static Colour DARK_RED2 = new Colour(0x25, "dark red", 0x80, 0x0, 0x0); + public final static Colour TEAL2 = new Colour(0x26, "teal", 0x0, 0x80, 0x80); + public final static Colour BLUE2 = new Colour(0x27, "blue", 0x0, 0x0, 0xff); + public final static Colour SKY_BLUE = new Colour(0x28, "sky blue", 0, 0xcc, 0xff); + public final static Colour LIGHT_TURQUOISE + = new Colour(0x29, "light turquoise", 0xcc, 0xff, 0xff); + public final static Colour LIGHT_GREEN = new Colour(0x2a, "light green", 0xcc, 0xff, 0xcc); + public final static Colour VERY_LIGHT_YELLOW + = new Colour(0x2b, "very light yellow", 0xff, 0xff, 0x99); + public final static Colour PALE_BLUE = new Colour(0x2c, "pale blue", 0x99, 0xcc, 0xff); + public final static Colour ROSE = new Colour(0x2d, "rose", 0xff, 0x99, 0xcc); + public final static Colour LAVENDER = new Colour(0x2e, "lavender", 0xcc, 0x99, 0xff); + public final static Colour TAN = new Colour(0x2f, "tan", 0xff, 0xcc, 0x99); + public final static Colour LIGHT_BLUE = new Colour(0x30, "light blue", 0x33, 0x66, 0xff); + public final static Colour AQUA = new Colour(0x31, "aqua", 0x33, 0xcc, 0xcc); + public final static Colour LIME = new Colour(0x32, "lime", 0x99, 0xcc, 0); + public final static Colour GOLD = new Colour(0x33, "gold", 0xff, 0xcc, 0); + public final static Colour LIGHT_ORANGE + = new Colour(0x34, "light orange", 0xff, 0x99, 0); + public final static Colour ORANGE = new Colour(0x35, "orange", 0xff, 0x66, 0); + public final static Colour BLUE_GREY = new Colour(0x36, "blue grey", 0x66, 0x66, 0xcc); + public final static Colour GREY_40_PERCENT = new Colour(0x37, "grey 40%", 0x96, 0x96, 0x96); + public final static Colour DARK_TEAL = new Colour(0x38, "dark teal", 0, 0x33, 0x66); + public final static Colour SEA_GREEN = new Colour(0x39, "sea green", 0x33, 0x99, 0x66); + public final static Colour DARK_GREEN = new Colour(0x3a, "dark green", 0, 0x33, 0); + public final static Colour OLIVE_GREEN = new Colour(0x3b, "olive green", 0x33, 0x33, 0); + public final static Colour BROWN = new Colour(0x3c, "brown", 0x99, 0x33, 0); + public final static Colour PLUM = new Colour(0x3d, "plum", 0x99, 0x33, 0x66); + public final static Colour INDIGO = new Colour(0x3e, "indigo", 0x33, 0x33, 0x99); + public final static Colour GREY_80_PERCENT = new Colour(0x3f, "grey 80%", 0x33, 0x33, 0x33); + public final static Colour AUTOMATIC = new Colour(0x40, "automatic", 0xff, 0xff, 0xff); + // Colours added for backwards compatibility + public final static Colour GRAY_80 = GREY_80_PERCENT; + public final static Colour GRAY_50 = GREY_50_PERCENT; + public final static Colour GRAY_25 = GREY_25_PERCENT; + /** + * The list of internal colours + */ + private static Colour[] colours = new Colour[0]; + /** + * The internal numerical representation of the colour + */ + private final int value; + /** + * The default RGB value + */ + private final RGB rgb; + /** + * The display string for the colour. Used when presenting the + * format information + */ + private final String string; + /** + * Private constructor + * + * @param val + * @param s the display string + * @param r the default red value + * @param g the default green value + * @param b the default blue value + */ + protected Colour(int val, String s, int r, int g, int b) { + value = val; + string = s; + rgb = new RGB(r, g, b); + + Colour[] oldcols = colours; + colours = new Colour[oldcols.length + 1]; + System.arraycopy(oldcols, 0, colours, 0, oldcols.length); + colours[oldcols.length] = this; + } + + /** + * Gets the internal colour from the value + * + * @param val + * @return the colour with that value + */ + public static Colour getInternalColour(int val) { + for (int i = 0; i < colours.length; i++) { + if (colours[i].getValue() == val) { + return colours[i]; + } + } + + return UNKNOWN; + } + + /** + * Returns all available colours - used when generating the default palette + * + * @return all available colours + */ + public static Colour[] getAllColours() { + return colours; + } + + /** + * Gets the value of this colour. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the string description for display purposes + * + * @return the string description + */ + public String getDescription() { + return string; + } + + /** + * Gets the default red content of this colour. Used when writing the + * default colour palette + * + * @return the red content of this colour + * @deprecated use getDefaultRGB instead + */ + public int getDefaultRed() { + return rgb.getRed(); + } + + /** + * Gets the default green content of this colour. Used when writing the + * default colour palette + * + * @return the green content of this colour + * @deprecated use getDefaultRGB instead + */ + public int getDefaultGreen() { + return rgb.getGreen(); + } + + /** + * Gets the default blue content of this colour. Used when writing the + * default colour palette + * + * @return the blue content of this colour + * @deprecated use getDefaultRGB instead + */ + public int getDefaultBlue() { + return rgb.getBlue(); + } + + /** + * Returns the default RGB of the colour + * + * @return the default RGB + */ + public RGB getDefaultRGB() { + return rgb; + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/Font.java b/datastructures-xslx/src/main/java/jxl/format/Font.java new file mode 100755 index 0000000..26d5524 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Font.java @@ -0,0 +1,82 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Interface which exposes the user font display information to the user + */ +public interface Font { + /** + * Gets the name of this font + * + * @return the name of this font + */ + String getName(); + + /** + * Gets the point size for this font, if the font hasn't been initialized + * + * @return the point size + */ + int getPointSize(); + + /** + * Gets the bold weight for this font + * + * @return the bold weight for this font + */ + int getBoldWeight(); + + /** + * Returns the italic flag + * + * @return TRUE if this font is italic, FALSE otherwise + */ + boolean isItalic(); + + /** + * Returns the strike-out flag + * + * @return TRUE if this font is struck-out, FALSE otherwise + */ + boolean isStruckout(); + + /** + * Gets the underline style for this font + * + * @return the underline style + */ + UnderlineStyle getUnderlineStyle(); + + /** + * Gets the colour for this font + * + * @return the colour + */ + Colour getColour(); + + /** + * Gets the script style + * + * @return the script style + */ + ScriptStyle getScriptStyle(); +} + diff --git a/datastructures-xslx/src/main/java/jxl/format/Format.java b/datastructures-xslx/src/main/java/jxl/format/Format.java new file mode 100755 index 0000000..842bd39 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Format.java @@ -0,0 +1,40 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Exposes the cell formatting information + */ +public interface Format { + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + String getFormatString(); +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/Orientation.java b/datastructures-xslx/src/main/java/jxl/format/Orientation.java new file mode 100755 index 0000000..b131415 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Orientation.java @@ -0,0 +1,128 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the orientation of data within a cell + */ +public final class Orientation { + /** + * Cells with this specified orientation will be horizontal + */ + public static Orientation HORIZONTAL = new Orientation(0, "horizontal"); + /** + * Cells with this specified orientation have their data + * presented vertically + */ + public static Orientation VERTICAL = new Orientation(0xff, "vertical"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation of 90 degrees upwards + */ + public static Orientation PLUS_90 = new Orientation(90, "up 90"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation of 90 degrees downwards + */ + public static Orientation MINUS_90 = new Orientation(180, "down 90"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation 45 degrees upwards + */ + public static Orientation PLUS_45 = new Orientation(45, "up 45"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation 45 degrees downwards + */ + public static Orientation MINUS_45 = new Orientation(135, "down 45"); + /** + * Cells with this specified orientation will have their text stacked + * downwards, but not rotated + */ + public static Orientation STACKED = new Orientation(255, "stacked"); + /** + * The list of alignments + */ + private static Orientation[] orientations = new Orientation[0]; + /** + * The internal binary value which gets written to the generated Excel file + */ + private final int value; + /** + * The textual description + */ + private final String string; + /** + * Constructor + * + * @param val + */ + protected Orientation(int val, String s) { + value = val; + string = s; + + Orientation[] oldorients = orientations; + orientations = new Orientation[oldorients.length + 1]; + System.arraycopy(oldorients, 0, orientations, 0, oldorients.length); + orientations[oldorients.length] = this; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static Orientation getOrientation(int val) { + for (int i = 0; i < orientations.length; i++) { + if (orientations[i].getValue() == val) { + return orientations[i]; + } + } + + return HORIZONTAL; + } + + /** + * Accessor for the binary value + * + * @return the internal binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the textual description + */ + public String getDescription() { + return string; + } + +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/PageOrder.java b/datastructures-xslx/src/main/java/jxl/format/PageOrder.java new file mode 100755 index 0000000..3a1c816 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/PageOrder.java @@ -0,0 +1,40 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the page orientation + */ +public class PageOrder { + /** + * Top to Down then Right. + */ + public static PageOrder DOWN_THEN_RIGHT = new PageOrder(); + /** + * Left to Right then Down. + */ + public static PageOrder RIGHT_THEN_DOWN = new PageOrder(); + + /** + * Constructor + */ + private PageOrder() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/format/PageOrientation.java b/datastructures-xslx/src/main/java/jxl/format/PageOrientation.java new file mode 100755 index 0000000..c65cb38 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/PageOrientation.java @@ -0,0 +1,48 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the page orientation + */ +public final class PageOrientation { + /** + * Portrait orientation + */ + public static PageOrientation PORTRAIT = new PageOrientation(); + /** + * Landscape orientation + */ + public static PageOrientation LANDSCAPE = new PageOrientation(); + /** + * Constructor + */ + private PageOrientation() { + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/PaperSize.java b/datastructures-xslx/src/main/java/jxl/format/PaperSize.java new file mode 100755 index 0000000..78fb4a8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/PaperSize.java @@ -0,0 +1,406 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which contains the available excel paper sizes and their + * codes + */ +public final class PaperSize { + /** + * US Letter 8.5 x 11" + */ + public static final PaperSize UNDEFINED = new PaperSize(0); + /** + * US Letter 8.5 x 11" + */ + public static final PaperSize LETTER = new PaperSize(1); + /** + * Letter small 8.5" x 11" + */ + public static final PaperSize LETTER_SMALL = new PaperSize(2); + /** + * Tabloid 11" x 17" + */ + public static final PaperSize TABLOID = new PaperSize(3); + /** + * Leger 17" x 11" + */ + public static final PaperSize LEDGER = new PaperSize(4); + /** + * US Legal 8.5" x 14" + */ + public static final PaperSize LEGAL = new PaperSize(5); + /** + * Statement 5.5" x 8.5" + */ + public static final PaperSize STATEMENT = new PaperSize(6); + /** + * Executive 7.25" x 10.5" + */ + public static final PaperSize EXECUTIVE = new PaperSize(7); + /** + * A3 297mm x 420mm + */ + public static final PaperSize A3 = new PaperSize(8); + /** + * A4 210mm x 297mm + */ + public static final PaperSize A4 = new PaperSize(9); + /** + * A4 Small 210mm x 297 mm + */ + public static final PaperSize A4_SMALL = new PaperSize(10); + /** + * A5 148mm x 210mm + */ + public static final PaperSize A5 = new PaperSize(11); + /** + * B4 (JIS) 257mm x 364mm + */ + public static final PaperSize B4 = new PaperSize(12); + /** + * B5 (JIS) 182mm x 257mm + */ + public static final PaperSize B5 = new PaperSize(13); + /** + * Folio 8.5" x 13" + */ + public static final PaperSize FOLIO = new PaperSize(14); + /** + * Quarto 215mm x 275mm + */ + public static final PaperSize QUARTO = new PaperSize(15); + /** + * 10" x 14" + */ + public static final PaperSize SIZE_10x14 = new PaperSize(16); + /** + * 11" x 17" + */ + public static final PaperSize SIZE_10x17 = new PaperSize(17); + /** + * NOTE 8.5" x 11" + */ + public static final PaperSize NOTE = new PaperSize(18); + /** + * Envelope #9 3 7/8" x 8 7/8" + */ + public static final PaperSize ENVELOPE_9 = new PaperSize(19); + /** + * Envelope #10 4 1/8" x 9.5" + */ + public static final PaperSize ENVELOPE_10 = new PaperSize(20); + /** + * Envelope #11 4.5" x 10 3/8" + */ + public static final PaperSize ENVELOPE_11 = new PaperSize(21); + /** + * Envelope #12 4.75" x 11" + */ + public static final PaperSize ENVELOPE_12 = new PaperSize(22); + /** + * Envelope #14 5" x 11.5" + */ + public static final PaperSize ENVELOPE_14 = new PaperSize(23); + /** + * C 17" x 22" + */ + public static final PaperSize C = new PaperSize(24); + /** + * D 22" x 34" + */ + public static final PaperSize D = new PaperSize(25); + /** + * E 34" x 44" + */ + public static final PaperSize E = new PaperSize(26); + /** + * Envelope DL 110mm � 220mm + */ + public static final PaperSize ENVELOPE_DL = new PaperSize(27); + /** + * Envelope C5 162mm � 229mm + */ + public static final PaperSize ENVELOPE_C5 = new PaperSize(28); + /** + * Envelope C3 324mm � 458mm + */ + public static final PaperSize ENVELOPE_C3 = new PaperSize(29); + /** + * Envelope C4 229mm � 324mm + */ + public static final PaperSize ENVELOPE_C4 = new PaperSize(30); + /** + * Envelope C6 114mm � 162mm + */ + public static final PaperSize ENVELOPE_C6 = new PaperSize(31); + /** + * Envelope C6/C5 114mm � 229mm + */ + public static final PaperSize ENVELOPE_C6_C5 = new PaperSize(32); + /** + * B4 (ISO) 250mm � 353mm + */ + public static final PaperSize B4_ISO = new PaperSize(33); + /** + * B5 (ISO) 176mm � 250mm + */ + public static final PaperSize B5_ISO = new PaperSize(34); + /** + * B6 (ISO) 125mm � 176mm + */ + public static final PaperSize B6_ISO = new PaperSize(35); + /** + * Envelope Italy 110mm � 230mm + */ + public static final PaperSize ENVELOPE_ITALY = new PaperSize(36); + /** + * Envelope Monarch 3 7/8" � 7.5" + */ + public static final PaperSize ENVELOPE_MONARCH = new PaperSize(37); + /** + * 6.75 Envelope 3 5/8" � 6.5" + */ + public static final PaperSize ENVELOPE_6_75 = new PaperSize(38); + /** + * US Standard Fanfold 14 7/8" � 11" + */ + public static final PaperSize US_FANFOLD = new PaperSize(39); + /** + * German Std. Fanfold 8.5" � 12" + */ + public static final PaperSize GERMAN_FANFOLD = new PaperSize(40); + /** + * German Legal Fanfold 8.5" � 13" + */ + public static final PaperSize GERMAN_LEGAL_FANFOLD = new PaperSize(41); + /** + * B4 (ISO) 250mm � 353mm + */ + public static final PaperSize B4_ISO_2 = new PaperSize(42); + /** + * Japanese Postcard 100mm � 148mm + */ + public static final PaperSize JAPANESE_POSTCARD = new PaperSize(43); + /** + * 9�11 9" � 11" + */ + public static final PaperSize SIZE_9x11 = new PaperSize(44); + /** + * 10�11 10" � 11" + */ + public static final PaperSize SIZE_10x11 = new PaperSize(45); + /** + * 15�11 15" � 11" + */ + public static final PaperSize SIZE_15x11 = new PaperSize(46); + /** + * Envelope Invite 220mm � 220mm + */ + public static final PaperSize ENVELOPE_INVITE = new PaperSize(47); + /** + * Letter Extra 9.5" � 12" + */ + public static final PaperSize LETTER_EXTRA = new PaperSize(50); + /** + * Legal Extra 9.5" � 15" + */ + public static final PaperSize LEGAL_EXTRA = new PaperSize(51); + /** + * Tabloid Extra 11 11/16" � 18" + */ + public static final PaperSize TABLOID_EXTRA = new PaperSize(52); + /** + * A4 Extra 235mm � 322mm + */ + public static final PaperSize A4_EXTRA = new PaperSize(53); + /** + * Letter Transverse 8.5" � 11" + */ + public static final PaperSize LETTER_TRANSVERSE = new PaperSize(54); + /** + * A4 Transverse 210mm � 297mm + */ + public static final PaperSize A4_TRANSVERSE = new PaperSize(55); + /** + * Letter Extra Transv. 9.5" � 12" + */ + public static final PaperSize LETTER_EXTRA_TRANSVERSE = new PaperSize(56); + + /* 48 & 49 Undefined */ + /** + * Super A/A4 227mm � 356mm + */ + public static final PaperSize SUPER_A_A4 = new PaperSize(57); + /** + * Super B/A3 305mm � 487mm + */ + public static final PaperSize SUPER_B_A3 = new PaperSize(58); + /** + * Letter Plus 8.5" x 12 11/16" + */ + public static final PaperSize LETTER_PLUS = new PaperSize(59); + /** + * A4 Plus 210mm � 330mm + */ + public static final PaperSize A4_PLUS = new PaperSize(60); + /** + * A5 Transverse 148mm � 210mm + */ + public static final PaperSize A5_TRANSVERSE = new PaperSize(61); + /** + * B5 (JIS) Transverse 182mm � 257mm + */ + public static final PaperSize B5_TRANSVERSE = new PaperSize(62); + /** + * A3 Extra 322mm � 445mm + */ + public static final PaperSize A3_EXTRA = new PaperSize(63); + /** + * A5 Extra 174mm � 235mm + */ + public static final PaperSize A5_EXTRA = new PaperSize(64); + /** + * B5 (ISO) Extra 201mm � 276mm + */ + public static final PaperSize B5_EXTRA = new PaperSize(65); + /** + * A2 420mm � 594mm + */ + public static final PaperSize A2 = new PaperSize(66); + /** + * A3 Transverse 297mm � 420mm + */ + public static final PaperSize A3_TRANSVERSE = new PaperSize(67); + /** + * A3 Extra Transverse 322mm � 445mm + */ + public static final PaperSize A3_EXTRA_TRANSVERSE = new PaperSize(68); + /** + * Dbl. Japanese Postcard 200mm � 148mm + */ + public static final PaperSize DOUBLE_JAPANESE_POSTCARD = new PaperSize(69); + /** + * A6 105mm � 148mm + */ + public static final PaperSize A6 = new PaperSize(70); + /** + * Letter Rotated 11" � 8.5" + */ + public static final PaperSize LETTER_ROTATED = new PaperSize(75); + /** + * A3 Rotated 420mm � 297mm + */ + public static final PaperSize A3_ROTATED = new PaperSize(76); + /** + * A4 Rotated 297mm � 210mm + */ + public static final PaperSize A4_ROTATED = new PaperSize(77); + /** + * A5 Rotated 210mm � 148mm + */ + public static final PaperSize A5_ROTATED = new PaperSize(78); + /** + * B4 (JIS) Rotated 364mm � 257mm + */ + public static final PaperSize B4_ROTATED = new PaperSize(79); + /** + * B5 (JIS) Rotated 257mm � 182mm + */ + public static final PaperSize B5_ROTATED = new PaperSize(80); + /** + * Japanese Postcard Rot. 148mm � 100mm + */ + public static final PaperSize JAPANESE_POSTCARD_ROTATED = new PaperSize(81); + + /* 71 - 74 undefined */ + /** + * Dbl. Jap. Postcard Rot. 148mm � 200mm + */ + public static final PaperSize DOUBLE_JAPANESE_POSTCARD_ROTATED = new PaperSize(82); + /** + * A6 Rotated 148mm � 105mm + */ + public static final PaperSize A6_ROTATED = new PaperSize(83); + /** + * B6 (JIS) 128mm � 182mm + */ + public static final PaperSize B6 = new PaperSize(88); + /** + * B6 (JIS) Rotated 182mm � 128mm + */ + public static final PaperSize B6_ROTATED = new PaperSize(89); + private static final int LAST_PAPER_SIZE = 89; + /** + * The paper sizes + */ + private static PaperSize[] paperSizes = new PaperSize[LAST_PAPER_SIZE + 1]; + /** + * The excel encoding + */ + private final int val; + + /** + * Constructor + */ + private PaperSize(int v, boolean growArray) { + val = v; + + if (v >= paperSizes.length && growArray) { + // Grow the array and add this to it + PaperSize[] newarray = new PaperSize[v + 1]; + System.arraycopy(paperSizes, 0, newarray, 0, paperSizes.length); + paperSizes = newarray; + } + if (v < paperSizes.length) { + paperSizes[v] = this; + } + } + + /** + * Constructor + */ + private PaperSize(int v) { + this(v, true); + } + + /* 84 - 87 undefined */ + + /** + * Gets the paper size for a specific value + * + * @param val the value + * @return the paper size + */ + public static PaperSize getPaperSize(int val) { + PaperSize p = val > paperSizes.length - 1 ? null : paperSizes[val]; + return p == null ? new PaperSize(val, false) : p; + } + + /** + * Accessor for the internal binary value association with this paper size + * + * @return the internal value + */ + public int getValue() { + return val; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/format/Pattern.java b/datastructures-xslx/src/main/java/jxl/format/Pattern.java new file mode 100755 index 0000000..30fdd90 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Pattern.java @@ -0,0 +1,120 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + + +/** + * Enumeration class which contains the various patterns available within + * the standard Excel pattern palette + */ +public /*final*/ class Pattern { + public final static Pattern NONE = new Pattern(0x0, "None"); + public final static Pattern SOLID = new Pattern(0x1, "Solid"); + public final static Pattern GRAY_50 = new Pattern(0x2, "Gray 50%"); + public final static Pattern GRAY_75 = new Pattern(0x3, "Gray 75%"); + public final static Pattern GRAY_25 = new Pattern(0x4, "Gray 25%"); + public final static Pattern PATTERN1 = new Pattern(0x5, "Pattern 1"); + public final static Pattern PATTERN2 = new Pattern(0x6, "Pattern 2"); + public final static Pattern PATTERN3 = new Pattern(0x7, "Pattern 3"); + public final static Pattern PATTERN4 = new Pattern(0x8, "Pattern 4"); + public final static Pattern PATTERN5 = new Pattern(0x9, "Pattern 5"); + public final static Pattern PATTERN6 = new Pattern(0xa, "Pattern 6"); + public final static Pattern PATTERN7 = new Pattern(0xb, "Pattern 7"); + public final static Pattern PATTERN8 = new Pattern(0xc, "Pattern 8"); + public final static Pattern PATTERN9 = new Pattern(0xd, "Pattern 9"); + public final static Pattern PATTERN10 = new Pattern(0xe, "Pattern 10"); + public final static Pattern PATTERN11 = new Pattern(0xf, "Pattern 11"); + public final static Pattern PATTERN12 = new Pattern(0x10, "Pattern 12"); + public final static Pattern PATTERN13 = new Pattern(0x11, "Pattern 13"); + public final static Pattern PATTERN14 = new Pattern(0x12, "Pattern 14"); + /** + * The list of patterns + */ + private static Pattern[] patterns = new Pattern[0]; + /** + * The internal numerical representation of the colour + */ + private final int value; + /** + * The textual description + */ + private final String string; + /** + * Private constructor + * + * @param val + * @param s + */ + protected Pattern(int val, String s) { + value = val; + string = s; + + Pattern[] oldcols = patterns; + patterns = new Pattern[oldcols.length + 1]; + System.arraycopy(oldcols, 0, patterns, 0, oldcols.length); + patterns[oldcols.length] = this; + } + + /** + * Gets the pattern from the value + * + * @param val + * @return the pattern with that value + */ + public static Pattern getPattern(int val) { + for (int i = 0; i < patterns.length; i++) { + if (patterns[i].getValue() == val) { + return patterns[i]; + } + } + + return NONE; + } + + /** + * Gets the value of this pattern. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the textual description + * + * @return the string + */ + public String getDescription() { + return string; + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/RGB.java b/datastructures-xslx/src/main/java/jxl/format/RGB.java new file mode 100755 index 0000000..9dbd994 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/RGB.java @@ -0,0 +1,82 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + + +package jxl.format; + +/** + * A structure which contains the RGB values for a particular colour + */ +public final class RGB { + /** + * The red component of this colour + */ + private final int red; + + /** + * The green component of this colour + */ + private final int green; + + /** + * The blue component of this colour + */ + private final int blue; + + /** + * Constructor + * + * @param r the red component + * @param g the green component + * @param b the blue component + */ + public RGB(int r, int g, int b) { + red = r; + green = g; + blue = b; + } + + /** + * Accessor for the red component + * + * @return the red component of the colour, between 0 and 255 + */ + public int getRed() { + return red; + } + + /** + * Accessor for the green component + * + * @return the green component of the colour, between 0 and 255 + */ + public int getGreen() { + return green; + } + + /** + * Accessor for the blue component + * + * @return the blue component of the colour, between 0 and 255 + */ + public int getBlue() { + return blue; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/format/ScriptStyle.java b/datastructures-xslx/src/main/java/jxl/format/ScriptStyle.java new file mode 100755 index 0000000..b09b789 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/ScriptStyle.java @@ -0,0 +1,108 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various script styles available + * within the standard Excel ScriptStyle palette + */ +public final class ScriptStyle { + // The script styles + public static final ScriptStyle NORMAL_SCRIPT = new ScriptStyle(0, "normal"); + public static final ScriptStyle SUPERSCRIPT = new ScriptStyle(1, "super"); + public static final ScriptStyle SUBSCRIPT = new ScriptStyle(2, "sub"); + /** + * The list of ScriptStyles + */ + private static ScriptStyle[] styles = new ScriptStyle[0]; + /** + * The internal numerical representation of the ScriptStyle + */ + private final int value; + /** + * The display string for the script style. Used when presenting the + * format information + */ + private final String string; + + /** + * Private constructor + * + * @param val + * @param s the display string + */ + protected ScriptStyle(int val, String s) { + value = val; + string = s; + + ScriptStyle[] oldstyles = styles; + styles = new ScriptStyle[oldstyles.length + 1]; + System.arraycopy(oldstyles, 0, styles, 0, oldstyles.length); + styles[oldstyles.length] = this; + } + + /** + * Gets the ScriptStyle from the value + * + * @param val + * @return the ScriptStyle with that value + */ + public static ScriptStyle getStyle(int val) { + for (int i = 0; i < styles.length; i++) { + if (styles[i].getValue() == val) { + return styles[i]; + } + } + + return NORMAL_SCRIPT; + } + + /** + * Gets the value of this style. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the string description for display purposes + * + * @return the string description + */ + public String getDescription() { + return string; + } + + +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/UnderlineStyle.java b/datastructures-xslx/src/main/java/jxl/format/UnderlineStyle.java new file mode 100755 index 0000000..0e34a39 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/UnderlineStyle.java @@ -0,0 +1,113 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various underline styles available + * within the standard Excel UnderlineStyle palette + */ +public final class UnderlineStyle { + // The underline styles + public static final UnderlineStyle NO_UNDERLINE = + new UnderlineStyle(0, "none"); + public static final UnderlineStyle SINGLE = + new UnderlineStyle(1, "single"); + public static final UnderlineStyle DOUBLE = + new UnderlineStyle(2, "double"); + public static final UnderlineStyle SINGLE_ACCOUNTING = + new UnderlineStyle(0x21, "single accounting"); + public static final UnderlineStyle DOUBLE_ACCOUNTING = + new UnderlineStyle(0x22, "double accounting"); + /** + * The list of UnderlineStyles + */ + private static UnderlineStyle[] styles = new UnderlineStyle[0]; + /** + * The internal numerical representation of the UnderlineStyle + */ + private final int value; + /** + * The display string for the underline style. Used when presenting the + * format information + */ + private final String string; + + /** + * Private constructor + * + * @param val + * @param s the display string + */ + protected UnderlineStyle(int val, String s) { + value = val; + string = s; + + UnderlineStyle[] oldstyles = styles; + styles = new UnderlineStyle[oldstyles.length + 1]; + System.arraycopy(oldstyles, 0, styles, 0, oldstyles.length); + styles[oldstyles.length] = this; + } + + /** + * Gets the UnderlineStyle from the value + * + * @param val + * @return the UnderlineStyle with that value + */ + public static UnderlineStyle getStyle(int val) { + for (int i = 0; i < styles.length; i++) { + if (styles[i].getValue() == val) { + return styles[i]; + } + } + + return NO_UNDERLINE; + } + + /** + * Gets the value of this style. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the string description for display purposes + * + * @return the string description + */ + public String getDescription() { + return string; + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/VerticalAlignment.java b/datastructures-xslx/src/main/java/jxl/format/VerticalAlignment.java new file mode 100755 index 0000000..028beb7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/VerticalAlignment.java @@ -0,0 +1,107 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the vertical alignment of data within a cell + */ +public /*final*/ class VerticalAlignment { + /** + * Cells with this specified vertical alignment will have their data + * aligned at the top + */ + public static VerticalAlignment TOP = new VerticalAlignment(0, "top"); + /** + * Cells with this specified vertical alignment will have their data + * aligned centrally + */ + public static VerticalAlignment CENTRE = new VerticalAlignment(1, "centre"); + /** + * Cells with this specified vertical alignment will have their data + * aligned at the bottom + */ + public static VerticalAlignment BOTTOM = new VerticalAlignment(2, "bottom"); + /** + * Cells with this specified vertical alignment will have their data + * justified + */ + public static VerticalAlignment JUSTIFY = new VerticalAlignment(3, "Justify"); + /** + * The list of alignments + */ + private static VerticalAlignment[] alignments = new VerticalAlignment[0]; + /** + * The internal binary value which gets written to the generated Excel file + */ + private final int value; + /** + * The textual description + */ + private final String string; + + + /** + * Constructor + * + * @param val + */ + protected VerticalAlignment(int val, String s) { + value = val; + string = s; + + VerticalAlignment[] oldaligns = alignments; + alignments = new VerticalAlignment[oldaligns.length + 1]; + System.arraycopy(oldaligns, 0, alignments, 0, oldaligns.length); + alignments[oldaligns.length] = this; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static VerticalAlignment getAlignment(int val) { + for (int i = 0; i < alignments.length; i++) { + if (alignments[i].getValue() == val) { + return alignments[i]; + } + } + + return BOTTOM; + } + + /** + * Accessor for the binary value + * + * @return the internal binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the textual description + */ + public String getDescription() { + return string; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BOFRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BOFRecord.java new file mode 100755 index 0000000..3086e37 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BOFRecord.java @@ -0,0 +1,159 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A Beginning Of File record, found at the commencement of all substreams + * within a biff8 file + */ +public class BOFRecord extends RecordData { + /** + * The code used for biff8 files + */ + private static final int Biff8 = 0x600; + /** + * The code used for biff8 files + */ + private static final int Biff7 = 0x500; + /** + * The code used for workbook globals + */ + private static final int WorkbookGlobals = 0x5; + /** + * The code used for worksheets + */ + private static final int Worksheet = 0x10; + /** + * The code used for charts + */ + private static final int Chart = 0x20; + /** + * The code used for macro sheets + */ + private static final int MacroSheet = 0x40; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(BOFRecord.class); + /** + * The biff version of this substream + */ + private final int version; + /** + * The type of this substream + */ + private final int substreamType; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + BOFRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + version = IntegerHelper.getInt(data[0], data[1]); + substreamType = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Interrogates this object to see if it is a biff8 substream + * + * @return TRUE if this substream is biff8, false otherwise + */ + public boolean isBiff8() { + return version == Biff8; + } + + /** + * Interrogates this object to see if it is a biff7 substream + * + * @return TRUE if this substream is biff7, false otherwise + */ + public boolean isBiff7() { + return version == Biff7; + } + + + /** + * Interrogates this substream to see if it represents the commencement of + * the workbook globals substream + * + * @return TRUE if this is the commencement of a workbook globals substream, + * FALSE otherwise + */ + boolean isWorkbookGlobals() { + return substreamType == WorkbookGlobals; + } + + /** + * Interrogates the substream to see if it is the commencement of a worksheet + * + * @return TRUE if this substream is the beginning of a worksheet, FALSE + * otherwise + */ + public boolean isWorksheet() { + return substreamType == Worksheet; + } + + /** + * Interrogates the substream to see if it is the commencement of a worksheet + * + * @return TRUE if this substream is the beginning of a worksheet, FALSE + * otherwise + */ + public boolean isMacroSheet() { + return substreamType == MacroSheet; + } + + /** + * Interrogates the substream to see if it is a chart + * + * @return TRUE if this substream is the beginning of a worksheet, FALSE + * otherwise + */ + public boolean isChart() { + return substreamType == Chart; + } + + /** + * Gets the length of the data portion of this record + * Used to adjust when reading sheets which contain just a chart + * + * @return the length of the data portion of this record + */ + int getLength() { + return getRecord().getLength(); + } + +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BaseSharedFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BaseSharedFormulaRecord.java new file mode 100755 index 0000000..9cb622b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BaseSharedFormulaRecord.java @@ -0,0 +1,164 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A base class for shared formula records + */ +public abstract class BaseSharedFormulaRecord extends CellValue + implements FormulaData { + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The position of the next record in the file. Used when looking for + * for subsequent records eg. a string value + */ + private final int filePos; + + /** + * The array of parsed tokens + */ + private byte[] tokens; + + /** + * The external sheet + */ + private final ExternalSheet externalSheet; + + /** + * The name table + */ + private final WorkbookMethods nameTable; + + /** + * Constructs this number + * + * @param t the record + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + * @param pos the position of the next record in the file + */ + public BaseSharedFormulaRecord(Record t, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + int pos) { + super(t, fr, si); + externalSheet = es; + nameTable = nt; + filePos = pos; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + + /** + * Accessor for the tokens which make up this formula + * + * @return the tokens + */ + protected final byte[] getTokens() { + return tokens; + } + + /** + * Called by the shared formula record to set the tokens for + * this formula + * + * @param t the tokens + */ + void setTokens(byte[] t) { + tokens = t; + } + + /** + * Access for the external sheet + * + * @return the external sheet + */ + protected final ExternalSheet getExternalSheet() { + return externalSheet; + } + + /** + * Access for the name table + * + * @return the name table + */ + protected final WorkbookMethods getNameTable() { + return nameTable; + } + + /** + * In case the shared formula is not added for any reason, we need + * to expose the raw record data , in order to try again + * + * @return the record data from the base class + */ + public Record getRecord() { + return super.getRecord(); + } + + /** + * Accessor for the position of the next record + * + * @return the position of the next record + */ + final int getFilePos() { + return filePos; + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BiffException.java b/datastructures-xslx/src/main/java/jxl/read/biff/BiffException.java new file mode 100755 index 0000000..92b1cd9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BiffException.java @@ -0,0 +1,96 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.JXLException; + +/** + * Exception thrown when reading a biff file + */ +public class BiffException extends JXLException { + /** + * + */ + static final BiffMessage unrecognizedBiffVersion = + new BiffMessage("Unrecognized biff version"); + /** + * + */ + static final BiffMessage expectedGlobals = + new BiffMessage("Expected globals"); + /** + * + */ + static final BiffMessage excelFileTooBig = + new BiffMessage("Not all of the excel file could be read"); + /** + * + */ + static final BiffMessage excelFileNotFound = + new BiffMessage("The input file was not found"); + /** + * + */ + static final BiffMessage unrecognizedOLEFile = + new BiffMessage("Unable to recognize OLE stream"); + /** + * + */ + static final BiffMessage streamNotFound = + new BiffMessage("Compound file does not contain the specified stream"); + /** + * + */ + static final BiffMessage passwordProtected = + new BiffMessage("The workbook is password protected"); + /** + * + */ + static final BiffMessage corruptFileFormat = + new BiffMessage("The file format is corrupt"); + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + public BiffException(BiffMessage m) { + super(m.message); + } + + /** + * Inner class containing the various error messages + */ + private static class BiffMessage { + /** + * The formatted message + */ + public String message; + + /** + * Constructs this exception with the specified message + * + * @param m the messageA + */ + BiffMessage(String m) { + message = m; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BiffRecordReader.java b/datastructures-xslx/src/main/java/jxl/read/biff/BiffRecordReader.java new file mode 100755 index 0000000..da2bca4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BiffRecordReader.java @@ -0,0 +1,74 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +/** + * Serves up Record objects from a biff file. This object is used by the + * demo programs BiffDump and ... only and has no influence whatsoever on + * the JExcelApi reading and writing of excel sheets + */ +public class BiffRecordReader { + /** + * The biff file + */ + private final File file; + + /** + * The current record retrieved + */ + private Record record; + + /** + * Constructor + * + * @param f the biff file + */ + public BiffRecordReader(File f) { + file = f; + } + + /** + * Sees if there are any more records to read + * + * @return TRUE if there are more records, FALSE otherwise + */ + public boolean hasNext() { + return file.hasNext(); + } + + /** + * Gets the next record + * + * @return the next record + */ + public Record next() { + record = file.next(); + return record; + } + + /** + * Gets the position of the current record in the biff file + * + * @return the position + */ + public int getPos() { + return file.getPos() - record.getLength() - 4; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BlankCell.java b/datastructures-xslx/src/main/java/jxl/read/biff/BlankCell.java new file mode 100755 index 0000000..736a678 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BlankCell.java @@ -0,0 +1,61 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.biff.FormattingRecords; + +/** + * A blank cell. Despite the fact that this cell has no contents, it + * has formatting information applied to it + */ +public class BlankCell extends CellValue { + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the available formats + * @param si the sheet + */ + BlankCell(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + } + + /** + * Returns the contents of this cell as an empty string + * + * @return the value formatted into a string + */ + public String getContents() { + return ""; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.EMPTY; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BooleanFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BooleanFormulaRecord.java new file mode 100755 index 0000000..977264f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BooleanFormulaRecord.java @@ -0,0 +1,157 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.BooleanCell; +import jxl.BooleanFormulaCell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; + +/** + * A boolean formula's last calculated value + */ +class BooleanFormulaRecord extends CellValue + implements BooleanCell, FormulaData, BooleanFormulaCell { + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The raw data + */ + private final byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param es the sheet + * @param nt the name table + */ + public BooleanFormulaRecord(Record t, FormattingRecords fr, + ExternalSheet es, WorkbookMethods nt, + SheetImpl si) { + super(t, fr, si); + externalSheet = es; + nameTable = nt; + value = false; + + data = getRecord().getData(); + + Assert.verify(data[6] != 2); + + value = data[8] == 1; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.BOOLEAN_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BooleanRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BooleanRecord.java new file mode 100755 index 0000000..715be90 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BooleanRecord.java @@ -0,0 +1,121 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.BooleanCell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.common.Assert; + +/** + * A boolean cell last calculated value + */ +class BooleanRecord extends CellValue implements BooleanCell { + /** + * Indicates whether this cell contains an error or a boolean + */ + private boolean error; + + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + */ + public BooleanRecord(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + error = false; + value = false; + + byte[] data = getRecord().getData(); + + error = (data[7] == 1); + + if (!error) { + value = data[6] == 1; + } + } + + /** + * Interface method which queries whether this cell contains an error. + * Returns TRUE if it does, otherwise returns FALSE. + * + * @return TRUE if this cell is an error, FALSE otherwise + */ + public boolean isError() { + return error; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + Assert.verify(!isError()); + + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.BOOLEAN; + } + + /** + * A special case which overrides the method in the subclass to get + * hold of the raw data + * + * @return the record + */ + public Record getRecord() { + return super.getRecord(); + } +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BottomMarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BottomMarginRecord.java new file mode 100755 index 0000000..1991023 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BottomMarginRecord.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class BottomMarginRecord extends MarginRecord { + /** + * Constructor + * + * @param r the record + */ + BottomMarginRecord(Record r) { + super(Type.BOTTOMMARGIN, r); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BoundsheetRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BoundsheetRecord.java new file mode 100755 index 0000000..3d49c64 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BoundsheetRecord.java @@ -0,0 +1,146 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * A boundsheet record, which contains the worksheet name + */ +class BoundsheetRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The offset into the sheet + */ + private final int offset; + /** + * The type of sheet this is + */ + private final byte typeFlag; + /** + * The visibility flag + */ + private final byte visibilityFlag; + /** + * The length of the worksheet name + */ + private final int length; + /** + * The worksheet name + */ + private final String name; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param s the workbook settings + */ + public BoundsheetRecord(Record t, WorkbookSettings s) { + super(t); + byte[] data = getRecord().getData(); + offset = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); + typeFlag = data[5]; + visibilityFlag = data[4]; + length = data[6]; + + if (data[7] == 0) { + // Standard ASCII encoding + byte[] bytes = new byte[length]; + System.arraycopy(data, 8, bytes, 0, length); + name = StringHelper.getString(bytes, length, 0, s); + } else { + // little endian Unicode encoding + byte[] bytes = new byte[length * 2]; + System.arraycopy(data, 8, bytes, 0, length * 2); + name = StringHelper.getUnicodeString(bytes, length, 0); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param biff7 a dummy value to tell the record to interpret the + * data as biff7 + */ + public BoundsheetRecord(Record t, Biff7 biff7) { + super(t); + byte[] data = getRecord().getData(); + offset = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); + typeFlag = data[5]; + visibilityFlag = data[4]; + length = data[6]; + byte[] bytes = new byte[length]; + System.arraycopy(data, 7, bytes, 0, length); + name = new String(bytes); + } + + /** + * Accessor for the worksheet name + * + * @return the worksheet name + */ + public String getName() { + return name; + } + + /** + * Accessor for the hidden flag + * + * @return TRUE if this is a hidden sheet, FALSE otherwise + */ + public boolean isHidden() { + return visibilityFlag != 0; + } + + /** + * Accessor to determine if this is a worksheet, or some other nefarious + * type of object + * + * @return TRUE if this is a worksheet, FALSE otherwise + */ + public boolean isSheet() { + return typeFlag == 0; + } + + /** + * Accessor to determine if this is a chart + * + * @return TRUE if this is a chart, FALSE otherwise + */ + public boolean isChart() { + return typeFlag == 2; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } + +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ButtonPropertySetRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ButtonPropertySetRecord.java new file mode 100755 index 0000000..e1606d3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ButtonPropertySetRecord.java @@ -0,0 +1,53 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Data associated with a button property set + */ +public class ButtonPropertySetRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(ButtonPropertySetRecord.class); + + + /** + * Constructor + * + * @param t the record + */ + ButtonPropertySetRecord(Record t) { + super(t); + } + + /** + * Accessor for the binary data + * + * @return the binary data + */ + public byte[] getData() { + return getRecord().getData(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CalcModeRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/CalcModeRecord.java new file mode 100755 index 0000000..ef1405b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CalcModeRecord.java @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A calculation mode record + */ +class CalcModeRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CalcModeRecord.class); + + /** + * The calculation mode + */ + private final boolean automatic; + + /** + * Constructor + * + * @param t the record + */ + public CalcModeRecord(Record t) { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + automatic = (mode == 1); + } + + /** + * Accessor for the calculation mode + * + * @return the calculation mode + */ + public boolean isAutomatic() { + return automatic; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CellFeaturesAccessor.java b/datastructures-xslx/src/main/java/jxl/read/biff/CellFeaturesAccessor.java new file mode 100755 index 0000000..d9a47d0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CellFeaturesAccessor.java @@ -0,0 +1,41 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellFeatures; + +/** + * Allows the setting of the cell features in this package only + */ +interface CellFeaturesAccessor { + /** + * Convenience function (due to casting) to get the cell features + * + * @return the cell features + */ + CellFeatures getCellFeatures(); + + /** + * Sets the cell features + * + * @param cf the cell features + */ + void setCellFeatures(CellFeatures cf); +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CellValue.java b/datastructures-xslx/src/main/java/jxl/read/biff/CellValue.java new file mode 100755 index 0000000..8391dd6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CellValue.java @@ -0,0 +1,191 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.XFRecord; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * Abstract class for all records which actually contain cell values + */ +public abstract class CellValue extends RecordData + implements Cell, CellFeaturesAccessor { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CellValue.class); + + /** + * The row number of this cell record + */ + private final int row; + + /** + * The column number of this cell record + */ + private final int column; + + /** + * The XF index + */ + private final int xfIndex; + + /** + * A handle to the formatting records, so that we can + * retrieve the formatting information + */ + private final FormattingRecords formattingRecords; + + /** + * A lazy initialize flag for the cell format + */ + private boolean initialized; + + /** + * The cell format + */ + private XFRecord format; + + /** + * A handle back to the sheet + */ + private final SheetImpl sheet; + + /** + * The cell features + */ + private CellFeatures features; + + /** + * Constructs this object from the raw cell data + * + * @param t the raw cell data + * @param fr the formatting records + * @param si the sheet containing this cell + */ + protected CellValue(Record t, FormattingRecords fr, SheetImpl si) { + super(t); + byte[] data = getRecord().getData(); + row = IntegerHelper.getInt(data[0], data[1]); + column = IntegerHelper.getInt(data[2], data[3]); + xfIndex = IntegerHelper.getInt(data[4], data[5]); + sheet = si; + formattingRecords = fr; + initialized = false; + } + + /** + * Interface method which returns the row number of this cell + * + * @return the zero base row number + */ + public final int getRow() { + return row; + } + + /** + * Interface method which returns the column number of this cell + * + * @return the zero based column number + */ + public final int getColumn() { + return column; + } + + /** + * Gets the XFRecord corresponding to the index number. Used when + * copying a spreadsheet + * + * @return the xf index for this cell + */ + public final int getXFIndex() { + return xfIndex; + } + + /** + * Gets the CellFormat object for this cell. Used by the WritableWorkbook + * API + * + * @return the CellFormat used for this cell + */ + public CellFormat getCellFormat() { + if (!initialized) { + format = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return format; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && (cir.getWidth() == 0 || cir.getHidden())) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Accessor for the sheet + * + * @return the sheet + */ + protected SheetImpl getSheet() { + return sheet; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features during the reading process + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) { + if (features != null) { + logger.warn("current cell features not null - overwriting"); + } + + features = cf; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CentreRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/CentreRecord.java new file mode 100755 index 0000000..b0fec80 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CentreRecord.java @@ -0,0 +1,53 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan, Adam Caldwell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Record which indicates the whether the horizontal center option has been set + */ +class CentreRecord extends RecordData { + /** + * The centre flag + */ + private final boolean centre; + + /** + * Constructor + * + * @param t the record to constructfrom + */ + public CentreRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + centre = IntegerHelper.getInt(data[0], data[1]) != 0; + } + + /** + * Accessor for the centre flag + * + * @return Returns the centre flag. + */ + public boolean isCentre() { + return centre; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CodepageRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/CodepageRecord.java new file mode 100755 index 0000000..529435f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CodepageRecord.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A codepage record + */ +class CodepageRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CodepageRecord.class); + + /** + * The character encoding + */ + private final int characterSet; + + /** + * Constructor + * + * @param t the record + */ + public CodepageRecord(Record t) { + super(t); + byte[] data = t.getData(); + characterSet = IntegerHelper.getInt(data[0], data[1]); + } + + /** + * Accessor for the encoding + * + * @return the character encoding + */ + public int getCharacterSet() { + return characterSet; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ColumnInfoRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ColumnInfoRecord.java new file mode 100755 index 0000000..a1d9d66 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ColumnInfoRecord.java @@ -0,0 +1,155 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Contains the display info data which affects the entire columns + */ +public class ColumnInfoRecord extends RecordData { + /** + * The raw data + */ + private final byte[] data; + + /** + * The start for which to apply the format information + */ + private final int startColumn; + + /** + * The end column for which to apply the format information + */ + private final int endColumn; + + /** + * The index to the XF record, which applies to each cell in this column + */ + private final int xfIndex; + + /** + * The width of the column in 1/256 of a character + */ + private final int width; + + /** + * A hidden flag + */ + private final boolean hidden; + + /** + * The column's outline level + */ + private final int outlineLevel; + + /** + * The column collapsed flag + */ + private final boolean collapsed; + + /** + * Constructor which creates this object from the binary data + * + * @param t the record + */ + ColumnInfoRecord(Record t) { + super(Type.COLINFO); + + data = t.getData(); + + startColumn = IntegerHelper.getInt(data[0], data[1]); + endColumn = IntegerHelper.getInt(data[2], data[3]); + width = IntegerHelper.getInt(data[4], data[5]); + xfIndex = IntegerHelper.getInt(data[6], data[7]); + + int options = IntegerHelper.getInt(data[8], data[9]); + hidden = ((options & 0x1) != 0); + outlineLevel = ((options & 0x700) >> 8); + collapsed = ((options & 0x1000) != 0); + } + + /** + * Accessor for the start column of this range + * + * @return the start column index + */ + public int getStartColumn() { + return startColumn; + } + + /** + * Accessor for the end column of this range + * + * @return the end column index + */ + public int getEndColumn() { + return endColumn; + } + + /** + * Accessor for the column format index + * + * @return the format index + */ + public int getXFIndex() { + return xfIndex; + } + + /** + * Accessor for the column's outline level + * + * @return the column's outline level + */ + public int getOutlineLevel() { + return outlineLevel; + } + + /** + * Accessor for whether the column is collapsed + * + * @return the column's collapsed state + */ + public boolean getCollapsed() { + return collapsed; + } + + /** + * Accessor for the width of the column + * + * @return the width + */ + public int getWidth() { + return width; + } + + /** + * Accessor for the hidden flag. Used when copying sheets + * + * @return TRUE if the columns are hidden, FALSE otherwise + */ + public boolean getHidden() { + return hidden; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CompoundFile.java b/datastructures-xslx/src/main/java/jxl/read/biff/CompoundFile.java new file mode 100755 index 0000000..da46167 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CompoundFile.java @@ -0,0 +1,561 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.WorkbookSettings; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * Reads in and defrags an OLE compound compound file + * (Made public only for the PropertySets demo) + */ +public final class CompoundFile extends BaseCompoundFile { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The original OLE stream, organized into blocks, which can + * appear at any physical location in the file + */ + private final byte[] data; + /** + * The number of blocks it takes to store the big block depot + */ + private final int numBigBlockDepotBlocks; + /** + * The start block of the small block depot + */ + private final int sbdStartBlock; + /** + * The start block of the root entry + */ + private final int rootStartBlock; + /** + * The header extension block + */ + private int extensionBlock; + /** + * The number of header extension blocks + */ + private final int numExtensionBlocks; + /** + * The root entry + */ + private final byte[] rootEntry; + /** + * The sequence of blocks which comprise the big block chain + */ + private int[] bigBlockChain; + /** + * The sequence of blocks which comprise the small block chain + */ + private int[] smallBlockChain; + /** + * The chain of blocks which comprise the big block depot + */ + private final int[] bigBlockDepotBlocks; + /** + * The list of property sets + */ + private final ArrayList propertySets; + + /** + * The workbook settings + */ + private final WorkbookSettings settings; + + /** + * The property storage root entry + */ + private PropertyStorage rootEntryPropertyStorage; + + /** + * Initializes the compound file + * + * @param d the raw data of the ole stream + * @param ws the workbook settings + * @throws BiffException + */ + public CompoundFile(byte[] d, WorkbookSettings ws) throws BiffException { + super(); + data = d; + settings = ws; + + // First verify the OLE identifier + for (int i = 0; i < IDENTIFIER.length; i++) { + if (data[i] != IDENTIFIER[i]) { + throw new BiffException(BiffException.unrecognizedOLEFile); + } + } + + propertySets = new ArrayList(); + numBigBlockDepotBlocks = IntegerHelper.getInt + (data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS], + data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS + 1], + data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS + 2], + data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS + 3]); + sbdStartBlock = IntegerHelper.getInt + (data[SMALL_BLOCK_DEPOT_BLOCK_POS], + data[SMALL_BLOCK_DEPOT_BLOCK_POS + 1], + data[SMALL_BLOCK_DEPOT_BLOCK_POS + 2], + data[SMALL_BLOCK_DEPOT_BLOCK_POS + 3]); + rootStartBlock = IntegerHelper.getInt + (data[ROOT_START_BLOCK_POS], + data[ROOT_START_BLOCK_POS + 1], + data[ROOT_START_BLOCK_POS + 2], + data[ROOT_START_BLOCK_POS + 3]); + extensionBlock = IntegerHelper.getInt + (data[EXTENSION_BLOCK_POS], + data[EXTENSION_BLOCK_POS + 1], + data[EXTENSION_BLOCK_POS + 2], + data[EXTENSION_BLOCK_POS + 3]); + numExtensionBlocks = IntegerHelper.getInt + (data[NUM_EXTENSION_BLOCK_POS], + data[NUM_EXTENSION_BLOCK_POS + 1], + data[NUM_EXTENSION_BLOCK_POS + 2], + data[NUM_EXTENSION_BLOCK_POS + 3]); + + bigBlockDepotBlocks = new int[numBigBlockDepotBlocks]; + + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + int bbdBlocks = numBigBlockDepotBlocks; + + if (numExtensionBlocks != 0) { + bbdBlocks = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS) / 4; + } + + for (int i = 0; i < bbdBlocks; i++) { + bigBlockDepotBlocks[i] = IntegerHelper.getInt + (d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); + pos += 4; + } + + for (int j = 0; j < numExtensionBlocks; j++) { + pos = (extensionBlock + 1) * BIG_BLOCK_SIZE; + int blocksToRead = Math.min(numBigBlockDepotBlocks - bbdBlocks, + BIG_BLOCK_SIZE / 4 - 1); + + for (int i = bbdBlocks; i < bbdBlocks + blocksToRead; i++) { + bigBlockDepotBlocks[i] = IntegerHelper.getInt + (d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); + pos += 4; + } + + bbdBlocks += blocksToRead; + if (bbdBlocks < numBigBlockDepotBlocks) { + extensionBlock = IntegerHelper.getInt + (d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); + } + } + + readBigBlockDepot(); + readSmallBlockDepot(); + + rootEntry = readData(rootStartBlock); + readPropertySets(); + } + + /** + * Reads the big block depot entries + */ + private void readBigBlockDepot() { + int pos = 0; + int index = 0; + bigBlockChain = new int[numBigBlockDepotBlocks * BIG_BLOCK_SIZE / 4]; + + for (int i = 0; i < numBigBlockDepotBlocks; i++) { + pos = (bigBlockDepotBlocks[i] + 1) * BIG_BLOCK_SIZE; + + for (int j = 0; j < BIG_BLOCK_SIZE / 4; j++) { + bigBlockChain[index] = IntegerHelper.getInt + (data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + pos += 4; + index++; + } + } + } + + /** + * Reads the small block chain's depot entries + */ + private void readSmallBlockDepot() throws BiffException { + int pos = 0; + int index = 0; + int sbdBlock = sbdStartBlock; + smallBlockChain = new int[0]; + + // Some non-excel generators specify -1 for an empty small block depot + // simply warn and return + if (sbdBlock == -1) { + logger.warn("invalid small block depot number"); + return; + } + + int blockCount = 0; + for (; blockCount <= bigBlockChain.length && sbdBlock != -2; blockCount++) { + // Allocate some more space to the small block chain + int[] oldChain = smallBlockChain; + smallBlockChain = new int[smallBlockChain.length + BIG_BLOCK_SIZE / 4]; + System.arraycopy(oldChain, 0, smallBlockChain, 0, oldChain.length); + + pos = (sbdBlock + 1) * BIG_BLOCK_SIZE; + + for (int j = 0; j < BIG_BLOCK_SIZE / 4; j++) { + smallBlockChain[index] = IntegerHelper.getInt + (data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + pos += 4; + index++; + } + + sbdBlock = bigBlockChain[sbdBlock]; + } + + if (blockCount > bigBlockChain.length) { + // Attempted to read more blocks than the block chain contains entries + // for. This indicates a loop in the chain + throw new BiffException(BiffException.corruptFileFormat); + } + } + + /** + * Reads all the property sets + */ + private void readPropertySets() { + int offset = 0; + byte[] d = null; + + while (offset < rootEntry.length) { + d = new byte[PROPERTY_STORAGE_BLOCK_SIZE]; + System.arraycopy(rootEntry, offset, d, 0, d.length); + PropertyStorage ps = new PropertyStorage(d); + + // sometimes the MAC Operating system leaves some property storage + // names blank. Contributed by Jacky + if (ps.name == null || ps.name.length() == 0) { + if (ps.type == ROOT_ENTRY_PS_TYPE) { + ps.name = ROOT_ENTRY_NAME; + logger.warn("Property storage name for " + ps.type + + " is empty - setting to " + ROOT_ENTRY_NAME); + } else { + if (ps.size != 0) { + logger.warn("Property storage type " + ps.type + + " is non-empty and has no associated name"); + } + } + } + propertySets.add(ps); + if (ps.name.equalsIgnoreCase(ROOT_ENTRY_NAME)) { + rootEntryPropertyStorage = ps; + } + offset += PROPERTY_STORAGE_BLOCK_SIZE; + } + + if (rootEntryPropertyStorage == null) { + rootEntryPropertyStorage = (PropertyStorage) propertySets.get(0); + } + } + + /** + * Gets the defragmented stream from this ole compound file + * + * @param streamName the stream name to get + * @return the defragmented ole stream + * @throws BiffException + */ + public byte[] getStream(String streamName) throws BiffException { + PropertyStorage ps = findPropertyStorage(streamName, + rootEntryPropertyStorage); + + // Property set can't be found from the direct hierarchy, so just + // search on the name + if (ps == null) { + ps = getPropertyStorage(streamName); + } + + if (ps.size >= SMALL_BLOCK_THRESHOLD || + streamName.equalsIgnoreCase(ROOT_ENTRY_NAME)) { + return getBigBlockStream(ps); + } else { + return getSmallBlockStream(ps); + } + } + + /** + * Gets the defragmented stream from this ole compound file. Used when + * copying workbooks with macros + * + * @param psIndex the property storage index + * @return the defragmented ole stream + * @throws BiffException + */ + public byte[] getStream(int psIndex) throws BiffException { + PropertyStorage ps = getPropertyStorage(psIndex); + + if (ps.size >= SMALL_BLOCK_THRESHOLD || + ps.name.equalsIgnoreCase(ROOT_ENTRY_NAME)) { + return getBigBlockStream(ps); + } else { + return getSmallBlockStream(ps); + } + } + + /** + * Recursively searches the property storages in hierarchy order + * for the appropriate name. This is the public version which is + * invoked from the writable version + * when copying a sheet with addition property sets. + */ + public PropertyStorage findPropertyStorage(String name) { + return findPropertyStorage(name, rootEntryPropertyStorage); + } + + /** + * Recursively searches the property storages in hierarchy order + * for the appropriate name. + */ + private PropertyStorage findPropertyStorage(String name, + PropertyStorage base) { + if (base.child == -1) { + return null; + } + + // Get the child + PropertyStorage child = getPropertyStorage(base.child); + if (child.name.equalsIgnoreCase(name)) { + return child; + } + + // Find the previous property storages on the same level + PropertyStorage prev = child; + while (prev.previous != -1) { + prev = getPropertyStorage(prev.previous); + if (prev.name.equalsIgnoreCase(name)) { + return prev; + } + } + + // Find the next property storages on the same level + PropertyStorage next = child; + while (next.next != -1) { + next = getPropertyStorage(next.next); + if (next.name.equalsIgnoreCase(name)) { + return next; + } + } + + return findPropertyStorage(name, child); + } + + /** + * Gets the property set with the specified name + * + * @param name the property storage name + * @return the property storage record + * @throws BiffException + * @deprecated remove me + */ + private PropertyStorage getPropertyStorage(String name) + throws BiffException { + // Find the workbook property + Iterator i = propertySets.iterator(); + boolean found = false; + boolean multiple = false; + PropertyStorage ps = null; + while (i.hasNext()) { + PropertyStorage ps2 = (PropertyStorage) i.next(); + if (ps2.name.equalsIgnoreCase(name)) { + multiple = found == true; + found = true; + ps = ps2; + } + } + + if (multiple) { + logger.warn("found multiple copies of property set " + name); + } + + if (!found) { + throw new BiffException(BiffException.streamNotFound); + } + + return ps; + } + + /** + * Gets the property set with the specified name + * + * @param index the index of the property storage + * @return the property storage record + */ + private PropertyStorage getPropertyStorage(int index) { + return (PropertyStorage) propertySets.get(index); + } + + /** + * Build up the resultant stream using the big blocks + * + * @param ps the property storage + * @return the big block stream + */ + private byte[] getBigBlockStream(PropertyStorage ps) { + int numBlocks = ps.size / BIG_BLOCK_SIZE; + if (ps.size % BIG_BLOCK_SIZE != 0) { + numBlocks++; + } + + byte[] streamData = new byte[numBlocks * BIG_BLOCK_SIZE]; + + int block = ps.startBlock; + + int count = 0; + int pos = 0; + while (block != -2 && count < numBlocks) { + pos = (block + 1) * BIG_BLOCK_SIZE; + System.arraycopy(data, pos, streamData, + count * BIG_BLOCK_SIZE, BIG_BLOCK_SIZE); + count++; + block = bigBlockChain[block]; + } + + if (block != -2 && count == numBlocks) { + logger.warn("Property storage size inconsistent with block chain."); + } + + return streamData; + } + + /** + * Build up the resultant stream using the small blocks + * + * @param ps the property storage + * @return the data + * @throws BiffException + */ + private byte[] getSmallBlockStream(PropertyStorage ps) + throws BiffException { + byte[] rootdata = readData(rootEntryPropertyStorage.startBlock); + byte[] sbdata = new byte[0]; + + int block = ps.startBlock; + int pos = 0; + + int blockCount = 0; + for (; blockCount <= smallBlockChain.length && block != -2; blockCount++) { + // grow the array + byte[] olddata = sbdata; + sbdata = new byte[olddata.length + SMALL_BLOCK_SIZE]; + System.arraycopy(olddata, 0, sbdata, 0, olddata.length); + + // Copy in the new data + pos = block * SMALL_BLOCK_SIZE; + System.arraycopy(rootdata, pos, sbdata, + olddata.length, SMALL_BLOCK_SIZE); + block = smallBlockChain[block]; + + if (block == -1) { + logger.warn("Incorrect terminator for small block stream " + ps.name); + block = -2; // kludge to force the loop termination + } + } + + if (blockCount > smallBlockChain.length) { + // Attempted to read more blocks than the block chain contains entries + // for. This indicates a loop in the chain + throw new BiffException(BiffException.corruptFileFormat); + } + + return sbdata; + } + + /** + * Reads the block chain from the specified block and returns the + * data as a continuous stream of bytes + * + * @param bl the block number + * @return the data + */ + private byte[] readData(int bl) throws BiffException { + int block = bl; + int pos = 0; + byte[] entry = new byte[0]; + + int blockCount = 0; + for (; blockCount <= bigBlockChain.length && block != -2; blockCount++) { + // Grow the array + byte[] oldEntry = entry; + entry = new byte[oldEntry.length + BIG_BLOCK_SIZE]; + System.arraycopy(oldEntry, 0, entry, 0, oldEntry.length); + pos = (block + 1) * BIG_BLOCK_SIZE; + System.arraycopy(data, pos, entry, + oldEntry.length, BIG_BLOCK_SIZE); + if (bigBlockChain[block] == block) { + throw new BiffException(BiffException.corruptFileFormat); + } + block = bigBlockChain[block]; + } + + if (blockCount > bigBlockChain.length) { + // Attempted to read more blocks than the block chain contains entries + // for. This indicates a loop in the chain + throw new BiffException(BiffException.corruptFileFormat); + } + + return entry; + } + + /** + * Gets the number of property sets + * + * @return the number of property sets + */ + public int getNumberOfPropertySets() { + return propertySets.size(); + } + + /** + * Gets the property set. Invoked when copying worksheets with macros. + * Simply calls the private counterpart + * + * @param ps the property set name + * @return the property set with the given name + */ + public PropertyStorage getPropertySet(int index) { + return getPropertyStorage(index); + } +} + + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CountryRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/CountryRecord.java new file mode 100755 index 0000000..0dd448a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CountryRecord.java @@ -0,0 +1,83 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +public class CountryRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CountryRecord.class); + + /** + * The user interface language + */ + private final int language; + + /** + * The regional settings + */ + private final int regionalSettings; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public CountryRecord(Record t) { + super(t); + byte[] data = t.getData(); + + language = IntegerHelper.getInt(data[0], data[1]); + regionalSettings = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Accessor for the language code + * + * @return the language code + */ + public int getLanguageCode() { + return language; + } + + /** + * Accessor for the regional settings code + * + * @return the regional settings code + */ + public int getRegionalSettingsCode() { + return regionalSettings; + } + +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DateFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DateFormulaRecord.java new file mode 100755 index 0000000..d886966 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DateFormulaRecord.java @@ -0,0 +1,141 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.NumberFormat; +import jxl.CellType; +import jxl.DateCell; +import jxl.DateFormulaCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A date formula's last calculated value + */ +class DateFormulaRecord extends DateRecord + implements DateCell, FormulaData, DateFormulaCell { + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The raw data + */ + private final byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the basic number formula record + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param nf flag indicating whether the 1904 date system is in use + * @param si the sheet + */ + public DateFormulaRecord(NumberFormulaRecord t, FormattingRecords fr, + ExternalSheet es, WorkbookMethods nt, + boolean nf, SheetImpl si) throws FormulaException { + super(t, t.getXFIndex(), fr, nf, si); + + externalSheet = es; + nameTable = nt; + data = t.getFormulaData(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.DATE_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Data is already the formula data, so don't do any more manipulation + return data; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + // Note that the standard information was lopped off by the NumberFormula + // record when creating this formula + if (formulaString == null) { + byte[] tokens = new byte[data.length - 16]; + System.arraycopy(data, 16, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public double getValue() { + return 0; + } + + /** + * Dummy implementation in order to adhere to the NumberCell interface + * + * @return NULL + */ + public NumberFormat getNumberFormat() { + return null; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DateRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DateRecord.java new file mode 100755 index 0000000..b954a49 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DateRecord.java @@ -0,0 +1,305 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import jxl.CellFeatures; +import jxl.CellType; +import jxl.DateCell; +import jxl.NumberCell; +import jxl.biff.FormattingRecords; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * A date which is stored in the cell + */ +class DateRecord implements DateCell, CellFeaturesAccessor { + // The default formats used when returning the date as a string + private static final SimpleDateFormat dateFormat = + new SimpleDateFormat("dd MMM yyyy"); + private static final SimpleDateFormat timeFormat = + new SimpleDateFormat("HH:mm:ss"); + // The number of days between 1 Jan 1900 and 1 March 1900. Excel thinks + // the day before this was 29th Feb 1900, but it was 28th Feb 1900. + // I guess the programmers thought nobody would notice that they + // couldn't be bothered to program this dating anomaly properly + private static final int nonLeapDay = 61; + private static final TimeZone gmtZone = TimeZone.getTimeZone("GMT"); + // The number of days between 01 Jan 1900 and 01 Jan 1970 - this gives + // the UTC offset + private static final int utcOffsetDays = 25569; + // The number of days between 01 Jan 1904 and 01 Jan 1970 - this gives + // the UTC offset using the 1904 date system + private static final int utcOffsetDays1904 = 24107; + // The number of milliseconds in a day + private static final long secondsInADay = 24 * 60 * 60; + private static final long msInASecond = 1000; + private static final long msInADay = secondsInADay * msInASecond; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DateRecord.class); + /** + * The date represented within this cell + */ + private final Date date; + /** + * The row number of this cell record + */ + private final int row; + /** + * The column number of this cell record + */ + private final int column; + /** + * Indicates whether this is a full date, or merely a time + */ + private final boolean time; + /** + * The format to use when displaying this cell's contents as a string + */ + private DateFormat format; + /** + * The raw cell format + */ + private CellFormat cellFormat; + /** + * The index to the XF Record + */ + private final int xfIndex; + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + /** + * The cell features + */ + private CellFeatures features; + /** + * A flag to indicate whether this objects formatting things have + * been initialized + */ + private boolean initialized; + + + /** + * Constructs this object from the raw data + * + * @param num the numerical representation of this + * @param xfi the java equivalent of the excel date format + * @param fr the formatting records + * @param nf flag indicating whether we are using the 1904 date system + * @param si the sheet + */ + public DateRecord(NumberCell num, + int xfi, FormattingRecords fr, + boolean nf, SheetImpl si) { + row = num.getRow(); + column = num.getColumn(); + xfIndex = xfi; + formattingRecords = fr; + sheet = si; + initialized = false; + + format = formattingRecords.getDateFormat(xfIndex); + + // This value represents the number of days since 01 Jan 1900 + double numValue = num.getValue(); + + if (Math.abs(numValue) < 1) { + if (format == null) { + format = timeFormat; + } + time = true; + } else { + if (format == null) { + format = dateFormat; + } + time = false; + } + + // Work round a bug in excel. Excel seems to think there is a date + // called the 29th Feb, 1900 - but in actual fact this was not a leap year. + // Therefore for values less than 61 in the 1900 date system, + // add one to the numeric value + if (!nf && !time && numValue < nonLeapDay) { + numValue += 1; + } + + // Get rid of any timezone adjustments - we are not interested + // in automatic adjustments + format.setTimeZone(gmtZone); + + // Convert this to the number of days since 01 Jan 1970 + int offsetDays = nf ? utcOffsetDays1904 : utcOffsetDays; + double utcDays = numValue - offsetDays; + + // Convert this into utc by multiplying by the number of milliseconds + // in a day. Use the round function prior to ms conversion due + // to a rounding feature of Excel (contributed by Jurgen + long utcValue = Math.round(utcDays * secondsInADay) * msInASecond; + + date = new Date(utcValue); + } + + /** + * Interface method which returns the row number of this cell + * + * @return the zero base row number + */ + public final int getRow() { + return row; + } + + /** + * Interface method which returns the column number of this cell + * + * @return the zero based column number + */ + public final int getColumn() { + return column; + } + + /** + * Gets the date + * + * @return the date + */ + public Date getDate() { + return date; + } + + /** + * Gets the cell contents as a string. This method will use the java + * equivalent of the excel formatting string + * + * @return the label + */ + public String getContents() { + return format.format(date); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.DATE; + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + public boolean isTime() { + return time; + } + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat() { + Assert.verify(format != null); + + return format; + } + + /** + * Gets the CellFormat object for this cell. Used by the WritableWorkbook + * API + * + * @return the CellFormat used for this cell + */ + public CellFormat getCellFormat() { + if (!initialized) { + cellFormat = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return cellFormat; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Accessor for the sheet + * + * @return the containing sheet + */ + protected final SheetImpl getSheet() { + return sheet; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) { + features = cf; + } + +} + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DefaultColumnWidthRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DefaultColumnWidthRecord.java new file mode 100755 index 0000000..9d56eb4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DefaultColumnWidthRecord.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the default column width for cells in this sheet + */ +class DefaultColumnWidthRecord extends RecordData { + /** + * The default columns width, in characters + */ + private final int width; + + /** + * Constructs the def col width from the raw data + * + * @param t the raw data + */ + public DefaultColumnWidthRecord(Record t) { + super(t); + byte[] data = t.getData(); + + width = IntegerHelper.getInt(data[0], data[1]); + } + + + /** + * Accessor for the default width + * + * @return the width + */ + public int getWidth() { + return width; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DefaultRowHeightRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DefaultRowHeightRecord.java new file mode 100755 index 0000000..f9d8951 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DefaultRowHeightRecord.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the default column width for cells in this sheet + */ +class DefaultRowHeightRecord extends RecordData { + /** + * The default row height, in 1/20ths of a point + */ + private int height; + + /** + * Constructs the def col width from the raw data + * + * @param t the raw data + */ + public DefaultRowHeightRecord(Record t) { + super(t); + byte[] data = t.getData(); + + if (data.length > 2) { + height = IntegerHelper.getInt(data[2], data[3]); + } + } + + /** + * Accessor for the default height + * + * @return the height + */ + public int getHeight() { + return height; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DimensionRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DimensionRecord.java new file mode 100755 index 0000000..108ce78 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DimensionRecord.java @@ -0,0 +1,126 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class DimensionRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DimensionRecord.class); + /** + * The number of rows in this sheet + */ + private int numRows; + /** + * The number of columns in this worksheet + */ + private int numCols; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public DimensionRecord(Record t) { + super(t); + byte[] data = t.getData(); + + // Sometimes, if the spreadsheet is being generated by dodgy VB modules, + // even though the excel format is biff8, the dimension record is + // generated in the old biff 7 format. This horrible if construct + // handles that eventuality + if (data.length == 10) { + read10ByteData(data); + } else { + read14ByteData(data); + } + } + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + * @param biff7 an indicator to initialise this record for biff 7 format + */ + public DimensionRecord(Record t, Biff7 biff7) { + super(t); + byte[] data = t.getData(); + read10ByteData(data); + } + + /** + * Reads in the data for data records of length 10 + * + * @param data the data to read + */ + private void read10ByteData(byte[] data) { + numRows = IntegerHelper.getInt(data[2], data[3]); + numCols = IntegerHelper.getInt(data[6], data[7]); + } + + /** + * Reads in the data for data records of length 14 + * + * @param data the data to read + */ + private void read14ByteData(byte[] data) { + numRows = IntegerHelper.getInt(data[4], data[5], data[6], data[7]); + numCols = IntegerHelper.getInt(data[10], data[11]); + } + + /** + * Accessor for the number of rows in this sheet + * + * @return the number of rows + */ + public int getNumberOfRows() { + return numRows; + } + + /** + * Accessor for the number of columns in this sheet + * + * @return the number of columns + */ + public int getNumberOfColumns() { + return numCols; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ErrorFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ErrorFormulaRecord.java new file mode 100755 index 0000000..8b3f51e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ErrorFormulaRecord.java @@ -0,0 +1,164 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.ErrorCell; +import jxl.ErrorFormulaCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaErrorCode; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; + +/** + * An error resulting from the calculation of a formula + */ +class ErrorFormulaRecord extends CellValue + implements ErrorCell, FormulaData, ErrorFormulaCell { + /** + * The error code of this cell + */ + private final int errorCode; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The raw data + */ + private final byte[] data; + + /** + * The error code + */ + private FormulaErrorCode error; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public ErrorFormulaRecord(Record t, FormattingRecords fr, ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + data = getRecord().getData(); + + Assert.verify(data[6] == 2); + + errorCode = data[8]; + } + + /** + * Interface method which gets the error code for this cell. If this cell + * does not represent an error, then it returns 0. Always use the + * method isError() to determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode() { + return errorCode; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + if (error == null) { + error = FormulaErrorCode.getErrorCode(errorCode); + } + + return error != FormulaErrorCode.UNKNOWN ? + error.getDescription() : "ERROR " + errorCode; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.FORMULA_ERROR; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ErrorRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ErrorRecord.java new file mode 100755 index 0000000..2b6f09a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ErrorRecord.java @@ -0,0 +1,80 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.ErrorCell; +import jxl.biff.FormattingRecords; + +/** + * A cell containing an error code. This will usually be the result + * of some error during the calculation of a formula + */ +class ErrorRecord extends CellValue implements ErrorCell { + /** + * The error code if this cell evaluates to an error, otherwise zer0 + */ + private final int errorCode; + + /** + * Constructs this object + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + */ + public ErrorRecord(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + + byte[] data = getRecord().getData(); + + errorCode = data[6]; + } + + /** + * Interface method which gets the error code for this cell. If this cell + * does not represent an error, then it returns 0. Always use the + * method isError() to determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode() { + return errorCode; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + return "ERROR " + errorCode; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.ERROR; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/Excel9FileRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/Excel9FileRecord.java new file mode 100755 index 0000000..409538b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/Excel9FileRecord.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A excel9file record + */ +class Excel9FileRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Excel9FileRecord.class); + + /** + * The template + */ + private final boolean excel9file; + + /** + * Constructor + * + * @param t the record + */ + public Excel9FileRecord(Record t) { + super(t); + excel9file = true; + } + + /** + * Accessor for the template mode + * + * @return the template mode + */ + public boolean getExcel9File() { + return excel9file; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ExternalNameRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ExternalNameRecord.java new file mode 100755 index 0000000..caa28b8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ExternalNameRecord.java @@ -0,0 +1,98 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Logger; + + +/** + * A row record + */ +public class ExternalNameRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ExternalNameRecord.class); + + /** + * The name + */ + private String name; + + /** + * Add in function flag + */ + private boolean addInFunction; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + */ + ExternalNameRecord(Record t, WorkbookSettings ws) { + super(t); + + byte[] data = getRecord().getData(); + int options = IntegerHelper.getInt(data[0], data[1]); + + if (options == 0) { + addInFunction = true; + } + + if (!addInFunction) { + return; + } + + int length = data[6]; + + boolean unicode = (data[7] != 0); + + if (unicode) { + name = StringHelper.getUnicodeString(data, length, 8); + } else { + name = StringHelper.getString(data, length, 8, ws); + } + } + + /** + * Queries whether this name record refers to an external record + * + * @return TRUE if this name record is an add in function, FALSE otherwise + */ + public boolean isAddInFunction() { + return addInFunction; + } + + /** + * Gets the name + * + * @return the name + */ + public String getName() { + return name; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ExternalSheetRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ExternalSheetRecord.java new file mode 100755 index 0000000..03ebe48 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ExternalSheetRecord.java @@ -0,0 +1,181 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * An Externsheet record, containing the details of externally references + * workbooks + */ +public class ExternalSheetRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ExternalSheetRecord.class); + + /** + * The array of XTI structures + */ + private XTI[] xtiArray; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + */ + ExternalSheetRecord(Record t, WorkbookSettings ws) { + super(t); + byte[] data = getRecord().getData(); + + int numxtis = IntegerHelper.getInt(data[0], data[1]); + + if (data.length < numxtis * 6 + 2) { + xtiArray = new XTI[0]; + logger.warn("Could not process external sheets. Formulas may " + + "be compromised."); + return; + } + + xtiArray = new XTI[numxtis]; + + int pos = 2; + for (int i = 0; i < numxtis; i++) { + int s = IntegerHelper.getInt(data[pos], data[pos + 1]); + int f = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + int l = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + xtiArray[i] = new XTI(s, f, l); + pos += 6; + } + } + + /** + * Constructs this object from the raw data in biff 7 format. + * Does nothing here + * + * @param t the raw data + * @param settings the workbook settings + * @param dummy dummy override to identify biff7 funcionality + */ + ExternalSheetRecord(Record t, WorkbookSettings settings, Biff7 dummy) { + super(t); + + logger.warn("External sheet record for Biff 7 not supported"); + } + + /** + * Accessor for the number of external sheet records + * + * @return the number of XTI records + */ + public int getNumRecords() { + return xtiArray != null ? xtiArray.length : 0; + } + + /** + * Gets the supbook index for the specified external sheet + * + * @param index the index of the supbook record + * @return the supbook index + */ + public int getSupbookIndex(int index) { + return xtiArray[index].supbookIndex; + } + + /** + * Gets the first tab index for the specified external sheet + * + * @param index the index of the supbook record + * @return the first tab index + */ + public int getFirstTabIndex(int index) { + return xtiArray[index].firstTab; + } + + /** + * Gets the last tab index for the specified external sheet + * + * @param index the index of the supbook record + * @return the last tab index + */ + public int getLastTabIndex(int index) { + return xtiArray[index].lastTab; + } + + /** + * Used when copying a workbook to access the raw external sheet data + * + * @return the raw external sheet data + */ + public byte[] getData() { + return getRecord().getData(); + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } + + /** + * An XTI structure + */ + private static class XTI { + /** + * the supbook index + */ + int supbookIndex; + /** + * the first tab + */ + int firstTab; + /** + * the last tab + */ + int lastTab; + + /** + * Constructor + * + * @param s the supbook index + * @param f the first tab + * @param l the last tab + */ + XTI(int s, int f, int l) { + supbookIndex = s; + firstTab = f; + lastTab = l; + } + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/File.java b/datastructures-xslx/src/main/java/jxl/read/biff/File.java new file mode 100755 index 0000000..d1a9d8d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/File.java @@ -0,0 +1,293 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import jxl.WorkbookSettings; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.common.Logger; + +/** + * File containing the data from the binary stream + */ +public class File { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(File.class); + + /** + * The data from the excel 97 file + */ + private byte[] data; + /** + * The current position within the file + */ + private int filePos; + /** + * The saved pos + */ + private int oldPos; + /** + * The initial file size + */ + private int initialFileSize; + /** + * The amount to increase the growable array by + */ + private int arrayGrowSize; + /** + * A handle to the compound file. This is only preserved when the + * copying of PropertySets is enabled + */ + private CompoundFile compoundFile; + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructs a file from the input stream + * + * @param is the input stream + * @param ws the workbook settings + * @throws IOException + * @throws BiffException + */ + public File(InputStream is, WorkbookSettings ws) + throws IOException, BiffException { + // Initialize the file sizing parameters from the settings + workbookSettings = ws; + initialFileSize = workbookSettings.getInitialFileSize(); + arrayGrowSize = workbookSettings.getArrayGrowSize(); + + byte[] d = new byte[initialFileSize]; + int bytesRead = is.read(d); + int pos = bytesRead; + + // Handle thread interruptions, in case the user keeps pressing + // the Submit button from a browser. Thanks to Mike Smith for this + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedIOException(); + } + + while (bytesRead != -1) { + if (pos >= d.length) { + // Grow the array + byte[] newArray = new byte[d.length + arrayGrowSize]; + System.arraycopy(d, 0, newArray, 0, d.length); + d = newArray; + } + bytesRead = is.read(d, pos, d.length - pos); + pos += bytesRead; + + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedIOException(); + } + } + + bytesRead = pos + 1; + + // Perform file reading checks and throw exceptions as necessary + if (bytesRead == 0) { + throw new BiffException(BiffException.excelFileNotFound); + } + + CompoundFile cf = new CompoundFile(d, ws); + try { + data = cf.getStream("workbook"); + } catch (BiffException e) { + // this might be in excel 95 format - try again + data = cf.getStream("book"); + } + + if (!workbookSettings.getPropertySetsDisabled() && + (cf.getNumberOfPropertySets() > + BaseCompoundFile.STANDARD_PROPERTY_SETS.length)) { + compoundFile = cf; + } + + cf = null; + + if (!workbookSettings.getGCDisabled()) { + System.gc(); + } + + // Uncomment the following lines to send the pure workbook stream + // (ie. a defragged ole stream) to an output file + +// FileOutputStream fos = new FileOutputStream("defraggedxls"); +// fos.write(data); +// fos.close(); + + } + + /** + * Constructs a file from already defragged binary data. Useful for + * displaying subportions of excel streams. This is only used during + * special runs of the "BiffDump" demo program and should not be invoked + * as part of standard JExcelApi parsing + * + * @param d the already parsed data + */ + public File(byte[] d) { + data = d; + } + + /** + * Returns the next data record and increments the pointer + * + * @return the next data record + */ + Record next() { + Record r = new Record(data, filePos, this); + return r; + } + + /** + * Peek ahead to the next record, without incrementing the file position + * + * @return the next record + */ + Record peek() { + int tempPos = filePos; + Record r = new Record(data, filePos, this); + filePos = tempPos; + return r; + } + + /** + * Skips forward the specified number of bytes + * + * @param bytes the number of bytes to skip forward + */ + public void skip(int bytes) { + filePos += bytes; + } + + /** + * Copies the bytes into a new array and returns it. + * + * @param pos the position to read from + * @param length the number of bytes to read + * @return The bytes read + */ + public byte[] read(int pos, int length) { + byte[] ret = new byte[length]; + try { + System.arraycopy(data, pos, ret, 0, length); + } catch (ArrayIndexOutOfBoundsException e) { + logger.error("Array index out of bounds at position " + pos + + " record length " + length); + throw e; + } + return ret; + } + + /** + * Gets the position in the stream + * + * @return the position in the stream + */ + public int getPos() { + return filePos; + } + + /** + * Saves the current position and temporarily sets the position to be the + * new one. The original position may be restored usind the restorePos() + * method. This is used when reading in the cell values of the sheet - an + * addition in 1.6 for memory allocation reasons. + *

+ * These methods are used by the SheetImpl.readSheet() when it is reading + * in all the cell values + * + * @param p the temporary position + */ + public void setPos(int p) { + oldPos = filePos; + filePos = p; + } + + /** + * Restores the original position + *

+ * These methods are used by the SheetImpl.readSheet() when it is reading + * in all the cell values + */ + public void restorePos() { + filePos = oldPos; + } + + /** + * Moves to the first bof in the file + */ + private void moveToFirstBof() { + boolean bofFound = false; + while (!bofFound) { + int code = IntegerHelper.getInt(data[filePos], data[filePos + 1]); + if (code == Type.BOF.value) { + bofFound = true; + } else { + skip(128); + } + } + } + + /** + * "Closes" the biff file + * + * @deprecated As of version 1.6 use workbook.close() instead + */ + public void close() { + } + + /** + * Clears the contents of the file + */ + public void clear() { + data = null; + } + + /** + * Determines if the current position exceeds the end of the file + * + * @return TRUE if there is more data left in the array, FALSE otherwise + */ + public boolean hasNext() { + // Allow four bytes for the record code and its length + return filePos < data.length - 4; + } + + /** + * Accessor for the compound file. The returned value will only be non-null + * if the property sets feature is enabled and the workbook contains + * additional property sets + * + * @return the compound file + */ + CompoundFile getCompoundFile() { + return compoundFile; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/FooterRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/FooterRecord.java new file mode 100755 index 0000000..57f8303 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/FooterRecord.java @@ -0,0 +1,95 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * A workbook page footer record + */ +public class FooterRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The footer + */ + private String footer; + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + */ + FooterRecord(Record t, WorkbookSettings ws) { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) { + return; + } + + int chars = IntegerHelper.getInt(data[0], data[1]); + + boolean unicode = data[2] == 1; + + if (unicode) { + footer = StringHelper.getUnicodeString(data, chars, 3); + } else { + footer = StringHelper.getString(data, chars, 3, ws); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + * @param dummy dummy record to indicate a biff7 document + */ + FooterRecord(Record t, WorkbookSettings ws, Biff7 dummy) { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) { + return; + } + + int chars = data[0]; + footer = StringHelper.getString(data, chars, 1, ws); + } + + /** + * Gets the footer string + * + * @return the footer string + */ + String getFooter() { + return footer; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/Formula.java b/datastructures-xslx/src/main/java/jxl/read/biff/Formula.java new file mode 100755 index 0000000..bdaba9a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/Formula.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.Cell; + +/** + * Interface which is used for copying formulas from a read only + * to a writable spreadsheet + */ +public interface Formula extends Cell { + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + byte[] getFormulaData(); +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/FormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/FormulaRecord.java new file mode 100755 index 0000000..aeaf5ab --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/FormulaRecord.java @@ -0,0 +1,243 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.WorkbookSettings; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A formula's last calculated value + */ +class FormulaRecord extends CellValue { + public static final IgnoreSharedFormula ignoreSharedFormula + = new IgnoreSharedFormula(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FormulaRecord.class); + /** + * The "real" formula record - will be either a string a or a number + */ + private final CellValue formula; + /** + * Flag to indicate whether this is a shared formula + */ + private boolean shared; + + /** + * Constructs this object from the raw data. Creates either a + * NumberFormulaRecord or a StringFormulaRecord depending on whether + * this formula represents a numerical calculation or not + * + * @param t the raw data + * @param excelFile the excel file + * @param fr the formatting records + * @param es the workbook, which contains the external sheet references + * @param nt the name table + * @param si the sheet + * @param ws the workbook settings + */ + public FormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + WorkbookSettings ws) { + super(t, fr, si); + + byte[] data = getRecord().getData(); + + shared = false; + + // Check to see if this forms part of a shared formula + int grbit = IntegerHelper.getInt(data[14], data[15]); + if ((grbit & 0x08) != 0) { + shared = true; + + if (data[6] == 0 && data[12] == -1 && data[13] == -1) { + // It is a shared string formula + formula = new SharedStringFormulaRecord + (t, excelFile, fr, es, nt, si, ws); + } else if (data[6] == 3 && data[12] == -1 && data[13] == -1) { + // We have a string which evaluates to null + formula = new SharedStringFormulaRecord + (t, excelFile, fr, es, nt, si, + SharedStringFormulaRecord.EMPTY_STRING); + } else if (data[6] == 2 && + data[12] == -1 && + data[13] == -1) { + // The cell is in error + int errorCode = data[8]; + formula = new SharedErrorFormulaRecord(t, excelFile, errorCode, + fr, es, nt, si); + } else if (data[6] == 1 && + data[12] == -1 && + data[13] == -1) { + boolean value = data[8] == 1; + formula = new SharedBooleanFormulaRecord + (t, excelFile, value, fr, es, nt, si); + } else { + // It is a numerical formula + double value = DoubleHelper.getIEEEDouble(data, 6); + SharedNumberFormulaRecord snfr = new SharedNumberFormulaRecord + (t, excelFile, value, fr, es, nt, si); + snfr.setNumberFormat(fr.getNumberFormat(getXFIndex())); + formula = snfr; + } + + return; + } + + // microsoft and their goddam magic values determine whether this + // is a string or a number value + if (data[6] == 0 && data[12] == -1 && data[13] == -1) { + // we have a string + formula = new StringFormulaRecord(t, excelFile, fr, es, nt, si, ws); + } else if (data[6] == 1 && + data[12] == -1 && + data[13] == -1) { + // We have a boolean formula + // multiple values. Thanks to Frank for spotting this + formula = new BooleanFormulaRecord(t, fr, es, nt, si); + } else if (data[6] == 2 && + data[12] == -1 && + data[13] == -1) { + // The cell is in error + formula = new ErrorFormulaRecord(t, fr, es, nt, si); + } else if (data[6] == 3 && data[12] == -1 && data[13] == -1) { + // we have a string which evaluates to null + formula = new StringFormulaRecord(t, fr, es, nt, si); + } else { + // it is most assuredly a number + formula = new NumberFormulaRecord(t, fr, es, nt, si); + } + } + + /** + * Constructs this object from the raw data. Creates either a + * NumberFormulaRecord or a StringFormulaRecord depending on whether + * this formula represents a numerical calculation or not + * + * @param t the raw data + * @param excelFile the excel file + * @param fr the formatting records + * @param es the workbook, which contains the external sheet references + * @param nt the name table + * @param i a dummy override to indicate that we don't want to do + * any shared formula processing + * @param si the sheet impl + * @param ws the workbook settings + */ + public FormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + IgnoreSharedFormula i, + SheetImpl si, + WorkbookSettings ws) { + super(t, fr, si); + byte[] data = getRecord().getData(); + + shared = false; + + // microsoft and their magic values determine whether this + // is a string or a number value + if (data[6] == 0 && data[12] == -1 && data[13] == -1) { + // we have a string + formula = new StringFormulaRecord(t, excelFile, fr, es, nt, si, ws); + } else if (data[6] == 1 && + data[12] == -1 && + data[13] == -1) { + // We have a boolean formula + // multiple values. Thanks to Frank for spotting this + formula = new BooleanFormulaRecord(t, fr, es, nt, si); + } else if (data[6] == 2 && + data[12] == -1 && + data[13] == -1) { + // The cell is in error + formula = new ErrorFormulaRecord(t, fr, es, nt, si); + } else { + // it is most assuredly a number + formula = new NumberFormulaRecord(t, fr, es, nt, si); + } + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + Assert.verify(false); + return ""; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + Assert.verify(false); + return CellType.EMPTY; + } + + /** + * Gets the "real" formula + * + * @return the cell value + */ + final CellValue getFormula() { + return formula; + } + + /** + * Interrogates this formula to determine if it forms part of a shared + * formula + * + * @return TRUE if this is shared formula, FALSE otherwise + */ + final boolean isShared() { + return shared; + } + + /** + * Static class for a dummy override, indicating that the formula + * passed in is not a shared formula + */ + private static class IgnoreSharedFormula { + } + +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/GuttersRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/GuttersRecord.java new file mode 100755 index 0000000..c27c750 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/GuttersRecord.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * The gutters record + */ +public class GuttersRecord extends RecordData { + private final int width; + private final int height; + private final int rowOutlineLevel; + private final int columnOutlineLevel; + + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + public GuttersRecord(Record r) { + super(r); + + byte[] data = getRecord().getData(); + width = IntegerHelper.getInt(data[0], data[1]); + height = IntegerHelper.getInt(data[2], data[3]); + rowOutlineLevel = IntegerHelper.getInt(data[4], data[5]); + columnOutlineLevel = IntegerHelper.getInt(data[6], data[7]); + } + + int getRowOutlineLevel() { + return rowOutlineLevel; + } + + int getColumnOutlineLevel() { + return columnOutlineLevel; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/HeaderRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/HeaderRecord.java new file mode 100755 index 0000000..4629806 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/HeaderRecord.java @@ -0,0 +1,100 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Logger; + +/** + * A workbook page header record + */ +public class HeaderRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(HeaderRecord.class); + /** + * The footer + */ + private String header; + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + */ + HeaderRecord(Record t, WorkbookSettings ws) { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) { + return; + } + + int chars = IntegerHelper.getInt(data[0], data[1]); + + boolean unicode = data[2] == 1; + + if (unicode) { + header = StringHelper.getUnicodeString(data, chars, 3); + } else { + header = StringHelper.getString(data, chars, 3, ws); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + * @param dummy dummy record to indicate a biff7 document + */ + HeaderRecord(Record t, WorkbookSettings ws, Biff7 dummy) { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) { + return; + } + + int chars = data[0]; + header = StringHelper.getString(data, chars, 1, ws); + } + + /** + * Gets the header string + * + * @return the header string + */ + String getHeader() { + return header; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/HideobjRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/HideobjRecord.java new file mode 100755 index 0000000..dae08fe --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/HideobjRecord.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A hideobj record + */ +class HideobjRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(HideobjRecord.class); + + /** + * The hide obj mode + */ + private final int hidemode; + + /** + * Constructor + * + * @param t the record + */ + public HideobjRecord(Record t) { + super(t); + byte[] data = t.getData(); + hidemode = IntegerHelper.getInt(data[0], data[1]); + } + + /** + * Accessor for the hide mode mode + * + * @return the hide mode + */ + public int getHideMode() { + return hidemode; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/HorizontalPageBreaksRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/HorizontalPageBreaksRecord.java new file mode 100755 index 0000000..792d9fc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/HorizontalPageBreaksRecord.java @@ -0,0 +1,101 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class HorizontalPageBreaksRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private final Logger logger = Logger.getLogger + (HorizontalPageBreaksRecord.class); + /** + * The row page breaks + */ + private final int[] rowBreaks; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public HorizontalPageBreaksRecord(Record t) { + super(t); + + byte[] data = t.getData(); + + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + rowBreaks = new int[numbreaks]; + + for (int i = 0; i < numbreaks; i++) { + rowBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 6; + } + } + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + * @param biff7 an indicator to initialise this record for biff 7 format + */ + public HorizontalPageBreaksRecord(Record t, Biff7 biff7) { + super(t); + + byte[] data = t.getData(); + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + rowBreaks = new int[numbreaks]; + for (int i = 0; i < numbreaks; i++) { + rowBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + } + + /** + * Gets the row breaks + * + * @return the row breaks on the current sheet + */ + public int[] getRowBreaks() { + return rowBreaks; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/HyperlinkRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/HyperlinkRecord.java new file mode 100755 index 0000000..81dad89 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/HyperlinkRecord.java @@ -0,0 +1,360 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import jxl.CellReferenceHelper; +import jxl.Hyperlink; +import jxl.Range; +import jxl.Sheet; +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.SheetRangeImpl; +import jxl.biff.StringHelper; +import jxl.common.Logger; + +/** + * A number record. This is stored as 8 bytes, as opposed to the + * 4 byte RK record + */ +public class HyperlinkRecord extends RecordData implements Hyperlink { + private static final LinkType urlLink = new LinkType(); + private static final LinkType fileLink = new LinkType(); + private static final LinkType workbookLink = new LinkType(); + private static final LinkType unknown = new LinkType(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(HyperlinkRecord.class); + /** + * The first row + */ + private final int firstRow; + /** + * The last row + */ + private final int lastRow; + /** + * The first column + */ + private final int firstColumn; + /** + * The last column + */ + private final int lastColumn; + /** + * The URL referred to by this hyperlink + */ + private URL url; + /** + * The local file referred to by this hyperlink + */ + private File file; + + /** + * The location in this workbook referred to by this hyperlink + */ + private String location; + /** + * The range of cells which activate this hyperlink + */ + private final SheetRangeImpl range; + /** + * The type of this hyperlink + */ + private LinkType linkType; + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param s the sheet + * @param ws the workbook settings + */ + HyperlinkRecord(Record t, Sheet s, WorkbookSettings ws) { + super(t); + + linkType = unknown; + + byte[] data = getRecord().getData(); + + // Build up the range of cells occupied by this hyperlink + firstRow = IntegerHelper.getInt(data[0], data[1]); + lastRow = IntegerHelper.getInt(data[2], data[3]); + firstColumn = IntegerHelper.getInt(data[4], data[5]); + lastColumn = IntegerHelper.getInt(data[6], data[7]); + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + + int options = IntegerHelper.getInt(data[28], data[29], data[30], data[31]); + + boolean description = (options & 0x14) != 0; + int startpos = 32; + int descbytes = 0; + if (description) { + int descchars = IntegerHelper.getInt + (data[startpos], data[startpos + 1], + data[startpos + 2], data[startpos + 3]); + descbytes = descchars * 2 + 4; + } + + startpos += descbytes; + + boolean targetFrame = (options & 0x80) != 0; + int targetbytes = 0; + if (targetFrame) { + int targetchars = IntegerHelper.getInt + (data[startpos], data[startpos + 1], + data[startpos + 2], data[startpos + 3]); + targetbytes = targetchars * 2 + 4; + } + + startpos += targetbytes; + + // Try and determine the type + if ((options & 0x3) == 0x03) { + linkType = urlLink; + + // check the guid monicker + if (data[startpos] == 0x03) { + linkType = fileLink; + } + } else if ((options & 0x01) != 0) { + linkType = fileLink; + // check the guid monicker + if (data[startpos] == (byte) 0xe0) { + linkType = urlLink; + } + } else if ((options & 0x08) != 0) { + linkType = workbookLink; + } + + // Try and determine the type + if (linkType == urlLink) { + String urlString = null; + try { + startpos += 16; + + // Get the url, ignoring the 0 char at the end + int bytes = IntegerHelper.getInt(data[startpos], + data[startpos + 1], + data[startpos + 2], + data[startpos + 3]); + + urlString = StringHelper.getUnicodeString(data, bytes / 2 - 1, + startpos + 4); + url = new URL(urlString); + } catch (MalformedURLException e) { + logger.warn("URL " + urlString + " is malformed. Trying a file"); + try { + linkType = fileLink; + file = new File(urlString); + } catch (Exception e3) { + logger.warn("Cannot set to file. Setting a default URL"); + + // Set a default URL + try { + linkType = urlLink; + url = new URL("http://www.andykhan.com/jexcelapi/index.html"); + } catch (MalformedURLException e2) { + // fail silently + } + } + } catch (Throwable e) { + StringBuffer sb1 = new StringBuffer(); + StringBuffer sb2 = new StringBuffer(); + CellReferenceHelper.getCellReference(firstColumn, firstRow, sb1); + CellReferenceHelper.getCellReference(lastColumn, lastRow, sb2); + sb1.insert(0, "Exception when parsing URL "); + sb1.append('\"').append(sb2).append("\". Using default."); + logger.warn(sb1, e); + + // Set a default URL + try { + url = new URL("http://www.andykhan.com/jexcelapi/index.html"); + } catch (MalformedURLException e2) { + // fail silently + } + } + } else if (linkType == fileLink) { + try { + startpos += 16; + + // Get the name of the local file, ignoring the zero character at the + // end + int upLevelCount = IntegerHelper.getInt(data[startpos], + data[startpos + 1]); + int chars = IntegerHelper.getInt(data[startpos + 2], + data[startpos + 3], + data[startpos + 4], + data[startpos + 5]); + String fileName = StringHelper.getString(data, chars - 1, + startpos + 6, ws); + + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < upLevelCount; i++) { + sb.append("..\\"); + } + + sb.append(fileName); + + file = new File(sb.toString()); + } catch (Throwable e) { + logger.warn("Exception when parsing file " + + e.getClass().getName() + "."); + file = new File("."); + } + } else if (linkType == workbookLink) { + int chars = IntegerHelper.getInt(data[32], data[33], data[34], data[35]); + location = StringHelper.getUnicodeString(data, chars - 1, 36); + } else { + // give up + logger.warn("Cannot determine link type"); + return; + } + } + + /** + * Determines whether this is a hyperlink to a file + * + * @return TRUE if this is a hyperlink to a file, FALSE otherwise + */ + public boolean isFile() { + return linkType == fileLink; + } + + /** + * Determines whether this is a hyperlink to a web resource + * + * @return TRUE if this is a URL + */ + public boolean isURL() { + return linkType == urlLink; + } + + /** + * Determines whether this is a hyperlink to a location in this workbook + * + * @return TRUE if this is a link to an internal location + */ + public boolean isLocation() { + return linkType == workbookLink; + } + + /** + * Returns the row number of the top left cell + * + * @return the row number of this cell + */ + public int getRow() { + return firstRow; + } + + /** + * Returns the column number of the top left cell + * + * @return the column number of this cell + */ + public int getColumn() { + return firstColumn; + } + + /** + * Returns the row number of the bottom right cell + * + * @return the row number of this cell + */ + public int getLastRow() { + return lastRow; + } + + /** + * Returns the column number of the bottom right cell + * + * @return the column number of this cell + */ + public int getLastColumn() { + return lastColumn; + } + + /** + * Gets the URL referenced by this Hyperlink + * + * @return the URL, or NULL if this hyperlink is not a URL + */ + public URL getURL() { + return url; + } + + /** + * Returns the local file eferenced by this Hyperlink + * + * @return the file, or NULL if this hyperlink is not a file + */ + public File getFile() { + return file; + } + + /** + * Exposes the base class method. This is used when copying hyperlinks + * + * @return the Record data + */ + public Record getRecord() { + return super.getRecord(); + } + + /** + * Gets the range of cells which activate this hyperlink + * The get sheet index methods will all return -1, because the + * cells will all be present on the same sheet + * + * @return the range of cells which activate the hyperlink + */ + public Range getRange() { + return range; + } + + /** + * Gets the location referenced by this hyperlink + * + * @return the location + */ + public String getLocation() { + return location; + } + + /** + * The excel type of hyperlink + */ + private static class LinkType { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/LabelRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/LabelRecord.java new file mode 100755 index 0000000..e497826 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/LabelRecord.java @@ -0,0 +1,114 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; + +/** + * A label which is stored in the cell + */ +class LabelRecord extends CellValue implements LabelCell { + public static Biff7 biff7 = new Biff7(); + /** + * The length of the label in characters + */ + private final int length; + /** + * The label + */ + private final String string; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param ws the workbook settings + */ + public LabelRecord(Record t, FormattingRecords fr, + SheetImpl si, WorkbookSettings ws) { + super(t, fr, si); + byte[] data = getRecord().getData(); + length = IntegerHelper.getInt(data[6], data[7]); + + if (data[8] == 0x0) { + string = StringHelper.getString(data, length, 9, ws); + } else { + string = StringHelper.getUnicodeString(data, length, 9); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param ws the workbook settings + * @param dummy dummy overload to indicate a biff 7 workbook + */ + public LabelRecord(Record t, FormattingRecords fr, SheetImpl si, + WorkbookSettings ws, Biff7 dummy) { + super(t, fr, si); + byte[] data = getRecord().getData(); + length = IntegerHelper.getInt(data[6], data[7]); + + string = StringHelper.getString(data, length, 8, ws); + } + + /** + * Gets the label + * + * @return the label + */ + public String getString() { + return string; + } + + /** + * Gets the cell contents as a string + * + * @return the label + */ + public String getContents() { + return string; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.LABEL; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/LabelSSTRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/LabelSSTRecord.java new file mode 100755 index 0000000..2e8058c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/LabelSSTRecord.java @@ -0,0 +1,83 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; + +/** + * A label which is stored in the shared string table + */ +class LabelSSTRecord extends CellValue implements LabelCell { + /** + * The index into the shared string table + */ + private final int index; + /** + * The label + */ + private final String string; + + /** + * Constructor. Retrieves the index from the raw data and looks it up + * in the shared string table + * + * @param stringTable the shared string table + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + */ + public LabelSSTRecord(Record t, SSTRecord stringTable, FormattingRecords fr, + SheetImpl si) { + super(t, fr, si); + byte[] data = getRecord().getData(); + index = IntegerHelper.getInt(data[6], data[7], data[8], data[9]); + string = stringTable.getString(index); + } + + /** + * Gets the label + * + * @return the label + */ + public String getString() { + return string; + } + + /** + * Gets this cell's contents as a string + * + * @return the label + */ + public String getContents() { + return string; + } + + /** + * Returns the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.LABEL; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/LeftMarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/LeftMarginRecord.java new file mode 100755 index 0000000..022b2a2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/LeftMarginRecord.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class LeftMarginRecord extends MarginRecord { + /** + * Constructor + * + * @param r the raw record + */ + LeftMarginRecord(Record r) { + super(Type.LEFTMARGIN, r); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/MarginRecord.java new file mode 100755 index 0000000..32fe710 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MarginRecord.java @@ -0,0 +1,58 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Abstract class containing the margin value for top,left,right and bottom + * margins + */ +abstract class MarginRecord extends RecordData { + /** + * The size of the margin + */ + private final double margin; + + /** + * Constructs this record from the raw data + * + * @param t the type + * @param r the record + */ + protected MarginRecord(Type t, Record r) { + super(t); + + byte[] data = r.getData(); + + margin = DoubleHelper.getIEEEDouble(data, 0); + } + + /** + * Accessor for the margin + * + * @return the margin + */ + double getMargin() { + return margin; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MergedCellsRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/MergedCellsRecord.java new file mode 100755 index 0000000..e6d4bcc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MergedCellsRecord.java @@ -0,0 +1,86 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.Range; +import jxl.Sheet; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.SheetRangeImpl; + +/** + * A merged cells record for a given sheet + */ +public class MergedCellsRecord extends RecordData { + /** + * The ranges of the cells merged on this sheet + */ + private final Range[] ranges; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param s the sheet + */ + MergedCellsRecord(Record t, Sheet s) { + super(t); + + byte[] data = getRecord().getData(); + + int numRanges = IntegerHelper.getInt(data[0], data[1]); + + ranges = new Range[numRanges]; + + int pos = 2; + int firstRow = 0; + int lastRow = 0; + int firstCol = 0; + int lastCol = 0; + + for (int i = 0; i < numRanges; i++) { + firstRow = IntegerHelper.getInt(data[pos], data[pos + 1]); + lastRow = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + firstCol = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + lastCol = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + + ranges[i] = new SheetRangeImpl(s, firstCol, firstRow, + lastCol, lastRow); + + pos += 8; + } + } + + /** + * Gets the ranges which have been merged in this sheet + * + * @return the ranges of cells which have been merged + */ + public Range[] getRanges() { + return ranges; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankCell.java b/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankCell.java new file mode 100755 index 0000000..4245860 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankCell.java @@ -0,0 +1,188 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * A blank cell value, initialized indirectly from a multiple biff record + * rather than directly from the binary data + */ +class MulBlankCell implements Cell, CellFeaturesAccessor { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(MulBlankCell.class); + + /** + * The row containing this blank + */ + private final int row; + /** + * The column containing this blank + */ + private final int column; + /** + * The raw cell format + */ + private CellFormat cellFormat; + + /** + * The index to the XF Record + */ + private final int xfIndex; + + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + + /** + * A flag to indicate whether this object's formatting things have + * been initialized + */ + private boolean initialized; + + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + + /** + * The cell features + */ + private CellFeatures features; + + + /** + * Constructs this cell + * + * @param r the zero based row + * @param c the zero base column + * @param xfi the xf index + * @param fr the formatting records + * @param si the sheet + */ + public MulBlankCell(int r, int c, + int xfi, + FormattingRecords fr, + SheetImpl si) { + row = r; + column = c; + xfIndex = xfi; + formattingRecords = fr; + sheet = si; + initialized = false; + } + + /** + * Accessor for the row + * + * @return the zero based row + */ + public final int getRow() { + return row; + } + + /** + * Accessor for the column + * + * @return the zero based column + */ + public final int getColumn() { + return column; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return ""; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.EMPTY; + } + + /** + * Gets the cell format for this cell + * + * @return the cell format for these cells + */ + public CellFormat getCellFormat() { + if (!initialized) { + cellFormat = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return cellFormat; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features during the reading process + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) { + if (features != null) { + logger.warn("current cell features not null - overwriting"); + } + + features = cf; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankRecord.java new file mode 100755 index 0000000..b28c7b2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankRecord.java @@ -0,0 +1,127 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains an array of Blank, formatted cells + */ +class MulBlankRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(MulBlankRecord.class); + + /** + * The row containing these numbers + */ + private final int row; + /** + * The first column these rk number occur on + */ + private final int colFirst; + /** + * The last column these blank numbers occur on + */ + private final int colLast; + /** + * The number of blank numbers contained in this record + */ + private final int numblanks; + /** + * The array of xf indices + */ + private final int[] xfIndices; + + /** + * Constructs the blank records from the raw data + * + * @param t the raw data + */ + public MulBlankRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + int length = getRecord().getLength(); + row = IntegerHelper.getInt(data[0], data[1]); + colFirst = IntegerHelper.getInt(data[2], data[3]); + colLast = IntegerHelper.getInt(data[length - 2], data[length - 1]); + numblanks = colLast - colFirst + 1; + xfIndices = new int[numblanks]; + + readBlanks(data); + } + + /** + * Reads the blanks from the raw data + * + * @param data the raw data + */ + private void readBlanks(byte[] data) { + int pos = 4; + for (int i = 0; i < numblanks; i++) { + xfIndices[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + } + + /** + * Accessor for the row + * + * @return the row of containing these blank numbers + */ + public int getRow() { + return row; + } + + /** + * The first column containing the blank numbers + * + * @return the first column + */ + public int getFirstColumn() { + return colFirst; + } + + /** + * Accessor for the number of blank values + * + * @return the number of blank values + */ + public int getNumberOfColumns() { + return numblanks; + } + + /** + * Return a specific formatting index + * + * @param index the cell index in the group + * @return the formatting index + */ + public int getXFIndex(int index) { + return xfIndices[index]; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MulRKRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/MulRKRecord.java new file mode 100755 index 0000000..c429f62 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MulRKRecord.java @@ -0,0 +1,146 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains an array of RK numbers + */ +class MulRKRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(MulRKRecord.class); + + /** + * The row containing these numbers + */ + private final int row; + /** + * The first column these rk number occur on + */ + private final int colFirst; + /** + * The last column these rk numbers occur on + */ + private final int colLast; + /** + * The number of rk numbers contained in this record + */ + private final int numrks; + /** + * The array of rk numbers + */ + private final int[] rknumbers; + /** + * The array of xf indices + */ + private final int[] xfIndices; + + /** + * Constructs the rk numbers from the raw data + * + * @param t the raw data + */ + public MulRKRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + int length = getRecord().getLength(); + row = IntegerHelper.getInt(data[0], data[1]); + colFirst = IntegerHelper.getInt(data[2], data[3]); + colLast = IntegerHelper.getInt(data[length - 2], data[length - 1]); + numrks = colLast - colFirst + 1; + rknumbers = new int[numrks]; + xfIndices = new int[numrks]; + + readRks(data); + } + + /** + * Reads the rks from the raw data + * + * @param data the raw data + */ + private void readRks(byte[] data) { + int pos = 4; + int rk; + for (int i = 0; i < numrks; i++) { + xfIndices[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + rk = IntegerHelper.getInt + (data[pos + 2], data[pos + 3], data[pos + 4], data[pos + 5]); + rknumbers[i] = rk; + pos += 6; + } + } + + /** + * Accessor for the row + * + * @return the row of containing these rk numbers + */ + public int getRow() { + return row; + } + + /** + * The first column containing the rk numbers + * + * @return the first column + */ + public int getFirstColumn() { + return colFirst; + } + + /** + * Accessor for the number of rk values + * + * @return the number of rk values + */ + public int getNumberOfColumns() { + return numrks; + } + + /** + * Returns a specific rk number + * + * @param index the rk number to return + * @return the rk number in bits + */ + public int getRKNumber(int index) { + return rknumbers[index]; + } + + /** + * Return a specific formatting index + * + * @param index the index of the cell in this group + * @return the xf index + */ + public int getXFIndex(int index) { + return xfIndices[index]; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NameRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/NameRecord.java new file mode 100755 index 0000000..7de4be1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NameRecord.java @@ -0,0 +1,510 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import jxl.WorkbookSettings; +import jxl.biff.BuiltInName; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Holds an excel name record, and the details of the cells/ranges it refers + * to + */ +public class NameRecord extends RecordData { + // Constants which refer to the name type + private static final int commandMacro = 0xc; + private static final int builtIn = 0x20; + // Constants which refer to the parse tokens after the string + private static final int cellReference = 0x3a; + private static final int areaReference = 0x3b; + private static final int subExpression = 0x29; + private static final int union = 0x10; + public static Biff7 biff7 = new Biff7(); + + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NameRecord.class); + /** + * The name + */ + private String name; + /** + * The built in name + */ + private BuiltInName builtInName; + /** + * The 0-based index in the name table + */ + private final int index; + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + */ + private int sheetRef = 0; + /** + * Indicates whether this is a biff8 name record. Used during copying + */ + private final boolean isbiff8; + /** + * The ranges referenced by this name + */ + private ArrayList ranges; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + * @param ind the index in the name table + */ + NameRecord(Record t, WorkbookSettings ws, int ind) { + super(t); + index = ind; + isbiff8 = true; + + try { + ranges = new ArrayList(); + + byte[] data = getRecord().getData(); + int option = IntegerHelper.getInt(data[0], data[1]); + int length = data[3]; + sheetRef = IntegerHelper.getInt(data[8], data[9]); + + if ((option & builtIn) != 0) { + builtInName = BuiltInName.getBuiltInName(data[15]); + } else { + name = StringHelper.getString(data, length, 15, ws); + } + + if ((option & commandMacro) != 0) { + // This is a command macro, so it has no cell references + return; + } + + int pos = length + 15; + + if (data[pos] == cellReference) { + int sheet = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + int row = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); + int columnMask = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); + int column = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + NameRange r = new NameRange(sheet, column, row, column, row); + ranges.add(r); + } else if (data[pos] == areaReference) { + int sheet1 = 0; + int r1 = 0; + int columnMask = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + while (pos < data.length) { + sheet1 = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + r1 = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); + r2 = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); + + columnMask = IntegerHelper.getInt(data[pos + 7], data[pos + 8]); + c1 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + columnMask = IntegerHelper.getInt(data[pos + 9], data[pos + 10]); + c2 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 11; + } + } else if (data[pos] == subExpression) { + int sheet1 = 0; + int r1 = 0; + int columnMask = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) { + if (data[pos] == subExpression) { + pos += 3; + } else if (data[pos] == union) { + pos += 1; + } + } + + while (pos < data.length) { + sheet1 = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + r1 = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); + r2 = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); + + columnMask = IntegerHelper.getInt(data[pos + 7], data[pos + 8]); + c1 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + columnMask = IntegerHelper.getInt(data[pos + 9], data[pos + 10]); + c2 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 11; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) { + if (data[pos] == subExpression) { + pos += 3; + } else if (data[pos] == union) { + pos += 1; + } + } + } + } else { + String n = name != null ? name : builtInName.getName(); + logger.warn("Cannot read name ranges for " + n + + " - setting to empty"); + NameRange range = new NameRange(0, 0, 0, 0, 0); + ranges.add(range); + } + } catch (Throwable t1) { + // Generate a warning + // Names are really a nice to have, and we don't want to halt the + // reading process for functionality that probably won't be used + logger.warn("Cannot read name"); + name = "ERROR"; + } + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + * @param ind the index in the name table + * @param dummy dummy parameter to indicate a biff7 workbook + */ + NameRecord(Record t, WorkbookSettings ws, int ind, Biff7 dummy) { + super(t); + index = ind; + isbiff8 = false; + + try { + ranges = new ArrayList(); + byte[] data = getRecord().getData(); + int length = data[3]; + sheetRef = IntegerHelper.getInt(data[8], data[9]); + name = StringHelper.getString(data, length, 14, ws); + + int pos = length + 14; + + if (pos >= data.length) { + // There appears to be nothing after the name, so return + return; + } + + if (data[pos] == cellReference) { + int sheet = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); + int row = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); + int column = data[pos + 17]; + + NameRange r = new NameRange(sheet, column, row, column, row); + ranges.add(r); + } else if (data[pos] == areaReference) { + int sheet1 = 0; + int r1 = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + while (pos < data.length) { + sheet1 = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); + r1 = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); + r2 = IntegerHelper.getInt(data[pos + 17], data[pos + 18]); + + c1 = data[pos + 19]; + c2 = data[pos + 20]; + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 21; + } + } else if (data[pos] == subExpression) { + int sheet1 = 0; + int sheet2 = 0; + int r1 = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) { + if (data[pos] == subExpression) { + pos += 3; + } else if (data[pos] == union) { + pos += 1; + } + } + + while (pos < data.length) { + sheet1 = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); + r1 = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); + r2 = IntegerHelper.getInt(data[pos + 17], data[pos + 18]); + + c1 = data[pos + 19]; + c2 = data[pos + 20]; + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 21; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) { + if (data[pos] == subExpression) { + pos += 3; + } else if (data[pos] == union) { + pos += 1; + } + } + } + } + } catch (Throwable t1) { + // Generate a warning + // Names are really a nice to have, and we don't want to halt the + // reading process for functionality that probably won't be used + logger.warn("Cannot read name."); + name = "ERROR"; + } + } + + /** + * Gets the name + * + * @return the strings + */ + public String getName() { + return name; + } + + /** + * Gets the built in name + * + * @return the built in name + */ + public BuiltInName getBuiltInName() { + return builtInName; + } + + /** + * Gets the array of ranges for this name. This method is public as it is + * used from the writable side when copying ranges + * + * @return the ranges + */ + public NameRange[] getRanges() { + NameRange[] nr = new NameRange[ranges.size()]; + return (NameRange[]) ranges.toArray(nr); + } + + /** + * Accessor for the index into the name table + * + * @return the 0-based index into the name table + */ + int getIndex() { + return index; + } + + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + * + * @return the sheet reference for name formula + */ + public int getSheetRef() { + return sheetRef; + } + + /** + * Set the index sheet reference for a record name + * 0 is for a global reference + */ + public void setSheetRef(int i) { + sheetRef = i; + } + + /** + * Called when copying a sheet. Just returns the raw data + * + * @return the raw data + */ + public byte[] getData() { + return getRecord().getData(); + } + + /** + * Called when copying to determine whether this is a biff8 name + * + * @return TRUE if this is a biff8 name record, FALSE otherwise + */ + public boolean isBiff8() { + return isbiff8; + } + + /** + * Queries whether this is a global name or not + * + * @return TRUE if this is a global name, FALSE otherwise + */ + public boolean isGlobal() { + return sheetRef == 0; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } + + /** + * A nested class to hold range information + */ + public class NameRange { + /** + * The first column + */ + private final int columnFirst; + + /** + * The first row + */ + private final int rowFirst; + + /** + * The last column + */ + private final int columnLast; + + /** + * The last row + */ + private final int rowLast; + + /** + * The first sheet + */ + private final int externalSheet; + + /** + * Constructor + * + * @param s1 the sheet + * @param c1 the first column + * @param r1 the first row + * @param c2 the last column + * @param r2 the last row + */ + NameRange(int s1, int c1, int r1, int c2, int r2) { + columnFirst = c1; + rowFirst = r1; + columnLast = c2; + rowLast = r2; + externalSheet = s1; + } + + /** + * Accessor for the first column + * + * @return the index of the first column + */ + public int getFirstColumn() { + return columnFirst; + } + + /** + * Accessor for the first row + * + * @return the index of the first row + */ + public int getFirstRow() { + return rowFirst; + } + + /** + * Accessor for the last column + * + * @return the index of the last column + */ + public int getLastColumn() { + return columnLast; + } + + /** + * Accessor for the last row + * + * @return the index of the last row + */ + public int getLastRow() { + return rowLast; + } + + /** + * Accessor for the first sheet + * + * @return the index of the external sheet + */ + public int getExternalSheet() { + return externalSheet; + } + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NineteenFourRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/NineteenFourRecord.java new file mode 100755 index 0000000..4dc7e3d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NineteenFourRecord.java @@ -0,0 +1,61 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * Identifies the date system as the 1904 system or not + */ +class NineteenFourRecord extends RecordData { + /** + * The base year for dates + */ + private final boolean nineteenFour; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public NineteenFourRecord(Record t) { + super(t); + + byte[] data = getRecord().getData(); + + nineteenFour = data[0] == 1; + + } + + /** + * Accessor to see whether this spreadsheets dates are based around + * 1904 + * + * @return true if this workbooks dates are based around the 1904 + * date system + */ + public boolean is1904() { + return nineteenFour; + } + + +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NumberFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/NumberFormulaRecord.java new file mode 100755 index 0000000..6919c86 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NumberFormulaRecord.java @@ -0,0 +1,179 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.NumberFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + +/** + * A formula's last calculated value + */ +class NumberFormulaRecord extends CellValue + implements NumberCell, FormulaData, NumberFormulaCell { + /** + * The string format for the double value + */ + private static final DecimalFormat defaultFormat = + new DecimalFormat("#.###"); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NumberFormulaRecord.class); + /** + * The last calculated value of the formula + */ + private final double value; + /** + * The number format + */ + private NumberFormat format; + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The raw data + */ + private final byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting record + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public NumberFormulaRecord(Record t, FormattingRecords fr, + ExternalSheet es, WorkbookMethods nt, + SheetImpl si) { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + data = getRecord().getData(); + + format = fr.getNumberFormat(getXFIndex()); + + if (format == null) { + format = defaultFormat; + } + + value = DoubleHelper.getIEEEDouble(data, 6); + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public double getValue() { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + return !Double.isNaN(value) ? format.format(value) : ""; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.NUMBER_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NumberRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/NumberRecord.java new file mode 100755 index 0000000..2828df6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NumberRecord.java @@ -0,0 +1,111 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.common.Logger; + +/** + * A number record. This is stored as 8 bytes, as opposed to the + * 4 byte RK record + */ +class NumberRecord extends CellValue implements NumberCell { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NumberRecord.class); + /** + * The formatter to convert the value into a string + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The value + */ + private final double value; + /** + * The java equivalent of the excel format + */ + private NumberFormat format; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the available formats + * @param si the sheet + */ + public NumberRecord(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + byte[] data = getRecord().getData(); + + value = DoubleHelper.getIEEEDouble(data, 6); + + // Now get the number format + format = fr.getNumberFormat(getXFIndex()); + if (format == null) { + format = defaultFormat; + } + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Returns the contents of this cell as a string + * + * @return the value formatted into a string + */ + public String getContents() { + return format.format(value); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.NUMBER; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NumberValue.java b/datastructures-xslx/src/main/java/jxl/read/biff/NumberValue.java new file mode 100755 index 0000000..92006fe --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NumberValue.java @@ -0,0 +1,225 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellFeatures; +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.FormattingRecords; +import jxl.format.CellFormat; + +/** + * A numerical cell value, initialized indirectly from a multiple biff record + * rather than directly from the binary data + */ +class NumberValue implements NumberCell, CellFeaturesAccessor { + /** + * The format in which to return this number as a string + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The row containing this number + */ + private final int row; + /** + * The column containing this number + */ + private final int column; + /** + * The value of this number + */ + private final double value; + /** + * The cell format + */ + private NumberFormat format; + /** + * The raw cell format + */ + private CellFormat cellFormat; + /** + * The cell features + */ + private CellFeatures features; + /** + * The index to the XF Record + */ + private final int xfIndex; + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + /** + * A flag to indicate whether this object's formatting things have + * been initialized + */ + private boolean initialized; + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + + /** + * Constructs this number + * + * @param r the zero based row + * @param c the zero base column + * @param val the value + * @param xfi the xf index + * @param fr the formatting records + * @param si the sheet + */ + public NumberValue(int r, int c, double val, + int xfi, + FormattingRecords fr, + SheetImpl si) { + row = r; + column = c; + value = val; + format = defaultFormat; + xfIndex = xfi; + formattingRecords = fr; + sheet = si; + initialized = false; + } + + /** + * Accessor for the row + * + * @return the zero based row + */ + public final int getRow() { + return row; + } + + /** + * Accessor for the column + * + * @return the zero based column + */ + public final int getColumn() { + return column; + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return format.format(value); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.NUMBER; + } + + /** + * Gets the cell format + * + * @return the cell format + */ + public CellFormat getCellFormat() { + if (!initialized) { + cellFormat = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return cellFormat; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } + + /** + * Sets the format for the number based on the Excel spreadsheets' format. + * This is called from SheetImpl when it has been definitely established + * that this cell is a number and not a date + * + * @param f the format + */ + final void setNumberFormat(NumberFormat f) { + if (f != null) { + format = f; + } + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) { + features = cf; + } + +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PLSRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PLSRecord.java new file mode 100755 index 0000000..f296cac --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PLSRecord.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * The environment specific print record + */ +public class PLSRecord extends RecordData { + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + public PLSRecord(Record r) { + super(r); + } + + /** + * Gets the data + * + * @return the binary data + */ + public byte[] getData() { + return getRecord().getData(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PaletteRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PaletteRecord.java new file mode 100755 index 0000000..fe92bca --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PaletteRecord.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * A password record + */ +public class PaletteRecord extends RecordData { + /** + * Constructor + * + * @param t the raw bytes + */ + PaletteRecord(Record t) { + super(t); + } + + /** + * Accessor for the binary data - used when copying + * + * @return the binary data + */ + public byte[] getData() { + return getRecord().getData(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PaneRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PaneRecord.java new file mode 100755 index 0000000..75a60b5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PaneRecord.java @@ -0,0 +1,81 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class PaneRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(PaneRecord.class); + + /** + * The number of rows visible in the top left pane + */ + private final int rowsVisible; + /** + * The number of columns visible in the top left pane + */ + private final int columnsVisible; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public PaneRecord(Record t) { + super(t); + byte[] data = t.getData(); + + columnsVisible = IntegerHelper.getInt(data[0], data[1]); + rowsVisible = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Accessor for the number of rows in the top left pane + * + * @return the number of rows visible in the top left pane + */ + public final int getRowsVisible() { + return rowsVisible; + } + + /** + * Accessor for the numbe rof columns visible in the top left pane + * + * @return the number of columns visible in the top left pane + */ + public final int getColumnsVisible() { + return columnsVisible; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PasswordException.java b/datastructures-xslx/src/main/java/jxl/read/biff/PasswordException.java new file mode 100755 index 0000000..ecf1c6a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PasswordException.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +/** + * A properly typed exception in case consumers of the API specifically + * wish to handle the case when the workbook is password protected + */ +public class PasswordException extends BiffException { + /** + * Constructor + */ + public PasswordException() { + super(passwordProtected); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PasswordRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PasswordRecord.java new file mode 100755 index 0000000..e13f337 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PasswordRecord.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * A password record + */ +class PasswordRecord extends RecordData { + /** + * The password + */ + private String password; + /** + * The binary data + */ + private final int passwordHash; + + /** + * Constructor + * + * @param t the raw bytes + */ + public PasswordRecord(Record t) { + super(Type.PASSWORD); + + byte[] data = t.getData(); + passwordHash = IntegerHelper.getInt(data[0], data[1]); + } + + /** + * Gets the binary data for output to file + * + * @return the password hash + */ + public int getPasswordHash() { + return passwordHash; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PrintGridLinesRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PrintGridLinesRecord.java new file mode 100755 index 0000000..22c6342 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PrintGridLinesRecord.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * Contains the print grid lines option of this worksheet + */ +class PrintGridLinesRecord extends RecordData { + /** + * print grid lines flag + */ + private final boolean printGridLines; + + /** + * Constructs the value from the raw data + * + * @param pgl the raw data + */ + public PrintGridLinesRecord(Record pgl) { + super(pgl); + byte[] data = pgl.getData(); + + printGridLines = (data[0] == 1); + } + + /** + * Accessor for the print grid lines flag + * + * @return TRUE to print grid lines, FALSE otherwise + */ + public boolean getPrintGridLines() { + return printGridLines; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PrintHeadersRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PrintHeadersRecord.java new file mode 100755 index 0000000..ca04b91 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PrintHeadersRecord.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * Contains the print grid lines option of this worksheet + */ +class PrintHeadersRecord extends RecordData { + /** + * print grid lines flag + */ + private final boolean printHeaders; + + /** + * Constructs the value from the raw data + * + * @param ph the raw data + */ + public PrintHeadersRecord(Record ph) { + super(ph); + byte[] data = ph.getData(); + + printHeaders = (data[0] == 1); + } + + /** + * Accessor for the print headers flag + * + * @return TRUE to print headers, FALSE otherwise + */ + public boolean getPrintHeaders() { + return printHeaders; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ProtectRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ProtectRecord.java new file mode 100755 index 0000000..8ef1c55 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ProtectRecord.java @@ -0,0 +1,67 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * A record detailing whether the sheet is protected + */ +class ProtectRecord extends RecordData { + /** + * Protected flag + */ + private final boolean prot; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + ProtectRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + + int protflag = IntegerHelper.getInt(data[0], data[1]); + + prot = (protflag == 1); + } + + /** + * Returns the protected flag + * + * @return TRUE if this is protected, FALSE otherwise + */ + boolean isProtected() { + return prot; + } + + +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RKHelper.java b/datastructures-xslx/src/main/java/jxl/read/biff/RKHelper.java new file mode 100755 index 0000000..d15d063 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RKHelper.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +/** + * Helper to convert an RK number into a double or an integer + */ +final class RKHelper { + /** + * Private constructor to prevent instantiation + */ + private RKHelper() { + } + + /** + * Converts excel's internal RK format into a double value + * + * @param rk the rk number in bits + * @return the double representation + */ + public static double getDouble(int rk) { + if ((rk & 0x02) != 0) { + int intval = rk >> 2; + + double value = intval; + if ((rk & 0x01) != 0) { + value /= 100; + } + + return value; + } else { + long valbits = (rk & 0xfffffffc); + valbits <<= 32; + double value = Double.longBitsToDouble(valbits); + + if ((rk & 0x01) != 0) { + value /= 100; + } + + return value; + } + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RKRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RKRecord.java new file mode 100755 index 0000000..a5a10b1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RKRecord.java @@ -0,0 +1,110 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * An individual RK record + */ +class RKRecord extends CellValue implements NumberCell { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(RKRecord.class); + /** + * The formatter to convert the value into a string + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The value + */ + private final double value; + /** + * The java equivalent of the excel format + */ + private NumberFormat format; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the available cell formats + * @param si the sheet + */ + public RKRecord(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + byte[] data = getRecord().getData(); + int rknum = IntegerHelper.getInt(data[6], data[7], data[8], data[9]); + value = RKHelper.getDouble(rknum); + + // Now get the number format + format = fr.getNumberFormat(getXFIndex()); + if (format == null) { + format = defaultFormat; + } + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Returns the contents of this cell as a string + * + * @return the value formatted into a string + */ + public String getContents() { + return format.format(value); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.NUMBER; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RStringRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RStringRecord.java new file mode 100755 index 0000000..1b03658 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RStringRecord.java @@ -0,0 +1,93 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; + +/** + * A label which is stored in the cell + */ +class RStringRecord extends CellValue implements LabelCell { + public static Biff7 biff7 = new Biff7(); + /** + * The length of the label in characters + */ + private final int length; + /** + * The label + */ + private final String string; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param ws the workbook settings + * @param dummy dummy overload to indicate a biff 7 workbook + */ + public RStringRecord(Record t, FormattingRecords fr, + SheetImpl si, WorkbookSettings ws, Biff7 dummy) { + super(t, fr, si); + byte[] data = getRecord().getData(); + length = IntegerHelper.getInt(data[6], data[7]); + + string = StringHelper.getString(data, length, 8, ws); + } + + /** + * Gets the label + * + * @return the label + */ + public String getString() { + return string; + } + + /** + * Gets the cell contents as a string + * + * @return the label + */ + public String getContents() { + return string; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.LABEL; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/Record.java b/datastructures-xslx/src/main/java/jxl/read/biff/Record.java new file mode 100755 index 0000000..43afc94 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/Record.java @@ -0,0 +1,169 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.common.Logger; + + +/** + * A container for the raw record data within a biff file + */ +public final class Record { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Record.class); + + /** + * The excel biff code + */ + private final int code; + /** + * The data type + */ + private Type type; + /** + * The length of this record + */ + private final int length; + /** + * A pointer to the beginning of the actual data + */ + private final int dataPos; + /** + * A handle to the excel 97 file + */ + private final File file; + /** + * The raw data within this record + */ + private byte[] data; + + /** + * Any continue records + */ + private ArrayList continueRecords; + + /** + * Constructor + * + * @param offset the offset in the raw file + * @param f the excel 97 biff file + * @param d the data record + */ + Record(byte[] d, int offset, File f) { + code = IntegerHelper.getInt(d[offset], d[offset + 1]); + length = IntegerHelper.getInt(d[offset + 2], d[offset + 3]); + file = f; + file.skip(4); + dataPos = f.getPos(); + file.skip(length); + type = Type.getType(code); + } + + /** + * Gets the biff type + * + * @return the biff type + */ + public Type getType() { + return type; + } + + /** + * In the case of dodgy records, this method may be called to forcibly + * set the type in order to continue processing + * + * @param t the forcibly overridden type + */ + void setType(Type t) { + type = t; + } + + /** + * Gets the length of the record + * + * @return the length of the record + */ + public int getLength() { + return length; + } + + /** + * Gets the data portion of the record + * + * @return the data portion of the record + */ + public byte[] getData() { + if (data == null) { + data = file.read(dataPos, length); + } + + // copy in any data from the continue records + if (continueRecords != null) { + int size = 0; + byte[][] contData = new byte[continueRecords.size()][]; + for (int i = 0; i < continueRecords.size(); i++) { + Record r = (Record) continueRecords.get(i); + contData[i] = r.getData(); + byte[] d2 = contData[i]; + size += d2.length; + } + + byte[] d3 = new byte[data.length + size]; + System.arraycopy(data, 0, d3, 0, data.length); + int pos = data.length; + for (int i = 0; i < contData.length; i++) { + byte[] d2 = contData[i]; + System.arraycopy(d2, 0, d3, pos, d2.length); + pos += d2.length; + } + + data = d3; + } + + return data; + } + + /** + * The excel 97 code + * + * @return the excel code + */ + public int getCode() { + return code; + } + + /** + * Adds a continue record to this data + * + * @param d the continue record + */ + public void addContinueRecord(Record d) { + if (continueRecords == null) { + continueRecords = new ArrayList(); + } + + continueRecords.add(d); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RefreshAllRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RefreshAllRecord.java new file mode 100755 index 0000000..88a0de1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RefreshAllRecord.java @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A Refresh all mode record + */ +class RefreshAllRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(RefreshAllRecord.class); + + /** + * The refresh all mode + */ + private final boolean refreshAll; + + /** + * Constructor + * + * @param t the record + */ + public RefreshAllRecord(Record t) { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + refreshAll = (mode == 1); + } + + /** + * Accessor for the refreshAll mode + * + * @return the refreshAll mode + */ + public boolean getRefreshAll() { + return refreshAll; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RightMarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RightMarginRecord.java new file mode 100755 index 0000000..3bbff05 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RightMarginRecord.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class RightMarginRecord extends MarginRecord { + /** + * Constructor + * + * @param r the record + */ + RightMarginRecord(Record r) { + super(Type.RIGHTMARGIN, r); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RowRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RowRecord.java new file mode 100755 index 0000000..76ec4d7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RowRecord.java @@ -0,0 +1,175 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A row record + */ +public class RowRecord extends RecordData { + /** + * Indicates that the row is default height + */ + private static final int defaultHeightIndicator = 0xff; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(RowRecord.class); + /** + * The number of this row + */ + private final int rowNumber; + /** + * The height of this row + */ + private final int rowHeight; + /** + * Flag to indicate whether this row is collapsed or not + */ + private final boolean collapsed; + /** + * Indicates whether this row has an explicit default format + */ + private final boolean defaultFormat; + /** + * Indicates whether the row record height matches the default font height + */ + private final boolean matchesDefFontHeight; + /** + * The (default) xf index for cells on this row + */ + private final int xfIndex; + /** + * The outline level of the row + */ + private final int outlineLevel; + /** + * Is this the icon indicator row of a group? + */ + private final boolean groupStart; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + RowRecord(Record t) { + super(t); + + byte[] data = getRecord().getData(); + rowNumber = IntegerHelper.getInt(data[0], data[1]); + rowHeight = IntegerHelper.getInt(data[6], data[7]); + + int options = IntegerHelper.getInt(data[12], data[13], + data[14], data[15]); + outlineLevel = (options & 0x7); + groupStart = (options & 0x10) != 0; + collapsed = (options & 0x20) != 0; + matchesDefFontHeight = (options & 0x40) == 0; + defaultFormat = (options & 0x80) != 0; + xfIndex = (options & 0x0fff0000) >> 16; + } + + /** + * Interrogates whether this row is of default height + * + * @return TRUE if this is set to the default height, FALSE otherwise + */ + boolean isDefaultHeight() { + return rowHeight == defaultHeightIndicator; + } + + /** + * Interrogates this row to see whether it matches the default font height + * + * @return TRUE if this matches the default font height, FALSE otherwise + */ + public boolean matchesDefaultFontHeight() { + return matchesDefFontHeight; + } + + /** + * Gets the row number + * + * @return the number of this row + */ + public int getRowNumber() { + return rowNumber; + } + + /** + * Accessor for the row's outline level + * + * @return the row's outline level + */ + public int getOutlineLevel() { + return outlineLevel; + } + + /** + * Accessor for row's groupStart value + * + * @return the row's groupStart value + */ + public boolean getGroupStart() { + return groupStart; + } + + /** + * Gets the height of the row + * + * @return the row height + */ + public int getRowHeight() { + return rowHeight; + } + + /** + * Queries whether the row is collapsed + * + * @return the collapsed indicator + */ + public boolean isCollapsed() { + return collapsed; + } + + /** + * Gets the default xf index for this row + * + * @return the xf index + */ + public int getXFIndex() { + return xfIndex; + } + + /** + * Queries whether the row has a specific default cell format applied + * + * @return the default cell format + */ + public boolean hasDefaultFormat() { + return defaultFormat; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SCLRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SCLRecord.java new file mode 100755 index 0000000..ff5d77d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SCLRecord.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan, Adam Caldwell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Class containing the zoom factor for display + */ +class SCLRecord extends RecordData { + /** + * The numerator of the zoom + */ + private final int numerator; + + /** + * The denominator of the zoom + */ + private final int denominator; + + /** + * Constructs this record from the raw data + * + * @param r the record + */ + protected SCLRecord(Record r) { + super(Type.SCL); + + byte[] data = r.getData(); + + numerator = IntegerHelper.getInt(data[0], data[1]); + denominator = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Accessor for the zoom factor + * + * @return the zoom factor as the nearest integer percentage + */ + public int getZoomFactor() { + return numerator * 100 / denominator; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SSTRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SSTRecord.java new file mode 100755 index 0000000..c1b66bf --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SSTRecord.java @@ -0,0 +1,391 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Assert; + +/** + * Holds all the strings in the shared string table + */ +class SSTRecord extends RecordData { + /** + * The total number of strings in this table + */ + private final int totalStrings; + /** + * The number of unique strings + */ + private final int uniqueStrings; + /** + * The shared strings + */ + private final String[] strings; + /** + * The array of continuation breaks + */ + private final int[] continuationBreaks; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param continuations the continuations + * @param ws the workbook settings + */ + public SSTRecord(Record t, Record[] continuations, WorkbookSettings ws) { + super(t); + + // If a continue record appears in the middle of + // a string, then the encoding character is repeated + + // Concatenate everything into one big bugger of a byte array + int totalRecordLength = 0; + + for (int i = 0; i < continuations.length; i++) { + totalRecordLength += continuations[i].getLength(); + } + totalRecordLength += getRecord().getLength(); + + byte[] data = new byte[totalRecordLength]; + + // First the original data gets put in + int pos = 0; + System.arraycopy(getRecord().getData(), 0, + data, 0, getRecord().getLength()); + pos += getRecord().getLength(); + + // Now copy in everything else. + continuationBreaks = new int[continuations.length]; + Record r = null; + for (int i = 0; i < continuations.length; i++) { + r = continuations[i]; + System.arraycopy(r.getData(), 0, + data, pos, + r.getLength()); + continuationBreaks[i] = pos; + pos += r.getLength(); + } + + totalStrings = IntegerHelper.getInt(data[0], data[1], + data[2], data[3]); + uniqueStrings = IntegerHelper.getInt(data[4], data[5], + data[6], data[7]); + + strings = new String[uniqueStrings]; + readStrings(data, 8, ws); + } + + /** + * Reads in all the strings from the raw data + * + * @param data the raw data + * @param offset the offset + * @param ws the workbook settings + */ + private void readStrings(byte[] data, int offset, WorkbookSettings ws) { + int pos = offset; + int numChars; + byte optionFlags; + String s = null; + boolean asciiEncoding = false; + boolean richString = false; + boolean extendedString = false; + int formattingRuns = 0; + int extendedRunLength = 0; + + for (int i = 0; i < uniqueStrings; i++) { + // Read in the number of characters + numChars = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + optionFlags = data[pos]; + pos++; + + // See if it is an extended string + extendedString = ((optionFlags & 0x04) != 0); + + // See if string contains formatting information + richString = ((optionFlags & 0x08) != 0); + + if (richString) { + // Read in the crun + formattingRuns = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + + if (extendedString) { + // Read in cchExtRst + extendedRunLength = IntegerHelper.getInt + (data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + pos += 4; + } + + // See if string is ASCII (compressed) or unicode + asciiEncoding = ((optionFlags & 0x01) == 0); + + ByteArrayHolder bah = new ByteArrayHolder(); + BooleanHolder bh = new BooleanHolder(); + bh.value = asciiEncoding; + pos += getChars(data, bah, pos, bh, numChars); + asciiEncoding = bh.value; + + if (asciiEncoding) { + s = StringHelper.getString(bah.bytes, numChars, 0, ws); + } else { + s = StringHelper.getUnicodeString(bah.bytes, numChars, 0); + } + + strings[i] = s; + + // For rich strings, skip over the formatting runs + if (richString) { + pos += 4 * formattingRuns; + } + + // For extended strings, skip over the extended string data + if (extendedString) { + pos += extendedRunLength; + } + + if (pos > data.length) { + Assert.verify(false, "pos exceeds record length"); + } + } + } + + /** + * Gets the chars in the ascii array, taking into account continuation + * breaks + * + * @param source the original source + * @param bah holder for the new byte array + * @param pos the current position in the source + * @param ascii holder for a return ascii flag + * @param numChars the number of chars in the string + * @return the number of bytes read from the source + */ + private int getChars(byte[] source, + ByteArrayHolder bah, + int pos, + BooleanHolder ascii, + int numChars) { + int i = 0; + boolean spansBreak = false; + + if (ascii.value) { + bah.bytes = new byte[numChars]; + } else { + bah.bytes = new byte[numChars * 2]; + } + + while (i < continuationBreaks.length && !spansBreak) { + spansBreak = pos <= continuationBreaks[i] && + (pos + bah.bytes.length > continuationBreaks[i]); + + if (!spansBreak) { + i++; + } + } + + // If it doesn't span a break simply do an array copy into the + // destination array and finish + if (!spansBreak) { + System.arraycopy(source, pos, bah.bytes, 0, bah.bytes.length); + return bah.bytes.length; + } + + // Copy the portion before the break pos into the array + int breakpos = continuationBreaks[i]; + System.arraycopy(source, pos, bah.bytes, 0, breakpos - pos); + + int bytesRead = breakpos - pos; + int charsRead; + if (ascii.value) { + charsRead = bytesRead; + } else { + charsRead = bytesRead / 2; + } + + bytesRead += getContinuedString(source, + bah, + bytesRead, + i, + ascii, + numChars - charsRead); + return bytesRead; + } + + /** + * Gets the rest of the string after a continuation break + * + * @param source the original bytes + * @param bah the holder for the new bytes + * @param destPos the des pos + * @param contBreakIndex the index of the continuation break + * @param ascii the ascii flag holder + * @param charsLeft the number of chars left in the array + * @return the number of bytes read in the continued string + */ + private int getContinuedString(byte[] source, + ByteArrayHolder bah, + int destPos, + int contBreakIndex, + BooleanHolder ascii, + int charsLeft) { + int breakpos = continuationBreaks[contBreakIndex]; + int bytesRead = 0; + + while (charsLeft > 0) { + Assert.verify(contBreakIndex < continuationBreaks.length, + "continuation break index"); + + if (ascii.value && source[breakpos] == 0) { + // The string is consistently ascii throughout + + int length = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft : + Math.min + (charsLeft, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + System.arraycopy(source, + breakpos + 1, + bah.bytes, + destPos, + length); + destPos += length; + bytesRead += length + 1; + charsLeft -= length; + ascii.value = true; + } else if (!ascii.value && source[breakpos] != 0) { + // The string is Unicode throughout + + int length = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft * 2 : + Math.min + (charsLeft * 2, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + // It looks like the string continues as Unicode too. That's handy + System.arraycopy(source, + breakpos + 1, + bah.bytes, + destPos, + length); + + destPos += length; + bytesRead += length + 1; + charsLeft -= length / 2; + ascii.value = false; + } else if (!ascii.value && source[breakpos] == 0) { + // Bummer - the string starts off as Unicode, but after the + // continuation it is in straightforward ASCII encoding + int chars = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft : + Math.min + (charsLeft, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + for (int j = 0; j < chars; j++) { + bah.bytes[destPos] = source[breakpos + j + 1]; + destPos += 2; + } + + bytesRead += chars + 1; + charsLeft -= chars; + ascii.value = false; + } else { + // Double Bummer - the string starts off as ASCII, but after the + // continuation it is in Unicode. This impacts the allocated array + + // Reallocate what we have of the byte array so that it is all + // Unicode + byte[] oldBytes = bah.bytes; + bah.bytes = new byte[destPos * 2 + charsLeft * 2]; + for (int j = 0; j < destPos; j++) { + bah.bytes[j * 2] = oldBytes[j]; + } + + destPos = destPos * 2; + + int length = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft * 2 : + Math.min + (charsLeft * 2, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + System.arraycopy(source, + breakpos + 1, + bah.bytes, + destPos, + length); + + destPos += length; + bytesRead += length + 1; + charsLeft -= length / 2; + ascii.value = false; + } + + contBreakIndex++; + + if (contBreakIndex < continuationBreaks.length) { + breakpos = continuationBreaks[contBreakIndex]; + } + } + + return bytesRead; + } + + /** + * Gets the string at the specified position + * + * @param index the index of the string to return + * @return the strings + */ + public String getString(int index) { + Assert.verify(index < uniqueStrings); + return strings[index]; + } + + /** + * A holder for a byte array + */ + private static class ByteArrayHolder { + /** + * the byte holder + */ + public byte[] bytes; + } + + /** + * A holder for a boolean + */ + private static class BooleanHolder { + /** + * the holder holder + */ + public boolean value; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SaveRecalcRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SaveRecalcRecord.java new file mode 100755 index 0000000..6346d10 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SaveRecalcRecord.java @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A calculation mode record + */ +class SaveRecalcRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SaveRecalcRecord.class); + + /** + * The calculation mode + */ + private final boolean recalculateOnSave; + + /** + * Constructor + * + * @param t the record + */ + public SaveRecalcRecord(Record t) { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + recalculateOnSave = (mode == 1); + } + + /** + * Accessor for the recalculate on save mode + * + * @return the recalculate on save mode + */ + public boolean getRecalculateOnSave() { + return recalculateOnSave; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SetupRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SetupRecord.java new file mode 100755 index 0000000..e9ae744 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SetupRecord.java @@ -0,0 +1,255 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; +import jxl.common.Logger; + +/** + * Contains the page set up for a sheet + */ +public class SetupRecord extends RecordData { + // The logger + private static final Logger logger = Logger.getLogger(SetupRecord.class); + + /** + * The raw data + */ + private final byte[] data; + + /** + * The orientation flag + */ + private final boolean portraitOrientation; + + /** + * The Page Order flag + */ + private final boolean pageOrder; + + /** + * The header margin + */ + private final double headerMargin; + + /** + * The footer margin + */ + private final double footerMargin; + + /** + * The paper size + */ + private final int paperSize; + + /** + * The scale factor + */ + private final int scaleFactor; + + /** + * The page start + */ + private final int pageStart; + + /** + * The fit width + */ + private final int fitWidth; + + /** + * The fit height + */ + private final int fitHeight; + + /** + * The horizontal print resolution + */ + private final int horizontalPrintResolution; + + /** + * The vertical print resolution + */ + private final int verticalPrintResolution; + + /** + * The number of copies + */ + private final int copies; + + /** + * Indicates whether the setup data should be initiliazed in the setup + * box + */ + private final boolean initialized; + + /** + * Constructor which creates this object from the binary data + * + * @param t the record + */ + SetupRecord(Record t) { + super(Type.SETUP); + + data = t.getData(); + + paperSize = IntegerHelper.getInt(data[0], data[1]); + scaleFactor = IntegerHelper.getInt(data[2], data[3]); + pageStart = IntegerHelper.getInt(data[4], data[5]); + fitWidth = IntegerHelper.getInt(data[6], data[7]); + fitHeight = IntegerHelper.getInt(data[8], data[9]); + horizontalPrintResolution = IntegerHelper.getInt(data[12], data[13]); + verticalPrintResolution = IntegerHelper.getInt(data[14], data[15]); + copies = IntegerHelper.getInt(data[32], data[33]); + + headerMargin = DoubleHelper.getIEEEDouble(data, 16); + footerMargin = DoubleHelper.getIEEEDouble(data, 24); + + + int grbit = IntegerHelper.getInt(data[10], data[11]); + pageOrder = ((grbit & 0x01) != 0); + portraitOrientation = ((grbit & 0x02) != 0); + initialized = ((grbit & 0x04) == 0); + } + + /** + * Accessor for the orientation. Called when copying sheets + * + * @return TRUE if the orientation is portrait, FALSE if it is landscape + */ + public boolean isPortrait() { + return portraitOrientation; + } + + + /** + * Accessor for the page order. Called when copying sheets + * + * @return TRUE if the page order is Left to Right, then Down, otherwise + * FALSE + */ + public boolean isRightDown() { + return pageOrder; + } + + /** + * Accessor for the header. Called when copying sheets + * + * @return the header margin + */ + public double getHeaderMargin() { + return headerMargin; + } + + /** + * Accessor for the footer. Called when copying sheets + * + * @return the footer margin + */ + public double getFooterMargin() { + return footerMargin; + } + + /** + * Accessor for the paper size. Called when copying sheets + * + * @return the footer margin + */ + public int getPaperSize() { + return paperSize; + } + + /** + * Accessor for the scale factor. Called when copying sheets + * + * @return the scale factor + */ + public int getScaleFactor() { + return scaleFactor; + } + + /** + * Accessor for the page height. called when copying sheets + * + * @return the page to start printing at + */ + public int getPageStart() { + return pageStart; + } + + /** + * Accessor for the fit width. Called when copying sheets + * + * @return the fit width + */ + public int getFitWidth() { + return fitWidth; + } + + /** + * Accessor for the fit height. Called when copying sheets + * + * @return the fit height + */ + public int getFitHeight() { + return fitHeight; + } + + /** + * The horizontal print resolution. Called when copying sheets + * + * @return the horizontal print resolution + */ + public int getHorizontalPrintResolution() { + return horizontalPrintResolution; + } + + /** + * Accessor for the vertical print resolution. Called when copying sheets + * + * @return an vertical print resolution + */ + public int getVerticalPrintResolution() { + return verticalPrintResolution; + } + + /** + * Accessor for the number of copies + * + * @return the number of copies + */ + public int getCopies() { + return copies; + } + + /** + * Accessor for the initialized flag + * + * @return whether the print page setup should be initialized in the dialog + * box + */ + public boolean getInitialized() { + return initialized; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedBooleanFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedBooleanFormulaRecord.java new file mode 100755 index 0000000..dd4c84d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedBooleanFormulaRecord.java @@ -0,0 +1,146 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.BooleanCell; +import jxl.BooleanFormulaCell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + +/** + * A shared boolean formula record + */ +public class SharedBooleanFormulaRecord extends BaseSharedFormulaRecord + implements BooleanCell, FormulaData, BooleanFormulaCell { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(SharedBooleanFormulaRecord.class); + + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private final boolean value; + + /** + * Constructs this number + * + * @param t the data + * @param excelFile the excel biff data + * @param v the value + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public SharedBooleanFormulaRecord(Record t, + File excelFile, + boolean v, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, es, nt, si, excelFile.getPos()); + value = v; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.BOOLEAN_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + data[6] = (byte) 1; + data[8] = (byte) (value == true ? 1 : 0); + data[12] = (byte) 0xff; + data[13] = (byte) 0xff; + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedDateFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedDateFormulaRecord.java new file mode 100755 index 0000000..7bcc522 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedDateFormulaRecord.java @@ -0,0 +1,182 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DateFormat; +import java.util.Date; +import jxl.CellType; +import jxl.DateCell; +import jxl.DateFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A number formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedDateFormulaRecord extends BaseSharedFormulaRecord + implements DateCell, FormulaData, DateFormulaCell { + /** + * Re-use the date record to handle all the formatting information and + * date calculations + */ + private final DateRecord dateRecord; + + /** + * The double value + */ + private final double value; + + /** + * Constructs this number formula + * + * @param nfr the number formula records + * @param fr the formatting records + * @param nf flag indicating whether this uses the 1904 date system + * @param si the sheet + * @param pos the position + */ + public SharedDateFormulaRecord(SharedNumberFormulaRecord nfr, + FormattingRecords fr, + boolean nf, + SheetImpl si, + int pos) { + super(nfr.getRecord(), + fr, + nfr.getExternalSheet(), + nfr.getNameTable(), + si, + pos); + dateRecord = new DateRecord(nfr, nfr.getXFIndex(), fr, nf, si); + value = nfr.getValue(); + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return dateRecord.getContents(); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.DATE_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + DoubleHelper.getIEEEBytes(value, data, 6); + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the date + * + * @return the date + */ + public Date getDate() { + return dateRecord.getDate(); + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + public boolean isTime() { + return dateRecord.isTime(); + } + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat() { + return dateRecord.getDateFormat(); + } + +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedErrorFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedErrorFormulaRecord.java new file mode 100755 index 0000000..4429656 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedErrorFormulaRecord.java @@ -0,0 +1,168 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.ErrorCell; +import jxl.ErrorFormulaCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaErrorCode; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + +/** + * A number formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedErrorFormulaRecord extends BaseSharedFormulaRecord + implements ErrorCell, FormulaData, ErrorFormulaCell { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(SharedErrorFormulaRecord.class); + + /** + * The error code of this cell + */ + private final int errorCode; + + /** + * The raw data + */ + private byte[] data; + + /** + * The error code + */ + private FormulaErrorCode error; + + /** + * Constructs this number + * + * @param t the data + * @param excelFile the excel biff data + * @param v the errorCode + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public SharedErrorFormulaRecord(Record t, + File excelFile, + int ec, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, es, nt, si, excelFile.getPos()); + errorCode = ec; + } + + /** + * Interface method which gets the error code for this cell. If this cell + * does not represent an error, then it returns 0. Always use the + * method isError() to determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode() { + return errorCode; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + if (error == null) { + error = FormulaErrorCode.getErrorCode(errorCode); + } + + return error != FormulaErrorCode.UNKNOWN ? + error.getDescription() : "ERROR " + errorCode; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.FORMULA_ERROR; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + + data[6] = (byte) 0x02; // indicates this cell is an error value + data[8] = (byte) errorCode; + data[12] = (byte) 0xff; + data[13] = (byte) 0xff; + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedFormulaRecord.java new file mode 100755 index 0000000..03cdfe9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedFormulaRecord.java @@ -0,0 +1,214 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.NumberFormat; +import java.util.ArrayList; +import jxl.Cell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.common.Logger; + +/** + * A shared formula + */ +class SharedFormulaRecord { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SharedFormulaRecord.class); + + /** + * The first row to which this shared formula applies + */ + private final int firstRow; + + /** + * The last row to which this shared formula applies + */ + private final int lastRow; + + /** + * The first column to which this shared formula applies + */ + private final int firstCol; + + /** + * The last column to which this shared formula applies + */ + private final int lastCol; + + /** + * The first (template) formula comprising this group + */ + private BaseSharedFormulaRecord templateFormula; + + /** + * The rest of the cells comprising this shared formula + */ + private final ArrayList formulas; + + /** + * The token data + */ + private final byte[] tokens; + + /** + * A handle to the external sheet + */ + private ExternalSheet externalSheet; + + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + + + /** + * Constructs this object from the raw data. Creates either a + * NumberFormulaRecord or a StringFormulaRecord depending on whether + * this formula represents a numerical calculation or not + * + * @param t the raw data + * @param fr the base shared formula + * @param es the workbook, which contains the external sheet references + * @param nt the workbook + * @param si the sheet + */ + public SharedFormulaRecord(Record t, BaseSharedFormulaRecord fr, + ExternalSheet es, WorkbookMethods nt, + SheetImpl si) { + sheet = si; + byte[] data = t.getData(); + + firstRow = IntegerHelper.getInt(data[0], data[1]); + lastRow = IntegerHelper.getInt(data[2], data[3]); + firstCol = data[4] & 0xff; + lastCol = data[5] & 0xff; + + formulas = new ArrayList(); + + templateFormula = fr; + + tokens = new byte[data.length - 10]; + System.arraycopy(data, 10, tokens, 0, tokens.length); + } + + /** + * Adds this formula to the list of formulas, if it falls within + * the bounds + * + * @param fr the formula record to test for membership of this group + * @return TRUE if the formulas was added, FALSE otherwise + */ + public boolean add(BaseSharedFormulaRecord fr) { + boolean added = false; + int r = fr.getRow(); + if (r >= firstRow && r <= lastRow) { + int c = fr.getColumn(); + if (c >= firstCol && c <= lastCol) { + formulas.add(fr); + added = true; + } + } + + + return added; + } + + /** + * Manufactures individual cell formulas out the whole shared formula + * debacle + * + * @param fr the formatting records + * @param nf flag indicating whether this uses the 1904 date system + * @return an array of formulas to be added to the sheet + */ + Cell[] getFormulas(FormattingRecords fr, boolean nf) { + Cell[] sfs = new Cell[formulas.size() + 1]; + + // This can happen if there are many identical formulas in the + // sheet and excel has not sliced and diced them exclusively + if (templateFormula == null) { + logger.warn("Shared formula template formula is null"); + return new Cell[0]; + } + + templateFormula.setTokens(tokens); + NumberFormat templateNumberFormat = null; + + // See if the template formula evaluates to date + if (templateFormula.getType() == CellType.NUMBER_FORMULA) { + SharedNumberFormulaRecord snfr = (SharedNumberFormulaRecord) + templateFormula; + templateNumberFormat = snfr.getNumberFormat(); + + if (fr.isDate(templateFormula.getXFIndex())) { + templateFormula = new SharedDateFormulaRecord(snfr, fr, nf, sheet, + snfr.getFilePos()); + templateFormula.setTokens(snfr.getTokens()); + } + } + + sfs[0] = templateFormula; + + BaseSharedFormulaRecord f = null; + + for (int i = 0; i < formulas.size(); i++) { + f = (BaseSharedFormulaRecord) formulas.get(i); + + // See if the formula evaluates to date + if (f.getType() == CellType.NUMBER_FORMULA) { + SharedNumberFormulaRecord snfr = (SharedNumberFormulaRecord) f; + + if (fr.isDate(f.getXFIndex())) { + f = new SharedDateFormulaRecord(snfr, fr, nf, sheet, + snfr.getFilePos()); + } else { + // snfr.setNumberFormat(templateNumberFormat); + } + } + + f.setTokens(tokens); + sfs[i + 1] = f; + } + + return sfs; + } + + /** + * Accessor for the template formula. Called when a shared formula has, + * for some reason, specified an inappropriate range and it is necessary + * to retrieve the template from a previously available shared formula + */ + BaseSharedFormulaRecord getTemplateFormula() { + return templateFormula; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedNumberFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedNumberFormulaRecord.java new file mode 100755 index 0000000..14a8499 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedNumberFormulaRecord.java @@ -0,0 +1,187 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.NumberFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + +/** + * A number formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedNumberFormulaRecord extends BaseSharedFormulaRecord + implements NumberCell, FormulaData, NumberFormulaCell { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(SharedNumberFormulaRecord.class); + /** + * The string format for the double value + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The value of this number + */ + private final double value; + /** + * The cell format + */ + private NumberFormat format; + /** + * A handle to the formatting records + */ + private FormattingRecords formattingRecords; + + /** + * Constructs this number + * + * @param t the data + * @param excelFile the excel biff data + * @param v the value + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public SharedNumberFormulaRecord(Record t, + File excelFile, + double v, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, es, nt, si, excelFile.getPos()); + value = v; + format = defaultFormat; // format is set up later from the + // SharedFormulaRecord + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return !Double.isNaN(value) ? format.format(value) : ""; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.NUMBER_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + DoubleHelper.getIEEEBytes(value, data, 6); + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } + + /** + * Sets the format for the number based on the Excel spreadsheets' format. + * This is called from SheetImpl when it has been definitely established + * that this cell is a number and not a date + * + * @param f the format + */ + final void setNumberFormat(NumberFormat f) { + if (f != null) { + format = f; + } + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedStringFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedStringFormulaRecord.java new file mode 100755 index 0000000..2b13945 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedStringFormulaRecord.java @@ -0,0 +1,241 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.StringFormulaCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A string formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedStringFormulaRecord extends BaseSharedFormulaRecord + implements LabelCell, FormulaData, StringFormulaCell { + protected static final EmptyString EMPTY_STRING = new EmptyString(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger + (SharedStringFormulaRecord.class); + /** + * The value of this string formula + */ + private final String value; + + /** + * Constructs this string formula + * + * @param t the record + * @param excelFile the excel file + * @param fr the formatting record + * @param es the external sheet + * @param nt the workbook + * @param si the sheet + * @param ws the workbook settings + */ + public SharedStringFormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + WorkbookSettings ws) { + super(t, fr, es, nt, si, excelFile.getPos()); + int pos = excelFile.getPos(); + + // Save the position in the excel file + int filepos = excelFile.getPos(); + + // Look for the string record in one of the records after the + // formula. Put a cap on it to prevent ednas + Record nextRecord = excelFile.next(); + int count = 0; + while (nextRecord.getType() != Type.STRING && count < 4) { + nextRecord = excelFile.next(); + count++; + } + Assert.verify(count < 4, " @ " + pos); + + byte[] stringData = nextRecord.getData(); + + // Read in any continuation records + nextRecord = excelFile.peek(); + while (nextRecord.getType() == Type.CONTINUE) { + nextRecord = excelFile.next(); // move the pointer within the data + byte[] d = new byte[stringData.length + nextRecord.getLength() - 1]; + System.arraycopy(stringData, 0, d, 0, stringData.length); + System.arraycopy(nextRecord.getData(), 1, d, + stringData.length, nextRecord.getLength() - 1); + stringData = d; + nextRecord = excelFile.peek(); + } + + int chars = IntegerHelper.getInt(stringData[0], stringData[1]); + + boolean unicode = false; + int startpos = 3; + if (stringData.length == chars + 2) { + // String might only consist of a one byte length indicator, instead + // of the more normal 2 + startpos = 2; + unicode = false; + } else if (stringData[2] == 0x1) { + // unicode string, two byte length indicator + startpos = 3; + unicode = true; + } else { + // ascii string, two byte length indicator + startpos = 3; + unicode = false; + } + + if (!unicode) { + value = StringHelper.getString(stringData, chars, startpos, ws); + } else { + value = StringHelper.getUnicodeString(stringData, chars, startpos); + } + + // Restore the position in the excel file, to enable the SHRFMLA + // record to be picked up + excelFile.setPos(filepos); + } + + /** + * Constructs this string formula + * + * @param t the record + * @param excelFile the excel file + * @param fr the formatting record + * @param es the external sheet + * @param nt the workbook + * @param si the sheet + * @param dummy the overload indicator + */ + public SharedStringFormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + EmptyString dummy) { + super(t, fr, es, nt, si, excelFile.getPos()); + + value = ""; + } + + /** + * Accessor for the value + * + * @return the value + */ + public String getString() { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return value; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.STRING_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + + // Set the two most significant bytes of the value to be 0xff in + // order to identify this as a string + data[6] = 0; + data[12] = -1; + data[13] = -1; + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + // Dummy value for overloading the constructor when the string evaluates + // to null + private static final class EmptyString { + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SheetImpl.java b/datastructures-xslx/src/main/java/jxl/read/biff/SheetImpl.java new file mode 100755 index 0000000..9ba897d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SheetImpl.java @@ -0,0 +1,1137 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.regex.Pattern; +import jxl.Cell; +import jxl.CellView; +import jxl.Hyperlink; +import jxl.Image; +import jxl.LabelCell; +import jxl.Range; +import jxl.Sheet; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.BuiltInName; +import jxl.biff.CellFinder; +import jxl.biff.CellReferenceHelper; +import jxl.biff.ConditionalFormat; +import jxl.biff.DataValidation; +import jxl.biff.EmptyCell; +import jxl.biff.FormattingRecords; +import jxl.biff.Type; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingData; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * Represents a sheet within a workbook. Provides a handle to the individual + * cells, or lines of cells (grouped by Row or Column) + * In order to simplify this class due to code bloat, the actual reading + * logic has been delegated to the SheetReaderClass. This class' main + * responsibility is now to implement the API methods declared in the + * Sheet interface + */ +public class SheetImpl implements Sheet { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SheetImpl.class); + + /** + * The excel file + */ + private final File excelFile; + /** + * A handle to the shared string table + */ + private final SSTRecord sharedStrings; + + /** + * A handle to the sheet BOF record, which indicates the stream type + */ + private final BOFRecord sheetBof; + + /** + * A handle to the workbook BOF record, which indicates the stream type + */ + private final BOFRecord workbookBof; + + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + + /** + * The name of this sheet + */ + private String name; + + /** + * The number of rows + */ + private int numRows; + + /** + * The number of columns + */ + private int numCols; + + /** + * The cells + */ + private Cell[][] cells; + + /** + * The start position in the stream of this sheet + */ + private int startPosition; + + /** + * The list of specified (ie. non default) column widths + */ + private ColumnInfoRecord[] columnInfos; + + /** + * The array of row records + */ + private RowRecord[] rowRecords; + + /** + * The list of non-default row properties + */ + private ArrayList rowProperties; + + /** + * An array of column info records. They are held this way before + * they are transferred to the more convenient array + */ + private ArrayList columnInfosArray; + + /** + * A list of shared formula groups + */ + private final ArrayList sharedFormulas; + + /** + * A list of hyperlinks on this page + */ + private ArrayList hyperlinks; + + /** + * A list of charts on this page + */ + private ArrayList charts; + + /** + * A list of drawings on this page + */ + private ArrayList drawings; + + /** + * A list of drawings (as opposed to comments/validation/charts) on this + * page + */ + private ArrayList images; + + /** + * A list of data validations on this page + */ + private DataValidation dataValidation; + + /** + * A list of merged cells on this page + */ + private Range[] mergedCells; + + /** + * Indicates whether the columnInfos array has been initialized + */ + private boolean columnInfosInitialized; + + /** + * Indicates whether the rowRecords array has been initialized + */ + private boolean rowRecordsInitialized; + + /** + * Indicates whether or not the dates are based around the 1904 date system + */ + private final boolean nineteenFour; + + /** + * The workspace options + */ + private WorkspaceInformationRecord workspaceOptions; + + /** + * The hidden flag + */ + private boolean hidden; + + /** + * The environment specific print record + */ + private PLSRecord plsRecord; + + /** + * The property set record associated with this workbook + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * The sheet settings + */ + private SheetSettings settings; + + /** + * The horizontal page breaks contained on this sheet + */ + private int[] rowBreaks; + + /** + * The vertical page breaks contained on this sheet + */ + private int[] columnBreaks; + + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + + /** + * The list of local names for this sheet + */ + private ArrayList localNames; + + /** + * The list of conditional formats for this sheet + */ + private ArrayList conditionalFormats; + + /** + * The autofilter information + */ + private AutoFilter autoFilter; + + /** + * A handle to the workbook which contains this sheet. Some of the records + * need this in order to reference external sheets + */ + private final WorkbookParser workbook; + + /** + * A handle to the workbook settings + */ + private final WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param f the excel file + * @param sst the shared string table + * @param fr formatting records + * @param sb the bof record which indicates the start of the sheet + * @param wb the bof record which indicates the start of the sheet + * @param nf the 1904 flag + * @param wp the workbook which this sheet belongs to + * @throws BiffException + */ + SheetImpl(File f, + SSTRecord sst, + FormattingRecords fr, + BOFRecord sb, + BOFRecord wb, + boolean nf, + WorkbookParser wp) + throws BiffException { + excelFile = f; + sharedStrings = sst; + formattingRecords = fr; + sheetBof = sb; + workbookBof = wb; + columnInfosArray = new ArrayList(); + sharedFormulas = new ArrayList(); + hyperlinks = new ArrayList(); + rowProperties = new ArrayList(10); + columnInfosInitialized = false; + rowRecordsInitialized = false; + nineteenFour = nf; + workbook = wp; + workbookSettings = workbook.getSettings(); + + // Mark the position in the stream, and then skip on until the end + startPosition = f.getPos(); + + if (sheetBof.isChart()) { + // Set the start pos to include the bof so the sheet reader can handle it + startPosition -= (sheetBof.getLength() + 4); + } + + Record r = null; + int bofs = 1; + + while (bofs >= 1) { + r = f.next(); + + // use this form for quick performance + if (r.getCode() == Type.EOF.value) { + bofs--; + } + + if (r.getCode() == Type.BOF.value) { + bofs++; + } + } + } + + /** + * Returns the cell for the specified location eg. "A4", using the + * CellReferenceHelper + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + public Cell getCell(String loc) { + return getCell(CellReferenceHelper.getColumn(loc), + CellReferenceHelper.getRow(loc)); + } + + /** + * Returns the cell specified at this row and at this column + * + * @param row the row number + * @param column the column number + * @return the cell at the specified co-ordinates + */ + public Cell getCell(int column, int row) { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + Cell c = cells[row][column]; + + if (c == null) { + c = new EmptyCell(column, row); + cells[row][column] = c; + } + + return c; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public Cell findCell(String contents) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastRow the last row within the range + * @param lastCol the last column within the ranage + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(pattern, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell methods in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public LabelCell findLabelCell(String contents) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findLabelCell(contents); + } + + /** + * Returns the number of rows in this sheet + * + * @return the number of rows in this sheet + */ + public int getRows() { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + return numRows; + } + + /** + * Returns the number of columns in this sheet + * + * @return the number of columns in this sheet + */ + public int getColumns() { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + return numCols; + } + + /** + * Gets all the cells on the specified row. The returned array will + * be stripped of all trailing empty cells + * + * @param row the rows whose cells are to be returned + * @return the cells on the given row + */ + public Cell[] getRow(int row) { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + // Find the last non-null cell + boolean found = false; + int col = numCols - 1; + while (col >= 0 && !found) { + if (cells[row][col] != null) { + found = true; + } else { + col--; + } + } + + // Only create entries for non-null cells + Cell[] c = new Cell[col + 1]; + + for (int i = 0; i <= col; i++) { + c[i] = getCell(i, row); + } + return c; + } + + /** + * Gets all the cells on the specified column. The returned array + * will be stripped of all trailing empty cells + * + * @param col the column whose cells are to be returned + * @return the cells on the specified column + */ + public Cell[] getColumn(int col) { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + // Find the last non-null cell + boolean found = false; + int row = numRows - 1; + while (row >= 0 && !found) { + if (cells[row][col] != null) { + found = true; + } else { + row--; + } + } + + // Only create entries for non-null cells + Cell[] c = new Cell[row + 1]; + + for (int i = 0; i <= row; i++) { + c[i] = getCell(col, i); + } + return c; + } + + /** + * Gets the name of this sheet + * + * @return the name of the sheet + */ + public String getName() { + return name; + } + + /** + * Sets the name of this sheet + * + * @param s the sheet name + */ + final void setName(String s) { + name = s; + } + + /** + * Determines whether the sheet is hidden + * + * @return whether or not the sheet is hidden + * @deprecated in favour of the getSettings function + */ + public boolean isHidden() { + return hidden; + } + + /** + * Sets the visibility of this sheet + * + * @param h hidden flag + */ + final void setHidden(boolean h) { + hidden = h; + } + + /** + * Gets the column info record for the specified column. If no + * column is specified, null is returned + * + * @param col the column + * @return the ColumnInfoRecord if specified, NULL otherwise + */ + public ColumnInfoRecord getColumnInfo(int col) { + if (!columnInfosInitialized) { + // Initialize the array + Iterator i = columnInfosArray.iterator(); + ColumnInfoRecord cir = null; + while (i.hasNext()) { + cir = (ColumnInfoRecord) i.next(); + + int startcol = Math.max(0, cir.getStartColumn()); + int endcol = Math.min(columnInfos.length - 1, cir.getEndColumn()); + + for (int c = startcol; c <= endcol; c++) { + columnInfos[c] = cir; + } + + if (endcol < startcol) { + columnInfos[startcol] = cir; + } + } + + columnInfosInitialized = true; + } + + return col < columnInfos.length ? columnInfos[col] : null; + } + + /** + * Gets all the column info records + * + * @return the ColumnInfoRecordArray + */ + public ColumnInfoRecord[] getColumnInfos() { + // Just chuck all the column infos we have into an array + ColumnInfoRecord[] infos = new ColumnInfoRecord[columnInfosArray.size()]; + for (int i = 0; i < columnInfosArray.size(); i++) { + infos[i] = (ColumnInfoRecord) columnInfosArray.get(i); + } + + return infos; + } + + /** + * Clears out the array of cells. This is done for memory allocation + * reasons when reading very large sheets + */ + final void clear() { + cells = null; + mergedCells = null; + columnInfosArray.clear(); + sharedFormulas.clear(); + hyperlinks.clear(); + columnInfosInitialized = false; + + if (!workbookSettings.getGCDisabled()) { + System.gc(); + } + } + + /** + * Reads in the contents of this sheet + */ + final void readSheet() { + // If this sheet contains only a chart, then set everything to + // empty and do not bother parsing the sheet + // Thanks to steve.brophy for spotting this + if (!sheetBof.isWorksheet()) { + numRows = 0; + numCols = 0; + cells = new Cell[0][0]; + // return; + } + + SheetReader reader = new SheetReader(excelFile, + sharedStrings, + formattingRecords, + sheetBof, + workbookBof, + nineteenFour, + workbook, + startPosition, + this); + reader.read(); + + // Take stuff that was read in + numRows = reader.getNumRows(); + numCols = reader.getNumCols(); + cells = reader.getCells(); + rowProperties = reader.getRowProperties(); + columnInfosArray = reader.getColumnInfosArray(); + hyperlinks = reader.getHyperlinks(); + conditionalFormats = reader.getConditionalFormats(); + autoFilter = reader.getAutoFilter(); + charts = reader.getCharts(); + drawings = reader.getDrawings(); + dataValidation = reader.getDataValidation(); + mergedCells = reader.getMergedCells(); + settings = reader.getSettings(); + settings.setHidden(hidden); + rowBreaks = reader.getRowBreaks(); + columnBreaks = reader.getColumnBreaks(); + workspaceOptions = reader.getWorkspaceOptions(); + plsRecord = reader.getPLS(); + buttonPropertySet = reader.getButtonPropertySet(); + maxRowOutlineLevel = reader.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = reader.getMaxColumnOutlineLevel(); + + reader = null; + + if (!workbookSettings.getGCDisabled()) { + System.gc(); + } + + if (columnInfosArray.size() > 0) { + ColumnInfoRecord cir = (ColumnInfoRecord) + columnInfosArray.get(columnInfosArray.size() - 1); + columnInfos = new ColumnInfoRecord[cir.getEndColumn() + 1]; + } else { + columnInfos = new ColumnInfoRecord[0]; + } + + // Add any local names + if (localNames != null) { + for (Iterator it = localNames.iterator(); it.hasNext(); ) { + NameRecord nr = (NameRecord) it.next(); + if (nr.getBuiltInName() == BuiltInName.PRINT_AREA) { + if (nr.getRanges().length > 0) { + NameRecord.NameRange rng = nr.getRanges()[0]; + settings.setPrintArea(rng.getFirstColumn(), + rng.getFirstRow(), + rng.getLastColumn(), + rng.getLastRow()); + } + } else if (nr.getBuiltInName() == BuiltInName.PRINT_TITLES) { + // There can be 1 or 2 entries. + // Row entries have hardwired column entries (first and last + // possible column) + // Column entries have hardwired row entries (first and last + // possible row) + for (int i = 0; i < nr.getRanges().length; i++) { + NameRecord.NameRange rng = nr.getRanges()[i]; + if (rng.getFirstColumn() == 0 && rng.getLastColumn() == 255) { + settings.setPrintTitlesRow(rng.getFirstRow(), + rng.getLastRow()); + } else { + settings.setPrintTitlesCol(rng.getFirstColumn(), + rng.getLastColumn()); + } + } + } + } + } + } + + /** + * Gets the hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + public Hyperlink[] getHyperlinks() { + Hyperlink[] hl = new Hyperlink[hyperlinks.size()]; + + for (int i = 0; i < hyperlinks.size(); i++) { + hl[i] = (Hyperlink) hyperlinks.get(i); + } + + return hl; + } + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + public Range[] getMergedCells() { + if (mergedCells == null) { + return new Range[0]; + } + + return mergedCells; + } + + /** + * Gets the non-default rows. Used when copying spreadsheets + * + * @return an array of row properties + */ + public RowRecord[] getRowProperties() { + RowRecord[] rp = new RowRecord[rowProperties.size()]; + for (int i = 0; i < rp.length; i++) { + rp[i] = (RowRecord) rowProperties.get(i); + } + + return rp; + } + + /** + * Gets the data validations. Used when copying sheets + * + * @return the data validations + */ + public DataValidation getDataValidation() { + return dataValidation; + } + + /** + * Gets the row record. Usually called by the cell in the specified + * row in order to determine its size + * + * @param r the row + * @return the RowRecord for the specified row + */ + RowRecord getRowInfo(int r) { + if (!rowRecordsInitialized) { + rowRecords = new RowRecord[getRows()]; + Iterator i = rowProperties.iterator(); + + int rownum = 0; + RowRecord rr = null; + while (i.hasNext()) { + rr = (RowRecord) i.next(); + rownum = rr.getRowNumber(); + if (rownum < rowRecords.length) { + rowRecords[rownum] = rr; + } + } + + rowRecordsInitialized = true; + } + + return r < rowRecords.length ? rowRecords[r] : null; + } + + /** + * Gets the row breaks. Called when copying sheets + * + * @return the explicit row breaks + */ + public final int[] getRowPageBreaks() { + return rowBreaks; + } + + /** + * Gets the row breaks. Called when copying sheets + * + * @return the explicit row breaks + */ + public final int[] getColumnPageBreaks() { + return columnBreaks; + } + + /** + * Gets the charts. Called when copying sheets + * + * @return the charts on this page + */ + public final Chart[] getCharts() { + Chart[] ch = new Chart[charts.size()]; + + for (int i = 0; i < ch.length; i++) { + ch[i] = (Chart) charts.get(i); + } + return ch; + } + + /** + * Gets the drawings. Called when copying sheets + * + * @return the drawings on this page + */ + public final DrawingGroupObject[] getDrawings() { + DrawingGroupObject[] dr = new DrawingGroupObject[drawings.size()]; + dr = (DrawingGroupObject[]) drawings.toArray(dr); + return dr; + } + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + * @deprecated in favour of the getSettings() api + */ + public boolean isProtected() { + return settings.isProtected(); + } + + /** + * Gets the workspace options for this sheet. Called during the copy + * process + * + * @return the workspace options + */ + public WorkspaceInformationRecord getWorkspaceOptions() { + return workspaceOptions; + } + + /** + * Accessor for the sheet settings + * + * @return the settings for this sheet + */ + public SheetSettings getSettings() { + return settings; + } + + /** + * Accessor for the workbook. In addition to be being used by this package, + * it is also used during the importSheet process + * + * @return the workbook + */ + public WorkbookParser getWorkbook() { + return workbook; + } + + /** + * Gets the column format for the specified column + * + * @param col the column number + * @return the column format, or NULL if the column has no specific format + * @deprecated use getColumnView instead + */ + public CellFormat getColumnFormat(int col) { + CellView cv = getColumnView(col); + return cv.getFormat(); + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column width, or the default width if the column has no + * specified format + */ + public int getColumnWidth(int col) { + return getColumnView(col).getSize() / 256; + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column format, or the default format if no override is + * specified + */ + public CellView getColumnView(int col) { + ColumnInfoRecord cir = getColumnInfo(col); + CellView cv = new CellView(); + + if (cir != null) { + cv.setDimension(cir.getWidth() / 256); //deprecated + cv.setSize(cir.getWidth()); + cv.setHidden(cir.getHidden()); + cv.setFormat(formattingRecords.getXFRecord(cir.getXFIndex())); + } else { + cv.setDimension(settings.getDefaultColumnWidth()); //deprecated + cv.setSize(settings.getDefaultColumnWidth() * 256); + } + + return cv; + } + + /** + * Gets the row height for the specified column + * + * @param row the row number + * @return the row height, or the default height if the row has no + * specified format + * @deprecated use getRowView instead + */ + public int getRowHeight(int row) { + return getRowView(row).getDimension(); + } + + /** + * Gets the row view for the specified row + * + * @param row the row number + * @return the row format, or the default format if no override is + * specified + */ + public CellView getRowView(int row) { + RowRecord rr = getRowInfo(row); + + CellView cv = new CellView(); + + if (rr != null) { + cv.setDimension(rr.getRowHeight()); //deprecated + cv.setSize(rr.getRowHeight()); + cv.setHidden(rr.isCollapsed()); + if (rr.hasDefaultFormat()) { + cv.setFormat(formattingRecords.getXFRecord(rr.getXFIndex())); + } + } else { + cv.setDimension(settings.getDefaultRowHeight()); + cv.setSize(settings.getDefaultRowHeight()); //deprecated + } + + return cv; + } + + + /** + * Used when copying sheets in order to determine the type of this sheet + * + * @return the BOF Record + */ + public BOFRecord getSheetBof() { + return sheetBof; + } + + /** + * Used when copying sheets in order to determine the type of the containing + * workboook + * + * @return the workbook BOF Record + */ + public BOFRecord getWorkbookBof() { + return workbookBof; + } + + /** + * Accessor for the environment specific print record, invoked when + * copying sheets + * + * @return the environment specific print record + */ + public PLSRecord getPLS() { + return plsRecord; + } + + /** + * Accessor for the button property set, used during copying + * + * @return the button property set + */ + public ButtonPropertySetRecord getButtonPropertySet() { + return buttonPropertySet; + } + + /** + * Accessor for the number of images on the sheet + * + * @return the number of images on this sheet + */ + public int getNumberOfImages() { + if (images == null) { + initializeImages(); + } + + return images.size(); + } + + /** + * Accessor for the image + * + * @param i the 0 based image number + * @return the image at the specified position + */ + public Image getDrawing(int i) { + if (images == null) { + initializeImages(); + } + + return (Image) images.get(i); + } + + /** + * Initializes the images + */ + private void initializeImages() { + if (images != null) { + return; + } + + images = new ArrayList(); + DrawingGroupObject[] dgos = getDrawings(); + + for (int i = 0; i < dgos.length; i++) { + if (dgos[i] instanceof Drawing) { + images.add(dgos[i]); + } + } + } + + /** + * Used by one of the demo programs for debugging purposes only + */ + public DrawingData getDrawingData() { + SheetReader reader = new SheetReader(excelFile, + sharedStrings, + formattingRecords, + sheetBof, + workbookBof, + nineteenFour, + workbook, + startPosition, + this); + reader.read(); + return reader.getDrawingData(); + } + + /** + * Adds a local name to this shate + * + * @param nr the local name to add + */ + void addLocalName(NameRecord nr) { + if (localNames == null) { + localNames = new ArrayList(); + } + + localNames.add(nr); + } + + /** + * Gets the conditional formats + * + * @return the conditional formats + */ + public ConditionalFormat[] getConditionalFormats() { + ConditionalFormat[] formats = + new ConditionalFormat[conditionalFormats.size()]; + formats = (ConditionalFormat[]) conditionalFormats.toArray(formats); + return formats; + } + + /** + * Returns the autofilter + * + * @return the autofilter + */ + public AutoFilter getAutoFilter() { + return autoFilter; + } + + /** + * Accessor for the maximum column outline level. Used during a copy + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level. Used during a copy + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() { + return maxRowOutlineLevel; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SheetReader.java b/datastructures-xslx/src/main/java/jxl/read/biff/SheetReader.java new file mode 100755 index 0000000..fc1e353 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SheetReader.java @@ -0,0 +1,1628 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellReferenceHelper; +import jxl.CellType; +import jxl.DateCell; +import jxl.HeaderFooter; +import jxl.Range; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.AutoFilterInfoRecord; +import jxl.biff.AutoFilterRecord; +import jxl.biff.ConditionalFormat; +import jxl.biff.ConditionalFormatRangeRecord; +import jxl.biff.ConditionalFormatRecord; +import jxl.biff.ContinueRecord; +import jxl.biff.DataValidation; +import jxl.biff.DataValidityListRecord; +import jxl.biff.DataValiditySettingsRecord; +import jxl.biff.FilterModeRecord; +import jxl.biff.FormattingRecords; +import jxl.biff.Type; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.drawing.Button; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.CheckBox; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Comment; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.Drawing2; +import jxl.biff.drawing.DrawingData; +import jxl.biff.drawing.DrawingDataException; +import jxl.biff.drawing.MsoDrawingRecord; +import jxl.biff.drawing.NoteRecord; +import jxl.biff.drawing.ObjRecord; +import jxl.biff.drawing.TextObjectRecord; +import jxl.biff.formula.FormulaException; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.PageOrder; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; + +/** + * Reads the sheet. This functionality was originally part of the + * SheetImpl class, but was separated out in order to simplify the former + * class + */ +final class SheetReader { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SheetReader.class); + + /** + * The excel file + */ + private final File excelFile; + + /** + * A handle to the shared string table + */ + private final SSTRecord sharedStrings; + + /** + * A handle to the sheet BOF record, which indicates the stream type + */ + private final BOFRecord sheetBof; + + /** + * A handle to the workbook BOF record, which indicates the stream type + */ + private final BOFRecord workbookBof; + + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + + /** + * The number of rows + */ + private int numRows; + + /** + * The number of columns + */ + private int numCols; + + /** + * The cells + */ + private Cell[][] cells; + + /** + * Any cells which are out of the defined bounds + */ + private final ArrayList outOfBoundsCells; + + /** + * The start position in the stream of this sheet + */ + private final int startPosition; + + /** + * The list of non-default row properties + */ + private final ArrayList rowProperties; + + /** + * An array of column info records. They are held this way before + * they are transferred to the more convenient array + */ + private final ArrayList columnInfosArray; + + /** + * A list of shared formula groups + */ + private final ArrayList sharedFormulas; + + /** + * A list of hyperlinks on this page + */ + private final ArrayList hyperlinks; + + /** + * The list of conditional formats on this page + */ + private final ArrayList conditionalFormats; + + /** + * The autofilter information + */ + private AutoFilter autoFilter; + + /** + * A list of merged cells on this page + */ + private Range[] mergedCells; + + /** + * The list of data validations on this page + */ + private DataValidation dataValidation; + + /** + * The list of charts on this page + */ + private final ArrayList charts; + + /** + * The list of drawings on this page + */ + private final ArrayList drawings; + + /** + * The drawing data for the drawings + */ + private DrawingData drawingData; + + /** + * Indicates whether or not the dates are based around the 1904 date system + */ + private final boolean nineteenFour; + + /** + * The PLS print record + */ + private PLSRecord plsRecord; + + /** + * The property set record associated with this workbook + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * The workspace options + */ + private WorkspaceInformationRecord workspaceOptions; + + /** + * The horizontal page breaks contained on this sheet + */ + private int[] rowBreaks; + + /** + * The vertical page breaks contained on this sheet + */ + private int[] columnBreaks; + + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + + /** + * The sheet settings + */ + private final SheetSettings settings; + + /** + * The workbook settings + */ + private final WorkbookSettings workbookSettings; + + /** + * A handle to the workbook which contains this sheet. Some of the records + * need this in order to reference external sheets + */ + private final WorkbookParser workbook; + + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + + /** + * Constructor + * + * @param fr the formatting records + * @param sst the shared string table + * @param f the excel file + * @param sb the bof record which indicates the start of the sheet + * @param wb the bof record which indicates the start of the sheet + * @param wp the workbook which this sheet belongs to + * @param sp the start position of the sheet bof in the excel file + * @param sh the sheet + * @param nf 1904 date record flag + * @throws BiffException + */ + SheetReader(File f, + SSTRecord sst, + FormattingRecords fr, + BOFRecord sb, + BOFRecord wb, + boolean nf, + WorkbookParser wp, + int sp, + SheetImpl sh) { + excelFile = f; + sharedStrings = sst; + formattingRecords = fr; + sheetBof = sb; + workbookBof = wb; + columnInfosArray = new ArrayList(); + sharedFormulas = new ArrayList(); + hyperlinks = new ArrayList(); + conditionalFormats = new ArrayList(); + rowProperties = new ArrayList(10); + charts = new ArrayList(); + drawings = new ArrayList(); + outOfBoundsCells = new ArrayList(); + nineteenFour = nf; + workbook = wp; + startPosition = sp; + sheet = sh; + settings = new SheetSettings(sh); + workbookSettings = workbook.getSettings(); + } + + /** + * Adds the cell to the array + * + * @param cell the cell to add + */ + private void addCell(Cell cell) { + // Sometimes multiple cells (eg. MULBLANK) can exceed the + // column/row boundaries. Ignore these + if (cell.getRow() < numRows && cell.getColumn() < numCols) { + if (cells[cell.getRow()][cell.getColumn()] != null) { + StringBuffer sb = new StringBuffer(); + CellReferenceHelper.getCellReference + (cell.getColumn(), cell.getRow(), sb); + logger.warn("Cell " + sb + + " already contains data"); + } + cells[cell.getRow()][cell.getColumn()] = cell; + } else { + outOfBoundsCells.add(cell); + /* + logger.warn("Cell " + + CellReferenceHelper.getCellReference + (cell.getColumn(), cell.getRow()) + + " exceeds defined cell boundaries in Dimension record " + + "(" + numCols + "x" + numRows + ")"); + */ + } + } + + /** + * Reads in the contents of this sheet + */ + final void read() { + Record r = null; + BaseSharedFormulaRecord sharedFormula = null; + boolean sharedFormulaAdded = false; + + boolean cont = true; + + // Set the position within the file + excelFile.setPos(startPosition); + + // Handles to the last drawing and obj records + MsoDrawingRecord msoRecord = null; + ObjRecord objRecord = null; + boolean firstMsoRecord = true; + + // Handle to the last conditional format record + ConditionalFormat condFormat = null; + + // Handle to the autofilter records + FilterModeRecord filterMode = null; + AutoFilterInfoRecord autoFilterInfo = null; + + // A handle to window2 record + Window2Record window2Record = null; + + // A handle to printgridlines record + PrintGridLinesRecord printGridLinesRecord = null; + + // A handle to printheaders record + PrintHeadersRecord printHeadersRecord = null; + + // Hash map of comments, indexed on objectId. As each corresponding + // note record is encountered, these are removed from the array + HashMap comments = new HashMap(); + + // A list of object ids - used for cross referencing + ArrayList objectIds = new ArrayList(); + + // A handle to a continue record read in + ContinueRecord continueRecord = null; + + while (cont) { + r = excelFile.next(); + Type type = r.getType(); + + if (type == Type.UNKNOWN && r.getCode() == 0) { + logger.warn("Biff code zero found"); + + // Try a dimension record + if (r.getLength() == 0xa) { + logger.warn("Biff code zero found - trying a dimension record."); + r.setType(Type.DIMENSION); + } else { + logger.warn("Biff code zero found - Ignoring."); + } + } + + if (type == Type.DIMENSION) { + DimensionRecord dr = null; + + if (workbookBof.isBiff8()) { + dr = new DimensionRecord(r); + } else { + dr = new DimensionRecord(r, DimensionRecord.biff7); + } + numRows = dr.getNumberOfRows(); + numCols = dr.getNumberOfColumns(); + cells = new Cell[numRows][numCols]; + } else if (type == Type.LABELSST) { + LabelSSTRecord label = new LabelSSTRecord(r, + sharedStrings, + formattingRecords, + sheet); + addCell(label); + } else if (type == Type.RK || type == Type.RK2) { + RKRecord rkr = new RKRecord(r, formattingRecords, sheet); + + if (formattingRecords.isDate(rkr.getXFIndex())) { + DateCell dc = new DateRecord + (rkr, rkr.getXFIndex(), formattingRecords, nineteenFour, sheet); + addCell(dc); + } else { + addCell(rkr); + } + } else if (type == Type.HLINK) { + HyperlinkRecord hr = new HyperlinkRecord(r, sheet, workbookSettings); + hyperlinks.add(hr); + } else if (type == Type.MERGEDCELLS) { + MergedCellsRecord mc = new MergedCellsRecord(r, sheet); + if (mergedCells == null) { + mergedCells = mc.getRanges(); + } else { + Range[] newMergedCells = + new Range[mergedCells.length + mc.getRanges().length]; + System.arraycopy(mergedCells, 0, newMergedCells, 0, + mergedCells.length); + System.arraycopy(mc.getRanges(), + 0, + newMergedCells, mergedCells.length, + mc.getRanges().length); + mergedCells = newMergedCells; + } + } else if (type == Type.MULRK) { + MulRKRecord mulrk = new MulRKRecord(r); + + // Get the individual cell records from the multiple record + int num = mulrk.getNumberOfColumns(); + int ixf = 0; + for (int i = 0; i < num; i++) { + ixf = mulrk.getXFIndex(i); + + NumberValue nv = new NumberValue + (mulrk.getRow(), + mulrk.getFirstColumn() + i, + RKHelper.getDouble(mulrk.getRKNumber(i)), + ixf, + formattingRecords, + sheet); + + + if (formattingRecords.isDate(ixf)) { + DateCell dc = new DateRecord(nv, + ixf, + formattingRecords, + nineteenFour, + sheet); + addCell(dc); + } else { + nv.setNumberFormat(formattingRecords.getNumberFormat(ixf)); + addCell(nv); + } + } + } else if (type == Type.NUMBER) { + NumberRecord nr = new NumberRecord(r, formattingRecords, sheet); + + if (formattingRecords.isDate(nr.getXFIndex())) { + DateCell dc = new DateRecord(nr, + nr.getXFIndex(), + formattingRecords, + nineteenFour, sheet); + addCell(dc); + } else { + addCell(nr); + } + } else if (type == Type.BOOLERR) { + BooleanRecord br = new BooleanRecord(r, formattingRecords, sheet); + + if (br.isError()) { + ErrorRecord er = new ErrorRecord(br.getRecord(), formattingRecords, + sheet); + addCell(er); + } else { + addCell(br); + } + } else if (type == Type.PRINTGRIDLINES) { + printGridLinesRecord = new PrintGridLinesRecord(r); + settings.setPrintGridLines(printGridLinesRecord.getPrintGridLines()); + } else if (type == Type.PRINTHEADERS) { + printHeadersRecord = new PrintHeadersRecord(r); + settings.setPrintHeaders(printHeadersRecord.getPrintHeaders()); + } else if (type == Type.WINDOW2) { + window2Record = null; + + if (workbookBof.isBiff8()) { + window2Record = new Window2Record(r); + } else { + window2Record = new Window2Record(r, Window2Record.biff7); + } + + settings.setShowGridLines(window2Record.getShowGridLines()); + settings.setDisplayZeroValues(window2Record.getDisplayZeroValues()); + settings.setSelected(true); + settings.setPageBreakPreviewMode(window2Record.isPageBreakPreview()); + } else if (type == Type.PANE) { + PaneRecord pr = new PaneRecord(r); + + if (window2Record != null && + window2Record.getFrozen()) { + settings.setVerticalFreeze(pr.getRowsVisible()); + settings.setHorizontalFreeze(pr.getColumnsVisible()); + } + } else if (type == Type.CONTINUE) { + // don't know what this is for, but keep hold of it anyway + continueRecord = new ContinueRecord(r); + } else if (type == Type.NOTE) { + if (!workbookSettings.getDrawingsDisabled()) { + NoteRecord nr = new NoteRecord(r); + + // Get the comment for the object id + Comment comment = (Comment) comments.remove + (new Integer(nr.getObjectId())); + + if (comment == null) { + logger.warn(" cannot find comment for note id " + + nr.getObjectId() + "...ignoring"); + } else { + comment.setNote(nr); + + drawings.add(comment); + + addCellComment(comment.getColumn(), + comment.getRow(), + comment.getText(), + comment.getWidth(), + comment.getHeight()); + } + } + } else if (type == Type.ARRAY) { + } else if (type == Type.PROTECT) { + ProtectRecord pr = new ProtectRecord(r); + settings.setProtected(pr.isProtected()); + } else if (type == Type.SHAREDFORMULA) { + if (sharedFormula == null) { + logger.warn("Shared template formula is null - " + + "trying most recent formula template"); + SharedFormulaRecord lastSharedFormula = + (SharedFormulaRecord) sharedFormulas.get(sharedFormulas.size() - 1); + + if (lastSharedFormula != null) { + sharedFormula = lastSharedFormula.getTemplateFormula(); + } + } + + SharedFormulaRecord sfr = new SharedFormulaRecord + (r, sharedFormula, workbook, workbook, sheet); + sharedFormulas.add(sfr); + sharedFormula = null; + } else if (type == Type.FORMULA || type == Type.FORMULA2) { + FormulaRecord fr = new FormulaRecord(r, + excelFile, + formattingRecords, + workbook, + workbook, + sheet, + workbookSettings); + + if (fr.isShared()) { + BaseSharedFormulaRecord prevSharedFormula = sharedFormula; + sharedFormula = (BaseSharedFormulaRecord) fr.getFormula(); + + // See if it fits in any of the shared formulas + sharedFormulaAdded = addToSharedFormulas(sharedFormula); + + if (sharedFormulaAdded) { + sharedFormula = prevSharedFormula; + } + + // If we still haven't added the previous base shared formula, + // revert it to an ordinary formula and add it to the cell + if (!sharedFormulaAdded && prevSharedFormula != null) { + // Do nothing. It's possible for the biff file to contain the + // record sequence + // FORMULA-SHRFMLA-FORMULA-SHRFMLA-FORMULA-FORMULA-FORMULA + // ie. it first lists all the formula templates, then it + // lists all the individual formulas + addCell(revertSharedFormula(prevSharedFormula)); + } + } else { + Cell cell = fr.getFormula(); + try { + // See if the formula evaluates to date + if (fr.getFormula().getType() == CellType.NUMBER_FORMULA) { + NumberFormulaRecord nfr = (NumberFormulaRecord) fr.getFormula(); + if (formattingRecords.isDate(nfr.getXFIndex())) { + cell = new DateFormulaRecord(nfr, + formattingRecords, + workbook, + workbook, + nineteenFour, + sheet); + } + } + + addCell(cell); + } catch (FormulaException e) { + // Something has gone wrong trying to read the formula data eg. it + // might be unsupported biff7 data + logger.warn + (CellReferenceHelper.getCellReference + (cell.getColumn(), cell.getRow()) + " " + e.getMessage()); + } + } + } else if (type == Type.LABEL) { + LabelRecord lr = null; + + if (workbookBof.isBiff8()) { + lr = new LabelRecord(r, formattingRecords, sheet, workbookSettings); + } else { + lr = new LabelRecord(r, formattingRecords, sheet, workbookSettings, + LabelRecord.biff7); + } + addCell(lr); + } else if (type == Type.RSTRING) { + RStringRecord lr = null; + + // RString records are obsolete in biff 8 + Assert.verify(!workbookBof.isBiff8()); + lr = new RStringRecord(r, formattingRecords, + sheet, workbookSettings, + RStringRecord.biff7); + addCell(lr); + } else if (type == Type.NAME) { + } else if (type == Type.PASSWORD) { + PasswordRecord pr = new PasswordRecord(r); + settings.setPasswordHash(pr.getPasswordHash()); + } else if (type == Type.ROW) { + RowRecord rr = new RowRecord(r); + + // See if the row has anything funny about it + if (!rr.isDefaultHeight() || + !rr.matchesDefaultFontHeight() || + rr.isCollapsed() || + rr.hasDefaultFormat() || + rr.getOutlineLevel() != 0) { + rowProperties.add(rr); + } + } else if (type == Type.BLANK) { + if (!workbookSettings.getIgnoreBlanks()) { + BlankCell bc = new BlankCell(r, formattingRecords, sheet); + addCell(bc); + } + } else if (type == Type.MULBLANK) { + if (!workbookSettings.getIgnoreBlanks()) { + MulBlankRecord mulblank = new MulBlankRecord(r); + + // Get the individual cell records from the multiple record + int num = mulblank.getNumberOfColumns(); + + for (int i = 0; i < num; i++) { + int ixf = mulblank.getXFIndex(i); + + MulBlankCell mbc = new MulBlankCell + (mulblank.getRow(), + mulblank.getFirstColumn() + i, + ixf, + formattingRecords, + sheet); + + addCell(mbc); + } + } + } else if (type == Type.SCL) { + SCLRecord scl = new SCLRecord(r); + settings.setZoomFactor(scl.getZoomFactor()); + } else if (type == Type.COLINFO) { + ColumnInfoRecord cir = new ColumnInfoRecord(r); + columnInfosArray.add(cir); + } else if (type == Type.HEADER) { + HeaderRecord hr = null; + if (workbookBof.isBiff8()) { + hr = new HeaderRecord(r, workbookSettings); + } else { + hr = new HeaderRecord(r, workbookSettings, HeaderRecord.biff7); + } + + HeaderFooter header = new HeaderFooter(hr.getHeader()); + settings.setHeader(header); + } else if (type == Type.FOOTER) { + FooterRecord fr = null; + if (workbookBof.isBiff8()) { + fr = new FooterRecord(r, workbookSettings); + } else { + fr = new FooterRecord(r, workbookSettings, FooterRecord.biff7); + } + + HeaderFooter footer = new HeaderFooter(fr.getFooter()); + settings.setFooter(footer); + } else if (type == Type.SETUP) { + SetupRecord sr = new SetupRecord(r); + + // If the setup record has its not initialized bit set, then + // use the sheet settings default values + if (sr.getInitialized()) { + if (sr.isPortrait()) { + settings.setOrientation(PageOrientation.PORTRAIT); + } else { + settings.setOrientation(PageOrientation.LANDSCAPE); + } + if (sr.isRightDown()) { + settings.setPageOrder(PageOrder.RIGHT_THEN_DOWN); + } else { + settings.setPageOrder(PageOrder.DOWN_THEN_RIGHT); + } + settings.setPaperSize(PaperSize.getPaperSize(sr.getPaperSize())); + settings.setHeaderMargin(sr.getHeaderMargin()); + settings.setFooterMargin(sr.getFooterMargin()); + settings.setScaleFactor(sr.getScaleFactor()); + settings.setPageStart(sr.getPageStart()); + settings.setFitWidth(sr.getFitWidth()); + settings.setFitHeight(sr.getFitHeight()); + settings.setHorizontalPrintResolution + (sr.getHorizontalPrintResolution()); + settings.setVerticalPrintResolution(sr.getVerticalPrintResolution()); + settings.setCopies(sr.getCopies()); + + if (workspaceOptions != null) { + settings.setFitToPages(workspaceOptions.getFitToPages()); + } + } + } else if (type == Type.WSBOOL) { + workspaceOptions = new WorkspaceInformationRecord(r); + } else if (type == Type.DEFCOLWIDTH) { + DefaultColumnWidthRecord dcwr = new DefaultColumnWidthRecord(r); + settings.setDefaultColumnWidth(dcwr.getWidth()); + } else if (type == Type.DEFAULTROWHEIGHT) { + DefaultRowHeightRecord drhr = new DefaultRowHeightRecord(r); + if (drhr.getHeight() != 0) { + settings.setDefaultRowHeight(drhr.getHeight()); + } + } else if (type == Type.CONDFMT) { + ConditionalFormatRangeRecord cfrr = + new ConditionalFormatRangeRecord(r); + condFormat = new ConditionalFormat(cfrr); + conditionalFormats.add(condFormat); + } else if (type == Type.CF) { + ConditionalFormatRecord cfr = new ConditionalFormatRecord(r); + condFormat.addCondition(cfr); + } else if (type == Type.FILTERMODE) { + filterMode = new FilterModeRecord(r); + } else if (type == Type.AUTOFILTERINFO) { + autoFilterInfo = new AutoFilterInfoRecord(r); + } else if (type == Type.AUTOFILTER) { + if (!workbookSettings.getAutoFilterDisabled()) { + AutoFilterRecord af = new AutoFilterRecord(r); + + if (autoFilter == null) { + autoFilter = new AutoFilter(filterMode, autoFilterInfo); + filterMode = null; + autoFilterInfo = null; + } + + autoFilter.add(af); + } + } else if (type == Type.LEFTMARGIN) { + MarginRecord m = new LeftMarginRecord(r); + settings.setLeftMargin(m.getMargin()); + } else if (type == Type.RIGHTMARGIN) { + MarginRecord m = new RightMarginRecord(r); + settings.setRightMargin(m.getMargin()); + } else if (type == Type.TOPMARGIN) { + MarginRecord m = new TopMarginRecord(r); + settings.setTopMargin(m.getMargin()); + } else if (type == Type.BOTTOMMARGIN) { + MarginRecord m = new BottomMarginRecord(r); + settings.setBottomMargin(m.getMargin()); + } else if (type == Type.HORIZONTALPAGEBREAKS) { + HorizontalPageBreaksRecord dr = null; + + if (workbookBof.isBiff8()) { + dr = new HorizontalPageBreaksRecord(r); + } else { + dr = new HorizontalPageBreaksRecord + (r, HorizontalPageBreaksRecord.biff7); + } + rowBreaks = dr.getRowBreaks(); + } else if (type == Type.VERTICALPAGEBREAKS) { + VerticalPageBreaksRecord dr = null; + + if (workbookBof.isBiff8()) { + dr = new VerticalPageBreaksRecord(r); + } else { + dr = new VerticalPageBreaksRecord + (r, VerticalPageBreaksRecord.biff7); + } + columnBreaks = dr.getColumnBreaks(); + } else if (type == Type.PLS) { + plsRecord = new PLSRecord(r); + + // Check for Continue records + while (excelFile.peek().getType() == Type.CONTINUE) { + r.addContinueRecord(excelFile.next()); + } + } else if (type == Type.DVAL) { + if (!workbookSettings.getCellValidationDisabled()) { + DataValidityListRecord dvlr = new DataValidityListRecord(r); + if (dvlr.getObjectId() == -1) { + if (msoRecord != null && objRecord == null) { + // there is a drop down associated with this data validation + if (drawingData == null) { + drawingData = new DrawingData(); + } + + Drawing2 d2 = new Drawing2(msoRecord, drawingData, + workbook.getDrawingGroup()); + drawings.add(d2); + msoRecord = null; + + dataValidation = new DataValidation(dvlr); + } else { + // no drop down + dataValidation = new DataValidation(dvlr); + } + } else if (objectIds.contains(new Integer(dvlr.getObjectId()))) { + dataValidation = new DataValidation(dvlr); + } else { + logger.warn("object id " + dvlr.getObjectId() + " referenced " + + " by data validity list record not found - ignoring"); + } + } + } else if (type == Type.HCENTER) { + CentreRecord hr = new CentreRecord(r); + settings.setHorizontalCentre(hr.isCentre()); + } else if (type == Type.VCENTER) { + CentreRecord vc = new CentreRecord(r); + settings.setVerticalCentre(vc.isCentre()); + } else if (type == Type.DV) { + if (!workbookSettings.getCellValidationDisabled()) { + DataValiditySettingsRecord dvsr = + new DataValiditySettingsRecord(r, + workbook, + workbook, + workbook.getSettings()); + if (dataValidation != null) { + dataValidation.add(dvsr); + addCellValidation(dvsr.getFirstColumn(), + dvsr.getFirstRow(), + dvsr.getLastColumn(), + dvsr.getLastRow(), + dvsr); + } else { + logger.warn("cannot add data validity settings"); + } + } + } else if (type == Type.OBJ) { + objRecord = new ObjRecord(r); + + if (!workbookSettings.getDrawingsDisabled()) { + // sometimes excel writes out continue records instead of drawing + // records, so forcibly hack the stashed continue record into + // a drawing record + if (msoRecord == null && continueRecord != null) { + logger.warn("Cannot find drawing record - using continue record"); + msoRecord = new MsoDrawingRecord(continueRecord.getRecord()); + continueRecord = null; + } + handleObjectRecord(objRecord, msoRecord, comments); + objectIds.add(new Integer(objRecord.getObjectId())); + } + + // Save chart handling until the chart BOF record appears + if (objRecord.getType() != ObjRecord.CHART) { + objRecord = null; + msoRecord = null; + } + } else if (type == Type.MSODRAWING) { + if (!workbookSettings.getDrawingsDisabled()) { + if (msoRecord != null) { + // For form controls, a rogue MSODRAWING record can crop up + // after the main one. Add these into the drawing data + drawingData.addRawData(msoRecord.getData()); + } + msoRecord = new MsoDrawingRecord(r); + + if (firstMsoRecord) { + msoRecord.setFirst(); + firstMsoRecord = false; + } + } + } else if (type == Type.BUTTONPROPERTYSET) { + buttonPropertySet = new ButtonPropertySetRecord(r); + } else if (type == Type.CALCMODE) { + CalcModeRecord cmr = new CalcModeRecord(r); + settings.setAutomaticFormulaCalculation(cmr.isAutomatic()); + } else if (type == Type.SAVERECALC) { + SaveRecalcRecord cmr = new SaveRecalcRecord(r); + settings.setRecalculateFormulasBeforeSave(cmr.getRecalculateOnSave()); + } else if (type == Type.GUTS) { + GuttersRecord gr = new GuttersRecord(r); + maxRowOutlineLevel = + gr.getRowOutlineLevel() > 0 ? gr.getRowOutlineLevel() - 1 : 0; + maxColumnOutlineLevel = + gr.getColumnOutlineLevel() > 0 ? gr.getRowOutlineLevel() - 1 : 0; + } else if (type == Type.BOF) { + BOFRecord br = new BOFRecord(r); + Assert.verify(!br.isWorksheet()); + + int startpos = excelFile.getPos() - r.getLength() - 4; + + // Skip to the end of the nested bof + // Thanks to Rohit for spotting this + Record r2 = excelFile.next(); + while (r2.getCode() != Type.EOF.value) { + r2 = excelFile.next(); + } + + if (br.isChart()) { + if (!workbook.getWorkbookBof().isBiff8()) { + logger.warn("only biff8 charts are supported"); + } else { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + if (!workbookSettings.getDrawingsDisabled()) { + Chart chart = new Chart(msoRecord, objRecord, drawingData, + startpos, excelFile.getPos(), + excelFile, workbookSettings); + charts.add(chart); + + if (workbook.getDrawingGroup() != null) { + workbook.getDrawingGroup().add(chart); + } + } + } + + // Reset the drawing records + msoRecord = null; + objRecord = null; + } + + // If this worksheet is just a chart, then the EOF reached + // represents the end of the sheet as well as the end of the chart + if (sheetBof.isChart()) { + cont = false; + } + } else if (type == Type.EOF) { + cont = false; + } + } + + // Restore the file to its accurate position + excelFile.restorePos(); + + // Add any out of bounds cells + if (outOfBoundsCells.size() > 0) { + handleOutOfBoundsCells(); + } + + // Add all the shared formulas to the sheet as individual formulas + Iterator i = sharedFormulas.iterator(); + + while (i.hasNext()) { + SharedFormulaRecord sfr = (SharedFormulaRecord) i.next(); + + Cell[] sfnr = sfr.getFormulas(formattingRecords, nineteenFour); + + for (int sf = 0; sf < sfnr.length; sf++) { + addCell(sfnr[sf]); + } + } + + // If the last base shared formula wasn't added to the sheet, then + // revert it to an ordinary formula and add it + if (!sharedFormulaAdded && sharedFormula != null) { + addCell(revertSharedFormula(sharedFormula)); + } + + // If there is a stray msoDrawing record, then flag to the drawing group + // that one has been omitted + if (msoRecord != null && workbook.getDrawingGroup() != null) { + workbook.getDrawingGroup().setDrawingsOmitted(msoRecord, objRecord); + } + + // Check that the comments hash is empty + if (!comments.isEmpty()) { + logger.warn("Not all comments have a corresponding Note record"); + } + } + + /** + * Sees if the shared formula belongs to any of the shared formula + * groups + * + * @param fr the candidate shared formula + * @return TRUE if the formula was added, FALSE otherwise + */ + private boolean addToSharedFormulas(BaseSharedFormulaRecord fr) { + boolean added = false; + SharedFormulaRecord sfr = null; + + for (int i = 0, size = sharedFormulas.size(); i < size && !added; ++i) { + sfr = (SharedFormulaRecord) sharedFormulas.get(i); + added = sfr.add(fr); + } + + return added; + } + + /** + * Reverts the shared formula passed in to an ordinary formula and adds + * it to the list + * + * @param f the formula + * @return the new formula + * @throws FormulaException + */ + private Cell revertSharedFormula(BaseSharedFormulaRecord f) { + // String formulas look for a STRING record soon after the formula + // occurred. Temporarily the position in the excel file back + // to the point immediately after the formula record + int pos = excelFile.getPos(); + excelFile.setPos(f.getFilePos()); + + FormulaRecord fr = new FormulaRecord(f.getRecord(), + excelFile, + formattingRecords, + workbook, + workbook, + FormulaRecord.ignoreSharedFormula, + sheet, + workbookSettings); + + try { + Cell cell = fr.getFormula(); + + // See if the formula evaluates to date + if (fr.getFormula().getType() == CellType.NUMBER_FORMULA) { + NumberFormulaRecord nfr = (NumberFormulaRecord) fr.getFormula(); + if (formattingRecords.isDate(fr.getXFIndex())) { + cell = new DateFormulaRecord(nfr, + formattingRecords, + workbook, + workbook, + nineteenFour, + sheet); + } + } + + excelFile.setPos(pos); + return cell; + } catch (FormulaException e) { + // Something has gone wrong trying to read the formula data eg. it + // might be unsupported biff7 data + logger.warn + (CellReferenceHelper.getCellReference(fr.getColumn(), fr.getRow()) + + " " + e.getMessage()); + + return null; + } + } + + + /** + * Accessor + * + * @return the number of rows + */ + final int getNumRows() { + return numRows; + } + + /** + * Accessor + * + * @return the number of columns + */ + final int getNumCols() { + return numCols; + } + + /** + * Accessor + * + * @return the cells + */ + final Cell[][] getCells() { + return cells; + } + + /** + * Accessor + * + * @return the row properties + */ + final ArrayList getRowProperties() { + return rowProperties; + } + + /** + * Accessor + * + * @return the column information + */ + final ArrayList getColumnInfosArray() { + return columnInfosArray; + } + + /** + * Accessor + * + * @return the hyperlinks + */ + final ArrayList getHyperlinks() { + return hyperlinks; + } + + /** + * Accessor + * + * @return the conditional formatting + */ + final ArrayList getConditionalFormats() { + return conditionalFormats; + } + + /** + * Accessor + * + * @return the autofilter + */ + final AutoFilter getAutoFilter() { + return autoFilter; + } + + /** + * Accessor + * + * @return the charts + */ + final ArrayList getCharts() { + return charts; + } + + /** + * Accessor + * + * @return the drawings + */ + final ArrayList getDrawings() { + return drawings; + } + + /** + * Accessor + * + * @return the data validations + */ + final DataValidation getDataValidation() { + return dataValidation; + } + + /** + * Accessor + * + * @return the ranges + */ + final Range[] getMergedCells() { + return mergedCells; + } + + /** + * Accessor + * + * @return the sheet settings + */ + final SheetSettings getSettings() { + return settings; + } + + /** + * Accessor + * + * @return the row breaks + */ + final int[] getRowBreaks() { + return rowBreaks; + } + + /** + * Accessor + * + * @return the column breaks + */ + final int[] getColumnBreaks() { + return columnBreaks; + } + + /** + * Accessor + * + * @return the workspace options + */ + final WorkspaceInformationRecord getWorkspaceOptions() { + return workspaceOptions; + } + + /** + * Accessor + * + * @return the environment specific print record + */ + final PLSRecord getPLS() { + return plsRecord; + } + + /** + * Accessor for the button property set, used during copying + * + * @return the button property set + */ + final ButtonPropertySetRecord getButtonPropertySet() { + return buttonPropertySet; + } + + /** + * Adds a cell comment to a cell just read in + * + * @param col the column for the comment + * @param row the row for the comment + * @param text the comment text + * @param width the width of the comment text box + * @param height the height of the comment text box + */ + private void addCellComment(int col, + int row, + String text, + double width, + double height) { + Cell c = cells[row][col]; + if (c == null) { + logger.warn("Cell at " + CellReferenceHelper.getCellReference(col, row) + + " not present - adding a blank"); + MulBlankCell mbc = new MulBlankCell(row, + col, + 0, + formattingRecords, + sheet); + CellFeatures cf = new CellFeatures(); + cf.setReadComment(text, width, height); + mbc.setCellFeatures(cf); + addCell(mbc); + + return; + } + + if (c instanceof CellFeaturesAccessor) { + CellFeaturesAccessor cv = (CellFeaturesAccessor) c; + CellFeatures cf = cv.getCellFeatures(); + + if (cf == null) { + cf = new CellFeatures(); + cv.setCellFeatures(cf); + } + + cf.setReadComment(text, width, height); + } else { + logger.warn("Not able to add comment to cell type " + + c.getClass().getName() + + " at " + CellReferenceHelper.getCellReference(col, row)); + } + } + + /** + * Adds a cell comment to a cell just read in + * + * @param col1 the column for the comment + * @param row1 the row for the comment + * @param col2 the row for the comment + * @param row2 the row for the comment + * @param dvsr the validation settings + */ + private void addCellValidation(int col1, + int row1, + int col2, + int row2, + DataValiditySettingsRecord dvsr) { + for (int row = row1; row <= row2; row++) { + for (int col = col1; col <= col2; col++) { + Cell c = null; + + if (cells.length > row && cells[row].length > col) { + c = cells[row][col]; + } + + if (c == null) { + MulBlankCell mbc = new MulBlankCell(row, + col, + 0, + formattingRecords, + sheet); + CellFeatures cf = new CellFeatures(); + cf.setValidationSettings(dvsr); + mbc.setCellFeatures(cf); + addCell(mbc); + } else if (c instanceof CellFeaturesAccessor) { // Check to see if the cell already contains a comment + CellFeaturesAccessor cv = (CellFeaturesAccessor) c; + CellFeatures cf = cv.getCellFeatures(); + + if (cf == null) { + cf = new CellFeatures(); + cv.setCellFeatures(cf); + } + + cf.setValidationSettings(dvsr); + } else { + logger.warn("Not able to add comment to cell type " + + c.getClass().getName() + + " at " + CellReferenceHelper.getCellReference(col, row)); + } + } + } + } + + /** + * Reads in the object record + * + * @param objRecord the obj record + * @param msoRecord the mso drawing record read in earlier + * @param comments the hash map of comments + */ + private void handleObjectRecord(ObjRecord objRecord, + MsoDrawingRecord msoRecord, + HashMap comments) { + if (msoRecord == null) { + logger.warn("Object record is not associated with a drawing " + + " record - ignoring"); + return; + } + + try { + // Handle images + if (objRecord.getType() == ObjRecord.PICTURE) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + Drawing drawing = new Drawing(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + sheet); + drawings.add(drawing); + return; + } + + // Handle comments + if (objRecord.getType() == ObjRecord.EXCELNOTE) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + Comment comment = new Comment(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + + // Sometimes Excel writes out Continue records instead of drawing + // records, so forcibly hack all of these into a drawing record + Record r2 = excelFile.next(); + if (r2.getType() == Type.MSODRAWING || r2.getType() == Type.CONTINUE) { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + comment.addMso(mso); + r2 = excelFile.next(); + } + Assert.verify(r2.getType() == Type.TXO); + TextObjectRecord txo = new TextObjectRecord(r2); + comment.setTextObject(txo); + + r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.CONTINUE); + ContinueRecord text = new ContinueRecord(r2); + comment.setText(text); + + r2 = excelFile.next(); + if (r2.getType() == Type.CONTINUE) { + ContinueRecord formatting = new ContinueRecord(r2); + comment.setFormatting(formatting); + } + + comments.put(new Integer(comment.getObjectId()), comment); + return; + } + + // Handle combo boxes + if (objRecord.getType() == ObjRecord.COMBOBOX) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + ComboBox comboBox = new ComboBox(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + drawings.add(comboBox); + return; + } + + // Handle check boxes + if (objRecord.getType() == ObjRecord.CHECKBOX) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + CheckBox checkBox = new CheckBox(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + + Record r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE); + if (r2.getType() == Type.MSODRAWING || r2.getType() == Type.CONTINUE) { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + checkBox.addMso(mso); + r2 = excelFile.next(); + } + + Assert.verify(r2.getType() == Type.TXO); + TextObjectRecord txo = new TextObjectRecord(r2); + checkBox.setTextObject(txo); + + if (txo.getTextLength() == 0) { + return; + } + + r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.CONTINUE); + ContinueRecord text = new ContinueRecord(r2); + checkBox.setText(text); + + r2 = excelFile.next(); + if (r2.getType() == Type.CONTINUE) { + ContinueRecord formatting = new ContinueRecord(r2); + checkBox.setFormatting(formatting); + } + + drawings.add(checkBox); + + return; + } + + // Handle form buttons + if (objRecord.getType() == ObjRecord.BUTTON) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + Button button = new Button(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + + Record r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE); + if (r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE) { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + button.addMso(mso); + r2 = excelFile.next(); + } + + Assert.verify(r2.getType() == Type.TXO); + TextObjectRecord txo = new TextObjectRecord(r2); + button.setTextObject(txo); + + r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.CONTINUE); + ContinueRecord text = new ContinueRecord(r2); + button.setText(text); + + r2 = excelFile.next(); + if (r2.getType() == Type.CONTINUE) { + ContinueRecord formatting = new ContinueRecord(r2); + button.setFormatting(formatting); + } + + drawings.add(button); + + return; + } + + // Non-supported types which have multiple record types + if (objRecord.getType() == ObjRecord.TEXT) { + logger.warn(objRecord.getType() + " Object on sheet \"" + + sheet.getName() + + "\" not supported - omitting"); + + // Still need to add the drawing data to preserve the hierarchy + if (drawingData == null) { + drawingData = new DrawingData(); + + } + drawingData.addData(msoRecord.getData()); + + Record r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE); + if (r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE) { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + drawingData.addRawData(mso.getData()); + r2 = excelFile.next(); + } + + Assert.verify(r2.getType() == Type.TXO); + + if (workbook.getDrawingGroup() != null) // can be null for Excel 95 + { + workbook.getDrawingGroup().setDrawingsOmitted(msoRecord, + objRecord); + } + + return; + } + + // Handle other types + if (objRecord.getType() != ObjRecord.CHART) { + logger.warn(objRecord.getType() + " Object on sheet \"" + + sheet.getName() + + "\" not supported - omitting"); + + // Still need to add the drawing data to preserve the hierarchy + if (drawingData == null) { + drawingData = new DrawingData(); + } + + drawingData.addData(msoRecord.getData()); + + if (workbook.getDrawingGroup() != null) // can be null for Excel 95 + { + workbook.getDrawingGroup().setDrawingsOmitted(msoRecord, + objRecord); + } + + return; + } + } catch (DrawingDataException e) { + logger.warn(e.getMessage() + + "...disabling drawings for the remainder of the workbook"); + workbookSettings.setDrawingsDisabled(true); + } + } + + /** + * Gets the drawing data - for use as part of the Escher debugging tool + */ + DrawingData getDrawingData() { + return drawingData; + } + + /** + * Handle any cells which fall outside of the bounds specified within + * the dimension record + */ + private void handleOutOfBoundsCells() { + int resizedRows = numRows; + int resizedCols = numCols; + + // First, determine the new bounds + for (Iterator i = outOfBoundsCells.iterator(); i.hasNext(); ) { + Cell cell = (Cell) i.next(); + resizedRows = Math.max(resizedRows, cell.getRow() + 1); + resizedCols = Math.max(resizedCols, cell.getColumn() + 1); + } + + // There used to be a warning here about exceeding the sheet dimensions, + // but removed it when I added the ability to perform data validation + // on entire rows or columns - in which case it would blow out any + // existing dimensions + + // Resize the columns, if necessary + if (resizedCols > numCols) { + for (int r = 0; r < numRows; r++) { + Cell[] newRow = new Cell[resizedCols]; + Cell[] oldRow = cells[r]; + System.arraycopy(oldRow, 0, newRow, 0, oldRow.length); + cells[r] = newRow; + } + } + + // Resize the rows, if necessary + if (resizedRows > numRows) { + Cell[][] newCells = new Cell[resizedRows][]; + System.arraycopy(cells, 0, newCells, 0, cells.length); + cells = newCells; + + // Create the new rows + for (int i = numRows; i < resizedRows; i++) { + newCells[i] = new Cell[resizedCols]; + } + } + + numRows = resizedRows; + numCols = resizedCols; + + // Now add all the out of bounds cells into the new cells + for (Iterator i = outOfBoundsCells.iterator(); i.hasNext(); ) { + Cell cell = (Cell) i.next(); + addCell(cell); + } + + outOfBoundsCells.clear(); + } + + /** + * Accessor for the maximum column outline level + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() { + return maxRowOutlineLevel; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SortRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SortRecord.java new file mode 100755 index 0000000..5bded55 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SortRecord.java @@ -0,0 +1,170 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan, Al Mantei + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.biff.Type; + +/** + * A storage area for the last Sort dialog box area + */ +public class SortRecord extends RecordData { + private final int col1Size; + private final int col2Size; + private final int col3Size; + private final String col1Name; + private final String col2Name; + private final String col3Name; + private final byte optionFlags; + private boolean sortColumns = false; + private boolean sortKey1Desc = false; + private boolean sortKey2Desc = false; + private boolean sortKey3Desc = false; + private boolean sortCaseSensitive = false; + + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + public SortRecord(Record r) { + super(Type.SORT); + + byte[] data = r.getData(); + + optionFlags = data[0]; + + sortColumns = ((optionFlags & 0x01) != 0); + sortKey1Desc = ((optionFlags & 0x02) != 0); + sortKey2Desc = ((optionFlags & 0x04) != 0); + sortKey3Desc = ((optionFlags & 0x08) != 0); + sortCaseSensitive = ((optionFlags & 0x10) != 0); + + // data[1] contains sort list index - not implemented... + + col1Size = data[2]; + col2Size = data[3]; + col3Size = data[4]; + int curPos = 5; + if (data[curPos++] == 0x00) { + col1Name = new String(data, curPos, col1Size); + curPos += col1Size; + } else { + col1Name = StringHelper.getUnicodeString(data, col1Size, curPos); + curPos += col1Size * 2; + } + + if (col2Size > 0) { + if (data[curPos++] == 0x00) { + col2Name = new String(data, curPos, col2Size); + curPos += col2Size; + } else { + col2Name = StringHelper.getUnicodeString(data, col2Size, curPos); + curPos += col2Size * 2; + } + } else { + col2Name = ""; + } + if (col3Size > 0) { + if (data[curPos++] == 0x00) { + col3Name = new String(data, curPos, col3Size); + curPos += col3Size; + } else { + col3Name = StringHelper.getUnicodeString(data, col3Size, curPos); + curPos += col3Size * 2; + } + } else { + col3Name = ""; + } + } + + /** + * Accessor for the 1st Sort Column Name + * + * @return the 1st Sort Column Name + */ + public String getSortCol1Name() { + return col1Name; + } + + /** + * Accessor for the 2nd Sort Column Name + * + * @return the 2nd Sort Column Name + */ + public String getSortCol2Name() { + return col2Name; + } + + /** + * Accessor for the 3rd Sort Column Name + * + * @return the 3rd Sort Column Name + */ + public String getSortCol3Name() { + return col3Name; + } + + /** + * Accessor for the Sort by Columns flag + * + * @return the Sort by Columns flag + */ + public boolean getSortColumns() { + return sortColumns; + } + + /** + * Accessor for the Sort Column 1 Descending flag + * + * @return the Sort Column 1 Descending flag + */ + public boolean getSortKey1Desc() { + return sortKey1Desc; + } + + /** + * Accessor for the Sort Column 2 Descending flag + * + * @return the Sort Column 2 Descending flag + */ + public boolean getSortKey2Desc() { + return sortKey2Desc; + } + + /** + * Accessor for the Sort Column 3 Descending flag + * + * @return the Sort Column 3 Descending flag + */ + public boolean getSortKey3Desc() { + return sortKey3Desc; + } + + /** + * Accessor for the Sort Case Sensitivity flag + * + * @return the Sort Case Secsitivity flag + */ + public boolean getSortCaseSensitive() { + return sortCaseSensitive; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/StringFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/StringFormulaRecord.java new file mode 100755 index 0000000..6158a3e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/StringFormulaRecord.java @@ -0,0 +1,268 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.StringFormulaCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A string formula's last calculated value + */ +class StringFormulaRecord extends CellValue + implements LabelCell, FormulaData, StringFormulaCell { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(StringFormulaRecord.class); + + /** + * The last calculated value of the formula + */ + private String value; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The raw data + */ + private final byte[] data; + + /** + * Constructs this object from the raw data. We need to use the excelFile + * to retrieve the String record which follows this formula record + * + * @param t the raw data + * @param excelFile the excel file + * @param fr the formatting records + * @param es the external sheet records + * @param nt the workbook + * @param si the sheet impl + * @param ws the workbook settings + */ + public StringFormulaRecord(Record t, File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + WorkbookSettings ws) { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + + data = getRecord().getData(); + + int pos = excelFile.getPos(); + + // Look for the string record in one of the records after the + // formula. Put a cap on it to prevent looping + + Record nextRecord = excelFile.next(); + int count = 0; + while (nextRecord.getType() != Type.STRING && count < 4) { + nextRecord = excelFile.next(); + count++; + } + Assert.verify(count < 4, " @ " + pos); + byte[] stringData = nextRecord.getData(); + + // Read in any continuation records + nextRecord = excelFile.peek(); + while (nextRecord.getType() == Type.CONTINUE) { + nextRecord = excelFile.next(); // move the pointer within the data + byte[] d = new byte[stringData.length + nextRecord.getLength() - 1]; + System.arraycopy(stringData, 0, d, 0, stringData.length); + System.arraycopy(nextRecord.getData(), 1, d, + stringData.length, nextRecord.getLength() - 1); + stringData = d; + nextRecord = excelFile.peek(); + } + readString(stringData, ws); + } + + /** + * Constructs this object from the raw data. Used when reading in formula + * strings which evaluate to null (in the case of some IF statements) + * + * @param t the raw data + * @param fr the formatting records + * @param es the external sheet records + * @param nt the workbook + * @param si the sheet impl + * @param ws the workbook settings + */ + public StringFormulaRecord(Record t, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + + data = getRecord().getData(); + value = ""; + } + + + /** + * Reads in the string + * + * @param d the data + * @param ws the workbook settings + */ + private void readString(byte[] d, WorkbookSettings ws) { + int pos = 0; + int chars = IntegerHelper.getInt(d[0], d[1]); + + if (chars == 0) { + value = ""; + return; + } + pos += 2; + int optionFlags = d[pos]; + pos++; + + if ((optionFlags & 0xf) != optionFlags) { + // Uh oh - looks like a plain old string, not unicode + // Recalculate all the positions + pos = 0; + chars = IntegerHelper.getInt(d[0], (byte) 0); + optionFlags = d[1]; + pos = 2; + } + + // See if it is an extended string + boolean extendedString = ((optionFlags & 0x04) != 0); + + // See if string contains formatting information + boolean richString = ((optionFlags & 0x08) != 0); + + if (richString) { + pos += 2; + } + + if (extendedString) { + pos += 4; + } + + // See if string is ASCII (compressed) or unicode + boolean asciiEncoding = ((optionFlags & 0x01) == 0); + + if (asciiEncoding) { + value = StringHelper.getString(d, chars, pos, ws); + } else { + value = StringHelper.getUnicodeString(d, chars, pos); + } + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public String getContents() { + return value; + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public String getString() { + return value; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.STRING_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbook().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SupbookRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SupbookRecord.java new file mode 100755 index 0000000..7136902 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SupbookRecord.java @@ -0,0 +1,277 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Logger; + +/** + * A record containing the references to the various sheets (internal and + * external) referenced by formulas in this workbook + */ +public class SupbookRecord extends RecordData { + public static final Type INTERNAL = new Type(); + public static final Type EXTERNAL = new Type(); + public static final Type ADDIN = new Type(); + public static final Type LINK = new Type(); + public static final Type UNKNOWN = new Type(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SupbookRecord.class); + + /** + * The type of this supbook record + */ + private final Type type; + /** + * The number of sheets - internal & external supbooks only + */ + private int numSheets; + /** + * The name of the external file + */ + private String fileName; + /** + * The names of the external sheets + */ + private String[] sheetNames; + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + */ + SupbookRecord(Record t, WorkbookSettings ws) { + super(t); + byte[] data = getRecord().getData(); + + // First deduce the type + if (data.length == 4) { + if (data[2] == 0x01 && data[3] == 0x04) { + type = INTERNAL; + } else if (data[2] == 0x01 && data[3] == 0x3a) { + type = ADDIN; + } else { + type = UNKNOWN; + } + } else if (data[0] == 0 && data[1] == 0) { + type = LINK; + } else { + type = EXTERNAL; + } + + if (type == INTERNAL) { + numSheets = IntegerHelper.getInt(data[0], data[1]); + } + + if (type == EXTERNAL) { + readExternal(data, ws); + } + } + + /** + * Reads the external data records + * + * @param data the data + * @param ws the workbook settings + */ + private void readExternal(byte[] data, WorkbookSettings ws) { + numSheets = IntegerHelper.getInt(data[0], data[1]); + + // subtract file name encoding from the length + int ln = IntegerHelper.getInt(data[2], data[3]) - 1; + int pos = 0; + + if (data[4] == 0) { + // non-unicode string + int encoding = data[5]; + pos = 6; + if (encoding == 0) { + fileName = StringHelper.getString(data, ln, pos, ws); + pos += ln; + } else { + fileName = getEncodedFilename(data, ln, pos); + pos += ln; + } + } else { + // unicode string + int encoding = IntegerHelper.getInt(data[5], data[6]); + pos = 7; + if (encoding == 0) { + fileName = StringHelper.getUnicodeString(data, ln, pos); + pos += ln * 2; + } else { + fileName = getUnicodeEncodedFilename(data, ln, pos); + pos += ln * 2; + } + } + + sheetNames = new String[numSheets]; + + for (int i = 0; i < sheetNames.length; i++) { + ln = IntegerHelper.getInt(data[pos], data[pos + 1]); + + if (data[pos + 2] == 0x0) { + sheetNames[i] = StringHelper.getString(data, ln, pos + 3, ws); + pos += ln + 3; + } else if (data[pos + 2] == 0x1) { + sheetNames[i] = StringHelper.getUnicodeString(data, ln, pos + 3); + pos += ln * 2 + 3; + } + } + } + + /** + * Gets the type of this supbook record + * + * @return the type of this supbook + */ + public Type getType() { + return type; + } + + /** + * Gets the number of sheets. This will only be non-zero for internal + * and external supbooks + * + * @return the number of sheets + */ + public int getNumberOfSheets() { + return numSheets; + } + + /** + * Gets the name of the external file + * + * @return the name of the external file + */ + public String getFileName() { + return fileName; + } + + /** + * Gets the name of the external sheet + * + * @param i the index of the external sheet + * @return the name of the sheet + */ + public String getSheetName(int i) { + return sheetNames[i]; + } + + /** + * Gets the data - used when copying a spreadsheet + * + * @return the raw external sheet data + */ + public byte[] getData() { + return getRecord().getData(); + } + + /** + * Gets the encoded string from the data array + * + * @param data the data + * @param ln length of the string + * @param pos the position in the array + * @return the string + */ + private String getEncodedFilename(byte[] data, int ln, int pos) { + StringBuffer buf = new StringBuffer(); + int endpos = pos + ln; + while (pos < endpos) { + char c = (char) data[pos]; + + if (c == '\u0001') { + // next character is a volume letter + pos++; + c = (char) data[pos]; + buf.append(c); + buf.append(":\\\\"); + } else if (c == '\u0002') { + // file is on the same volume + buf.append('\\'); + } else if (c == '\u0003') { + // down directory + buf.append('\\'); + } else if (c == '\u0004') { + // up directory + buf.append("..\\"); + } else { + // just add on the character + buf.append(c); + } + + pos++; + } + + return buf.toString(); + } + + /** + * Gets the encoded string from the data array + * + * @param data the data + * @param ln length of the string + * @param pos the position in the array + * @return the string + */ + private String getUnicodeEncodedFilename(byte[] data, int ln, int pos) { + StringBuffer buf = new StringBuffer(); + int endpos = pos + ln * 2; + while (pos < endpos) { + char c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]); + + if (c == '\u0001') { + // next character is a volume letter + pos += 2; + c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]); + buf.append(c); + buf.append(":\\\\"); + } else if (c == '\u0002') { + // file is on the same volume + buf.append('\\'); + } else if (c == '\u0003') { + // down directory + buf.append('\\'); + } else if (c == '\u0004') { + // up directory + buf.append("..\\"); + } else { + // just add on the character + buf.append(c); + } + + pos += 2; + } + + return buf.toString(); + } + + /** + * The type of supbook this refers to + */ + private static class Type { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/TemplateRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/TemplateRecord.java new file mode 100755 index 0000000..ec5d4cd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/TemplateRecord.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A template record + */ +class TemplateRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(TemplateRecord.class); + + /** + * The template + */ + private final boolean template; + + /** + * Constructor + * + * @param t the record + */ + public TemplateRecord(Record t) { + super(t); + template = true; + } + + /** + * Accessor for the template mode + * + * @return the template mode + */ + public boolean getTemplate() { + return template; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/TopMarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/TopMarginRecord.java new file mode 100755 index 0000000..efaae34 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/TopMarginRecord.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class TopMarginRecord extends MarginRecord { + /** + * Constructor + * + * @param r the record + */ + TopMarginRecord(Record r) { + super(Type.TOPMARGIN, r); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/VerticalPageBreaksRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/VerticalPageBreaksRecord.java new file mode 100755 index 0000000..895dbfd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/VerticalPageBreaksRecord.java @@ -0,0 +1,101 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class VerticalPageBreaksRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private final Logger logger = Logger.getLogger + (VerticalPageBreaksRecord.class); + /** + * The row page breaks + */ + private final int[] columnBreaks; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public VerticalPageBreaksRecord(Record t) { + super(t); + + byte[] data = t.getData(); + + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + columnBreaks = new int[numbreaks]; + + for (int i = 0; i < numbreaks; i++) { + columnBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 6; + } + } + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + * @param biff7 an indicator to initialise this record for biff 7 format + */ + public VerticalPageBreaksRecord(Record t, Biff7 biff7) { + super(t); + + byte[] data = t.getData(); + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + columnBreaks = new int[numbreaks]; + for (int i = 0; i < numbreaks; i++) { + columnBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + } + + /** + * Gets the row breaks + * + * @return the row breaks on the current sheet + */ + public int[] getColumnBreaks() { + return columnBreaks; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/Window2Record.java b/datastructures-xslx/src/main/java/jxl/read/biff/Window2Record.java new file mode 100755 index 0000000..a21ab22 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/Window2Record.java @@ -0,0 +1,194 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class Window2Record extends RecordData { + public static final Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Window2Record.class); + /** + * Selected flag + */ + private final boolean selected; + /** + * Show grid lines flag + */ + private final boolean showGridLines; + /** + * Display zero values flag + */ + private final boolean displayZeroValues; + /** + * The window contains frozen panes + */ + private final boolean frozenPanes; + /** + * The window contains panes that are frozen but not split + */ + private final boolean frozenNotSplit; + /** + * The view mode: normal or pagebreakpreview + */ + private final boolean pageBreakPreviewMode; + /** + * The page break preview magnification + */ + private int pageBreakPreviewMagnification; + /** + * The normal view magnification + */ + private int normalMagnification; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public Window2Record(Record t) { + super(t); + byte[] data = t.getData(); + + int options = IntegerHelper.getInt(data[0], data[1]); + + selected = ((options & 0x200) != 0); + showGridLines = ((options & 0x02) != 0); + frozenPanes = ((options & 0x08) != 0); + displayZeroValues = ((options & 0x10) != 0); + frozenNotSplit = ((options & 0x100) != 0); + pageBreakPreviewMode = ((options & 0x800) != 0); + + pageBreakPreviewMagnification = IntegerHelper.getInt(data[10], data[11]); + normalMagnification = IntegerHelper.getInt(data[12], data[13]); + } + + /** + * Constructs the dimensions from the raw data. Dummy overload for + * biff7 workbooks + * + * @param t the raw data + * @param dummy the overload + */ + public Window2Record(Record t, Biff7 biff7) { + super(t); + byte[] data = t.getData(); + + int options = IntegerHelper.getInt(data[0], data[1]); + + selected = ((options & 0x200) != 0); + showGridLines = ((options & 0x02) != 0); + frozenPanes = ((options & 0x08) != 0); + displayZeroValues = ((options & 0x10) != 0); + frozenNotSplit = ((options & 0x100) != 0); + pageBreakPreviewMode = ((options & 0x800) != 0); + } + + /** + * Accessor for the selected flag + * + * @return TRUE if this sheet is selected, FALSE otherwise + */ + public boolean isSelected() { + return selected; + } + + /** + * Accessor for the show grid lines flag + * + * @return TRUE to show grid lines, FALSE otherwise + */ + public boolean getShowGridLines() { + return showGridLines; + } + + /** + * Accessor for the zero values flag + * + * @return TRUE if this sheet displays zero values, FALSE otherwise + */ + public boolean getDisplayZeroValues() { + return displayZeroValues; + } + + /** + * Accessor for the frozen panes flag + * + * @return TRUE if this contains frozen panes, FALSE otherwise + */ + public boolean getFrozen() { + return frozenPanes; + } + + /** + * Accessor for the frozen not split flag + * + * @return TRUE if this contains frozen, FALSE otherwise + */ + public boolean getFrozenNotSplit() { + return frozenNotSplit; + } + + /** + * Accessor for the page break preview mode + * + * @return TRUE if this sheet is in page break preview, FALSE otherwise + */ + public boolean isPageBreakPreview() { + return pageBreakPreviewMode; + } + + /** + * Accessor for the page break preview magnification + * + * @return the cached paged break preview magnfication factor in percent + */ + public int getPageBreakPreviewMagnificaiton() { + return pageBreakPreviewMagnification; + } + + /** + * Accessor for the normal view magnification + * + * @return the cached normal view magnfication factor in percent + */ + public int getNormalMagnificaiton() { + return normalMagnification; + } + + // Dummy overload + private static class Biff7 { + } + +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/WindowProtectedRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/WindowProtectedRecord.java new file mode 100755 index 0000000..a2ed5d9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/WindowProtectedRecord.java @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A windowProtected mode record + */ +class WindowProtectedRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(WindowProtectedRecord.class); + + /** + * The windowProtected mode + */ + private final boolean windowProtected; + + /** + * Constructor + * + * @param t the record + */ + public WindowProtectedRecord(Record t) { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + windowProtected = (mode == 1); + } + + /** + * Accessor for the windowProtected + * + * @return the windowProtected mode + */ + public boolean getWindowProtected() { + return windowProtected; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/WorkbookParser.java b/datastructures-xslx/src/main/java/jxl/read/biff/WorkbookParser.java new file mode 100755 index 0000000..924dc94 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/WorkbookParser.java @@ -0,0 +1,1108 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.Cell; +import jxl.Range; +import jxl.Sheet; +import jxl.Workbook; +import jxl.WorkbookSettings; +import jxl.biff.BuiltInName; +import jxl.biff.CellReferenceHelper; +import jxl.biff.EmptyCell; +import jxl.biff.FontRecord; +import jxl.biff.Fonts; +import jxl.biff.FormatRecord; +import jxl.biff.FormattingRecords; +import jxl.biff.NameRangeException; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.PaletteRecord; +import jxl.biff.RangeImpl; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.XCTRecord; +import jxl.biff.XFRecord; +import jxl.biff.drawing.DrawingGroup; +import jxl.biff.drawing.MsoDrawingGroupRecord; +import jxl.biff.drawing.Origin; +import jxl.biff.formula.ExternalSheet; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Parses the biff file passed in, and builds up an internal representation of + * the spreadsheet + */ +public class WorkbookParser extends Workbook + implements ExternalSheet, WorkbookMethods { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(WorkbookParser.class); + + /** + * The excel file + */ + private final File excelFile; + /** + * The number of open bofs + */ + private int bofs; + /** + * Indicates whether or not the dates are based around the 1904 date system + */ + private boolean nineteenFour; + /** + * The shared string table + */ + private SSTRecord sharedStrings; + /** + * The names of all the worksheets + */ + private final ArrayList boundsheets; + /** + * The xf records + */ + private final FormattingRecords formattingRecords; + /** + * The fonts used by this workbook + */ + private final Fonts fonts; + + /** + * The sheets contained in this workbook + */ + private final ArrayList sheets; + + /** + * The last sheet accessed + */ + private SheetImpl lastSheet; + + /** + * The index of the last sheet retrieved + */ + private int lastSheetIndex; + + /** + * The named records found in this workbook + */ + private final HashMap namedRecords; + + /** + * The list of named records + */ + private ArrayList nameTable; + + /** + * The list of add in functions + */ + private ArrayList addInFunctions; + + /** + * The external sheet record. Used by formulas, and names + */ + private ExternalSheetRecord externSheet; + + /** + * The list of supporting workbooks - used by formulas + */ + private final ArrayList supbooks; + + /** + * The bof record for this workbook + */ + private BOFRecord workbookBof; + + /** + * The Mso Drawing Group record for this workbook + */ + private MsoDrawingGroupRecord msoDrawingGroup; + + /** + * The property set record associated with this workbook + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * Workbook protected flag + */ + private boolean wbProtected; + + /** + * Contains macros flag + */ + private boolean containsMacros; + + /** + * The workbook settings + */ + private final WorkbookSettings settings; + + /** + * The drawings contained in this workbook + */ + private DrawingGroup drawingGroup; + + /** + * The country record (containing the language and regional settings) + * for this workbook + */ + private CountryRecord countryRecord; + + private final ArrayList xctRecords; + + /** + * Constructs this object from the raw excel data + * + * @param f the excel 97 biff file + * @param s the workbook settings + */ + public WorkbookParser(File f, WorkbookSettings s) { + super(); + excelFile = f; + boundsheets = new ArrayList(10); + fonts = new Fonts(); + formattingRecords = new FormattingRecords(fonts); + sheets = new ArrayList(10); + supbooks = new ArrayList(10); + namedRecords = new HashMap(); + lastSheetIndex = -1; + wbProtected = false; + containsMacros = false; + settings = s; + xctRecords = new ArrayList(10); + } + + /** + * Gets the sheets within this workbook. + * NOTE: Use of this method for + * very large worksheets can cause performance and out of memory problems. + * Use the alternative method getSheet() to retrieve each sheet individually + * + * @return an array of the individual sheets + */ + public Sheet[] getSheets() { + Sheet[] sheetArray = new Sheet[getNumberOfSheets()]; + return (Sheet[]) sheets.toArray(sheetArray); + } + + /** + * Interface method from WorkbookMethods - gets the specified + * sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + public Sheet getReadSheet(int index) { + return getSheet(index); + } + + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + public Sheet getSheet(int index) { + // First see if the last sheet index is the same as this sheet index. + // If so, then the same sheet is being re-requested, so simply + // return it instead of rereading it + if ((lastSheet != null) && lastSheetIndex == index) { + return lastSheet; + } + + // Flush out all of the cached data in the last sheet + if (lastSheet != null) { + lastSheet.clear(); + + if (!settings.getGCDisabled()) { + System.gc(); + } + } + + lastSheet = (SheetImpl) sheets.get(index); + lastSheetIndex = index; + lastSheet.readSheet(); + + return lastSheet; + } + + /** + * Gets the sheet with the specified name from within this workbook + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public Sheet getSheet(String name) { + // Iterate through the boundsheet records + int pos = 0; + boolean found = false; + Iterator i = boundsheets.iterator(); + BoundsheetRecord br = null; + + while (i.hasNext() && !found) { + br = (BoundsheetRecord) i.next(); + + if (br.getName().equals(name)) { + found = true; + } else { + pos++; + } + } + + return found ? getSheet(pos) : null; + } + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public String[] getSheetNames() { + String[] names = new String[boundsheets.size()]; + + BoundsheetRecord br = null; + for (int i = 0; i < names.length; i++) { + br = (BoundsheetRecord) boundsheets.get(i); + names[i] = br.getName(); + } + + return names; + } + + + /** + * Package protected function which gets the real internal sheet index + * based upon the external sheet reference. This is used for extern sheet + * references which are specified in formulas + * + * @param index the external sheet reference + * @return the actual sheet index + */ + public int getExternalSheetIndex(int index) { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) { + return index; + } + + Assert.verify(externSheet != null); + + int firstTab = externSheet.getFirstTabIndex(index); + + return firstTab; + } + + /** + * Package protected function which gets the real internal sheet index + * based upon the external sheet reference. This is used for extern sheet + * references which are specified in formulas + * + * @param index the external sheet reference + * @return the actual sheet index + */ + public int getLastExternalSheetIndex(int index) { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) { + return index; + } + + Assert.verify(externSheet != null); + + int lastTab = externSheet.getLastTabIndex(index); + + return lastTab; + } + + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getExternalSheetName(int index) { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(index); + + return br.getName(); + } + + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int firstTab = externSheet.getFirstTabIndex(index); + int lastTab = externSheet.getLastTabIndex(index); + String firstTabName = ""; + String lastTabName = ""; + + if (sr.getType() == SupbookRecord.INTERNAL) { + // It's an internal reference - get the name from the boundsheets list + if (firstTab == 65535) { + firstTabName = "#REF"; + } else { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(firstTab); + firstTabName = br.getName(); + } + + if (lastTab == 65535) { + lastTabName = "#REF"; + } else { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(lastTab); + lastTabName = br.getName(); + } + + String sheetName = (firstTab == lastTab) ? firstTabName : + firstTabName + ':' + lastTabName; + + // if the sheet name contains apostrophes then escape them + sheetName = sheetName.indexOf('\'') == -1 ? sheetName : + StringHelper.replace(sheetName, "'", "''"); + + + // if the sheet name contains spaces, then enclose in quotes + return sheetName.indexOf(' ') == -1 ? sheetName : + '\'' + sheetName + '\''; + } else if (sr.getType() == SupbookRecord.EXTERNAL) { + // External reference - get the sheet name from the supbook record + StringBuffer sb = new StringBuffer(); + java.io.File fl = new java.io.File(sr.getFileName()); + sb.append("'"); + sb.append(fl.getAbsolutePath()); + sb.append("["); + sb.append(fl.getName()); + sb.append("]"); + sb.append((firstTab == 65535) ? "#REF" : sr.getSheetName(firstTab)); + if (lastTab != firstTab) { + sb.append(sr.getSheetName(lastTab)); + } + sb.append("'"); + return sb.toString(); + } + + // An unknown supbook - return unkown + logger.warn("Unknown Supbook 3"); + return "[UNKNOWN]"; + } + + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getLastExternalSheetName(int index) { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(index); + + return br.getName(); + } + + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int lastTab = externSheet.getLastTabIndex(index); + + if (sr.getType() == SupbookRecord.INTERNAL) { + // It's an internal reference - get the name from the boundsheets list + if (lastTab == 65535) { + return "#REF"; + } else { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(lastTab); + return br.getName(); + } + } else if (sr.getType() == SupbookRecord.EXTERNAL) { + // External reference - get the sheet name from the supbook record + StringBuffer sb = new StringBuffer(); + java.io.File fl = new java.io.File(sr.getFileName()); + sb.append("'"); + sb.append(fl.getAbsolutePath()); + sb.append("["); + sb.append(fl.getName()); + sb.append("]"); + sb.append((lastTab == 65535) ? "#REF" : sr.getSheetName(lastTab)); + sb.append("'"); + return sb.toString(); + } + + // An unknown supbook - return unkown + logger.warn("Unknown Supbook 4"); + return "[UNKNOWN]"; + } + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public int getNumberOfSheets() { + return sheets.size(); + } + + /** + * Closes this workbook, and frees makes any memory allocated available + * for garbage collection + */ + public void close() { + if (lastSheet != null) { + lastSheet.clear(); + } + excelFile.clear(); + + if (!settings.getGCDisabled()) { + System.gc(); + } + } + + /** + * Adds the sheet to the end of the array + * + * @param s the sheet to add + */ + final void addSheet(Sheet s) { + sheets.add(s); + } + + /** + * Does the hard work of building up the object graph from the excel bytes + * + * @throws BiffException + * @throws PasswordException if the workbook is password protected + */ + protected void parse() throws BiffException, PasswordException { + Record r = null; + + BOFRecord bof = new BOFRecord(excelFile.next()); + workbookBof = bof; + bofs++; + + if (!bof.isBiff8() && !bof.isBiff7()) { + throw new BiffException(BiffException.unrecognizedBiffVersion); + } + + if (!bof.isWorkbookGlobals()) { + throw new BiffException(BiffException.expectedGlobals); + } + ArrayList continueRecords = new ArrayList(); + ArrayList localNames = new ArrayList(); + nameTable = new ArrayList(); + addInFunctions = new ArrayList(); + + // Skip to the first worksheet + while (bofs == 1) { + r = excelFile.next(); + + if (r.getType() == Type.SST) { + continueRecords.clear(); + Record nextrec = excelFile.peek(); + while (nextrec.getType() == Type.CONTINUE) { + continueRecords.add(excelFile.next()); + nextrec = excelFile.peek(); + } + + // cast the array + Record[] records = new Record[continueRecords.size()]; + records = (Record[]) continueRecords.toArray(records); + + sharedStrings = new SSTRecord(r, records, settings); + } else if (r.getType() == Type.FILEPASS) { + throw new PasswordException(); + } else if (r.getType() == Type.NAME) { + NameRecord nr = null; + + if (bof.isBiff8()) { + nr = new NameRecord(r, settings, nameTable.size()); + + } else { + nr = new NameRecord(r, settings, nameTable.size(), + NameRecord.biff7); + } + + // Add all local and global names to the name table in order to + // preserve the indexing + nameTable.add(nr); + + if (nr.isGlobal()) { + namedRecords.put(nr.getName(), nr); + } else { + localNames.add(nr); + } + } else if (r.getType() == Type.FONT) { + FontRecord fr = null; + + if (bof.isBiff8()) { + fr = new FontRecord(r, settings); + } else { + fr = new FontRecord(r, settings, FontRecord.biff7); + } + fonts.addFont(fr); + } else if (r.getType() == Type.PALETTE) { + PaletteRecord palette = new PaletteRecord(r); + formattingRecords.setPalette(palette); + } else if (r.getType() == Type.NINETEENFOUR) { + NineteenFourRecord nr = new NineteenFourRecord(r); + nineteenFour = nr.is1904(); + } else if (r.getType() == Type.FORMAT) { + FormatRecord fr = null; + if (bof.isBiff8()) { + fr = new FormatRecord(r, settings, FormatRecord.biff8); + } else { + fr = new FormatRecord(r, settings, FormatRecord.biff7); + } + try { + formattingRecords.addFormat(fr); + } catch (NumFormatRecordsException e) { + // This should not happen. Bomb out + Assert.verify(false, e.getMessage()); + } + } else if (r.getType() == Type.XF) { + XFRecord xfr = null; + if (bof.isBiff8()) { + xfr = new XFRecord(r, settings, XFRecord.biff8); + } else { + xfr = new XFRecord(r, settings, XFRecord.biff7); + } + + try { + formattingRecords.addStyle(xfr); + } catch (NumFormatRecordsException e) { + // This should not happen. Bomb out + Assert.verify(false, e.getMessage()); + } + } else if (r.getType() == Type.BOUNDSHEET) { + BoundsheetRecord br = null; + + if (bof.isBiff8()) { + br = new BoundsheetRecord(r, settings); + } else { + br = new BoundsheetRecord(r, BoundsheetRecord.biff7); + } + + if (br.isSheet()) { + boundsheets.add(br); + } else if (br.isChart() && !settings.getDrawingsDisabled()) { + boundsheets.add(br); + } + } else if (r.getType() == Type.EXTERNSHEET) { + if (bof.isBiff8()) { + externSheet = new ExternalSheetRecord(r, settings); + } else { + externSheet = new ExternalSheetRecord(r, settings, + ExternalSheetRecord.biff7); + } + } else if (r.getType() == Type.XCT) { + XCTRecord xctr = new XCTRecord(r); + xctRecords.add(xctr); + } else if (r.getType() == Type.CODEPAGE) { + CodepageRecord cr = new CodepageRecord(r); + settings.setCharacterSet(cr.getCharacterSet()); + } else if (r.getType() == Type.SUPBOOK) { + Record nextrec = excelFile.peek(); + while (nextrec.getType() == Type.CONTINUE) { + r.addContinueRecord(excelFile.next()); + nextrec = excelFile.peek(); + } + + SupbookRecord sr = new SupbookRecord(r, settings); + supbooks.add(sr); + } else if (r.getType() == Type.EXTERNNAME) { + ExternalNameRecord enr = new ExternalNameRecord(r, settings); + + if (enr.isAddInFunction()) { + addInFunctions.add(enr.getName()); + } + } else if (r.getType() == Type.PROTECT) { + ProtectRecord pr = new ProtectRecord(r); + wbProtected = pr.isProtected(); + } else if (r.getType() == Type.OBJPROJ) { + containsMacros = true; + } else if (r.getType() == Type.COUNTRY) { + countryRecord = new CountryRecord(r); + } else if (r.getType() == Type.MSODRAWINGGROUP) { + if (!settings.getDrawingsDisabled()) { + msoDrawingGroup = new MsoDrawingGroupRecord(r); + + if (drawingGroup == null) { + drawingGroup = new DrawingGroup(Origin.READ); + } + + drawingGroup.add(msoDrawingGroup); + + Record nextrec = excelFile.peek(); + while (nextrec.getType() == Type.CONTINUE) { + drawingGroup.add(excelFile.next()); + nextrec = excelFile.peek(); + } + } + } else if (r.getType() == Type.BUTTONPROPERTYSET) { + buttonPropertySet = new ButtonPropertySetRecord(r); + } else if (r.getType() == Type.EOF) { + bofs--; + } else if (r.getType() == Type.REFRESHALL) { + RefreshAllRecord rfm = new RefreshAllRecord(r); + settings.setRefreshAll(rfm.getRefreshAll()); + } else if (r.getType() == Type.TEMPLATE) { + TemplateRecord rfm = new TemplateRecord(r); + settings.setTemplate(rfm.getTemplate()); + } else if (r.getType() == Type.EXCEL9FILE) { + Excel9FileRecord e9f = new Excel9FileRecord(r); + settings.setExcel9File(e9f.getExcel9File()); + } else if (r.getType() == Type.WINDOWPROTECT) { + WindowProtectedRecord winp = new WindowProtectedRecord(r); + settings.setWindowProtected(winp.getWindowProtected()); + } else if (r.getType() == Type.HIDEOBJ) { + HideobjRecord hobj = new HideobjRecord(r); + settings.setHideobj(hobj.getHideMode()); + } else if (r.getType() == Type.WRITEACCESS) { + WriteAccessRecord war = new WriteAccessRecord(r, bof.isBiff8(), + settings); + settings.setWriteAccess(war.getWriteAccess()); + } else { + // logger.info("Unsupported record type: " + + // Integer.toHexString(r.getCode())+"h"); + } + } + + bof = null; + if (excelFile.hasNext()) { + r = excelFile.next(); + + if (r.getType() == Type.BOF) { + bof = new BOFRecord(r); + } + } + + // Only get sheets for which there is a corresponding Boundsheet record + while (bof != null && getNumberOfSheets() < boundsheets.size()) { + if (!bof.isBiff8() && !bof.isBiff7()) { + throw new BiffException(BiffException.unrecognizedBiffVersion); + } + + if (bof.isWorksheet()) { + // Read the sheet in + SheetImpl s = new SheetImpl(excelFile, + sharedStrings, + formattingRecords, + bof, + workbookBof, + nineteenFour, + this); + + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get + (getNumberOfSheets()); + s.setName(br.getName()); + s.setHidden(br.isHidden()); + addSheet(s); + } else if (bof.isChart()) { + // Read the sheet in + SheetImpl s = new SheetImpl(excelFile, + sharedStrings, + formattingRecords, + bof, + workbookBof, + nineteenFour, + this); + + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get + (getNumberOfSheets()); + s.setName(br.getName()); + s.setHidden(br.isHidden()); + addSheet(s); + } else { + logger.warn("BOF is unrecognized"); + + + while (excelFile.hasNext() && r.getType() != Type.EOF) { + r = excelFile.next(); + } + } + + // The next record will normally be a BOF or empty padding until + // the end of the block is reached. In exceptionally unlucky cases, + // the last EOF will coincide with a block division, so we have to + // check there is more data to retrieve. + // Thanks to liamg for spotting this + bof = null; + if (excelFile.hasNext()) { + r = excelFile.next(); + + if (r.getType() == Type.BOF) { + bof = new BOFRecord(r); + } + } + } + + // Add all the local names to the specific sheets + for (Iterator it = localNames.iterator(); it.hasNext(); ) { + NameRecord nr = (NameRecord) it.next(); + + if (nr.getBuiltInName() == null) { + logger.warn("Usage of a local non-builtin name"); + } else if (nr.getBuiltInName() == BuiltInName.PRINT_AREA || + nr.getBuiltInName() == BuiltInName.PRINT_TITLES) { + // appears to use the internal tab number rather than the + // external sheet index + SheetImpl s = (SheetImpl) sheets.get(nr.getSheetRef() - 1); + s.addLocalName(nr); + } + } + } + + /** + * Accessor for the formattingRecords, used by the WritableWorkbook + * when creating a copy of this + * + * @return the formatting records + */ + public FormattingRecords getFormattingRecords() { + return formattingRecords; + } + + /** + * Accessor for the externSheet, used by the WritableWorkbook + * when creating a copy of this + * + * @return the external sheet record + */ + public ExternalSheetRecord getExternalSheetRecord() { + return externSheet; + } + + /** + * Accessor for the MsoDrawingGroup, used by the WritableWorkbook + * when creating a copy of this + * + * @return the Mso Drawing Group record + */ + public MsoDrawingGroupRecord getMsoDrawingGroupRecord() { + return msoDrawingGroup; + } + + /** + * Accessor for the supbook records, used by the WritableWorkbook + * when creating a copy of this + * + * @return the supbook records + */ + public SupbookRecord[] getSupbookRecords() { + SupbookRecord[] sr = new SupbookRecord[supbooks.size()]; + return (SupbookRecord[]) supbooks.toArray(sr); + } + + /** + * Accessor for the name records. Used by the WritableWorkbook when + * creating a copy of this + * + * @return the array of names + */ + public NameRecord[] getNameRecords() { + NameRecord[] na = new NameRecord[nameTable.size()]; + return (NameRecord[]) nameTable.toArray(na); + } + + /** + * Accessor for the fonts, used by the WritableWorkbook + * when creating a copy of this + * + * @return the fonts used in this workbook + */ + public Fonts getFonts() { + return fonts; + } + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public Cell getCell(String loc) { + Sheet s = getSheet(CellReferenceHelper.getSheet(loc)); + return s.getCell(loc); + } + + /** + * Gets the named cell from this workbook. If the name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be found, null is returned + * + * @param name the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public Cell findCellByName(String name) { + NameRecord nr = (NameRecord) namedRecords.get(name); + + if (nr == null) { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + // Go and retrieve the first cell in the first range + Sheet s = getSheet(getExternalSheetIndex(ranges[0].getExternalSheet())); + int col = ranges[0].getFirstColumn(); + int row = ranges[0].getFirstRow(); + + // If the sheet boundaries fall short of the named cell, then return + // an empty cell to stop an exception being thrown + if (col > s.getColumns() || row > s.getRows()) { + return new EmptyCell(col, row); + } + + Cell cell = s.getCell(col, row); + + return cell; + } + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param name the name to find + * @return the range of cells + */ + public Range[] findByName(String name) { + NameRecord nr = (NameRecord) namedRecords.get(name); + + if (nr == null) { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + Range[] cellRanges = new Range[ranges.length]; + + for (int i = 0; i < ranges.length; i++) { + cellRanges[i] = new RangeImpl + (this, + getExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getFirstColumn(), + ranges[i].getFirstRow(), + getLastExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getLastColumn(), + ranges[i].getLastRow()); + } + + return cellRanges; + } + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public String[] getRangeNames() { + Object[] keys = namedRecords.keySet().toArray(); + String[] names = new String[keys.length]; + System.arraycopy(keys, 0, names, 0, keys.length); + + return names; + } + + /** + * Method used when parsing formulas to make sure we are trying + * to parse a supported biff version + * + * @return the BOF record + */ + public BOFRecord getWorkbookBof() { + return workbookBof; + } + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + */ + public boolean isProtected() { + return wbProtected; + } + + /** + * Accessor for the settings + * + * @return the workbook settings + */ + public WorkbookSettings getSettings() { + return settings; + } + + /** + * Accessor/implementation method for the external sheet reference + * + * @param sheetName the sheet name to look for + * @return the external sheet index + */ + public int getExternalSheetIndex(String sheetName) { + return 0; + } + + /** + * Accessor/implementation method for the external sheet reference + * + * @param sheetName the sheet name to look for + * @return the external sheet index + */ + public int getLastExternalSheetIndex(String sheetName) { + return 0; + } + + /** + * Gets the name at the specified index + * + * @param index the index into the name table + * @return the name of the cell + * @throws NameRangeException + */ + public String getName(int index) throws NameRangeException { + // Assert.verify(index >= 0 && index < nameTable.size()); + if (index < 0 || index >= nameTable.size()) { + throw new NameRangeException(); + } + return ((NameRecord) nameTable.get(index)).getName(); + } + + /** + * Gets the index of the name record for the name + * + * @param name the name to search for + * @return the index in the name table + */ + public int getNameIndex(String name) { + NameRecord nr = (NameRecord) namedRecords.get(name); + + return nr != null ? nr.getIndex() : 0; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() { + return drawingGroup; + } + + /** + * Accessor for the CompoundFile. For this feature to return non-null + * value, the propertySets feature in WorkbookSettings must be enabled + * and the workbook must contain additional property sets. This + * method is used during the workbook copy + * + * @return the base compound file if it contains additional data items + * and property sets are enabled + */ + public CompoundFile getCompoundFile() { + return excelFile.getCompoundFile(); + } + + /** + * Accessor for the containsMacros + * + * @return TRUE if this workbook contains macros, FALSE otherwise + */ + public boolean containsMacros() { + return containsMacros; + } + + /** + * Accessor for the button property set, used during copying + * + * @return the button property set + */ + public ButtonPropertySetRecord getButtonPropertySet() { + return buttonPropertySet; + } + + /** + * Accessor for the country record, using during copying + * + * @return the country record read in + */ + public CountryRecord getCountryRecord() { + return countryRecord; + } + + /** + * Accessor for addin function names + * + * @return list of add in function names + */ + public String[] getAddInFunctionNames() { + String[] addins = new String[0]; + return (String[]) addInFunctions.toArray(addins); + } + + /** + * Gets the sheet index in this workbook. Used when importing a sheet + * + * @param sheet the sheet + * @return the 0-based sheet index, or -1 if it is not found + */ + public int getIndex(Sheet sheet) { + String name = sheet.getName(); + int index = -1; + int pos = 0; + + for (Iterator i = boundsheets.iterator(); i.hasNext() && index == -1; ) { + BoundsheetRecord br = (BoundsheetRecord) i.next(); + + if (br.getName().equals(name)) { + index = pos; + } else { + pos++; + } + } + + return index; + } + + public XCTRecord[] getXCTRecords() { + XCTRecord[] xctr = new XCTRecord[0]; + return (XCTRecord[]) xctRecords.toArray(xctr); + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/WriteAccessRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/WriteAccessRecord.java new file mode 100755 index 0000000..16e3a55 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/WriteAccessRecord.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.biff.Type; + +/** + * A write access record + */ +class WriteAccessRecord extends RecordData { + /** + * The write access user name + */ + private final String wauser; + + /** + * Constructor + * + * @param t the raw bytes + * @param isBiff8 Is record BIFF8 (else BIFF7) + */ + public WriteAccessRecord(Record t, boolean isBiff8, WorkbookSettings ws) { + super(Type.WRITEACCESS); + + byte[] data = t.getData(); + if (isBiff8) { + wauser = StringHelper.getUnicodeString(data, 112 / 2, 0); + } else { + // BIFF7 does not use unicode encoding in string + int length = data[1]; + wauser = StringHelper.getString(data, length, 1, ws); + } + } + + /** + * Gets the binary data for output to file + * + * @return write access user name + */ + public String getWriteAccess() { + return wauser; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/Alignment.java b/datastructures-xslx/src/main/java/jxl/write/Alignment.java new file mode 100755 index 0000000..84339fc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Alignment.java @@ -0,0 +1,37 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class which contains the various alignments for data within a + * cell + * + * @deprecated Repackaged as jxl.Alignment. This version is retained + * for backwards compatibility + */ +public final class Alignment extends jxl.format.Alignment { + /** + * Private constructor + */ + private Alignment() { + super(0, null); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/Blank.java b/datastructures-xslx/src/main/java/jxl/write/Blank.java new file mode 100755 index 0000000..928a1ca --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Blank.java @@ -0,0 +1,89 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.Cell; +import jxl.format.CellFormat; +import jxl.write.biff.BlankRecord; + +/** + * A blank cell. Despite not having any contents, it may contain + * formatting information. Such cells are typically used when creating + * templates + */ +public class Blank extends BlankRecord implements WritableCell { + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates + * + * @param c the column + * @param r the row + */ + public Blank(int c, int r) { + super(c, r); + } + + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates + * in the manner specified by the CellFormat parameter + * + * @param c the column + * @param r the row + * @param st the cell format + */ + public Blank(int c, int r, CellFormat st) { + super(c, r, st); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet being read in + * + * @param lc the cell to copy + */ + public Blank(Cell lc) { + super(lc); + } + + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param b the balnk cell to copy + */ + protected Blank(int col, int row, Blank b) { + super(col, row, b); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Blank(col, row, this); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/BoldStyle.java b/datastructures-xslx/src/main/java/jxl/write/BoldStyle.java new file mode 100755 index 0000000..a4f8a2d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/BoldStyle.java @@ -0,0 +1,35 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class containing the various bold styles for data + */ +public final class BoldStyle extends jxl.format.BoldStyle { + /** + * Constructor + * + * @param val a dummy value + */ + private BoldStyle(int val) { + super(0, ""); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/Boolean.java b/datastructures-xslx/src/main/java/jxl/write/Boolean.java new file mode 100755 index 0000000..c390f43 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Boolean.java @@ -0,0 +1,97 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.BooleanCell; +import jxl.format.CellFormat; +import jxl.write.biff.BooleanRecord; + +/** + * A cell, created by user applications, which contains a boolean (or + * in some cases an error) value + */ +public class Boolean extends BooleanRecord implements WritableCell, BooleanCell { + /** + * Constructs a boolean value, which, when added to a spreadsheet, will + * display the specified value at the column/row position indicated. + * + * @param c the column + * @param r the row + * @param val the value + */ + public Boolean(int c, int r, boolean val) { + super(c, r, val); + } + + /** + * Constructs a boolean, which, when added to a spreadsheet, will display the + * specified value at the column/row position with the specified CellFormat. + * The CellFormat may specify font information + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + public Boolean(int c, int r, boolean val, CellFormat st) { + super(c, r, val, st); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet that has been read in + * + * @param nc the cell to copy + */ + public Boolean(BooleanCell nc) { + super(nc); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param b the cell to copy + */ + protected Boolean(int col, int row, Boolean b) { + super(col, row, b); + } + + /** + * Sets the boolean value for this cell + * + * @param val the value + */ + public void setValue(boolean val) { + super.setValue(val); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Boolean(col, row, this); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/Border.java b/datastructures-xslx/src/main/java/jxl/write/Border.java new file mode 100755 index 0000000..b2f8481 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Border.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * @deprecated repackaged as jxl.format.Border + */ +public final class Border extends jxl.format.Border { + /** + * Constructor + */ + private Border() { + super(null); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/BorderLineStyle.java b/datastructures-xslx/src/main/java/jxl/write/BorderLineStyle.java new file mode 100755 index 0000000..c282d7e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/BorderLineStyle.java @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * @deprecated Repackaged as jxl.format.BorderLineStyle + */ +public final class BorderLineStyle extends jxl.format.BorderLineStyle { + /** + * Constructor + */ + private BorderLineStyle() { + super(0, null); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/Colour.java b/datastructures-xslx/src/main/java/jxl/write/Colour.java new file mode 100755 index 0000000..95dd57f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Colour.java @@ -0,0 +1,47 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class which contains the various colours available within + * the standard Excel colour palette + * + * @deprecated This has been repackaged as jxl.format.Colour + */ +public final class Colour extends jxl.format.Colour { + /** + * Constructor + * This is currently just a placeholder for backwards compatibility + */ + private Colour() { + super(0, null, 0, 0, 0); + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/DateFormat.java b/datastructures-xslx/src/main/java/jxl/write/DateFormat.java new file mode 100755 index 0000000..7cb7c43 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/DateFormat.java @@ -0,0 +1,49 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.text.SimpleDateFormat; +import jxl.biff.DisplayFormat; +import jxl.write.biff.DateFormatRecord; + +/** + * A custom user defined number format which may be instantiated within user + * applications in order to present date and time values in the appropriate + * format. + * The string format used to create a DateFormat adheres to the standard + * java specification, and JExcelApi makes the necessary modifications so + * that it is rendered as its nearest equivalent in Excel. + * Once created, this may be used within a CellFormat object, which in turn + * is a parameter passed to the constructor of the DateTime cell + */ +public class DateFormat extends DateFormatRecord implements DisplayFormat { + /** + * Constructor. The date format that is passed should comply to the standard + * Java date formatting conventions + * + * @param format the date format + */ + public DateFormat(String format) { + super(format); + + // Verify that the format is valid + SimpleDateFormat df = new SimpleDateFormat(format); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/DateFormats.java b/datastructures-xslx/src/main/java/jxl/write/DateFormats.java new file mode 100755 index 0000000..4a029b6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/DateFormats.java @@ -0,0 +1,200 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.biff.DisplayFormat; + +/** + * Static class which contains Excels predefined Date formats + */ +public final class DateFormats { + /** + * The default format. This is equivalent to a date format of "M/d/yy" + */ + public static final DisplayFormat FORMAT1 = + new BuiltInFormat(0x0e, "M/d/yy"); + + // The available built in date formats + /** + * The default format. This is equivalent to a date format of "M/d/yy" + */ + public static final DisplayFormat DEFAULT = FORMAT1; + /** + * Equivalent to a date format of "d-MMM-yy" + */ + public static final DisplayFormat FORMAT2 = + new BuiltInFormat(0xf, "d-MMM-yy"); + /** + * Equivalent to a date format of "d-MMM" + */ + public static final DisplayFormat FORMAT3 = + new BuiltInFormat(0x10, "d-MMM"); + /** + * Equivalent to a date format of "MMM-yy" + */ + public static final DisplayFormat FORMAT4 = + new BuiltInFormat(0x11, "MMM-yy"); + /** + * Equivalent to a date format of "h:mm a" + */ + public static final DisplayFormat FORMAT5 = + new BuiltInFormat(0x12, "h:mm a"); + /** + * Equivalent to a date format of "h:mm:ss a" + */ + public static final DisplayFormat FORMAT6 = + new BuiltInFormat(0x13, "h:mm:ss a"); + /** + * Equivalent to a date format of "H:mm" + */ + public static final DisplayFormat FORMAT7 = + new BuiltInFormat(0x14, "H:mm"); + /** + * Equivalent to a date format of "H:mm:ss" + */ + public static final DisplayFormat FORMAT8 = + new BuiltInFormat(0x15, "H:mm:ss"); + /** + * Equivalent to a date format of "M/d/yy H:mm" + */ + public static final DisplayFormat FORMAT9 = + new BuiltInFormat(0x16, "M/d/yy H:mm"); + /** + * Equivalent to a date format of "mm:ss" + */ + public static final DisplayFormat FORMAT10 = + new BuiltInFormat(0x2d, "mm:ss"); + /** + * Equivalent to a date format of "H:mm:ss" + */ + public static final DisplayFormat FORMAT11 = + new BuiltInFormat(0x2e, "H:mm:ss"); + /** + * Equivalent to a date format of "mm:ss.S" + */ + public static final DisplayFormat FORMAT12 = + new BuiltInFormat(0x2f, "H:mm:ss"); + + /** + * Inner class which holds the format index + */ + private static class BuiltInFormat implements DisplayFormat { + /** + * The index of this date format + */ + private final int index; + /** + * The excel format + */ + private final String formatString; + + /** + * Constructor + * + * @param i the index + * @param s the format string + */ + public BuiltInFormat(int i, String s) { + index = i; + formatString = s; + } + + /** + * Gets the format index + * + * @return the format index + */ + public int getFormatIndex() { + return index; + } + + /** + * Interface method which determines whether the index has been set. For + * built ins this is always true + * + * @return TRUE, since this is a built in format + */ + public boolean isInitialized() { + return true; + } + + /** + * Initialize this format with the specified position. Since this is a + * built in format, this is always initialized, so this method body for + * this is empty + * + * @param pos the position in the array + */ + public void initialize(int pos) { + } + + /** + * Determines whether this format is a built in format + * + * @return TRUE, since this is a built in format + */ + public boolean isBuiltIn() { + return true; + } + + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString() { + return formatString; + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the two objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof BuiltInFormat)) { + return false; + } + + BuiltInFormat bif = (BuiltInFormat) o; + + return index == bif.index; + } + + /** + * Hash code implementation + * + * @return the hash code + */ + public int hashCode() { + return index; + } + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/DateTime.java b/datastructures-xslx/src/main/java/jxl/write/DateTime.java new file mode 100755 index 0000000..89c6220 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/DateTime.java @@ -0,0 +1,167 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.util.Date; +import jxl.DateCell; +import jxl.format.CellFormat; +import jxl.write.biff.DateRecord; + +/** + * A Date which may be created on the fly by a user application and added to a + * spreadsheet + *

+ * NOTE: By default, all dates will have local timezone information added to + * their UTC value. If this is not desired (eg. if the date entered + * represents an interval eg. 9.83s for the 100m world record, then use + * the overloaded constructor which indicate that the date passed in was + * created under the GMT timezone. It is important that when the date + * was created, an instruction like + * Calendar.setTimeZone(TimeZone.getTimeZone("GMT")) + * was made prior to that + */ +public class DateTime extends DateRecord implements WritableCell, DateCell { + /** + * Instance variable for dummy variable overload + */ + public static final GMTDate GMT = new GMTDate(); + + /** + * Constructor. The date will be displayed with date and time components + * using the default date format + * + * @param c the column + * @param r the row + * @param d the date + */ + public DateTime(int c, int r, Date d) { + super(c, r, d); + } + + /** + * Constructor, which adjusts the specified date to take timezone + * considerations into account. The date passed in will be displayed with + * date and time components using the default date format + * + * @param c the column + * @param r the row + * @param d the date + * @param a dummy overload + */ + public DateTime(int c, int r, Date d, GMTDate a) { + super(c, r, d, a); + } + + /** + * Constructor which takes the format for this cell + * + * @param c the column + * @param r the row + * @param st the format + * @param d the date + */ + public DateTime(int c, int r, Date d, CellFormat st) { + super(c, r, d, st); + } + + /** + * Constructor, which adjusts the specified date to take timezone + * considerations into account + * + * @param c the column + * @param r the row + * @param d the date + * @param st the cell format + * @param a the cummy overload + */ + public DateTime(int c, int r, Date d, CellFormat st, GMTDate a) { + super(c, r, d, st, a); + } + + /** + * Constructor which takes the format for the cell and an indicator + * as to whether this cell is a full date time or purely just a time + * eg. if the spreadsheet is to contain the world record for 100m, then the + * value would be 9.83s, which would be indicated as just a time + * + * @param c the column + * @param r the row + * @param st the style + * @param tim flag indicating that this represents a time + * @param d the date + */ + public DateTime(int c, int r, Date d, CellFormat st, boolean tim) { + super(c, r, d, st, tim); + } + + /** + * A constructor called by the worksheet when creating a writable version + * of a spreadsheet that has been read in + * + * @param dc the date to copy + */ + public DateTime(DateCell dc) { + super(dc); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param dt the date to copy + */ + protected DateTime(int col, int row, DateTime dt) { + super(col, row, dt); + } + + + /** + * Sets the date for this cell + * + * @param d the date + */ + public void setDate(Date d) { + super.setDate(d); + } + + /** + * Sets the date for this cell, performing the necessary timezone adjustments + * + * @param d the date + * @param a the dummy overload + */ + public void setDate(Date d, GMTDate a) { + super.setDate(d, a); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new DateTime(col, row, this); + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/Font.java b/datastructures-xslx/src/main/java/jxl/write/Font.java new file mode 100755 index 0000000..5fdccba --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Font.java @@ -0,0 +1,212 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.format.Colour; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; + +/** + * A class which is instantiated when the user application wishes to specify + * the font for a particular cell + * + * @deprecated Renamed to writable font + */ +public class Font extends WritableFont { + /** + * Objects created with this font name will be rendered within Excel as ARIAL + * fonts + * + * @deprecated + */ + public static final FontName ARIAL = WritableFont.ARIAL; + /** + * Objects created with this font name will be rendered within Excel as TIMES + * fonts + * + * @deprecated + */ + public static final FontName TIMES = WritableFont.TIMES; + + // The bold styles + + /** + * Indicates that this font should not be presented as bold + * + * @deprecated + */ + public static final BoldStyle NO_BOLD = WritableFont.NO_BOLD; + /** + * Indicates that this font should be presented in a BOLD style + * + * @deprecated + */ + public static final BoldStyle BOLD = WritableFont.BOLD; + + // The underline styles + /** + * @deprecated + */ + public static final UnderlineStyle NO_UNDERLINE = + UnderlineStyle.NO_UNDERLINE; + + /** + * @deprecated + */ + public static final UnderlineStyle SINGLE = UnderlineStyle.SINGLE; + + /** + * @deprecated + */ + public static final UnderlineStyle DOUBLE = UnderlineStyle.DOUBLE; + + /** + * @deprecated + */ + public static final UnderlineStyle SINGLE_ACCOUNTING = + UnderlineStyle.SINGLE_ACCOUNTING; + + /** + * @deprecated + */ + public static final UnderlineStyle DOUBLE_ACCOUNTING = + UnderlineStyle.DOUBLE_ACCOUNTING; + + // The script styles + public static final ScriptStyle NORMAL_SCRIPT = ScriptStyle.NORMAL_SCRIPT; + public static final ScriptStyle SUPERSCRIPT = ScriptStyle.SUPERSCRIPT; + public static final ScriptStyle SUBSCRIPT = ScriptStyle.SUBSCRIPT; + + /** + * Creates a default font, vanilla font of the specified face and with + * default point size. + * + * @param fn the font name + * @deprecated Use jxl.write.WritableFont + */ + public Font(FontName fn) { + super(fn); + } + + /** + * Constructs of font of the specified face and of size given by the + * specified point size + * + * @param ps the point size + * @param fn the font name + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, int ps) { + super(fn, ps); + } + + /** + * Creates a font of the specified face, point size and bold style + * + * @param ps the point size + * @param bs the bold style + * @param fn the font name + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, int ps, BoldStyle bs) { + super(fn, ps, bs); + } + + /** + * Creates a font of the specified face, point size, bold weight and + * italicised option. + * + * @param ps the point size + * @param bs the bold style + * @param italic italic flag + * @param fn the font name + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, int ps, BoldStyle bs, boolean italic) { + super(fn, ps, bs, italic); + } + + /** + * Creates a font of the specified face, point size, bold weight, + * italicisation and underline style + * + * @param ps the point size + * @param bs the bold style + * @param us underscore flag + * @param fn font name + * @param it italic flag + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us) { + super(fn, ps, bs, it, us); + } + + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style and colour + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it italic flag + * @param c the colour + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c) { + super(fn, ps, bs, it, us, c); + } + + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style, colour, and script + * style (superscript/subscript) + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it the italic flag + * @param c the colour + * @param ss the script style + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c, + ScriptStyle ss) { + super(fn, ps, bs, it, us, c, ss); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/Formula.java b/datastructures-xslx/src/main/java/jxl/write/Formula.java new file mode 100755 index 0000000..3456356 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Formula.java @@ -0,0 +1,73 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.format.CellFormat; +import jxl.write.biff.FormulaRecord; + +/** + * A cell, created by user applications, which contains a numerical value + */ +public class Formula extends FormulaRecord implements WritableCell { + /** + * Constructs the formula + * + * @param c the column + * @param r the row + * @param form the formula + */ + public Formula(int c, int r, String form) { + super(c, r, form); + } + + /** + * Constructs a formula + * + * @param c the column + * @param r the row + * @param form the formula + * @param st the cell style + */ + public Formula(int c, int r, String form, CellFormat st) { + super(c, r, form, st); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param f the record to copy + */ + protected Formula(int c, int r, Formula f) { + super(c, r, f); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Formula(col, row, this); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/Label.java b/datastructures-xslx/src/main/java/jxl/write/Label.java new file mode 100755 index 0000000..b1eaffa --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Label.java @@ -0,0 +1,97 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.LabelCell; +import jxl.format.CellFormat; +import jxl.write.biff.LabelRecord; + +/** + * A cell containing text which may be created by user applications + */ +public class Label extends LabelRecord implements WritableCell, LabelCell { + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates and will contain the specified text + * + * @param c the column + * @param cont the text + * @param r the row + */ + public Label(int c, int r, String cont) { + super(c, r, cont); + } + + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates and will present the specified text + * in the manner specified by the CellFormat parameter + * + * @param c the column + * @param cont the data + * @param r the row + * @param st the cell format + */ + public Label(int c, int r, String cont, CellFormat st) { + super(c, r, cont, st); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param l the label to copy + */ + protected Label(int col, int row, Label l) { + super(col, row, l); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet being read in + * + * @param lc the label to copy + */ + public Label(LabelCell lc) { + super(lc); + } + + /** + * Sets the string contents of this cell + * + * @param s the new data + */ + public void setString(String s) { + super.setString(s); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Label(col, row, this); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/Number.java b/datastructures-xslx/src/main/java/jxl/write/Number.java new file mode 100755 index 0000000..5ee2300 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Number.java @@ -0,0 +1,99 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.NumberCell; +import jxl.format.CellFormat; +import jxl.write.biff.NumberRecord; + +/** + * A cell, created by user applications, which contains a numerical value + */ +public class Number extends NumberRecord implements WritableCell, NumberCell { + /** + * Constructs a number, which, when added to a spreadsheet, will display the + * specified value at the column/row position indicated. By default, the + * cell will display with an accuracy of 3 decimal places + * + * @param c the column + * @param r the row + * @param val the value + */ + public Number(int c, int r, double val) { + super(c, r, val); + } + + /** + * Constructs a number, which, when added to a spreadsheet, will display the + * specified value at the column/row position with the specified CellFormat. + * The CellFormat may specify font information and number format information + * such as the number of decimal places + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + public Number(int c, int r, double val, CellFormat st) { + super(c, r, val, st); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet that has been read in + * + * @param nc the cell to copy + */ + public Number(NumberCell nc) { + super(nc); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param n the number to copy + */ + protected Number(int col, int row, Number n) { + super(col, row, n); + } + + /** + * Sets the numerical value for this cell + * + * @param val the value + */ + public void setValue(double val) { + super.setValue(val); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Number(col, row, this); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/NumberFormat.java b/datastructures-xslx/src/main/java/jxl/write/NumberFormat.java new file mode 100755 index 0000000..c8eaf67 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/NumberFormat.java @@ -0,0 +1,135 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.text.DecimalFormat; +import jxl.biff.DisplayFormat; +import jxl.write.biff.NumberFormatRecord; + +/** + * A custom user defined number format, which may be instantiated within user + * applications in order to present numerical values to the appropriate level + * of accuracy. + * The string format used to create a number format adheres to the standard + * java specification, and JExcelAPI makes the necessary modifications so + * that it is rendered in Excel as the nearest possible equivalent. + * Once created, this may be used within a CellFormat object, which in turn + * is a parameter passed to the constructor of the Number cell + */ +public class NumberFormat extends NumberFormatRecord implements DisplayFormat { + /** + * Pass in to the constructor to bypass the format validation + */ + public static final NonValidatingFormat COMPLEX_FORMAT = + new NonValidatingFormat(); + + // Some format strings + + /** + * Constant format string for the Euro currency symbol where it precedes + * the format + */ + public static final String CURRENCY_EURO_PREFIX = "[$�-2]"; + + /** + * Constant format string for the Euro currency symbol where it precedes + * the format + */ + public static final String CURRENCY_EURO_SUFFIX = "[$�-1]"; + + /** + * Constant format string for the UK pound sign + */ + public static final String CURRENCY_POUND = "�"; + + /** + * Constant format string for the Japanese Yen sign + */ + public static final String CURRENCY_JAPANESE_YEN = "[$�-411]"; + + /** + * Constant format string for the US Dollar sign + */ + public static final String CURRENCY_DOLLAR = "[$$-409]"; + + /** + * Constant format string for three digit fractions + */ + public static final String FRACTION_THREE_DIGITS = "???/???"; + + /** + * Constant format string for fractions as halves + */ + public static final String FRACTION_HALVES = "?/2"; + + /** + * Constant format string for fractions as quarter + */ + public static final String FRACTION_QUARTERS = "?/4"; + + /** + * Constant format string for fractions as eighths + */ + public static final String FRACTIONS_EIGHTHS = "?/8"; + + /** + * Constant format string for fractions as sixteenths + */ + public static final String FRACTION_SIXTEENTHS = "?/16"; + + /** + * Constant format string for fractions as tenths + */ + public static final String FRACTION_TENTHS = "?/10"; + + /** + * Constant format string for fractions as hundredths + */ + public static final String FRACTION_HUNDREDTHS = "?/100"; + + /** + * Constructor, taking in the Java compliant number format + * + * @param format the format string + */ + public NumberFormat(String format) { + super(format); + + // Verify that the format is valid + DecimalFormat df = new DecimalFormat(format); + } + + /** + * Constructor, taking in the non-Java compliant number format. This + * may be used for currencies and more complex custom formats, which + * will not be subject to the standard validation rules. + * As there is no validation, there is a resultant risk that the + * generated Excel file will be corrupt + *

+ * USE THIS CONSTRUCTOR ONLY IF YOU ARE CERTAIN THAT THE NUMBER FORMAT + * YOU ARE USING IS EXCEL COMPLIANT + * + * @param format the format string + * @param dummy dummy parameter + */ + public NumberFormat(String format, NonValidatingFormat dummy) { + super(format, dummy); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/NumberFormats.java b/datastructures-xslx/src/main/java/jxl/write/NumberFormats.java new file mode 100755 index 0000000..43469ae --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/NumberFormats.java @@ -0,0 +1,273 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.biff.DisplayFormat; +import jxl.format.Format; + +/** + * Static class which contains the available list of built in Number formats + */ +public final class NumberFormats { + /** + * The default format. This is equivalent to a number format of '#' + */ + public static final DisplayFormat DEFAULT = new BuiltInFormat(0x0, "#"); + + + // The available built in number formats + // First describe the fairly bog standard formats + /** + * Formatting for an integer number. This is equivalent to a DecimalFormat + * of "0" + */ + public static final DisplayFormat INTEGER = new BuiltInFormat(0x1, "0"); + /** + * Formatting for a float. This formats number to two decimal places. It + * is equivalent to a DecimalFormat of "0.00" + */ + public static final DisplayFormat FLOAT = new BuiltInFormat(0x2, "0.00"); + /** + * Formatting for an integer that has a thousands separator. + * Equivalent to a DecimalFormat of "#,##0" + */ + public static final DisplayFormat THOUSANDS_INTEGER = + new BuiltInFormat(0x3, "#,##0"); + /** + * Formatting for a float that has a thousands separator. + * Equivalent to a DecimalFormat of "#,##0.00" + */ + public static final DisplayFormat THOUSANDS_FLOAT = + new BuiltInFormat(0x4, "#,##0.00"); + /** + * Formatting for an integer which is presented in accounting format + * (ie. deficits appear in parentheses) + * Equivalent to a DecimalFormat of "$#,##0;($#,##0)" + */ + public static final DisplayFormat ACCOUNTING_INTEGER = + new BuiltInFormat(0x5, "$#,##0;($#,##0)"); + /** + * As ACCOUNTING_INTEGER except that deficits appear coloured red + */ + public static final DisplayFormat ACCOUNTING_RED_INTEGER = + new BuiltInFormat(0x6, "$#,##0;($#,##0)"); + /** + * Formatting for an integer which is presented in accounting format + * (ie. deficits appear in parentheses) + * Equivalent to a DecimalFormat of "$#,##0;($#,##0)" + */ + public static final DisplayFormat ACCOUNTING_FLOAT = + new BuiltInFormat(0x7, "$#,##0;($#,##0)"); + /** + * As ACCOUNTING_FLOAT except that deficits appear coloured red + */ + public static final DisplayFormat ACCOUNTING_RED_FLOAT = + new BuiltInFormat(0x8, "$#,##0;($#,##0)"); + /** + * Formatting for an integer presented as a percentage + * Equivalent to a DecimalFormat of "0%" + */ + public static final DisplayFormat PERCENT_INTEGER = + new BuiltInFormat(0x9, "0%"); + /** + * Formatting for a float percentage + * Equivalent to a DecimalFormat "0.00%" + */ + public static final DisplayFormat PERCENT_FLOAT = + new BuiltInFormat(0xa, "0.00%"); + /** + * Formatting for exponential or scientific notation + * Equivalent to a DecimalFormat "0.00E00" + */ + public static final DisplayFormat EXPONENTIAL = + new BuiltInFormat(0xb, "0.00E00"); + /** + * Formatting for one digit fractions + */ + public static final DisplayFormat FRACTION_ONE_DIGIT = + new BuiltInFormat(0xc, "?/?"); + /** + * Formatting for two digit fractions + */ + public static final DisplayFormat FRACTION_TWO_DIGITS = + new BuiltInFormat(0xd, "??/??"); + /** + * Equivalent to a DecimalFormat "#,##0;(#,##0)" + */ + public static final DisplayFormat FORMAT1 = + new BuiltInFormat(0x25, "#,##0;(#,##0)"); + + // Now describe the more obscure formats + /** + * Equivalent to FORMAT1 except deficits are coloured red + */ + public static final DisplayFormat FORMAT2 = + new BuiltInFormat(0x26, "#,##0;(#,##0)"); + /** + * Equivalent to DecimalFormat "#,##0.00;(#,##0.00)" + */ + public static final DisplayFormat FORMAT3 = + new BuiltInFormat(0x27, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to FORMAT3 except deficits are coloured red + */ + public static final DisplayFormat FORMAT4 = + new BuiltInFormat(0x28, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to DecimalFormat "#,##0;(#,##0)" + */ + public static final DisplayFormat FORMAT5 = + new BuiltInFormat(0x29, "#,##0;(#,##0)"); + /** + * Equivalent to FORMAT5 except deficits are coloured red + */ + public static final DisplayFormat FORMAT6 = + new BuiltInFormat(0x2a, "#,##0;(#,##0)"); + /** + * Equivalent to DecimalFormat "#,##0.00;(#,##0.00)" + */ + public static final DisplayFormat FORMAT7 = + new BuiltInFormat(0x2b, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to FORMAT7 except deficits are coloured red + */ + public static final DisplayFormat FORMAT8 = + new BuiltInFormat(0x2c, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to FORMAT7 + */ + public static final DisplayFormat FORMAT9 = + new BuiltInFormat(0x2e, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to DecimalFormat "##0.0E0" + */ + public static final DisplayFormat FORMAT10 = + new BuiltInFormat(0x30, "##0.0E0"); + /** + * Forces numbers to be interpreted as text + */ + public static final DisplayFormat TEXT = new BuiltInFormat(0x31, "@"); + + /** + * Inner class which holds the format index + */ + private static class BuiltInFormat implements DisplayFormat, Format { + /** + * The built in number format index + */ + private final int index; + + /** + * The format string + */ + private final String formatString; + + /** + * Constructor, using the predetermined index + * + * @param i the index + * @param s the string + */ + public BuiltInFormat(int i, String s) { + index = i; + formatString = s; + } + + /** + * Accessor for the format index + * + * @return the index + */ + public int getFormatIndex() { + return index; + } + + /** + * Accessor to determine if this format has been initialized. Since it is + * built in, this will always return TRUE + * + * @return TRUE, since this is a built in format + */ + public boolean isInitialized() { + return true; + } + + /** + * Determines whether this format is a built in format + * + * @return TRUE, since this is a built in numerical format + */ + public boolean isBuiltIn() { + return true; + } + + /** + * Initializes this format with a dynamically determined index value. + * Since this is a built in, and hence the index value is predetermined, + * this method has an empty body + * + * @param pos the pos in the number formats list + */ + public void initialize(int pos) { + } + + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString() { + return formatString; + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the two objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof BuiltInFormat)) { + return false; + } + + BuiltInFormat bif = (BuiltInFormat) o; + + return index == bif.index; + } + + /** + * Standard hash code method + * + * @return the hash code + */ + public int hashCode() { + return index; + } + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/Pattern.java b/datastructures-xslx/src/main/java/jxl/write/Pattern.java new file mode 100755 index 0000000..ea60896 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Pattern.java @@ -0,0 +1,48 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class which contains the various patterns available within + * the standard Excel pattern palette + * + * @deprecated Repackaged as jxl.format.Pattern + */ +public final class Pattern extends jxl.format.Pattern { + /** + * Private constructor + * + * @param val + */ + private Pattern() { + super(0, null); + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/VerticalAlignment.java b/datastructures-xslx/src/main/java/jxl/write/VerticalAlignment.java new file mode 100755 index 0000000..21a0cec --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/VerticalAlignment.java @@ -0,0 +1,35 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration type which describes the vertical alignment of data within a cell + * + * @deprecated Repackaged as jxl.format.VerticalAlignment + */ +public final class VerticalAlignment extends jxl.format.VerticalAlignment { + /** + * Constructor + */ + private VerticalAlignment() { + super(0, null); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableCell.java b/datastructures-xslx/src/main/java/jxl/write/WritableCell.java new file mode 100755 index 0000000..e37eff9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableCell.java @@ -0,0 +1,61 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.Cell; +import jxl.format.CellFormat; + +/** + * The interface for all writable cells + */ +public interface WritableCell extends Cell { + /** + * Sets the cell format for this cell + * + * @param cf the cell format + */ + void setCellFormat(CellFormat cf); + + /** + * A deep copy. The returned cell still needs to be added to the sheet. + * By not automatically adding the cell to the sheet, the client program + * may change certain attributes, such as the value or the format + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + WritableCell copyTo(int col, int row); + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + WritableCellFeatures getWritableCellFeatures(); + + /** + * Sets the cell features + * + * @param cf the cell features + */ + void setCellFeatures(WritableCellFeatures cf); +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableCellFeatures.java b/datastructures-xslx/src/main/java/jxl/write/WritableCellFeatures.java new file mode 100755 index 0000000..238cd88 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableCellFeatures.java @@ -0,0 +1,156 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.util.Collection; +import jxl.CellFeatures; +import jxl.biff.BaseCellFeatures; + +/** + * Container for any additional cell features + */ +public class WritableCellFeatures extends CellFeatures { + // shadow the conditions in the base class so that they appear on + // the public generated javadoc + public static final ValidationCondition BETWEEN = BaseCellFeatures.BETWEEN; + public static final ValidationCondition NOT_BETWEEN = + BaseCellFeatures.NOT_BETWEEN; + public static final ValidationCondition EQUAL = BaseCellFeatures.EQUAL; + public static final ValidationCondition NOT_EQUAL = + BaseCellFeatures.NOT_EQUAL; + public static final ValidationCondition GREATER_THAN = + BaseCellFeatures.GREATER_THAN; + public static final ValidationCondition LESS_THAN = + BaseCellFeatures.LESS_THAN; + public static final ValidationCondition GREATER_EQUAL = + BaseCellFeatures.GREATER_EQUAL; + public static final ValidationCondition LESS_EQUAL = + BaseCellFeatures.LESS_EQUAL; + + /** + * Constructor + */ + public WritableCellFeatures() { + super(); + } + + /** + * Copy constructor + * + * @param cf the cell to copy + */ + public WritableCellFeatures(CellFeatures cf) { + super(cf); + } + + /** + * Sets the cell comment + * + * @param s the comment + */ + public void setComment(String s) { + super.setComment(s); + } + + /** + * Sets the cell comment and sets the size of the text box (in cells) + * in which the comment is displayed + * + * @param s the comment + * @param width the width of the comment box in cells + * @param height the height of the comment box in cells + */ + public void setComment(String s, double width, double height) { + super.setComment(s, width, height); + } + + /** + * Removes the cell comment, if present + */ + public void removeComment() { + super.removeComment(); + } + + + /** + * Removes any data validation, if present + */ + public void removeDataValidation() { + super.removeDataValidation(); + } + + /** + * The list of items to validate for this cell. For each object in the + * collection, the toString() method will be called and the data entered + * will be validated against that string + * + * @param c the list of valid values + */ + public void setDataValidationList(Collection c) { + super.setDataValidationList(c); + } + + /** + * The list of items to validate for this cell in the form of a cell range. + * + * @param col1 the first column containing the data to validate against + * @param row1 the first row containing the data to validate against + * @param col2 the second column containing the data to validate against + * @param row2 the second row containing the data to validate against + */ + public void setDataValidationRange(int col1, int row1, int col2, int row2) { + super.setDataValidationRange(col1, row1, col2, row2); + } + + /** + * Sets the data validation based upon a named range. If the namedRange + * is an empty string ("") then the cell is effectively made read only + * + * @param namedRange the workbook named range defining the validation + * boundaries + */ + public void setDataValidationRange(String namedRange) { + super.setDataValidationRange(namedRange); + } + + + /** + * Sets the numeric value against which to validate + * + * @param val the number + * @param c the validation condition + */ + public void setNumberValidation(double val, ValidationCondition c) { + super.setNumberValidation(val, c); + } + + /** + * Sets the numeric range against which to validate the data + * + * @param val1 the first number + * @param val2 the second number + * @param c the validation condition + */ + public void setNumberValidation(double val1, + double val2, + ValidationCondition c) { + super.setNumberValidation(val1, val2, c); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableCellFormat.java b/datastructures-xslx/src/main/java/jxl/write/WritableCellFormat.java new file mode 100755 index 0000000..7195e1a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableCellFormat.java @@ -0,0 +1,219 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.biff.DisplayFormat; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.Orientation; +import jxl.format.Pattern; +import jxl.format.VerticalAlignment; +import jxl.write.biff.CellXFRecord; + +/** + * A user specified cell format, which may be reused across many cells. + * The constructors takes parameters, such as font details and the numerical + * date formats, which specify to Excel how cells with this format should be + * displayed. + * Once a CellFormat has been added to a Cell which has been added to + * a sheet, then the CellFormat becomes immutable (to prevent unforeseen + * effects on other cells which share the same format). Attempts to + * call the various set... functions on a WritableCellFormat after this + * time will result in a runtime exception. + */ +public class WritableCellFormat extends CellXFRecord { + /** + * A default constructor, which uses the default font and format. + * This constructor should be used in conjunction with the more + * advanced two-phase methods setAlignment, setBorder etc. + */ + public WritableCellFormat() { + this(WritableWorkbook.ARIAL_10_PT, NumberFormats.DEFAULT); + } + + /** + * A CellFormat which specifies the font for cells with this format + * + * @param font the font + */ + public WritableCellFormat(WritableFont font) { + this(font, NumberFormats.DEFAULT); + } + + /** + * A constructor which specifies a date/number format for Cells which + * use this format object + * + * @param format the format + */ + public WritableCellFormat(DisplayFormat format) { + this(WritableWorkbook.ARIAL_10_PT, format); + } + + /** + * A constructor which specifies the font and date/number format for cells + * which wish to use this format + * + * @param font the font + * @param format the date/number format + */ + public WritableCellFormat(WritableFont font, DisplayFormat format) { + super(font, format); + } + + /** + * A public copy constructor which can be used for copy formats between + * different sheets + * + * @param format the cell format to copy + */ + public WritableCellFormat(CellFormat format) { + super(format); + } + + /** + * Sets the horizontal alignment for this format + * + * @param a the alignment + * @throws WriteException + */ + public void setAlignment(Alignment a) throws WriteException { + super.setAlignment(a); + } + + /** + * Sets the vertical alignment for this format + * + * @param va the vertical alignment + * @throws WriteException + */ + public void setVerticalAlignment(VerticalAlignment va) throws WriteException { + super.setVerticalAlignment(va); + } + + /** + * Sets the text orientation for this format + * + * @param o the orientation + * @throws WriteException + */ + public void setOrientation(Orientation o) throws WriteException { + super.setOrientation(o); + } + + /** + * Sets the wrap indicator for this format. If the wrap is set to TRUE, then + * Excel will wrap data in cells with this format so that it fits within the + * cell boundaries + * + * @param w the wrap flag + * @throws WriteException + */ + public void setWrap(boolean w) throws WriteException { + super.setWrap(w); + } + + /** + * Sets the specified border for this format + * + * @param b the border + * @param ls the border line style + * @throws WriteException + */ + public void setBorder(Border b, BorderLineStyle ls) throws WriteException { + super.setBorder(b, ls, Colour.BLACK); + } + + /** + * Sets the specified border for this format + * + * @param b the border + * @param ls the border line style + * @param c the colour of the specified border + * @throws WriteException + */ + public void setBorder(Border b, BorderLineStyle ls, Colour c) + throws WriteException { + super.setBorder(b, ls, c); + } + + /** + * Sets the background colour for this cell format + * + * @param c the bacground colour + * @throws WriteException + */ + public void setBackground(Colour c) throws WriteException { + this.setBackground(c, Pattern.SOLID); + } + + /** + * Sets the background colour and pattern for this cell format + * + * @param c the colour + * @param p the pattern + * @throws WriteException + */ + public void setBackground(Colour c, Pattern p) throws WriteException { + super.setBackground(c, p); + } + + /** + * Sets the shrink to fit flag + * + * @param s shrink to fit flag + * @throws WriteException + */ + public void setShrinkToFit(boolean s) throws WriteException { + super.setShrinkToFit(s); + } + + /** + * Sets the indentation of the cell text + * + * @param i the indentation + */ + public void setIndentation(int i) throws WriteException { + super.setIndentation(i); + } + + + /** + * Sets whether or not this XF record locks the cell. For this to + * have any effect, the sheet containing cells with this format must + * also be locke3d + * + * @param l the locked flag + * @throws WriteException + */ + public void setLocked(boolean l) throws WriteException { + super.setLocked(l); + } + +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableFont.java b/datastructures-xslx/src/main/java/jxl/write/WritableFont.java new file mode 100755 index 0000000..692559c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableFont.java @@ -0,0 +1,333 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.format.Colour; +import jxl.format.Font; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; +import jxl.write.biff.WritableFontRecord; + +/** + * A class which is instantiated when the user application wishes to specify + * the font for a particular cell + */ +public class WritableFont extends WritableFontRecord { + /** + * Objects created with this font name will be rendered within Excel as ARIAL + * fonts + */ + public static final FontName ARIAL = new FontName("Arial"); + /** + * Objects created with this font name will be rendered within Excel as TIMES + * fonts + */ + public static final FontName TIMES = new FontName("Times New Roman"); + /** + * Objects created with this font name will be rendered within Excel as + * COURIER fonts + */ + public static final FontName COURIER = new FontName("Courier New"); + /** + * Objects created with this font name will be rendered within Excel as + * TAHOMA fonts + */ + public static final FontName TAHOMA = new FontName("Tahoma"); + /** + * Indicates that this font should not be presented as bold + */ + public static final BoldStyle NO_BOLD = new BoldStyle(0x190); + /** + * Indicates that this font should be presented in a BOLD style + */ + public static final BoldStyle BOLD = new BoldStyle(0x2bc); + + // The bold styles + /** + * The default point size for all Fonts + */ + public static final int DEFAULT_POINT_SIZE = 10; + /** + * Creates a default font, vanilla font of the specified face and with + * default point size. + * + * @param fn the font name + */ + public WritableFont(FontName fn) { + this(fn, + DEFAULT_POINT_SIZE, + NO_BOLD, + false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Publicly available copy constructor + * + * @param f the font to copy + */ + public WritableFont(Font f) { + super(f); + } + + /** + * Constructs of font of the specified face and of size given by the + * specified point size + * + * @param ps the point size + * @param fn the font name + */ + public WritableFont(FontName fn, int ps) { + this(fn, ps, NO_BOLD, false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size and bold style + * + * @param ps the point size + * @param bs the bold style + * @param fn the font name + */ + public WritableFont(FontName fn, int ps, BoldStyle bs) { + this(fn, ps, bs, false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold weight and + * italicised option. + * + * @param ps the point size + * @param bs the bold style + * @param italic italic flag + * @param fn the font name + */ + public WritableFont(FontName fn, int ps, BoldStyle bs, boolean italic) { + this(fn, ps, bs, italic, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold weight, + * italicisation and underline style + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it italic flag + */ + public WritableFont(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us) { + this(fn, ps, bs, it, us, Colour.BLACK, ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style and colour + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it italic flag + * @param c the colour + */ + public WritableFont(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c) { + this(fn, ps, bs, it, us, c, ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style, colour, and script + * style (superscript/subscript) + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it the italic flag + * @param c the colour + * @param ss the script style + */ + public WritableFont(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c, + ScriptStyle ss) { + super(fn.name, ps, bs.value, it, + us.getValue(), + c.getValue(), ss.getValue()); + } + + /** + * Factory method which creates the specified font name. This method + * should be used with care, since the string used to create the font + * name must be recognized by Excel's internal processing + * + * @param fontName the name of the Excel font + * @return the font name + */ + public static FontName createFont(String fontName) { + return new FontName(fontName); + } + + /** + * Sets the point size for this font, if the font hasn't been initialized + * + * @param pointSize the point size + * @throws WriteException, if this font is already in use elsewhere + */ + public void setPointSize(int pointSize) throws WriteException { + super.setPointSize(pointSize); + } + + /** + * Sets the bold style for this font, if the font hasn't been initialized + * + * @param boldStyle the bold style + * @throws WriteException, if this font is already in use elsewhere + */ + public void setBoldStyle(BoldStyle boldStyle) throws WriteException { + super.setBoldStyle(boldStyle.value); + } + + /** + * Sets the italic indicator for this font, if the font hasn't been + * initialized + * + * @param italic the italic flag + * @throws WriteException, if this font is already in use elsewhere + */ + public void setItalic(boolean italic) throws WriteException { + super.setItalic(italic); + } + + /** + * Sets the underline style for this font, if the font hasn't been + * initialized + * + * @param us the underline style + * @throws WriteException, if this font is already in use elsewhere + */ + public void setUnderlineStyle(UnderlineStyle us) throws WriteException { + super.setUnderlineStyle(us.getValue()); + } + + /** + * Sets the colour for this font, if the font hasn't been + * initialized + * + * @param colour the colour + * @throws WriteException, if this font is already in use elsewhere + */ + public void setColour(Colour colour) throws WriteException { + super.setColour(colour.getValue()); + } + + /** + * Sets the script style (eg. superscript, subscript) for this font, + * if the font hasn't been initialized + * + * @param scriptStyle the colour + * @throws WriteException, if this font is already in use elsewhere + */ + public void setScriptStyle(ScriptStyle scriptStyle) throws WriteException { + super.setScriptStyle(scriptStyle.getValue()); + } + + /** + * Accessor for the strike-out flag + * + * @return the strike-out flag + */ + public boolean isStruckout() { + return super.isStruckout(); + } + + /** + * Sets Accessor for the strike-out flag + * + * @param struckout TRUE if this is a struckout font + * @return the strike-out flag + * @throws WriteException, if this font is already in use elsewhere + */ + public void setStruckout(boolean struckout) throws WriteException { + super.setStruckout(struckout); + } + + /** + * Static inner class used for classifying the font names + */ + public static class FontName { + /** + * The name + */ + String name; + + /** + * Constructor + * + * @param s the font name + */ + FontName(String s) { + name = s; + } + } + + /** + * Static inner class used for the boldness of the fonts + */ + /*private*/ static class BoldStyle { + /** + * The value + */ + public int value; + + /** + * Constructor + * + * @param val the value + */ + BoldStyle(int val) { + value = val; + } + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableHyperlink.java b/datastructures-xslx/src/main/java/jxl/write/WritableHyperlink.java new file mode 100755 index 0000000..28ac95c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableHyperlink.java @@ -0,0 +1,232 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.io.File; +import java.net.URL; +import jxl.Hyperlink; +import jxl.write.biff.HyperlinkRecord; + +/** + * A writable hyperlink. Provides API to modify the contents of the hyperlink + */ +public class WritableHyperlink extends HyperlinkRecord implements Hyperlink { + /** + * Constructor used internally by the worksheet when making a copy + * of worksheet + * + * @param h the hyperlink being read in + * @param ws the writable sheet containing the hyperlink + */ + public WritableHyperlink(Hyperlink h, WritableSheet ws) { + super(h, ws); + } + + /** + * Constructs a URL hyperlink in a single cell + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param url the hyperlink + */ + public WritableHyperlink(int col, int row, URL url) { + this(col, row, col, row, url); + } + + /** + * Constructs a url hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param url the hyperlink + */ + public WritableHyperlink(int col, int row, int lastcol, int lastrow, URL url) { + this(col, row, lastcol, lastrow, url, null); + } + + /** + * Constructs a url hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param url the hyperlink + * @param desc the description text to place in the cell + */ + public WritableHyperlink(int col, + int row, + int lastcol, + int lastrow, + URL url, + String desc) { + super(col, row, lastcol, lastrow, url, desc); + } + + /** + * Constructs a file hyperlink in a single cell + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param file the hyperlink + */ + public WritableHyperlink(int col, int row, File file) { + this(col, row, col, row, file, null); + } + + /** + * Constructs a file hyperlink in a single cell + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param file the hyperlink + * @param desc the hyperlink description + */ + public WritableHyperlink(int col, int row, File file, String desc) { + this(col, row, col, row, file, desc); + } + + /** + * Constructs a File hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param file the hyperlink + */ + public WritableHyperlink(int col, int row, int lastcol, int lastrow, + File file) { + super(col, row, lastcol, lastrow, file, null); + } + + /** + * Constructs a File hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param file the hyperlink + * @param desc the description + */ + public WritableHyperlink(int col, + int row, + int lastcol, + int lastrow, + File file, + String desc) { + super(col, row, lastcol, lastrow, file, desc); + } + + /** + * Constructs a hyperlink to some cells within this workbook + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param desc the cell contents for this hyperlink + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + */ + public WritableHyperlink(int col, int row, + String desc, + WritableSheet sheet, + int destcol, int destrow) { + this(col, row, col, row, + desc, + sheet, destcol, destrow, destcol, destrow); + } + + /** + * Constructs a hyperlink to some cells within this workbook + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param desc the cell contents for this hyperlink + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + public WritableHyperlink(int col, int row, + int lastcol, int lastrow, + String desc, + WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + super(col, row, lastcol, lastrow, + desc, + sheet, destcol, destrow, + lastdestcol, lastdestrow); + } + + /** + * Sets the URL of this hyperlink + * + * @param url the url + */ + public void setURL(URL url) { + super.setURL(url); + } + + /** + * Sets the file activated by this hyperlink + * + * @param file the file + */ + public void setFile(File file) { + super.setFile(file); + } + + /** + * Sets the description to appear in the hyperlink cell + * + * @param desc the description + */ + public void setDescription(String desc) { + super.setContents(desc); + } + + /** + * Sets the location of the cells to be linked to within this workbook + * + * @param desc the label describing the link + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + public void setLocation(String desc, + WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + super.setLocation(desc, sheet, destcol, destrow, lastdestcol, lastdestrow); + } + +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableImage.java b/datastructures-xslx/src/main/java/jxl/write/WritableImage.java new file mode 100755 index 0000000..151eb07 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableImage.java @@ -0,0 +1,207 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.io.File; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroup; +import jxl.biff.drawing.DrawingGroupObject; + +/** + * Allows an image to be created, or an existing image to be manipulated + * Note that co-ordinates and dimensions are given in cells, so that if for + * example the width or height of a cell which the image spans is altered, + * the image will have a correspondign distortion + */ +public class WritableImage extends Drawing { + // Shadow these values from the superclass. The only practical reason + // for doing this is that they appear nicely in the javadoc + + /** + * Image anchor properties which will move and resize an image + * along with the cells + */ + public static ImageAnchorProperties MOVE_AND_SIZE_WITH_CELLS = + Drawing.MOVE_AND_SIZE_WITH_CELLS; + + /** + * Image anchor properties which will move an image + * when cells are inserted or deleted + */ + public static ImageAnchorProperties MOVE_WITH_CELLS = + Drawing.MOVE_WITH_CELLS; + + /** + * Image anchor properties which will leave an image unaffected when + * other cells are inserted, removed or resized + */ + public static ImageAnchorProperties NO_MOVE_OR_SIZE_WITH_CELLS = + Drawing.NO_MOVE_OR_SIZE_WITH_CELLS; + + /** + * Constructor + * + * @param x the column number at which to position the image + * @param y the row number at which to position the image + * @param width the number of columns cells which the image spans + * @param height the number of rows which the image spans + * @param image the source image file + */ + public WritableImage(double x, double y, + double width, double height, + File image) { + super(x, y, width, height, image); + } + + /** + * Constructor + * + * @param x the column number at which to position the image + * @param y the row number at which to position the image + * @param width the number of columns cells which the image spans + * @param height the number of rows which the image spans + * @param imageData the image data + */ + public WritableImage(double x, + double y, + double width, + double height, + byte[] imageData) { + super(x, y, width, height, imageData); + } + + /** + * Constructor, used when copying sheets + * + * @param d the image to copy + * @param dg the drawing group + */ + public WritableImage(DrawingGroupObject d, DrawingGroup dg) { + super(d, dg); + } + + /** + * Accessor for the image position + * + * @return the column number at which the image is positioned + */ + public double getColumn() { + return super.getX(); + } + + /** + * Accessor for the image position + * + * @param c the column number at which the image should be positioned + */ + public void setColumn(double c) { + super.setX(c); + } + + /** + * Accessor for the image position + * + * @return the row number at which the image is positions + */ + public double getRow() { + return super.getY(); + } + + /** + * Accessor for the image position + * + * @param c the row number at which the image should be positioned + */ + public void setRow(double c) { + super.setY(c); + } + + /** + * Accessor for the image dimensions + * + * @return the number of columns this image spans + */ + public double getWidth() { + return super.getWidth(); + } + + /** + * Accessor for the image dimensions + * Note that the actual size of the rendered image will depend on the + * width of the columns it spans + * + * @param c the number of columns which this image spans + */ + public void setWidth(double c) { + super.setWidth(c); + } + + /** + * Accessor for the image dimensions + * + * @return the number of rows which this image spans + */ + public double getHeight() { + return super.getHeight(); + } + + /** + * Accessor for the image dimensions + * Note that the actual size of the rendered image will depend on the + * height of the rows it spans + * + * @param c the number of rows which this image should span + */ + public void setHeight(double c) { + super.setHeight(c); + } + + /** + * Accessor for the image file + * + * @return the file which the image references + */ + public File getImageFile() { + return super.getImageFile(); + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() { + return super.getImageData(); + } + + /** + * Accessor for the anchor properties + */ + public ImageAnchorProperties getImageAnchor() { + return super.getImageAnchor(); + } + + /** + * Accessor for the anchor properties + */ + public void setImageAnchor(ImageAnchorProperties iap) { + super.setImageAnchor(iap); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableSheet.java b/datastructures-xslx/src/main/java/jxl/write/WritableSheet.java new file mode 100755 index 0000000..0dfae07 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableSheet.java @@ -0,0 +1,433 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.CellView; +import jxl.Range; +import jxl.Sheet; +import jxl.format.CellFormat; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; +import jxl.write.biff.RowsExceededException; + +/** + * Interface for a worksheet that may be modified. The most + * important modification for a sheet is to have cells added to it + */ +public interface WritableSheet extends Sheet { + /** + * Adds a cell to this sheet + * The RowsExceededException may be caught if client code wishes to + * explicitly trap the case where too many rows have been written + * to the current sheet. If this behaviour is not desired, it is + * sufficient simply to handle the WriteException, since this is a base + * class of RowsExceededException + * + * @param cell the cell to add + * @throws jxl.write..WriteException + * @throws RowsExceededException + */ + void addCell(WritableCell cell) + throws WriteException, RowsExceededException; + + /** + * Sets the name of this sheet + * + * @param name the name of the sheet + */ + void setName(String name); + + /** + * Indicates whether or not this sheet is hidden + * + * @param hidden hidden flag + * @deprecated use the SheetSettings bean instead + */ + void setHidden(boolean hidden); + + /** + * Indicates whether or not this sheet is protected + * + * @param prot Protected flag + * @deprecated use the SheetSettings bean instead + */ + void setProtected(boolean prot); + + /** + * Sets the width of the column on this sheet, in characters. This causes + * Excel to resize the entire column. + * If the columns specified already has view information associated + * with it, then it is replaced by the new data + * + * @param col the column to be formatted + * @param width the width of the column + */ + void setColumnView(int col, int width); + + /** + * Sets the width and style of every cell in the specified column. + * If the columns specified already has view information associated + * with it, then it is replaced by the new data + * + * @param col the column to be formatted + * @param format the format of every cell in the column + * @param width the width of the column, in characters + * @deprecated Use the CellView bean instead + */ + void setColumnView(int col, int width, CellFormat format); + + /** + * Sets the view for this column + * + * @param col the column on which to set the view + * @param view the view to set + */ + void setColumnView(int col, CellView view); + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in characters + * @throws RowsExceededException + */ + void setRowView(int row, int height) + throws RowsExceededException; + + /** + * Sets the properties of the specified row + * + * @param row the row to be formatted + * @param collapsed indicates whether the row is collapsed + * @throws RowsExceededException + */ + void setRowView(int row, boolean collapsed) + throws RowsExceededException; + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in 1/20th of a point + * @param collapsed indicates whether the row is collapsed + * @throws RowsExceededException + */ + void setRowView(int row, int height, + boolean collapsed) + throws RowsExceededException; + + /** + * Sets the view for this column + * + * @param row the column on which to set the view + * @param view the view to set + * @throws RowsExceededException + */ + void setRowView(int row, CellView view) throws RowsExceededException; + + /** + * Gets the writable cell from this sheet. Use of this method allows + * the returned cell to be modified by the users application + * + * @param column the column + * @param row the row + * @return the cell at the specified position + */ + WritableCell getWritableCell(int column, int row); + + /** + * Returns the cell for the specified location eg. "A4". Note that this + * method is identical to calling getCell(CellReferenceHelper.getColumn(loc), + * CellReferenceHelper.getRow(loc)) and its implicit performance + * overhead for string parsing. As such,this method should therefore + * be used sparingly + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + WritableCell getWritableCell(String loc); + + /** + * Gets the writable hyperlinks from this sheet. The hyperlinks + * that are returned may be modified by user applications + * + * @return the writable hyperlinks + */ + WritableHyperlink[] getWritableHyperlinks(); + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + void insertRow(int row); + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + void insertColumn(int col); + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + void removeColumn(int col); + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + void removeRow(int row); + + /** + * Merges the specified cells. Any clashes or intersections between + * merged cells are resolved when the spreadsheet is written out + * + * @param col1 the column number of the top left cell + * @param row1 the row number of the top left cell + * @param col2 the column number of the bottom right cell + * @param row2 the row number of the bottom right cell + * @return the Range object representing the merged cells + * @throws jxl.write..WriteException + * @throws RowsExceededException + */ + Range mergeCells(int col1, int row1, int col2, int row2) + throws WriteException, RowsExceededException; + + /** + * Sets a row grouping + * + * @param row1 the first row of the group + * @param row2 the last row of the group + * @param collapsed should the group be collapsed? + * @throws WriteException + * @throws RowsExceededException + */ + void setRowGroup(int row1, int row2, boolean collapsed) + throws WriteException, RowsExceededException; + + /** + * Unsets a row grouping + * + * @param row1 the first row to unset + * @param row2 the last row to unset + * @throws WriteException + * @throws RowsExceededException + */ + void unsetRowGroup(int row1, int row2) + throws WriteException, RowsExceededException; + + /** + * Sets a column grouping + * + * @param col1 the first column of the group + * @param col2 the last column of the group + * @param collapsed should the group be collapsed? + * @throws WriteException + * @throws RowsExceededException + */ + void setColumnGroup(int col1, int col2, boolean collapsed) + throws WriteException, RowsExceededException; + + /** + * Unsets a column grouping + * + * @param col1 the first column to unset + * @param col2 the last column to unset + * @throws WriteException + * @throws RowsExceededException + */ + void unsetColumnGroup(int col1, int col2) + throws WriteException, RowsExceededException; + + /** + * Unmerges the specified cells. The Range passed in should be one that + * has been previously returned as a result of the getMergedCells method + * + * @param r the range of cells to unmerge + */ + void unmergeCells(Range r); + + /** + * Adds the specified hyperlink. Adding a hyperlink causes any populated + * cells in the range of the hyperlink to be set to empty + * If the cells which activate this hyperlink clash with any other cells, + * they are still added to the worksheet and it is left to Excel to + * handle this. + * + * @param h the hyperlink + * @throws jxl.write..WriteException + * @throws RowsExceededException + */ + void addHyperlink(WritableHyperlink h) + throws WriteException, RowsExceededException; + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. The contents of the cell which + * activate the hyperlink are removed. + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + */ + void removeHyperlink(WritableHyperlink h); + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. + * If the preserveLabel field is set, the cell contents of the + * hyperlink are preserved, although the hyperlink is deactivated. If + * this value is FALSE, the cell contents are removed + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + * @param preserveLabel if TRUE preserves the label contents, if FALSE + * removes them + */ + void removeHyperlink(WritableHyperlink h, boolean preserveLabel); + + /** + * Sets the header for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated use the SheetSettings bean + */ + void setHeader(String l, String c, String r); + + /** + * Sets the footer for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated use the SheetSettings bean + */ + void setFooter(String l, String c, String r); + + /** + * Sets the page setup details + * + * @param p the page orientation + */ + void setPageSetup(PageOrientation p); + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + */ + void setPageSetup(PageOrientation p, double hm, double fm); + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param ps the paper size + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + */ + void setPageSetup(PageOrientation p, PaperSize ps, + double hm, double fm); + + /** + * Forces a page break at the specified row + * + * @param row the row to break at + */ + void addRowPageBreak(int row); + + /** + * Forces a page break at the specified column + * + * @param col the column to break at + */ + void addColumnPageBreak(int col); + + /** + * Adds an image to the sheet + * + * @param image the image to add + */ + void addImage(WritableImage image); + + /** + * Accessor for the number of images on the sheet + * + * @return the number of images on this sheet + */ + int getNumberOfImages(); + + /** + * Accessor for the image + * + * @param i the 0 based image number + * @return the image at the specified position + */ + WritableImage getImage(int i); + + /** + * Removes the specified image from the sheet. The image passed in + * must be the same instance as that previously retrieved using the + * getImage() method + * + * @param wi the image to remove + */ + void removeImage(WritableImage wi); + + /** + * Extend the data validation contained in the specified cell across and + * downwards. + * NOTE: The source cell (top left) must have been added to the sheet prior + * to this method being called + * + * @param col the number of cells accross to apply this data validation + * @param row the number of cells downwards to apply this data validation + */ + void applySharedDataValidation(WritableCell cell, int col, int row) + throws WriteException; + + /** + * Remove the shared data validation from multiple cells. The cell passed + * in is the top left cell. The data validation is removed from this + * cell and all cells which share the same validation. + * + * @param cell the top left cell containing the shared data validation + */ + void removeSharedDataValidation(WritableCell cell) + throws WriteException; +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableWorkbook.java b/datastructures-xslx/src/main/java/jxl/write/WritableWorkbook.java new file mode 100755 index 0000000..a8354f3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableWorkbook.java @@ -0,0 +1,306 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.io.IOException; +import jxl.Range; +import jxl.Sheet; +import jxl.Workbook; +import jxl.format.Colour; +import jxl.format.UnderlineStyle; + +/** + * A writable workbook + */ +public abstract class WritableWorkbook { + // Globally available stuff + + /** + * The default font for Cell formats + */ + public static final WritableFont ARIAL_10_PT = + new WritableFont(WritableFont.ARIAL); + + /** + * The font used for hyperlinks + */ + public static final WritableFont HYPERLINK_FONT = + new WritableFont(WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.SINGLE, + Colour.BLUE); + + /** + * The default style for cells + */ + public static final WritableCellFormat NORMAL_STYLE = + new WritableCellFormat(ARIAL_10_PT, NumberFormats.DEFAULT); + + /** + * The style used for hyperlinks + */ + public static final WritableCellFormat HYPERLINK_STYLE = + new WritableCellFormat(HYPERLINK_FONT); + + /** + * A cell format used to hide the cell contents + */ + public static final WritableCellFormat HIDDEN_STYLE = + new WritableCellFormat(new DateFormat(";;;")); + + /** + * Constructor used by the implemenation class + */ + protected WritableWorkbook() { + } + + /** + * Gets the sheets within this workbook. Use of this method for + * large worksheets can cause performance problems. + * + * @return an array of the individual sheets + */ + public abstract WritableSheet[] getSheets(); + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public abstract String[] getSheetNames(); + + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the reQuired sheet + * @return The sheet specified by the index + * @throws IndexOutOfBoundsException when index refers to a non-existent + * sheet + */ + public abstract WritableSheet getSheet(int index) + throws IndexOutOfBoundsException; + + /** + * Gets the sheet with the specified name from within this workbook + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public abstract WritableSheet getSheet(String name); + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public abstract WritableCell getWritableCell(String loc); + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public abstract int getNumberOfSheets(); + + /** + * Closes this workbook, and makes any memory allocated available + * for garbage collection. Also closes the underlying output stream + * if necessary. + * + * @throws IOException + * @throws WriteException + */ + public abstract void close() throws IOException, WriteException; + + /** + * Creates, and returns a worksheet at the specified position + * with the specified name + * If the index specified is less than or equal to zero, the new sheet + * is created at the beginning of the workbook. If the index is greater + * than the number of sheet, then the sheet is created at the + * end of the workbook. + * + * @param name the sheet name + * @param index the index number at which to insert + * @return the new sheet + */ + public abstract WritableSheet createSheet(String name, int index); + + /** + * Imports a sheet from a different workbook. Does a deep copy on all + * elements within that sheet + * + * @param name the name of the new sheet + * @param index the position for the new sheet within this workbook + * @param sheet the sheet (from another workbook) to merge into this one + * @return the new sheet + */ + public abstract WritableSheet importSheet(String name, int index, Sheet sheet); + + /** + * Copy sheet within the same workbook. The sheet specified is copied to + * the new sheet name at the position + * + * @param s the index of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public abstract void copySheet(int s, String name, int index); + + /** + * Copies the specified sheet and places it at the index + * specified by the parameter + * + * @param s the name of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public abstract void copySheet(String s, String name, int index); + + /** + * Removes the sheet at the specified index from this workbook + * + * @param index the sheet index to remove + */ + public abstract void removeSheet(int index); + + /** + * Moves the specified sheet within this workbook to another index + * position. + * + * @param fromIndex the zero based index of the required sheet + * @param toIndex the zero based index of the required sheet + * @return the sheet that has been moved + */ + public abstract WritableSheet moveSheet(int fromIndex, int toIndex); + + /** + * Writes out the data held in this workbook in Excel format + * + * @throws IOException + */ + public abstract void write() throws IOException; + + /** + * Indicates whether or not this workbook is protected + * + * @param prot Protected flag + */ + public abstract void setProtected(boolean prot); + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public abstract void setColourRGB(Colour c, int r, int g, int b); + + /** + * This method can be used to create a writable clone of some other + * workbook + * + * @param w the workdoock to copy + * @deprecated Copying now occurs implicitly as part of the overloaded + * factory method Workbook.createWorkbood + */ + public void copy(Workbook w) { + // Was an abstract method - leave the method body blank + } + + /** + * Gets the named cell from this workbook. The name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be, null is returned + * + * @param name the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public abstract WritableCell findCellByName(String name); + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param name the name of the cell/range to search for + * @return the range of cells + */ + public abstract Range[] findByName(String name); + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public abstract String[] getRangeNames(); + + /** + * Removes the specified named range from the workbook. Note that + * removing a name could cause formulas which use that name to + * calculate their results incorrectly + * + * @param name the name to remove + */ + public abstract void removeRangeName(String name); + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + */ + public abstract void addNameArea(String name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow); + + /** + * Sets a new output file. This allows the same workbook to be + * written to various different output files without having to + * read in any templates again + * + * @param fileName the file name + * @throws IOException + */ + public abstract void setOutputFile(java.io.File fileName) + throws IOException; +} diff --git a/datastructures-xslx/src/main/java/jxl/write/WriteException.java b/datastructures-xslx/src/main/java/jxl/write/WriteException.java new file mode 100755 index 0000000..a8a7035 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WriteException.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.JXLException; + +/** + * Exception thrown when using the API to generate an Excel file + */ +public abstract class WriteException extends JXLException { + /** + * Constructs this exception with the specified message + * + * @param s the message + */ + protected WriteException(String s) { + super(s); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ArbitraryRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ArbitraryRecord.java new file mode 100755 index 0000000..7038876 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ArbitraryRecord.java @@ -0,0 +1,69 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; + +/** + * Writes out some arbitrary record data. Used during the debug process + */ +class ArbitraryRecord extends WritableRecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ArbitraryRecord.class); + + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param type the biff code + * @param d the data + */ + public ArbitraryRecord(int type, byte[] d) { + super(Type.createType(type)); + + data = d; + logger.warn("ArbitraryRecord of type " + type + " created"); + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BOFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BOFRecord.java new file mode 100755 index 0000000..ec24f80 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BOFRecord.java @@ -0,0 +1,114 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record to indicate the beginning of a new stream in the Compound + * File + */ +class BOFRecord extends WritableRecordData { + public final static WorkbookGlobalsBOF workbookGlobals + = new WorkbookGlobalsBOF(); + public final static SheetBOF sheet = new SheetBOF(); + + /** + * The data to write to the file + */ + private final byte[] data; + + /** + * Constructor for generating a workbook globals BOF record + * + * @param dummy - a dummy argument for overloading purposes + */ + public BOFRecord(WorkbookGlobalsBOF dummy) { + super(Type.BOF); + + // Create the data as biff 8 format with a substream type of + // workbook globals + data = new byte[] + {(byte) 0x0, + (byte) 0x6, + (byte) 0x5, // substream type + (byte) 0x0, // substream type + (byte) 0xf2, // rupBuild + (byte) 0x15, // rupBuild + (byte) 0xcc, // rupYear + (byte) 0x07, // rupYear + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x6, // sfo + (byte) 0x0, // sfo, + (byte) 0x0, // sfo + (byte) 0x0 // sfo + }; + } + /** + * Constructor for generating a sheet BOF record + * + * @param dummy - a dummy argument for overloading purposes + */ + public BOFRecord(SheetBOF dummy) { + super(Type.BOF); + + // Create the data as biff 8 format with a substream type of + // worksheet + data = new byte[] + {(byte) 0x0, + (byte) 0x6, + (byte) 0x10, // substream type + (byte) 0x0, // substream type + (byte) 0xf2, // rupBuild + (byte) 0x15, // rupBuild + (byte) 0xcc, // rupYear + (byte) 0x07, // rupYear + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x6, // sfo + (byte) 0x0, // sfo, + (byte) 0x0, // sfo + (byte) 0x0 // sfo + }; + } + + /** + * Gets the data for writing to the output file + * + * @return the binary data for writing + */ + public byte[] getData() { + return data; + } + + // Dummy types for constructor overloading + private static class WorkbookGlobalsBOF { + } + + private static class SheetBOF { + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BackupRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BackupRecord.java new file mode 100755 index 0000000..45da57d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BackupRecord.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates whether Excel should save backup versions of the + * file + */ +class BackupRecord extends WritableRecordData { + /** + * Flag to indicate whether or not Excel should make backups + */ + private final boolean backup; + /** + * The data array + */ + private final byte[] data; + + /** + * Constructor + * + * @param bu backup flag + */ + public BackupRecord(boolean bu) { + super(Type.BACKUP); + + backup = bu; + + // Hard code in an unprotected workbook + data = new byte[2]; + + if (backup) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Returns the binary data for writing to the output file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BlankRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BlankRecord.java new file mode 100755 index 0000000..8cdab87 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BlankRecord.java @@ -0,0 +1,101 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.Cell; +import jxl.CellType; +import jxl.biff.Type; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * A blank record, which is used to contain formatting information + */ +public abstract class BlankRecord extends CellValue { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(BlankRecord.class); + + /** + * Consructor used when creating a label from the user API + * + * @param c the column + * @param cont the contents + * @param r the row + */ + protected BlankRecord(int c, int r) { + super(Type.BLANK, c, r); + } + + /** + * Constructor used when creating a label from the API. This is + * overloaded to allow formatting information to be passed to the record + * + * @param c the column + * @param r the row + * @param st the format applied to the cell + */ + protected BlankRecord(int c, int r, CellFormat st) { + super(Type.BLANK, c, r, st); + } + + /** + * Constructor used when copying a formatted blank cell from a read only + * spreadsheet + * + * @param c the blank cell to copy + */ + protected BlankRecord(Cell c) { + super(Type.BLANK, c); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param b the record to copy + */ + protected BlankRecord(int c, int r, BlankRecord br) { + super(Type.BLANK, c, r, br); + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.EMPTY; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * + * @return the contents of this cell as a string + */ + public String getContents() { + return ""; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BookboolRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BookboolRecord.java new file mode 100755 index 0000000..8d21708 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BookboolRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Writes out the workbook option flag (should it save the external + * link options) + */ +class BookboolRecord extends WritableRecordData { + /** + * The external link option flag + */ + private final boolean externalLink; + /** + * The binary data to write out + */ + private final byte[] data; + + /** + * Constructor + * + * @param extlink the external link options flag + */ + public BookboolRecord(boolean extlink) { + super(Type.BOOKBOOL); + + externalLink = extlink; + data = new byte[2]; + + if (!externalLink) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BooleanRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BooleanRecord.java new file mode 100755 index 0000000..cdbcfc1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BooleanRecord.java @@ -0,0 +1,150 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + + +import jxl.BooleanCell; +import jxl.CellType; +import jxl.biff.Type; +import jxl.format.CellFormat; + + +/** + * A boolean cell's last calculated value + */ +public abstract class BooleanRecord extends CellValue { + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param val the value + */ + protected BooleanRecord(int c, int r, boolean val) { + super(Type.BOOLERR, c, r); + value = val; + } + + /** + * Overloaded constructor invoked from the API, which takes a cell + * format + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + protected BooleanRecord(int c, int r, boolean val, CellFormat st) { + super(Type.BOOLERR, c, r, st); + value = val; + } + + /** + * Constructor used when copying a workbook + * + * @param nc the number to copy + */ + protected BooleanRecord(BooleanCell nc) { + super(Type.BOOLERR, nc); + value = nc.getValue(); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param br the record to copy + */ + protected BooleanRecord(int c, int r, BooleanRecord br) { + super(Type.BOOLERR, c, r, br); + value = br.value; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() { + return value; + } + + /** + * Sets the value + * + * @param val the boolean value + */ + protected void setValue(boolean val) { + value = val; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.BOOLEAN; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 2]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + + if (value) { + data[celldata.length] = 1; + } + + return data; + } + +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BottomMarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BottomMarginRecord.java new file mode 100755 index 0000000..d7b2bdd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BottomMarginRecord.java @@ -0,0 +1,31 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class BottomMarginRecord extends MarginRecord { + BottomMarginRecord(double v) { + super(Type.BOTTOMMARGIN, v); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BoundsheetRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BoundsheetRecord.java new file mode 100755 index 0000000..a626301 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BoundsheetRecord.java @@ -0,0 +1,110 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which stores the sheet name, the sheet type and the stream + * position + */ +class BoundsheetRecord extends WritableRecordData { + /** + * Hidden flag + */ + private boolean hidden; + + /** + * Chart only flag + */ + private boolean chartOnly; + + /** + * The name of the sheet + */ + private final String name; + + /** + * The data to write to the output file + */ + private byte[] data; + + /** + * Constructor + * + * @param n the sheet name + */ + public BoundsheetRecord(String n) { + super(Type.BOUNDSHEET); + name = n; + hidden = false; + chartOnly = false; + } + + /** + * Sets the hidden flag + */ + void setHidden() { + hidden = true; + } + + /** + * Sets the chart only flag + */ + void setChartOnly() { + chartOnly = true; + } + + /** + * Gets the data to write out to the binary file + * + * @return the data to write out + */ + public byte[] getData() { + data = new byte[name.length() * 2 + 8]; + + if (chartOnly) { + data[5] = 0x02; + } else { + data[5] = 0; // set stream type to worksheet + } + + if (hidden) { + data[4] = 0x1; + data[5] = 0x0; + } + + data[6] = (byte) name.length(); + data[7] = 1; + StringHelper.getUnicodeBytes(name, data, 8); + + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ButtonPropertySetRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ButtonPropertySetRecord.java new file mode 100755 index 0000000..8d1a8e6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ButtonPropertySetRecord.java @@ -0,0 +1,68 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Any arbitrary excel record. Used during development only + */ +class ButtonPropertySetRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + */ + public ButtonPropertySetRecord(jxl.read.biff.ButtonPropertySetRecord bps) { + super(Type.BUTTONPROPERTYSET); + + data = bps.getData(); + } + + /** + * Constructor + */ + public ButtonPropertySetRecord(ButtonPropertySetRecord bps) { + super(Type.BUTTONPROPERTYSET); + + data = bps.getData(); + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CalcCountRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CalcCountRecord.java new file mode 100755 index 0000000..dda09ae --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CalcCountRecord.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which stores the maximum iterations option from the Options + * dialog box + */ +class CalcCountRecord extends WritableRecordData { + /** + * The iteration count + */ + private final int calcCount; + /** + * The binary data to write to the output file + */ + private byte[] data; + + /** + * Constructor + * + * @param cnt the count indicator + */ + public CalcCountRecord(int cnt) { + super(Type.CALCCOUNT); + calcCount = cnt; + } + + + /** + * Gets the data to write out to the file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[2]; + + IntegerHelper.getTwoBytes(calcCount, data, 0); + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CalcModeRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CalcModeRecord.java new file mode 100755 index 0000000..81ee47a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CalcModeRecord.java @@ -0,0 +1,87 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The calculation mode for the workbook, as set from the Options + * dialog box + */ +class CalcModeRecord extends WritableRecordData { + /** + * Manual calculation + */ + static CalcMode manual = new CalcMode(0); + /** + * Automatic calculation + */ + static CalcMode automatic = new CalcMode(1); + /** + * Automatic calculation, except tables + */ + static CalcMode automaticNoTables = new CalcMode(-1); + /** + * The calculation mode (manual, automatic) + */ + private final CalcMode calculationMode; + /** + * Constructor + * + * @param cm the calculation mode + */ + public CalcModeRecord(CalcMode cm) { + super(Type.CALCMODE); + calculationMode = cm; + } + + /** + * Gets the binary to data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[2]; + + IntegerHelper.getTwoBytes(calculationMode.value, data, 0); + + return data; + } + + private static class CalcMode { + /** + * The indicator as written to the output file + */ + int value; + + /** + * Constructor + * + * @param m + */ + public CalcMode(int m) { + value = m; + } + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CellValue.java b/datastructures-xslx/src/main/java/jxl/write/biff/CellValue.java new file mode 100755 index 0000000..87dbb52 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CellValue.java @@ -0,0 +1,595 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellReferenceHelper; +import jxl.Sheet; +import jxl.biff.DVParser; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.biff.XFRecord; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Comment; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; +import jxl.write.WritableCell; +import jxl.write.WritableCellFeatures; +import jxl.write.WritableWorkbook; + +/** + * Abstract class which stores the jxl.common.data used for cells, such + * as row, column and formatting information. + * Any record which directly represents the contents of a cell, such + * as labels and numbers, are derived from this class + * data store + */ +public abstract class CellValue extends WritableRecordData + implements WritableCell { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CellValue.class); + + /** + * The row in the worksheet at which this cell is located + */ + private int row; + + /** + * The column in the worksheet at which this cell is located + */ + private int column; + + /** + * The format applied to this cell + */ + private XFRecord format; + + /** + * A handle to the formatting records, used in case we want + * to change the format of the cell once it has been added + * to the spreadsheet + */ + private FormattingRecords formattingRecords; + + /** + * A flag to indicate that this record is already referenced within + * a worksheet + */ + private boolean referenced; + + /** + * A handle to the sheet + */ + private WritableSheetImpl sheet; + + /** + * The cell features + */ + private WritableCellFeatures features; + + /** + * Internal copied flag, to prevent cell features being added multiple + * times to the drawing array + */ + private boolean copied; + + /** + * Constructor used when building writable cells from the Java API + * + * @param c the column + * @param t the type indicator + * @param r the row + */ + protected CellValue(Type t, int c, int r) { + this(t, c, r, WritableWorkbook.NORMAL_STYLE); + copied = false; + } + + /** + * Constructor used when creating a writable cell from a read-only cell + * (when copying a workbook) + * + * @param c the cell to clone + * @param t the type of this cell + */ + protected CellValue(Type t, Cell c) { + this(t, c.getColumn(), c.getRow()); + copied = true; + + format = (XFRecord) c.getCellFormat(); + + if (c.getCellFeatures() != null) { + features = new WritableCellFeatures(c.getCellFeatures()); + features.setWritableCell(this); + } + } + + /** + * Overloaded constructor used when building writable cells from the + * Java API which also takes a format + * + * @param c the column + * @param t the cell type + * @param r the row + * @param st the format to apply to this cell + */ + protected CellValue(Type t, int c, int r, CellFormat st) { + super(t); + row = r; + column = c; + format = (XFRecord) st; + referenced = false; + copied = false; + } + + /** + * Copy constructor + * + * @param c the column + * @param t the cell type + * @param r the row + * @param cv the value to copy + */ + protected CellValue(Type t, int c, int r, CellValue cv) { + super(t); + row = r; + column = c; + format = cv.format; + referenced = false; + copied = false; // used during a deep copy, so the cell features need + // to be added again + + if (cv.features != null) { + features = new WritableCellFeatures(cv.features); + features.setWritableCell(this); + } + } + + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + public int getRow() { + return row; + } + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + public int getColumn() { + return column; + } + + /** + * Indicates whether or not this cell is hidden, by virtue of either + * the entire row or column being collapsed + * + * @return TRUE if this cell is hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Gets the data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + byte[] mydata = new byte[6]; + IntegerHelper.getTwoBytes(row, mydata, 0); + IntegerHelper.getTwoBytes(column, mydata, 2); + IntegerHelper.getTwoBytes(format.getXFIndex(), mydata, 4); + return mydata; + } + + /** + * Called when the cell is added to the worksheet in order to indicate + * that this object is already added to the worksheet + * This method also verifies that the associated formats and formats + * have been initialized correctly + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s the sheet this is being added to + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) { + referenced = true; + sheet = s; + formattingRecords = fr; + + addCellFormat(); + addCellFeatures(); + } + + /** + * Internal method to see if this cell is referenced within the workbook. + * Once this has been placed in the workbook, it becomes immutable + * + * @return TRUE if this cell has been added to a sheet, FALSE otherwise + */ + final boolean isReferenced() { + return referenced; + } + + /** + * Gets the internal index of the formatting record + * + * @return the index of the format record + */ + final int getXFIndex() { + return format.getXFIndex(); + } + + /** + * API method which gets the format applied to this cell + * + * @return the format for this cell + */ + public CellFormat getCellFormat() { + return format; + } + + /** + * An API function which sets the format to apply to this cell + * + * @param cf the format to apply to this cell + */ + public void setCellFormat(CellFormat cf) { + format = (XFRecord) cf; + + // If the referenced flag has not been set, this cell has not + // been added to the spreadsheet, so we don't need to perform + // any further logic + if (!referenced) { + return; + } + + // The cell has already been added to the spreadsheet, so the + // formattingRecords reference must be initialized + Assert.verify(formattingRecords != null); + + addCellFormat(); + } + + /** + * Increments the row of this cell by one. Invoked by the sheet when + * inserting rows + */ + void incrementRow() { + row++; + + if (features != null) { + Comment c = features.getCommentDrawing(); + if (c != null) { + c.setX(column); + c.setY(row); + } + } + } + + /** + * Decrements the row of this cell by one. Invoked by the sheet when + * removing rows + */ + void decrementRow() { + row--; + + if (features != null) { + Comment c = features.getCommentDrawing(); + if (c != null) { + c.setX(column); + c.setY(row); + } + + if (features.hasDropDown()) { + logger.warn("need to change value for drop down drawing"); + } + } + } + + /** + * Increments the column of this cell by one. Invoked by the sheet when + * inserting columns + */ + void incrementColumn() { + column++; + + if (features != null) { + Comment c = features.getCommentDrawing(); + if (c != null) { + c.setX(column); + c.setY(row); + } + } + + } + + /** + * Decrements the column of this cell by one. Invoked by the sheet when + * removing columns + */ + void decrementColumn() { + column--; + + if (features != null) { + Comment c = features.getCommentDrawing(); + if (c != null) { + c.setX(column); + c.setY(row); + } + } + + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(Sheet s, int sheetIndex, int col) { + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnRemoved(Sheet s, int sheetIndex, int col) { + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(Sheet s, int sheetIndex, int row) { + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the row was removed + * @param sheetIndex the sheet index on which the column was removed + * @param row the column number which was removed + */ + void rowRemoved(Sheet s, int sheetIndex, int row) { + } + + /** + * Accessor for the sheet containing this cell + * + * @return the sheet containing this cell + */ + public WritableSheetImpl getSheet() { + return sheet; + } + + /** + * Adds the format information to the shared records. Performs the necessary + * checks (and clones) to ensure that the formats are not shared. + * Called from setCellDetails and setCellFormat + */ + private void addCellFormat() { + // Check to see if the format is one of the shared Workbook defaults. If + // so, then get hold of the Workbook's specific instance + Styles styles = sheet.getWorkbook().getStyles(); + format = styles.getFormat(format); + + try { + if (!format.isInitialized()) { + formattingRecords.addStyle(format); + } + } catch (NumFormatRecordsException e) { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + format = styles.getNormalStyle(); + } + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(WritableCellFeatures cf) { + if (features != null) { + logger.warn("current cell features for " + + CellReferenceHelper.getCellReference(this) + + " not null - overwriting"); + + // Check to see if the features include a shared data validation + if (features.hasDataValidation() && + features.getDVParser() != null && + features.getDVParser().extendedCellsValidation()) { + DVParser dvp = features.getDVParser(); + logger.warn("Cannot add cell features to " + + CellReferenceHelper.getCellReference(this) + + " because it is part of the shared cell validation " + + "group " + + CellReferenceHelper.getCellReference(dvp.getFirstColumn(), + dvp.getFirstRow()) + + "-" + + CellReferenceHelper.getCellReference(dvp.getLastColumn(), + dvp.getLastRow())); + return; + } + } + + features = cf; + cf.setWritableCell(this); + + // If the cell is already on the worksheet, then add the cell features + // to the workbook + if (referenced) { + addCellFeatures(); + } + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public WritableCellFeatures getWritableCellFeatures() { + return features; + } + + /** + * Handles any addition cell features, such as comments or data + * validation. Called internally from this class when a cell is + * added to the workbook, and also externally from BaseCellFeatures + * following a call to setComment + */ + public final void addCellFeatures() { + if (features == null) { + return; + } + + if (copied == true) { + copied = false; + + return; + } + + if (features.getComment() != null) { + Comment comment = new Comment(features.getComment(), + column, row); + comment.setWidth(features.getCommentWidth()); + comment.setHeight(features.getCommentHeight()); + sheet.addDrawing(comment); + sheet.getWorkbook().addDrawing(comment); + features.setCommentDrawing(comment); + } + + if (features.hasDataValidation()) { + try { + features.getDVParser().setCell(column, + row, + sheet.getWorkbook(), + sheet.getWorkbook(), + sheet.getWorkbookSettings()); + } catch (jxl.biff.formula.FormulaException e) { + Assert.verify(false); + } + + sheet.addValidationCell(this); + if (!features.hasDropDown()) { + return; + } + + // Get the combo box drawing object for list validations + if (sheet.getComboBox() == null) { + // Need to add the combo box the first time, since even though + // it doesn't need a separate Sp entry, it still needs to increment + // the shape id + ComboBox cb = new ComboBox(); + sheet.addDrawing(cb); + sheet.getWorkbook().addDrawing(cb); + sheet.setComboBox(cb); + } + + features.setComboBox(sheet.getComboBox()); + } + } + + /** + * Internal function invoked by WritableSheetImpl called when shared data + * validation is removed + */ + public final void removeCellFeatures() { + /* + // Remove the comment + features.removeComment(); + + // Remove the data validation + features.removeDataValidation(); + */ + + features = null; + } + + + /** + * Called by the cell features to remove a comment + * + * @param c the comment to remove + */ + public final void removeComment(Comment c) { + sheet.removeDrawing(c); + } + + /** + * Called by the cell features to remove the data validation + */ + public final void removeDataValidation() { + sheet.removeDataValidation(this); + } + + /** + * Called when doing a copy of a writable object to indicate the source + * was writable than a read only copy and certain things (most notably + * the comments will need to be re-evaluated) + * + * @param boolean the copied flag + */ + final void setCopied(boolean c) { + copied = c; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CellXFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CellXFRecord.java new file mode 100755 index 0000000..07bd727 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CellXFRecord.java @@ -0,0 +1,214 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DisplayFormat; +import jxl.biff.FontRecord; +import jxl.biff.XFRecord; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.Orientation; +import jxl.format.Pattern; +import jxl.format.VerticalAlignment; +import jxl.write.WriteException; + +/** + * A cell XF Record + */ +public class CellXFRecord extends XFRecord { + /** + * Constructor + * + * @param fnt the font + * @param form the format + */ + protected CellXFRecord(FontRecord fnt, DisplayFormat form) { + super(fnt, form); + setXFDetails(XFRecord.cell, 0); + } + + /** + * Copy constructor. Invoked when copying formats to handle cell merging + * + * @param fmt the format to copy + */ + CellXFRecord(XFRecord fmt) { + super(fmt); + setXFDetails(XFRecord.cell, 0); + } + + /** + * A public copy constructor which can be used for copy formats between + * different sheets + */ + protected CellXFRecord(CellFormat format) { + super(format); + } + + /** + * Sets the alignment for the cell + * + * @param a the alignment + * @throws WriteException + */ + public void setAlignment(Alignment a) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFAlignment(a); + } + + /** + * Sets the background for the cell + * + * @param c the background colour + * @param p the background patter + * @throws WriteException + */ + public void setBackground(Colour c, Pattern p) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFBackground(c, p); + super.setXFCellOptions(0x4000); + } + + /** + * Sets whether or not this XF record locks the cell + * + * @param l the locked flag + * @throws WriteException + */ + public void setLocked(boolean l) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFLocked(l); + super.setXFCellOptions(0x8000); + } + + /** + * Sets the indentation of the cell text + * + * @param i the indentation + */ + public void setIndentation(int i) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFIndentation(i); + } + + /** + * Sets the shrink to fit flag + * + * @param b the shrink to fit flag + */ + public void setShrinkToFit(boolean s) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFShrinkToFit(s); + } + + /** + * Sets the vertical alignment for cells with this style + * + * @param va the vertical alignment + * @throws WriteException + */ + public void setVerticalAlignment(VerticalAlignment va) + throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setXFVerticalAlignment(va); + } + + /** + * Sets the text orientation for cells with this style + * + * @param o the orientation + * @throws WriteException + */ + public void setOrientation(Orientation o) + throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setXFOrientation(o); + } + + /** + * Sets the text wrapping for cells with this style. If the parameter is + * set to TRUE, then data in this cell will be wrapped around, and the + * cell's height adjusted accordingly + * + * @param w the wrap + * @throws WriteException + */ + public void setWrap(boolean w) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setXFWrap(w); + } + + /** + * Sets the border style for cells with this format + * + * @param b the border + * @param ls the line for the specified border + * @throws WriteException + */ + public void setBorder(Border b, BorderLineStyle ls, Colour c) + throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + if (b == Border.ALL) { + // Apply to all + super.setXFBorder(Border.LEFT, ls, c); + super.setXFBorder(Border.RIGHT, ls, c); + super.setXFBorder(Border.TOP, ls, c); + super.setXFBorder(Border.BOTTOM, ls, c); + return; + } + + if (b == Border.NONE) { + // Apply to all + super.setXFBorder(Border.LEFT, BorderLineStyle.NONE, Colour.BLACK); + super.setXFBorder(Border.RIGHT, BorderLineStyle.NONE, Colour.BLACK); + super.setXFBorder(Border.TOP, BorderLineStyle.NONE, Colour.BLACK); + super.setXFBorder(Border.BOTTOM, BorderLineStyle.NONE, Colour.BLACK); + return; + } + + super.setXFBorder(b, ls, c); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CodepageRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CodepageRecord.java new file mode 100755 index 0000000..73ff9fe --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CodepageRecord.java @@ -0,0 +1,53 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the default character set in operation when the workbook was + * saved + */ +class CodepageRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + */ + public CodepageRecord() { + super(Type.CODEPAGE); + + // Hard code inthe ANSI character set for Microsoft + data = new byte[]{(byte) 0xe4, (byte) 0x4}; + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ColumnInfoRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ColumnInfoRecord.java new file mode 100755 index 0000000..bc14e47 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ColumnInfoRecord.java @@ -0,0 +1,389 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + + +import jxl.biff.FormattingRecords; +import jxl.biff.IndexMapping; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.biff.XFRecord; + +/** + * Describes the column formatting for a particular column + */ +class ColumnInfoRecord extends WritableRecordData { + /** + * The binary data + */ + private byte[] data; + /** + * The column number which this format applies to + */ + private int column; + /** + * The style for the column + */ + private XFRecord style; + /** + * The index for the style of this column + */ + private int xfIndex; + + /** + * The width of the column in 1/256 of a character + */ + private int width; + + /** + * Flag to indicate the hidden status of this column + */ + private boolean hidden; + + /** + * The column's outline level + */ + private int outlineLevel; + + /** + * Column collapsed flag + */ + private boolean collapsed; + + + /** + * Constructor used when setting column information from the user + * API + * + * @param w the width of the column in characters + * @param col the column to format + * @param xf the style for the column + */ + public ColumnInfoRecord(int col, int w, XFRecord xf) { + super(Type.COLINFO); + + column = col; + width = w; + style = xf; + xfIndex = style.getXFIndex(); + hidden = false; + } + + /** + * Copy constructor used when copying from sheet to sheet within the + * same workbook + * + * @param the record to copy + */ + public ColumnInfoRecord(ColumnInfoRecord cir) { + super(Type.COLINFO); + + column = cir.column; + width = cir.width; + style = cir.style; + xfIndex = cir.xfIndex; + hidden = cir.hidden; + outlineLevel = cir.outlineLevel; + collapsed = cir.collapsed; + + } + + + /** + * Constructor used when copying an existing spreadsheet + * + * @param col the column number + * @param cir the column info record read in + * @param fr the format records + */ + public ColumnInfoRecord(jxl.read.biff.ColumnInfoRecord cir, + int col, + FormattingRecords fr) { + super(Type.COLINFO); + + column = col; + width = cir.getWidth(); + xfIndex = cir.getXFIndex(); + style = fr.getXFRecord(xfIndex); + outlineLevel = cir.getOutlineLevel(); + collapsed = cir.getCollapsed(); + } + + /** + * Constructor used when importing a sheet from another + * spreadsheet + * + * @param col the column number + * @param cir the column info record read in + */ + public ColumnInfoRecord(jxl.read.biff.ColumnInfoRecord cir, + int col) { + super(Type.COLINFO); + + column = col; + width = cir.getWidth(); + xfIndex = cir.getXFIndex(); + outlineLevel = cir.getOutlineLevel(); + collapsed = cir.getCollapsed(); + } + + /** + * Gets the column this format applies to + * + * @return the column which is formatted + */ + public int getColumn() { + return column; + } + + /** + * Increments the column. Called when inserting a new column into + * the sheet + */ + public void incrementColumn() { + column++; + } + + /** + * Decrements the column. Called when removing a column from + * the sheet + */ + public void decrementColumn() { + column--; + } + + /** + * Accessor for the width + * + * @return the width + */ + int getWidth() { + return width; + } + + /** + * Sets the width. Used when autosizing columns + * + * @param w the new width + */ + void setWidth(int w) { + width = w; + } + + /** + * Gets the binary data to be written to the output file + * + * @return the data to write to file + */ + public byte[] getData() { + data = new byte[0x0c]; + + IntegerHelper.getTwoBytes(column, data, 0); + IntegerHelper.getTwoBytes(column, data, 2); + IntegerHelper.getTwoBytes(width, data, 4); + IntegerHelper.getTwoBytes(xfIndex, data, 6); + + // int options = 0x2; + int options = 0x6 | (outlineLevel << 8); + if (hidden) { + options |= 0x1; + } + + outlineLevel = ((options & 0x700) / 0x100); + + if (collapsed) { + options |= 0x1000; + } + + IntegerHelper.getTwoBytes(options, data, 8); + // IntegerHelper.getTwoBytes(2, data, 10); + + return data; + } + + /** + * Gets the cell format associated with this column info record + * + * @return the cell format for this column + */ + public XFRecord getCellFormat() { + return style; + } + + /** + * Sets the cell format. Used when importing spreadsheets + * + * @param xfr the xf record + */ + public void setCellFormat(XFRecord xfr) { + style = xfr; + } + + /** + * Accessor for the xf index, used when importing from another spreadsheet + * + * @return the xf index + */ + public int getXfIndex() { + return xfIndex; + } + + /** + * Rationalizes the sheets xf index mapping + * + * @param xfmapping the index mapping + */ + void rationalize(IndexMapping xfmapping) { + xfIndex = xfmapping.getNewIndex(xfIndex); + } + + /** + * Accessor for the hidden flag + * + * @return TRUE if this column is hidden, FALSE otherwise + */ + boolean getHidden() { + return hidden; + } + + /** + * Sets this column to be hidden (or otherwise) + * + * @param h TRUE if the column is to be hidden, FALSE otherwise + */ + void setHidden(boolean h) { + hidden = h; + } + + /** + * Standard equals method + * + * @return TRUE if these objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof ColumnInfoRecord)) { + return false; + } + + ColumnInfoRecord cir = (ColumnInfoRecord) o; + + if (column != cir.column || + xfIndex != cir.xfIndex || + width != cir.width || + hidden != cir.hidden || + outlineLevel != cir.outlineLevel || + collapsed != cir.collapsed) { + return false; + } + + if ((style == null && cir.style != null) || + (style != null && cir.style == null)) { + return false; + } + + return style.equals(cir.style); + } + + /** + * Standard hashCode method + * + * @return the hashCode + */ + public int hashCode() { + int hashValue = 137; + int oddPrimeNumber = 79; + + hashValue = hashValue * oddPrimeNumber + column; + hashValue = hashValue * oddPrimeNumber + xfIndex; + hashValue = hashValue * oddPrimeNumber + width; + hashValue = hashValue * oddPrimeNumber + (hidden ? 1 : 0); + + if (style != null) { + hashValue ^= style.hashCode(); + } + + return hashValue; + } + + /** + * Accessor for the column's outline level + * + * @return the column's outline level + */ + public int getOutlineLevel() { + return outlineLevel; + } + + /** + * Sets the column's outline level + * + * @param level the column's outline level + */ + public void setOutlineLevel(int level) { + outlineLevel = level; + } + + /** + * Accessor for whether the column is collapsed + * + * @return the column's collapsed state + */ + public boolean getCollapsed() { + return collapsed; + } + + /** + * Sets the column's collapsed state + * + * @param value the column's collapsed state + */ + public void setCollapsed(boolean value) { + collapsed = value; + } + + /** + * Increments the column's outline level. This is how groups are made + * as well + */ + public void incrementOutlineLevel() { + outlineLevel++; + } + + /** + * Decrements the column's outline level. This removes it from a + * grouping level. If + * all outline levels are gone the uncollapse the column. + */ + public void decrementOutlineLevel() { + if (0 < outlineLevel) { + outlineLevel--; + } + + if (0 == outlineLevel) { + collapsed = false; + } + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ColumnsExceededException.java b/datastructures-xslx/src/main/java/jxl/write/biff/ColumnsExceededException.java new file mode 100755 index 0000000..e4b5cd0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ColumnsExceededException.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +/** + * Exception thrown when attempting to add a column to a spreadsheet which + * has already reached the maximum amount + */ +public class ColumnsExceededException extends JxlWriteException { + /** + * Constructor + */ + public ColumnsExceededException() { + super(maxColumnsExceeded); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java new file mode 100755 index 0000000..2cee9d6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java @@ -0,0 +1,1056 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + *

+ * Header block is -1 + * Excel data is e..n (where e is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private final OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private final ExcelDataOutput excelData; + + /** + * The size of the array + */ + private final int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private final int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private final int numExtensionBlocks; + + /** + * The extension block for the header + */ + private final int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private final int excelDataBlocks; + + /** + * The start block of the root entry + */ + private final int rootStartBlock; + + /** + * The start block of the excel data + */ + private final int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private final int bbdStartBlock; + + /** + * The start block of the small block depot + */ + private int sbdStartBlockChain; + + /** + * The start block of the small block depot + */ + private int sbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The number of small blocks + */ + private int numSmallBlocks; + + /** + * The total number of property sets in this compound file + */ + private final int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * The map of standard property sets, keyed on name + */ + private HashMap standardPropertySets; + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(ExcelDataOutput data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + + if (additionalPropertySets != null) { + numSmallBlockDepotChainBlocks = getBigBlocksRequired(numSmallBlocks * 4); + numSmallBlockDepotBlocks = getBigBlocksRequired + (numSmallBlocks * SMALL_BLOCK_SIZE); + + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) { + requiredSize = SMALL_BLOCK_THRESHOLD; + } else { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + + // Do the calculations + excelDataBlocks = requiredSize / BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS) / 4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numSmallBlockDepotBlocks + + numSmallBlockDepotChainBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil((double) totalBlocks / + (double) (BIG_BLOCK_SIZE / 4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil((double) totalBlocks / + (double) (BIG_BLOCK_SIZE / 4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1) { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE / 4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil((double) totalBlocks / + (double) (BIG_BLOCK_SIZE / 4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } else { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + // Set the start block of the small block depot + sbdStartBlock = -2; + if (additionalPropertySets != null && numSmallBlockDepotBlocks != 0) { + sbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + } + + // Set the sbd chain start block to be after the excel data and the + // small block depot + sbdStartBlockChain = -2; + + if (sbdStartBlock != -2) { + sbdStartBlockChain = sbdStartBlock + numSmallBlockDepotBlocks; + } + + // Set the bbd start block to be after all the excel data + if (sbdStartBlockChain != -2) { + bbdStartBlock = sbdStartBlockChain + + numSmallBlockDepotChainBlocks; + } else { + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + } + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @param readCompoundFile the file read in + * @throws CopyAdditionalPropertySetsException + * @throws IOException + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException { + if (readCompoundFile == null) { + return; + } + + additionalPropertySets = new ArrayList(); + standardPropertySets = new HashMap(); + int blocksRequired = 0; + + int numPropertySets = readCompoundFile.getNumberOfPropertySets(); + + for (int i = 0; i < numPropertySets; i++) { + PropertyStorage ps = readCompoundFile.getPropertySet(i); + + boolean standard = false; + + if (ps.name.equalsIgnoreCase(ROOT_ENTRY_NAME)) { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + standardPropertySets.put(ROOT_ENTRY_NAME, rps); + } + + // See if it is a standard property set + for (int j = 0; j < STANDARD_PROPERTY_SETS.length && !standard; j++) { + if (ps.name.equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) { + // See if it comes directly off the root entry + PropertyStorage ps2 = readCompoundFile.findPropertyStorage(ps.name); + Assert.verify(ps2 != null); + + if (ps2 == ps) { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + standardPropertySets.put(STANDARD_PROPERTY_SETS[j], rps); + } + } + } + + if (!standard) { + try { + byte[] data = null; + if (ps.size > 0) { + data = readCompoundFile.getStream(i); + } else { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + additionalPropertySets.add(rps); + + if (data.length > SMALL_BLOCK_THRESHOLD) { + int blocks = getBigBlocksRequired(data.length); + blocksRequired += blocks; + } else { + int blocks = getSmallBlocksRequired(data.length); + numSmallBlocks += blocks; + } + } catch (BiffException e) { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + /** + * Writes out the excel file in OLE compound file format + * + * @throws IOException + */ + public void write() throws IOException { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeSmallBlockDepot(); + writeSmallBlockDepotChain(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException { + if (additionalPropertySets == null) { + return; + } + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + if (data.length > SMALL_BLOCK_THRESHOLD) { + int numBlocks = getBigBlocksRequired(data.length); + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @throws IOException + */ + private void writeExcelData() throws IOException { + excelData.writeData(out); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @throws IOException + */ + private void writeDocumentSummaryData() throws IOException { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @throws IOException + */ + private void writeSummaryData() throws IOException { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @throws IOException + */ + private void writeHeader() throws IOException { + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain + IntegerHelper.getFourBytes(sbdStartBlockChain, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the number of blocks in the small block depot chain + IntegerHelper.getFourBytes(numSmallBlockDepotChainBlocks, + headerBlock, + NUM_SMALL_BLOCK_DEPOT_BLOCKS_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS) / 4); + int blocksWritten = 0; + + for (int i = 0; i < blocksToWrite; i++) { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE / 4 - 1); + + for (int j = 0; j < blocksToWrite; j++) { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock + 1; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos += 4; + } + + if (numExtensionBlocks > 0) { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @throws IOException + */ + private void checkBbdPos() throws IOException { + if (bbdPos >= BIG_BLOCK_SIZE) { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @throws IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos) / 4); + + for (int i = 0; i < bbdBlocks; i++) { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos += 4; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @throws IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException { + if (additionalPropertySets == null) { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks + 16; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + if (rps.data.length > SMALL_BLOCK_THRESHOLD) { + int numBlocks = getBigBlocksRequired(rps.data.length); + + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + } + + /** + * Writes out the chains for the small block depot + */ + private void writeSmallBlockDepotChain() throws IOException { + if (sbdStartBlockChain == -2) { + return; + } + + byte[] smallBlockDepotChain = + new byte[numSmallBlockDepotChainBlocks * BIG_BLOCK_SIZE]; + + int pos = 0; + int sbdBlockNumber = 1; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + if (rps.data.length <= SMALL_BLOCK_THRESHOLD && + rps.data.length != 0) { + int numSmallBlocks = getSmallBlocksRequired(rps.data.length); + for (int j = 0; j < numSmallBlocks - 1; j++) { + IntegerHelper.getFourBytes(sbdBlockNumber, + smallBlockDepotChain, + pos); + pos += 4; + sbdBlockNumber++; + } + + // Write out the end of chain + IntegerHelper.getFourBytes(-2, smallBlockDepotChain, pos); + pos += 4; + sbdBlockNumber++; + } + } + + out.write(smallBlockDepotChain); + } + + /** + * Writes out all the data in the small block depot + * + * @throws + */ + private void writeSmallBlockDepot() throws IOException { + if (additionalPropertySets == null) { + return; + } + + byte[] smallBlockDepot = + new byte[numSmallBlockDepotBlocks * BIG_BLOCK_SIZE]; + + int pos = 0; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + if (rps.data.length <= SMALL_BLOCK_THRESHOLD) { + int smallBlocks = getSmallBlocksRequired(rps.data.length); + int length = smallBlocks * SMALL_BLOCK_SIZE; + System.arraycopy(rps.data, 0, smallBlockDepot, pos, rps.data.length); + pos += length; + } + } + + out.write(smallBlockDepot); + } + + /** + * Writes out the Big Block Depot + * + * @throws IOException + */ + private void writeBigBlockDepot() throws IOException { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0; i < numExtensionBlocks; i++) { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + if (sbdStartBlock != -2) { + // Write out the block chain for the small block depot + writeBlockChain(sbdStartBlock, numSmallBlockDepotBlocks); + + // Write out the block chain for the small block depot chain + writeBlockChain(sbdStartBlockChain, numSmallBlockDepotChainBlocks); + } + + // The Big Block Depot immediately follows. Denote these as a special + // block + for (int i = 0; i < numBigBlockDepotBlocks; i++) { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0) ? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0) ? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @throws IOException + */ + private void writePropertySets() throws IOException { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0; i < STANDARD_PROPERTY_SETS.length; i++) { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(STANDARD_PROPERTY_SETS[i]); + + if (rps != null) { + mappings[rps.number] = i; + } else { + logger.warn("Standard property set " + STANDARD_PROPERTY_SETS[i] + + " not present in source file"); + } + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int child = 0; + int previous = 0; + int next = 0; + + // Compute the size of the root property set + int size = 0; + + if (additionalPropertySets != null) { + // Workbook + size += getBigBlocksRequired(requiredSize) * BIG_BLOCK_SIZE; + + // The two information blocks + size += getBigBlocksRequired(SMALL_BLOCK_THRESHOLD) * BIG_BLOCK_SIZE; + size += getBigBlocksRequired(SMALL_BLOCK_THRESHOLD) * BIG_BLOCK_SIZE; + + // Additional property sets + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + if (rps.propertyStorage.type != 1) { + if (rps.propertyStorage.size >= SMALL_BLOCK_THRESHOLD) { + size += getBigBlocksRequired(rps.propertyStorage.size) * + BIG_BLOCK_SIZE; + } else { + size += getSmallBlocksRequired(rps.propertyStorage.size) * + SMALL_BLOCK_SIZE; + } + } + } + } + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(sbdStartBlock); + ps.setSize(size); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + child = 1; + if (additionalPropertySets != null) { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(ROOT_ENTRY_NAME); + child = mappings[rps.propertyStorage.child]; + } + ps.setChild(child); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // always use a big block stream - none of that messing around + // with small blocks + + previous = 3; + next = -1; + + if (additionalPropertySets != null) { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(WORKBOOK_NAME); + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setChild(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(SUMMARY_INFORMATION_NAME); + + if (rps != null) { + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + } + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setChild(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setChild(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Write out the additional property sets + if (additionalPropertySets == null) { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16; + int smallBlock = 0; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int block = rps.data.length > SMALL_BLOCK_THRESHOLD ? + bigBlock : smallBlock; + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(block); + ps.setSize(rps.propertyStorage.size); + // ps.setColour(rps.propertyStorage.colour); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + child = rps.propertyStorage.child != -1 ? + mappings[rps.propertyStorage.child] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setChild(child); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + if (rps.data.length > SMALL_BLOCK_THRESHOLD) { + bigBlock += getBigBlocksRequired(rps.data.length); + } else { + smallBlock += getSmallBlocksRequired(rps.data.length); + } + } + + out.write(propertySetStorage); + } + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) { + propertyStorage = ps; + data = d; + number = n; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java3 b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java3 new file mode 100755 index 0000000..a54ac83 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java3 @@ -0,0 +1,1020 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.HashMap; + +import common.Assert; +import common.Logger; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + * + * Header block is -1 + * Excel data is e..n (where is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private byte[] excelData; + + /** + * The size of the array + */ + private int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private int numExtensionBlocks; + + /** + * The extension block for the header + */ + private int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private int excelDataBlocks; + + /** + * The start block of the root entry + */ + private int rootStartBlock; + + /** + * The start block of the excel data + */ + private int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private int bbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The total number of property sets in this compound file + */ + private int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * A hash map of the original property sets keyed on name + */ + private HashMap readPropertySets; + + /** + * The array of standard property set mappings + */ + private int[] standardPropertySetMappings; + + private ReadPropertyStorage rootEntryPropertySet; + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage + { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) + { + propertyStorage = ps; + data = d; + number = n; + } + } + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(byte[] data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException + { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + if (additionalPropertySets != null) + { + try + { + rootEntryPropertySet = new ReadPropertyStorage(rcf.getPropertySet(ROOT_ENTRY_NAME), rcf.getStream(ROOT_ENTRY_NAME), 0); + int blocks = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + additionalPropertyBlocks += blocks; + } + catch(BiffException e) + { + e.printStackTrace(); + } + + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + logger.debug("root entry requires " + numRootEntryBlocks + " blocks"); + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) + { + requiredSize = SMALL_BLOCK_THRESHOLD; + } + else + { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + // logger.debug("smallBlockDepot requires " + numSmallBlockDepotBlocks + " big blocks"); + + // Do the calculations + excelDataBlocks = requiredSize/BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1 ) + { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE/4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } + else + { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + logger.debug("excelDataStartBlock " + excelDataStartBlock); + + // Set the bbd start block to be after all the excel data + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + + logger.debug("bbdStartBlock " + bbdStartBlock); + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) + { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @return the number of blocks needed to store these property sets + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException + { + if (readCompoundFile == null) + { + return; + } + + additionalPropertySets = new ArrayList(); + readPropertySets = new HashMap(); + + String[] psnames = readCompoundFile.getPropertySetNames(); + int blocksRequired = 0; + standardPropertySetMappings = new int[STANDARD_PROPERTY_SETS.length]; + + for (int i = 0 ; i < psnames.length ; i++) + { + // Add it to the hash map for later + PropertyStorage ps = readCompoundFile.getPropertySet(psnames[i]); + + // If the name is non standard, then retrieve the property set + // information + boolean standard = false; + for (int j = 0 ; j < STANDARD_PROPERTY_SETS.length && !standard ; j++) + { + if (psnames[i].equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) + { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + readPropertySets.put(psnames[i], rps); + } + } + + if (!standard) + { + try + { + byte[] data = null; + if (ps.size > 0 ) + { + data = readCompoundFile.getStream(ps.name); + } + else + { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + readPropertySets.put(psnames[i], rps); + additionalPropertySets.add(rps); + + int blocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + blocksRequired += blocks; + } + catch (BiffException e) + { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + + /** + * Writes out the excel file in OLE compound file format + * + * @exception IOException + */ + public void write() throws IOException + { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + + logger.debug("Writing property set " + rootEntryPropertySet.propertyStorage.name); + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize2 = numBlocks2 * BIG_BLOCK_SIZE; + + out.write(rootEntryPropertySet.data, 0, rootEntryPropertySet.data.length); + + byte[] padding2 = new byte[requiredSize2 - rootEntryPropertySet.data.length]; + out.write(padding2, 0, padding2.length); + + logger.debug("data length " + rootEntryPropertySet.data.length + " Padding " + padding2.length); + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ;) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + logger.debug("Writing property set " + rps.propertyStorage.name); + int numBlocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @exception IOException + */ + private void writeExcelData() throws IOException + { + logger.debug("num excel data blocks " + excelDataBlocks + " excelData size " + requiredSize); + out.write(excelData, 0, size); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @exception IOException + */ + private void writeDocumentSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @exception IOException + */ + private void writeSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @exception IOException + */ + private void writeHeader() throws IOException + { + logger.debug("num extensions blocks for header: " + numExtensionBlocks); + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain to -2 ie. no small block chain + IntegerHelper.getFourBytes(-2, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS)/4); + int extensionBlock = 0; + int blocksWritten = 0; + + for (int i = 0 ; i < blocksToWrite; i++) + { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) + { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) + { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE/4 -1); + + for(int j = 0 ; j < blocksToWrite; j++) + { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock+1 ; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos +=4; + } + + if (numExtensionBlocks > 0) + { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) + { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @exception IOException + */ + private void checkBbdPos() throws IOException + { + if (bbdPos >= BIG_BLOCK_SIZE) + { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @exception IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException + { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) + { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos)/4); + + for (int i = 0 ; i < bbdBlocks; i++) + { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos +=4 ; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @exception IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks + 16; + + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname2 = rootEntryPropertySet.propertyStorage.name; + logger.debug("writing big block chain for " + psname2 + " block " + blockNumber + " numBlocks " + numBlocks2); + writeBlockChain(blockNumber, numBlocks2); + blockNumber += numBlocks2; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int numBlocks = rps.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rps.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname = rps.propertyStorage.name; + logger.debug("writing big block chain for " + psname + " block " + blockNumber + " numBlocks " + numBlocks); + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + + /** + * Writes out the Big Block Depot + * + * @exception IOException + */ + private void writeBigBlockDepot() throws IOException + { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0 ; i < numExtensionBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + // The Big Block Depot immediately follows the document summary. Denote + // these as a special block + for (int i = 0; i < numBigBlockDepotBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) + { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) + { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) + { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) + { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @exception IOException + */ + private void writePropertySets() throws IOException + { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) + { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0 ; i < STANDARD_PROPERTY_SETS.length ; i++) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(STANDARD_PROPERTY_SETS[i]); + mappings[rps.number] = i; + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int dir = 0; + int previous = 0; + int next = 0; + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(-2); + ps.setSize(0); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + dir = 2; + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(ROOT_ENTRY_NAME); + dir = mappings[rps.propertyStorage.directory]; + } + ps.setDirectory(dir); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // alway use a big block stream - none of that messing around + // with small blocks + ps.setColour(1); + + previous = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(WORKBOOK_NAME); + previous = mappings[rps.propertyStorage.previous]; + } + + ps.setPrevious(previous); + ps.setNext(-1); + ps.setDirectory(-1); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setColour(1); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(SUMMARY_INFORMATION_NAME); + + previous = rps.propertyStorage.previous != - 1 ? + mappings[rps.propertyStorage.previous] : -1 ; + next = rps.propertyStorage.next != - 1 ? + mappings[rps.propertyStorage.next] : -1 ; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(-1); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setDirectory(-1); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + + // Write out the additional property sets + if (additionalPropertySets == null) + { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16 + + 18; + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(bigBlock); + ps.setSize(Math.max(rps.propertyStorage.size, SMALL_BLOCK_THRESHOLD)); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + dir = rps.propertyStorage.directory != -1 ? + mappings[rps.propertyStorage.directory] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(dir); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + if (rps.data.length >= SMALL_BLOCK_THRESHOLD) + { + bigBlock += getBigBlocksRequired(rps.data.length); + } + else + { + bigBlock += SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + } + } + + out.write(propertySetStorage); + } +} + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java4 b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java4 new file mode 100755 index 0000000..30dc0f4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java4 @@ -0,0 +1,1025 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.HashMap; + +import common.Assert; +import common.Logger; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + * + * Header block is -1 + * Excel data is e..n (where is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private byte[] excelData; + + /** + * The size of the array + */ + private int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private int numExtensionBlocks; + + /** + * The extension block for the header + */ + private int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private int excelDataBlocks; + + /** + * The start block of the root entry + */ + private int rootStartBlock; + + /** + * The start block of the excel data + */ + private int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private int bbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The total number of property sets in this compound file + */ + private int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * A hash map of the original property sets keyed on name + */ + private HashMap readPropertySets; + + /** + * The array of standard property set mappings + */ + private int[] standardPropertySetMappings; + + private ReadPropertyStorage rootEntryPropertySet; + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage + { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) + { + propertyStorage = ps; + data = d; + number = n; + } + } + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(byte[] data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException + { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + if (additionalPropertySets != null) + { + /* + try + { + rootEntryPropertySet = new ReadPropertyStorage(rcf.getPropertySet(ROOT_ENTRY_NAME), rcf.getStream(ROOT_ENTRY_NAME), 0); + int blocks = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + additionalPropertyBlocks += blocks; + } + catch(BiffException e) + { + e.printStackTrace(); + } + */ + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + logger.debug("root entry requires " + numRootEntryBlocks + " blocks"); + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) + { + requiredSize = SMALL_BLOCK_THRESHOLD; + } + else + { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + // logger.debug("smallBlockDepot requires " + numSmallBlockDepotBlocks + " big blocks"); + + // Do the calculations + excelDataBlocks = requiredSize/BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1 ) + { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE/4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } + else + { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + logger.debug("excelDataStartBlock " + excelDataStartBlock); + + // Set the bbd start block to be after all the excel data + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + + logger.debug("bbdStartBlock " + bbdStartBlock); + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) + { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @return the number of blocks needed to store these property sets + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException + { + if (readCompoundFile == null) + { + return; + } + + additionalPropertySets = new ArrayList(); + readPropertySets = new HashMap(); + + String[] psnames = readCompoundFile.getPropertySetNames(); + int blocksRequired = 0; + standardPropertySetMappings = new int[STANDARD_PROPERTY_SETS.length]; + + for (int i = 0 ; i < psnames.length ; i++) + { + // Add it to the hash map for later + PropertyStorage ps = readCompoundFile.getPropertySet(psnames[i]); + + // If the name is non standard, then retrieve the property set + // information + boolean standard = false; + for (int j = 0 ; j < STANDARD_PROPERTY_SETS.length && !standard ; j++) + { + if (psnames[i].equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) + { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + readPropertySets.put(psnames[i], rps); + } + } + + if (!standard) + { + try + { + byte[] data = null; + if (ps.size > 0 ) + { + data = readCompoundFile.getStream(ps.name); + } + else + { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + readPropertySets.put(psnames[i], rps); + additionalPropertySets.add(rps); + + int blocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + blocksRequired += blocks; + } + catch (BiffException e) + { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + + /** + * Writes out the excel file in OLE compound file format + * + * @exception IOException + */ + public void write() throws IOException + { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + + /* + logger.debug("Writing property set " + rootEntryPropertySet.propertyStorage.name); + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize2 = numBlocks2 * BIG_BLOCK_SIZE; + + out.write(rootEntryPropertySet.data, 0, rootEntryPropertySet.data.length); + + byte[] padding2 = new byte[requiredSize2 - rootEntryPropertySet.data.length]; + out.write(padding2, 0, padding2.length); + + logger.debug("data length " + rootEntryPropertySet.data.length + " Padding " + padding2.length); + */ + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ;) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + logger.debug("Writing property set " + rps.propertyStorage.name); + int numBlocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @exception IOException + */ + private void writeExcelData() throws IOException + { + logger.debug("num excel data blocks " + excelDataBlocks + " excelData size " + requiredSize); + out.write(excelData, 0, size); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @exception IOException + */ + private void writeDocumentSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @exception IOException + */ + private void writeSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @exception IOException + */ + private void writeHeader() throws IOException + { + logger.debug("num extensions blocks for header: " + numExtensionBlocks); + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain to -2 ie. no small block chain + IntegerHelper.getFourBytes(-2, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS)/4); + int extensionBlock = 0; + int blocksWritten = 0; + + for (int i = 0 ; i < blocksToWrite; i++) + { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) + { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) + { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE/4 -1); + + for(int j = 0 ; j < blocksToWrite; j++) + { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock+1 ; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos +=4; + } + + if (numExtensionBlocks > 0) + { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) + { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @exception IOException + */ + private void checkBbdPos() throws IOException + { + if (bbdPos >= BIG_BLOCK_SIZE) + { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @exception IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException + { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) + { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos)/4); + + for (int i = 0 ; i < bbdBlocks; i++) + { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos +=4 ; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @exception IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks + 16; + + /* + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname2 = rootEntryPropertySet.propertyStorage.name; + logger.debug("writing big block chain for " + psname2 + " block " + blockNumber + " numBlocks " + numBlocks2); + writeBlockChain(blockNumber, numBlocks2); + blockNumber += numBlocks2; + */ + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int numBlocks = rps.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rps.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname = rps.propertyStorage.name; + logger.debug("writing big block chain for " + psname + " block " + blockNumber + " numBlocks " + numBlocks); + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + + /** + * Writes out the Big Block Depot + * + * @exception IOException + */ + private void writeBigBlockDepot() throws IOException + { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0 ; i < numExtensionBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + // The Big Block Depot immediately follows the document summary. Denote + // these as a special block + for (int i = 0; i < numBigBlockDepotBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) + { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) + { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) + { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) + { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @exception IOException + */ + private void writePropertySets() throws IOException + { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) + { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0 ; i < STANDARD_PROPERTY_SETS.length ; i++) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(STANDARD_PROPERTY_SETS[i]); + mappings[rps.number] = i; + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int dir = 0; + int previous = 0; + int next = 0; + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(-2); + ps.setSize(0); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + dir = 2; + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(ROOT_ENTRY_NAME); + dir = mappings[rps.propertyStorage.directory]; + } + ps.setDirectory(dir); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // alway use a big block stream - none of that messing around + // with small blocks + ps.setColour(1); + + previous = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(WORKBOOK_NAME); + previous = mappings[rps.propertyStorage.previous]; + } + + ps.setPrevious(previous); + ps.setNext(-1); + ps.setDirectory(-1); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + // ps.setColour(1); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(SUMMARY_INFORMATION_NAME); + + previous = rps.propertyStorage.previous != - 1 ? + mappings[rps.propertyStorage.previous] : -1 ; + next = rps.propertyStorage.next != - 1 ? + mappings[rps.propertyStorage.next] : -1 ; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(-1); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setDirectory(-1); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + + // Write out the additional property sets + if (additionalPropertySets == null) + { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16 + + 18; + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(bigBlock); + ps.setSize(Math.max(rps.propertyStorage.size, SMALL_BLOCK_THRESHOLD)); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + dir = rps.propertyStorage.directory != -1 ? + mappings[rps.propertyStorage.directory] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(dir); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + if (rps.data.length >= SMALL_BLOCK_THRESHOLD) + { + bigBlock += getBigBlocksRequired(rps.data.length); + } + else + { + bigBlock += SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + } + } + + out.write(propertySetStorage); + } +} + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CopyAdditionalPropertySetsException.java b/datastructures-xslx/src/main/java/jxl/write/biff/CopyAdditionalPropertySetsException.java new file mode 100755 index 0000000..30799d1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CopyAdditionalPropertySetsException.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +/** + * Exception thrown when attempting to copy a workbook which contains + * additional property sets + */ +public class CopyAdditionalPropertySetsException extends JxlWriteException { + /** + * Constructor + */ + public CopyAdditionalPropertySetsException() { + super(copyPropertySets); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CountryRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CountryRecord.java new file mode 100755 index 0000000..d5b7fc3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CountryRecord.java @@ -0,0 +1,80 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.CountryCode; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + + +/** + * Record containing the localization information + */ +class CountryRecord extends WritableRecordData { + /** + * The user interface language + */ + private final int language; + + /** + * The regional settings + */ + private final int regionalSettings; + + /** + * Constructor + */ + public CountryRecord(CountryCode lang, CountryCode r) { + super(Type.COUNTRY); + + language = lang.getValue(); + regionalSettings = r.getValue(); + } + + public CountryRecord(jxl.read.biff.CountryRecord cr) { + super(Type.COUNTRY); + + language = cr.getLanguageCode(); + regionalSettings = cr.getRegionalSettingsCode(); + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[4]; + + IntegerHelper.getTwoBytes(language, data, 0); + IntegerHelper.getTwoBytes(regionalSettings, data, 2); + + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DBCellRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DBCellRecord.java new file mode 100755 index 0000000..dd514ad --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DBCellRecord.java @@ -0,0 +1,116 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Indexes the first row record of the block and each individual cell. + * This is invoked by the sheets write process + */ +class DBCellRecord extends WritableRecordData { + /** + * The file position of the first Row record in this block + */ + private final int rowPos; + + /** + * The position of the start of the next cell after the first row. This + * is used as the offset for the cell positions + */ + private int cellOffset; + + /** + * The list of all cell positions in this block + */ + private final ArrayList cellRowPositions; + + /** + * The position of this record in the file. Vital for calculating offsets + */ + private int position; + + /** + * Constructor + * + * @param rp the position of this row + */ + public DBCellRecord(int rp) { + super(Type.DBCELL); + rowPos = rp; + cellRowPositions = new ArrayList(10); + } + + /** + * Sets the offset of this cell record within the sheet stream + * + * @param pos the offset + */ + void setCellOffset(int pos) { + cellOffset = pos; + } + + /** + * Adds a cell + * + * @param pos + */ + void addCellRowPosition(int pos) { + cellRowPositions.add(new Integer(pos)); + } + + /** + * Sets the position of this cell within the sheet stream + * + * @param pos the position + */ + void setPosition(int pos) { + position = pos; + } + + /** + * Gets the binary data for this cell record + * + * @return the binary data + */ + protected byte[] getData() { + byte[] data = new byte[4 + 2 * cellRowPositions.size()]; + + // Set the offset to the first row + IntegerHelper.getFourBytes(position - rowPos, data, 0); + + // Now add in all the cell offsets + int pos = 4; + int lastCellPos = cellOffset; + Iterator i = cellRowPositions.iterator(); + while (i.hasNext()) { + int cellPos = ((Integer) i.next()).intValue(); + IntegerHelper.getTwoBytes(cellPos - lastCellPos, data, pos); + lastCellPos = cellPos; + pos += 2; + } + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DSFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DSFRecord.java new file mode 100755 index 0000000..8a3ff90 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DSFRecord.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores a flag which indicates whether the file is a double stream + * file. For files written by JExcelAPI, this FALSE + */ +class DSFRecord extends WritableRecordData { + /** + * The data to be written to the binary file + */ + private final byte[] data; + + /** + * Constructor + */ + public DSFRecord() { + super(Type.DSF); + + // Hard code the fact that this is most assuredly not a double + // stream file + data = new byte[]{0, 0}; + } + + /** + * The binary data to be written out + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DateFormatRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DateFormatRecord.java new file mode 100755 index 0000000..2f370a2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DateFormatRecord.java @@ -0,0 +1,47 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.FormatRecord; + +/** + * A class which contains a date format + */ +public class DateFormatRecord extends FormatRecord { + /** + * Constructor + * + * @param fmt the date format + */ + protected DateFormatRecord(String fmt) { + super(); + + // Do the replacements in the format string + String fs = fmt; + + fs = replace(fs, "a", "AM/PM"); + fs = replace(fs, "S", "0"); + + setFormatString(fs); + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DateRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DateRecord.java new file mode 100755 index 0000000..35ffbbb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DateRecord.java @@ -0,0 +1,320 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; +import jxl.CellType; +import jxl.DateCell; +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.common.Logger; +import jxl.format.CellFormat; +import jxl.write.DateFormats; +import jxl.write.WritableCellFormat; + +/** + * A date stored in the database + */ +public abstract class DateRecord extends CellValue { + /** + * This is package protected so that the worksheet might detect + * whether or not to override it with the column cell format + */ + static final WritableCellFormat defaultDateFormat = + new WritableCellFormat(DateFormats.DEFAULT); + /** + * + */ + private final static int utcOffsetDays = 25569; + /** + * + */ + private final static long msInADay = 24 * 60 * 60 * 1000; + /** + * + */ + private final static int nonLeapDay = 61; + + // The number of days between 01 Jan 1900 and 01 Jan 1970 - this gives + // the UTC offset + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DateRecord.class); + + // The number of milliseconds in a day + /** + * The excel value of the date + */ + private double value; + /** + * The java representation of the date + */ + private Date date; + + // The number of days between 1 Jan 1900 and 1 March 1900. Excel thinks + // the day before this was 29th Feb 1900, but it was 28th Feb 19000. + // I guess the programmers thought nobody would notice that they + // couldn't be bothered to program this dating anomaly properly + /** + * Indicates whether this is a full date, or just a time only + */ + private boolean time; + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param d the date + */ + protected DateRecord(int c, int r, Date d) { + this(c, r, d, defaultDateFormat, false); + } + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param d the date + * @param a adjust timezone + */ + protected DateRecord(int c, int r, Date d, GMTDate a) { + this(c, r, d, defaultDateFormat, false); + } + + /** + * Constructor invoked from the user API + * + * @param c the column + * @param r the row + * @param st the format for the date + * @param d the date + */ + protected DateRecord(int c, int r, Date d, CellFormat st) { + super(Type.NUMBER, c, r, st); + date = d; + calculateValue(true); + } + + /** + * Constructor invoked from the user API + * + * @param c the column + * @param r the row + * @param st the format for the date + * @param d the date + * @param a adjust for the timezone + */ + protected DateRecord(int c, int r, Date d, CellFormat st, GMTDate a) { + super(Type.NUMBER, c, r, st); + date = d; + calculateValue(false); + } + + /** + * Constructor invoked from the API + * + * @param c the column + * @param r the row + * @param st the date format + * @param tim time indicator + * @param d the date + */ + protected DateRecord(int c, int r, Date d, CellFormat st, boolean tim) { + super(Type.NUMBER, c, r, st); + date = d; + time = tim; + calculateValue(false); + } + + /** + * Constructor invoked when copying a readable spreadsheet + * + * @param dc the date to copy + */ + protected DateRecord(DateCell dc) { + super(Type.NUMBER, dc); + date = dc.getDate(); + time = dc.isTime(); + calculateValue(false); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param dr the record to copy + */ + protected DateRecord(int c, int r, DateRecord dr) { + super(Type.NUMBER, c, r, dr); + value = dr.value; + time = dr.time; + date = dr.date; + } + + /** + * Calculates the 1900 based numerical value based upon the utc value held + * in the date object + * + * @param adjust TRUE if we want to incorporate timezone information + * into the raw UTC date eg. when copying from a spreadsheet + */ + private void calculateValue(boolean adjust) { + // Offsets for current time zone + long zoneOffset = 0; + long dstOffset = 0; + + // Get the timezone and dst offsets if we want to take these into + // account + if (adjust) { + // Get the current calender, replete with timezone information + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + + zoneOffset = cal.get(Calendar.ZONE_OFFSET); + dstOffset = cal.get(Calendar.DST_OFFSET); + } + + long utcValue = date.getTime() + zoneOffset + dstOffset; + + // Convert this to the number of days, plus fractions of a day since + // 01 Jan 1970 + double utcDays = (double) utcValue / (double) msInADay; + + // Add in the offset to get the number of days since 01 Jan 1900 + value = utcDays + utcOffsetDays; + + // Work round a bug in excel. Excel seems to think there is a date + // called the 29th Feb, 1900 - but this was not a leap year. + // Therefore for values less than 61, we must subtract 1. Only do + // this for full dates, not times + if (!time && value < nonLeapDay) { + value -= 1; + } + + // If this refers to a time, then get rid of the integer part + if (time) { + value = value - (int) value; + } + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.DATE; + } + + /** + * Gets the binary data for writing + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 8]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + DoubleHelper.getIEEEBytes(value, data, celldata.length); + + return data; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() { + return date.toString(); + } + + /** + * Sets the date in this cell, taking the timezone into account + * + * @param d the date + * @param a adjust for timezone + */ + protected void setDate(Date d, GMTDate a) { + date = d; + calculateValue(false); + } + + /** + * Gets the date contained in this cell + * + * @return the cell contents + */ + public Date getDate() { + return date; + } + + /** + * Sets the date in this cell + * + * @param d the date + */ + protected void setDate(Date d) { + date = d; + calculateValue(true); + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time. When writing a cell, all dates are fully defined, + * even if they refer to a time + * + * @return FALSE if this is full date, TRUE if a time + */ + public boolean isTime() { + return time; + } + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat() { + return null; + } + + /** + * Class definition for a dummy variable + */ + protected static final class GMTDate { + public GMTDate() { + } + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DefaultColumnWidth.java b/datastructures-xslx/src/main/java/jxl/write/biff/DefaultColumnWidth.java new file mode 100755 index 0000000..09bf5d0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DefaultColumnWidth.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The default column width for a workbook + */ +class DefaultColumnWidth extends WritableRecordData { + /** + * The default column width + */ + private final int width; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param w the default column width + */ + public DefaultColumnWidth(int w) { + super(Type.DEFCOLWIDTH); + width = w; + data = new byte[2]; + IntegerHelper.getTwoBytes(width, data, 0); + } + + /** + * Gets the binary data for writing to the stream + * + * @return the binary data + */ + protected byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DefaultRowHeightRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DefaultRowHeightRecord.java new file mode 100755 index 0000000..5dccd54 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DefaultRowHeightRecord.java @@ -0,0 +1,73 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The default row height for cells in the workbook + */ +class DefaultRowHeightRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * The default row height + */ + private final int rowHeight; + + /** + * Indicates whether or not the default row height has been changed + */ + private final boolean changed; + + /** + * Constructor + * + * @param height the default row height + * @param ch TRUE if the default value has been changed, false + * otherwise + */ + public DefaultRowHeightRecord(int h, boolean ch) { + super(Type.DEFAULTROWHEIGHT); + data = new byte[4]; + rowHeight = h; + changed = ch; + } + + /** + * Gets the binary data for writing to the output stream + * + * @return the binary data + */ + public byte[] getData() { + if (changed) { + data[0] |= 0x1; + } + + IntegerHelper.getTwoBytes(rowHeight, data, 2); + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DeltaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DeltaRecord.java new file mode 100755 index 0000000..c84ab53 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DeltaRecord.java @@ -0,0 +1,76 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which stores the maximum change value from the Options + * dialog + */ +class DeltaRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * The number of iterations + */ + private final double iterationValue; + + /** + * Constructor + * + * @param itval + */ + public DeltaRecord(double itval) { + super(Type.DELTA); + iterationValue = itval; + + data = new byte[8]; + } + + + /** + * Gets the binary data for writing to the output file + * + * @return the binary data + */ + public byte[] getData() { + DoubleHelper.getIEEEBytes(iterationValue, data, 0); + + /* long val = Double.doubleToLongBits(iterationValue); + data[0] = (byte) (val & 0xff); + data[1] = (byte) ((val & 0xff00) >> 8); + data[2] = (byte) ((val & 0xff0000) >> 16); + data[3] = (byte) ((val & 0xff000000) >> 24); + data[4] = (byte) ((val & 0xff00000000L) >> 32); + data[5] = (byte) ((val & 0xff0000000000L) >> 40); + data[6] = (byte) ((val & 0xff000000000000L) >> 48); + data[7] = (byte) ((val & 0xff00000000000000L) >> 56) ; + */ + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DimensionRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DimensionRecord.java new file mode 100755 index 0000000..2b81984 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DimensionRecord.java @@ -0,0 +1,70 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which contains the bounds of the sheet + */ +class DimensionRecord extends WritableRecordData { + /** + * The number of rows in the sheet + */ + private final int numRows; + /** + * The number of columns in the sheet + */ + private final int numCols; + + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param c the number of columns + * @param r the number of rows + */ + public DimensionRecord(int r, int c) { + super(Type.DIMENSION); + numRows = r; + numCols = c; + + data = new byte[14]; + + IntegerHelper.getFourBytes(numRows, data, 4); + IntegerHelper.getTwoBytes(numCols, data, 10); + } + + /** + * Gets the binary data to be written to the output file + * + * @return the binary data + */ + protected byte[] getData() { + return data; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/EOFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/EOFRecord.java new file mode 100755 index 0000000..04747d0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/EOFRecord.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which marks the end of the current stream + */ +class EOFRecord extends WritableRecordData { + /** + * Constructor + */ + public EOFRecord() { + super(Type.EOF); + } + + /** + * Returns the binary data to be written to the output file + * + * @return the binary data + */ + public byte[] getData() { + return new byte[0]; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Excel9FileRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/Excel9FileRecord.java new file mode 100755 index 0000000..b098949 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/Excel9FileRecord.java @@ -0,0 +1,47 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates whether this file has been generated by excel 2000 + * should be refreshed when the workbook is loaded + */ +class Excel9FileRecord extends WritableRecordData { + /** + * Constructor + * + * @param template flag + */ + public Excel9FileRecord() { + super(Type.EXCEL9FILE); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return new byte[0]; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ExcelDataOutput.java b/datastructures-xslx/src/main/java/jxl/write/biff/ExcelDataOutput.java new file mode 100755 index 0000000..9225028 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ExcelDataOutput.java @@ -0,0 +1,61 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Interface to abstract away an in-memory output or a temporary file + * output. Used by the File object + */ +interface ExcelDataOutput { + /** + * Appends the bytes to the end of the output + * + * @param d the data to write to the end of the array + */ + void write(byte[] bytes) throws IOException; + + /** + * Gets the current position within the file + * + * @return the position within the file + */ + int getPosition() throws IOException; + + /** + * Sets the data at the specified position to the contents of the array + * + * @param pos the position to alter + * @param newdata the data to modify + */ + void setData(byte[] newdata, int pos) throws IOException; + + /** + * Writes the data to the output stream + */ + void writeData(OutputStream out) throws IOException; + + /** + * Called when the final compound file has been written + */ + void close() throws IOException; +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ExtendedSSTRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ExtendedSSTRecord.java new file mode 100755 index 0000000..f4cf1bb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ExtendedSSTRecord.java @@ -0,0 +1,108 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Indicates an extension to the Shared String Table. Currently this + * contains blank records + *

+ * Thanks to Guenther for contributing a proper implementation of the EXTSST + * record, replacing my previous dummy version + */ +class ExtendedSSTRecord extends WritableRecordData { + private static final int infoRecordSize = 8; + private final int numberOfStrings; + private final int[] absoluteStreamPositions; + private final int[] relativeStreamPositions; + private int currentStringIndex = 0; + + /** + * Constructor + * + * @param numstrings the number of strings per bucket + * @param streampos the absolute stream position of the beginning of + * the SST record + */ + public ExtendedSSTRecord(int newNumberOfStrings) { + super(Type.EXTSST); + numberOfStrings = newNumberOfStrings; + int numberOfBuckets = getNumberOfBuckets(); + absoluteStreamPositions = new int[numberOfBuckets]; + relativeStreamPositions = new int[numberOfBuckets]; + currentStringIndex = 0; + } + + public int getNumberOfBuckets() { + int numberOfStringsPerBucket = getNumberOfStringsPerBucket(); + return numberOfStringsPerBucket != 0 ? + (numberOfStrings + numberOfStringsPerBucket - 1) / + numberOfStringsPerBucket : 0; + } + + public int getNumberOfStringsPerBucket() { + // XXX + // should come up with a more clever calculation + // bucket limit should not be bigger than 1024, otherwise we end + // up with too many buckets and would have to write continue records + // for the EXTSST record which we want to avoid for now. + final int bucketLimit = 128; + return (numberOfStrings + bucketLimit - 1) / bucketLimit; + } + + public void addString(int absoluteStreamPosition, + int relativeStreamPosition) { + absoluteStreamPositions[currentStringIndex] = + absoluteStreamPosition + relativeStreamPosition; + relativeStreamPositions[currentStringIndex] = relativeStreamPosition; + currentStringIndex++; + } + + /** + * Gets the binary data to be written out + * + * @return the binary data + */ + public byte[] getData() { + int numberOfBuckets = getNumberOfBuckets(); + byte[] data = new byte[2 + (8 * numberOfBuckets)]; + // number of strings per bucket + IntegerHelper.getTwoBytes(getNumberOfStringsPerBucket(), data, 0); + + for (int i = 0; i < numberOfBuckets; i++) { + // absolute stream position + IntegerHelper.getFourBytes(absoluteStreamPositions[i], + data, + 2 + (i * infoRecordSize)); + // relative offset + IntegerHelper.getTwoBytes(relativeStreamPositions[i], + data, + 6 + (i * infoRecordSize)); + // reserved + // IntegerHelper.getTwoBytes(0x0, data, 8 + (i * infoRecordSize)); + } + + return data; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ExternalNameRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ExternalNameRecord.java new file mode 100755 index 0000000..38f4e1a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ExternalNameRecord.java @@ -0,0 +1,70 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; + +/** + * An external sheet record, used to maintain integrity when formulas + * are copied from read databases + */ +class ExternalNameRecord extends WritableRecordData { + /** + * The logger + */ + Logger logger = Logger.getLogger(ExternalNameRecord.class); + + /** + * The name of the addin + */ + private final String name; + + /** + * Constructor used for writable workbooks + */ + public ExternalNameRecord(String n) { + super(Type.EXTERNNAME); + name = n; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[name.length() * 2 + 12]; + + data[6] = (byte) name.length(); + data[7] = 0x1; + StringHelper.getUnicodeBytes(name, data, 8); + + int pos = 8 + name.length() * 2; + data[pos] = 0x2; + data[pos + 1] = 0x0; + data[pos + 2] = 0x1c; + data[pos + 3] = 0x17; + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ExternalSheetRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ExternalSheetRecord.java new file mode 100755 index 0000000..b21aa83 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ExternalSheetRecord.java @@ -0,0 +1,225 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * An external sheet record, used to maintain integrity when formulas + * are copied from read databases + */ +class ExternalSheetRecord extends WritableRecordData { + /** + * The underlying external sheet data + */ + private byte[] data; + + /** + * The list of XTI structures + */ + private final ArrayList xtis; + + /** + * Constructor + * + * @param esf the external sheet record to copy + */ + public ExternalSheetRecord(jxl.read.biff.ExternalSheetRecord esf) { + super(Type.EXTERNSHEET); + + xtis = new ArrayList(esf.getNumRecords()); + XTI xti = null; + for (int i = 0; i < esf.getNumRecords(); i++) { + xti = new XTI(esf.getSupbookIndex(i), + esf.getFirstTabIndex(i), + esf.getLastTabIndex(i)); + xtis.add(xti); + } + } + + /** + * Constructor used for writable workbooks + */ + public ExternalSheetRecord() { + super(Type.EXTERNSHEET); + xtis = new ArrayList(); + } + + /** + * Gets the extern sheet index for the specified parameters, creating + * a new xti record if necessary + * + * @param supbookind the internal supbook reference + * @param sheetind the sheet index + */ + int getIndex(int supbookind, int sheetind) { + Iterator i = xtis.iterator(); + XTI xti = null; + boolean found = false; + int pos = 0; + while (i.hasNext() && !found) { + xti = (XTI) i.next(); + + if (xti.supbookIndex == supbookind && + xti.firstTab == sheetind) { + found = true; + } else { + pos++; + } + } + + if (!found) { + xti = new XTI(supbookind, sheetind, sheetind); + xtis.add(xti); + pos = xtis.size() - 1; + } + + return pos; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[2 + xtis.size() * 6]; + + int pos = 0; + IntegerHelper.getTwoBytes(xtis.size(), data, 0); + pos += 2; + + Iterator i = xtis.iterator(); + XTI xti = null; + while (i.hasNext()) { + xti = (XTI) i.next(); + IntegerHelper.getTwoBytes(xti.supbookIndex, data, pos); + IntegerHelper.getTwoBytes(xti.firstTab, data, pos + 2); + IntegerHelper.getTwoBytes(xti.lastTab, data, pos + 4); + pos += 6; + } + + return data; + } + + /** + * Gets the supbook index for the specified external sheet + * + * @param the index of the supbook record + * @return the supbook index + */ + public int getSupbookIndex(int index) { + return ((XTI) xtis.get(index)).supbookIndex; + } + + /** + * Gets the first tab index for the specified external sheet + * + * @param the index of the supbook record + * @return the first tab index + */ + public int getFirstTabIndex(int index) { + return ((XTI) xtis.get(index)).firstTab; + } + + /** + * Gets the last tab index for the specified external sheet + * + * @param the index of the supbook record + * @return the last tab index + */ + public int getLastTabIndex(int index) { + return ((XTI) xtis.get(index)).lastTab; + } + + /** + * Called when a sheet has been inserted via the API + * + * @param the position of the insertion + */ + void sheetInserted(int index) { + XTI xti = null; + for (Iterator i = xtis.iterator(); i.hasNext(); ) { + xti = (XTI) i.next(); + xti.sheetInserted(index); + } + } + + /** + * Called when a sheet has been removed via the API + * + * @param the position of the insertion + */ + void sheetRemoved(int index) { + XTI xti = null; + for (Iterator i = xtis.iterator(); i.hasNext(); ) { + xti = (XTI) i.next(); + xti.sheetRemoved(index); + } + } + + /** + * An XTI structure + */ + private static class XTI { + int supbookIndex; + int firstTab; + int lastTab; + + XTI(int s, int f, int l) { + supbookIndex = s; + firstTab = f; + lastTab = l; + } + + void sheetInserted(int index) { + if (firstTab >= index) { + firstTab++; + } + + if (lastTab >= index) { + lastTab++; + } + } + + void sheetRemoved(int index) { + if (firstTab == index) { + firstTab = 0; + } + + if (lastTab == index) { + lastTab = 0; + } + + if (firstTab > index) { + firstTab--; + } + + if (lastTab > index) { + lastTab--; + } + } + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/File.java b/datastructures-xslx/src/main/java/jxl/write/biff/File.java new file mode 100755 index 0000000..8ef73be --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/File.java @@ -0,0 +1,174 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; +import jxl.WorkbookSettings; +import jxl.biff.ByteData; +import jxl.common.Logger; + +/** + * A file of excel data to be written out. All the excel data is held + * in memory, and when the close method is called a CompoundFile object + * is used to write the Biff oriented excel data in the CompoundFile + * format + */ +public final class File { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(File.class); + /** + * The read compound file. This will only be non-null if there are macros + * or other property sets of that ilk which that we should be copying + */ + jxl.read.biff.CompoundFile readCompoundFile; + /** + * The data from the excel 97 file + */ + private ExcelDataOutput data; + /** + * The current position within the file + */ + private int pos; + /** + * The output stream + */ + private OutputStream outputStream; + /** + * The initial file size + */ + private int initialFileSize; + /** + * The amount to increase the growable array by + */ + private int arrayGrowSize; + /** + * The workbook settings + */ + private final WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param os the output stream + * @param ws the configuration settings for this workbook + * @param rcf the rea compound file + */ + File(OutputStream os, WorkbookSettings ws, jxl.read.biff.CompoundFile rcf) + throws IOException { + outputStream = os; + workbookSettings = ws; + readCompoundFile = rcf; + createDataOutput(); + } + + private void createDataOutput() throws IOException { + if (workbookSettings.getUseTemporaryFileDuringWrite()) { + data = new FileDataOutput + (workbookSettings.getTemporaryFileDuringWriteDirectory()); + } else { + initialFileSize = workbookSettings.getInitialFileSize(); + arrayGrowSize = workbookSettings.getArrayGrowSize(); + + data = new MemoryDataOutput(initialFileSize, arrayGrowSize); + } + } + + /** + * Closes the file. In fact, this writes out all the excel data + * to disk using a CompoundFile object, and then frees up all the memory + * allocated to the workbook + * + * @param cs TRUE if this should close the stream, FALSE if the application + * closes it + * @throws IOException + */ + void close(boolean cs) throws IOException, JxlWriteException { + CompoundFile cf = new CompoundFile(data, + data.getPosition(), + outputStream, + readCompoundFile); + cf.write(); + + outputStream.flush(); + data.close(); + + if (cs) { + outputStream.close(); + } + + // Cleanup the memory a bit + data = null; + + if (!workbookSettings.getGCDisabled()) { + System.gc(); + } + } + + /** + * Adds the biff record data to the memory allocated for this File + * + * @param record the record to add to the excel data + * @throws IOException + */ + public void write(ByteData record) throws IOException { + byte[] bytes = record.getBytes(); + + data.write(bytes); + } + + /** + * Gets the current position within the file + * + * @return the current position + */ + int getPos() throws IOException { + return data.getPosition(); + } + + /** + * Used to manually alter the contents of the written out data. This + * is used when cross-referencing cell records + * + * @param pos the position to alter + * @param newdata the data to modify + */ + void setData(byte[] newdata, int pos) throws IOException { + data.setData(newdata, pos); + } + + /** + * Sets a new output file. This allows the same workbook to be + * written to various different output files without having to + * read in any templates again + * + * @param os the output stream + */ + public void setOutputFile(OutputStream os) throws IOException { + if (data != null) { + logger.warn("Rewriting a workbook with non-empty data"); + } + + outputStream = os; + createDataOutput(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/FileDataOutput.java b/datastructures-xslx/src/main/java/jxl/write/biff/FileDataOutput.java new file mode 100755 index 0000000..c3026be --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/FileDataOutput.java @@ -0,0 +1,114 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import jxl.common.Logger; + +/** + * Used to generate the excel biff data using a temporary file. This + * class wraps a RandomAccessFile + */ +class FileDataOutput implements ExcelDataOutput { + // The logger + private static final Logger logger = Logger.getLogger(FileDataOutput.class); + + /** + * The temporary file + */ + private final File temporaryFile; + + /** + * The excel data + */ + private final RandomAccessFile data; + + /** + * Constructor + * + * @param tmpdir the temporary directory used to write files. If this is + * NULL then the sytem temporary directory will be used + */ + public FileDataOutput(File tmpdir) throws IOException { + temporaryFile = File.createTempFile("jxl", ".tmp", tmpdir); + temporaryFile.deleteOnExit(); + data = new RandomAccessFile(temporaryFile, "rw"); + } + + /** + * Writes the bytes to the end of the array, growing the array + * as needs dictate + * + * @param d the data to write to the end of the array + */ + public void write(byte[] bytes) throws IOException { + data.write(bytes); + } + + /** + * Gets the current position within the file + * + * @return the position within the file + */ + public int getPosition() throws IOException { + // As all excel data structures are four bytes anyway, it's ok to + // truncate the long to an int + return (int) data.getFilePointer(); + } + + /** + * Sets the data at the specified position to the contents of the array + * + * @param pos the position to alter + * @param newdata the data to modify + */ + public void setData(byte[] newdata, int pos) throws IOException { + long curpos = data.getFilePointer(); + data.seek(pos); + data.write(newdata); + data.seek(curpos); + } + + /** + * Writes the data to the output stream + */ + public void writeData(OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + int length = 0; + data.seek(0); + while ((length = data.read(buffer)) != -1) { + out.write(buffer, 0, length); + } + } + + /** + * Called when the final compound file has been written + */ + public void close() throws IOException { + data.close(); + + // Explicitly delete the temporary file, since sometimes it is the case + // that a single process may be generating multiple different excel files + temporaryFile.delete(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/FooterRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/FooterRecord.java new file mode 100755 index 0000000..665c827 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/FooterRecord.java @@ -0,0 +1,84 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Places a string at the bottom of each page when the file is printed. + * JExcelApi sets this to be blank + */ +class FooterRecord extends WritableRecordData { + /** + * The binary data + */ + private byte[] data; + /** + * The footer string + */ + private final String footer; + + /** + * Consructor + * + * @param s the footer + */ + public FooterRecord(String s) { + super(Type.FOOTER); + + footer = s; + } + + /** + * Consructor invoked when copying a sheets + * + * @param fr the read footer record + */ + public FooterRecord(FooterRecord fr) { + super(Type.FOOTER); + + footer = fr.footer; + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + if (footer == null || footer.length() == 0) { + data = new byte[0]; + return data; + } + + data = new byte[footer.length() * 2 + 3]; + IntegerHelper.getTwoBytes(footer.length(), data, 0); + data[2] = (byte) 0x1; + + StringHelper.getUnicodeBytes(footer, data, 3); + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/FormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/FormulaRecord.java new file mode 100755 index 0000000..16fb8bd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/FormulaRecord.java @@ -0,0 +1,344 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.CellReferenceHelper; +import jxl.CellType; +import jxl.Sheet; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; +import jxl.write.WritableCell; + +/** + * A formula record. Parses the string passed in to deduce the set of + * formula records + */ +public class FormulaRecord extends CellValue implements FormulaData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FormulaRecord.class); + + /** + * The formula to parse + */ + private String formulaToParse; + + /** + * The formula parser + */ + private FormulaParser parser; + + /** + * The parsed formula string + */ + private String formulaString; + + /** + * The parsed formula bytes + */ + private byte[] formulaBytes; + + /** + * The location where this formula was copied from. It is used subsequently + * to adjust relative cell references + */ + private CellValue copiedFrom; + + /** + * Constructor + * + * @param f the formula to copy + */ + public FormulaRecord(int c, int r, String f) { + super(Type.FORMULA2, c, r); + formulaToParse = f; + copiedFrom = null; + } + + /** + * Constructor + * + * @param f the formula to copy + */ + public FormulaRecord(int c, int r, String f, CellFormat st) { + super(Type.FORMULA, c, r, st); + formulaToParse = f; + copiedFrom = null; + } + + /** + * Copy constructor for writable formulas + * + * @param c the column + * @param r the row + * @param fr the record to copy + */ + protected FormulaRecord(int c, int r, FormulaRecord fr) { + super(Type.FORMULA, c, r, fr); + copiedFrom = fr; + formulaBytes = new byte[fr.formulaBytes.length]; + System.arraycopy(fr.formulaBytes, 0, formulaBytes, 0, formulaBytes.length); + } + + /** + * Copy constructor for formulas read in - invoked from writable formulas + * + * @param c the column + * @param r the row + * @param rfr the formula data to copy + */ + protected FormulaRecord(int c, int r, ReadFormulaRecord rfr) { + super(Type.FORMULA, c, r, rfr); + try { + copiedFrom = rfr; + formulaBytes = rfr.getFormulaBytes(); + } catch (FormulaException e) { + // Fail silently + logger.error("", e); + } + } + + /** + * Initializes the string and the formula bytes. In order to get + * access to the workbook settings, the object is not initialized until + * it is added to the sheet + * + * @param ws the workbook settings + * @param es the external sheet + * @param nt the name table + */ + private void initialize(WorkbookSettings ws, ExternalSheet es, + WorkbookMethods nt) { + if (copiedFrom != null) { + initializeCopiedFormula(ws, es, nt); + return; + } + + parser = new FormulaParser(formulaToParse, es, nt, ws); + + try { + parser.parse(); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + } catch (FormulaException e) { + logger.warn + (e.getMessage() + + " when parsing formula " + formulaToParse + " in cell " + + getSheet().getName() + "!" + + CellReferenceHelper.getCellReference(getColumn(), getRow())); + + try { + // try again, with an error formula + formulaToParse = "ERROR(1)"; + parser = new FormulaParser(formulaToParse, es, nt, ws); + parser.parse(); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + } catch (FormulaException e2) { + // fail silently + logger.error("", e2); + } + } + } + + /** + * This formula was copied from a formula already present in the writable + * workbook. Requires special handling to sort out the cell references + * + * @param ws the workbook settings + * @param es the external sheet + * @param nt the name table + */ + private void initializeCopiedFormula(WorkbookSettings ws, + ExternalSheet es, WorkbookMethods nt) { + try { + parser = new FormulaParser(formulaBytes, this, es, nt, ws); + parser.parse(); + parser.adjustRelativeCellReferences + (getColumn() - copiedFrom.getColumn(), + getRow() - copiedFrom.getRow()); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + } catch (FormulaException e) { + try { + // try again, with an error formula + formulaToParse = "ERROR(1)"; + parser = new FormulaParser(formulaToParse, es, nt, ws); + parser.parse(); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + + } catch (FormulaException e2) { + // fail silently + logger.error("", e2); + } + } + } + + /** + * Called when the cell is added to the worksheet. Overrides the + * method in the base class in order to get a handle to the + * WorkbookSettings so that this formula may be initialized + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s the sheet this is being added to + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) { + super.setCellDetails(fr, ss, s); + initialize(s.getWorkbookSettings(), s.getWorkbook(), s.getWorkbook()); + s.getWorkbook().addRCIRCell(this); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] formulaData = getFormulaData(); + byte[] data = new byte[formulaData.length + celldata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(formulaData, 0, data, celldata.length, + formulaData.length); + return data; + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.ERROR; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() { + return formulaString; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() { + byte[] data = new byte[formulaBytes.length + 16]; + System.arraycopy(formulaBytes, 0, data, 16, formulaBytes.length); + + data[6] = (byte) 0x10; + data[7] = (byte) 0x40; + data[12] = (byte) 0xe0; + data[13] = (byte) 0xfc; + // Set the recalculate on load bit + data[8] |= 0x02; + + // Set the length of the rpn array + IntegerHelper.getTwoBytes(formulaBytes.length, data, 14); + + return data; + } + + /** + * A dummy implementation to keep the compiler quiet. This object needs + * to be instantiated from ReadFormulaRecord + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return NOTHING + */ + public WritableCell copyTo(int col, int row) { + Assert.verify(false); + return null; + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(Sheet s, int sheetIndex, int col) { + parser.columnInserted(sheetIndex, col, s == getSheet()); + formulaBytes = parser.getBytes(); + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnRemoved(Sheet s, int sheetIndex, int col) { + parser.columnRemoved(sheetIndex, col, s == getSheet()); + formulaBytes = parser.getBytes(); + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(Sheet s, int sheetIndex, int row) { + parser.rowInserted(sheetIndex, row, s == getSheet()); + formulaBytes = parser.getBytes(); + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the row was removed + * @param sheetIndex the sheet index on which the column was removed + * @param row the column number which was removed + */ + void rowRemoved(Sheet s, int sheetIndex, int row) { + parser.rowRemoved(sheetIndex, row, s == getSheet()); + formulaBytes = parser.getBytes(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/FunctionGroupCountRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/FunctionGroupCountRecord.java new file mode 100755 index 0000000..aeb2c47 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/FunctionGroupCountRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the number of build in function groups + */ +class FunctionGroupCountRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * The number of built in function groups + */ + private final int numFunctionGroups; + + /** + * Constructor + */ + public FunctionGroupCountRecord() { + super(Type.FNGROUPCOUNT); + + numFunctionGroups = 0xe; + + data = new byte[2]; + + IntegerHelper.getTwoBytes(numFunctionGroups, data, 0); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/GridSetRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/GridSetRecord.java new file mode 100755 index 0000000..0041104 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/GridSetRecord.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates that the user changed the stae of the + * GridLines option + */ +class GridSetRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * Gridset flag + */ + private final boolean gridSet; + + /** + * Constructor + * + * @param gs grid set flag + */ + public GridSetRecord(boolean gs) { + super(Type.GRIDSET); + gridSet = gs; + + data = new byte[2]; + + if (gridSet) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/GuttersRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/GuttersRecord.java new file mode 100755 index 0000000..6ceb6ef --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/GuttersRecord.java @@ -0,0 +1,112 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which contains the size of the row and column gutters. These are + * all set to zero by default + */ +class GuttersRecord extends WritableRecordData { + /** + * The binary data + */ + private byte[] data; + /** + * The rowGutter + */ + private int rowGutter; + /** + * The column gutter + */ + private int colGutter; + /** + * The maximum outline level for the row gutter + */ + private int maxRowOutline; + /** + * The maximum row outline level for the column gutter + */ + private int maxColumnOutline; + + /** + * Constructor + */ + public GuttersRecord() { + super(Type.GUTS); + } + + /** + * Gets the binary data for output + * + * @return the binary data + */ + public byte[] getData() { + data = new byte[8]; + IntegerHelper.getTwoBytes(rowGutter, data, 0); + IntegerHelper.getTwoBytes(colGutter, data, 2); + IntegerHelper.getTwoBytes(maxRowOutline, data, 4); + IntegerHelper.getTwoBytes(maxColumnOutline, data, 6); + return data; + } + + /** + * Accessor for the maximum row outline + * + * @return the maximum row outline + */ + public int getMaxRowOutline() { + return maxRowOutline; + } + + /** + * Sets the maximum row outline + * + * @param value the maximum row outline + */ + public void setMaxRowOutline(int value) { + maxRowOutline = value; + rowGutter = 1 + 14 * value; + } + + /** + * Accessor for the maximum column outline + * + * @return the maximum column outline + */ + public int getMaxColumnOutline() { + return maxColumnOutline; + } + + /** + * Sets the maximum column outline + * + * @param value the maximum row outline + */ + public void setMaxColumnOutline(int value) { + maxColumnOutline = value; + colGutter = 1 + 14 * value; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HeaderRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HeaderRecord.java new file mode 100755 index 0000000..7b8431d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HeaderRecord.java @@ -0,0 +1,83 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a print header for a work sheet + */ +class HeaderRecord extends WritableRecordData { + /** + * The binary data + */ + private byte[] data; + /** + * The print header string + */ + private final String header; + + /** + * Constructor + * + * @param s the header string + */ + public HeaderRecord(String h) { + super(Type.HEADER); + + header = h; + } + + /** + * Consructor invoked when copying a sheets + * + * @param hr the read header record + */ + public HeaderRecord(HeaderRecord hr) { + super(Type.HEADER); + + header = hr.header; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + if (header == null || header.length() == 0) { + data = new byte[0]; + return data; + } + + data = new byte[header.length() * 2 + 3]; + IntegerHelper.getTwoBytes(header.length(), data, 0); + data[2] = (byte) 0x1; + + StringHelper.getUnicodeBytes(header, data, 3); + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HideobjRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HideobjRecord.java new file mode 100755 index 0000000..b1fd757 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HideobjRecord.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores options selected in the Options dialog box + *

+ * =2 if the Hide All option is turned on + * =1 if the Show Placeholders option is turned on + * =0 if the Show All option is turned on + */ +class HideobjRecord extends WritableRecordData { + /** + * Hide object mode + */ + private final int hidemode; + + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param newHideMode the hide all flag + */ + public HideobjRecord(int newHideMode) { + super(Type.HIDEOBJ); + + hidemode = newHideMode; + data = new byte[2]; + + IntegerHelper.getTwoBytes(hidemode, data, 0); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalCentreRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalCentreRecord.java new file mode 100755 index 0000000..2e88e0f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalCentreRecord.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates the whether the horizontal center option has + * been set + */ +class HorizontalCentreRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * The centre flag + */ + private final boolean centre; + + /** + * Constructor + * + * @param ce the centre flag + */ + public HorizontalCentreRecord(boolean ce) { + super(Type.HCENTER); + + centre = ce; + + data = new byte[2]; + + if (centre) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalPageBreaksRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalPageBreaksRecord.java new file mode 100755 index 0000000..752ad1d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalPageBreaksRecord.java @@ -0,0 +1,68 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the list of explicit horizontal page breaks on the current sheet + */ +class HorizontalPageBreaksRecord extends WritableRecordData { + /** + * The row breaks + */ + private final int[] rowBreaks; + + /** + * Constructor + * + * @param break the row breaks + */ + public HorizontalPageBreaksRecord(int[] breaks) { + super(Type.HORIZONTALPAGEBREAKS); + + rowBreaks = breaks; + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[rowBreaks.length * 6 + 2]; + + // The number of breaks on the list + IntegerHelper.getTwoBytes(rowBreaks.length, data, 0); + int pos = 2; + + for (int i = 0; i < rowBreaks.length; i++) { + IntegerHelper.getTwoBytes(rowBreaks[i], data, pos); + IntegerHelper.getTwoBytes(0xff, data, pos + 4); + pos += 6; + } + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HyperlinkRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HyperlinkRecord.java new file mode 100755 index 0000000..9c99607 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HyperlinkRecord.java @@ -0,0 +1,1140 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import jxl.CellType; +import jxl.Hyperlink; +import jxl.Range; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.SheetRangeImpl; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.Label; +import jxl.write.WritableCell; +import jxl.write.WritableSheet; + + +/** + * A hyperlink + */ +public class HyperlinkRecord extends WritableRecordData { + private static final LinkType urlLink = new LinkType(); + private static final LinkType fileLink = new LinkType(); + private static final LinkType uncLink = new LinkType(); + private static final LinkType workbookLink = new LinkType(); + private static final LinkType unknown = new LinkType(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(HyperlinkRecord.class); + /** + * The first row + */ + private int firstRow; + /** + * The last row + */ + private int lastRow; + /** + * The first column + */ + private int firstColumn; + /** + * The last column + */ + private int lastColumn; + /** + * The URL referred to by this hyperlink + */ + private URL url; + /** + * The local file referred to by this hyperlink + */ + private File file; + /** + * The location in this workbook referred to by this hyperlink + */ + private String location; + /** + * The cell contents of the cell which activate this hyperlink + */ + private String contents; + /** + * The type of this hyperlink + */ + private LinkType linkType; + + /** + * The data for this hyperlink + */ + private byte[] data; + /** + * The range of this hyperlink. When creating a hyperlink, this will + * be null until the hyperlink is added to the sheet + */ + private Range range; + /** + * The sheet containing this hyperlink + */ + private WritableSheet sheet; + /** + * Indicates whether this record has been modified since it was copied + */ + private boolean modified; + /** + * Constructs this object from the readable spreadsheet + * + * @param hl the hyperlink from the read spreadsheet + */ + protected HyperlinkRecord(Hyperlink h, WritableSheet s) { + super(Type.HLINK); + + if (h instanceof jxl.read.biff.HyperlinkRecord) { + copyReadHyperlink(h, s); + } else { + copyWritableHyperlink(h, s); + } + } + + /** + * Constructs a URL hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param url the hyperlink + * @param desc the description + */ + protected HyperlinkRecord(int col, int row, + int lastcol, int lastrow, + URL url, + String desc) { + super(Type.HLINK); + + firstColumn = col; + firstRow = row; + + lastColumn = Math.max(firstColumn, lastcol); + lastRow = Math.max(firstRow, lastrow); + + this.url = url; + contents = desc; + + linkType = urlLink; + + modified = true; + } + + /** + * Constructs a File hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param file the hyperlink + * @param desc the description + */ + protected HyperlinkRecord(int col, int row, int lastcol, int lastrow, + File file, String desc) { + super(Type.HLINK); + + firstColumn = col; + firstRow = row; + + lastColumn = Math.max(firstColumn, lastcol); + lastRow = Math.max(firstRow, lastrow); + contents = desc; + + this.file = file; + + if (file.getPath().startsWith("\\\\")) { + linkType = uncLink; + } else { + linkType = fileLink; + } + + modified = true; + } + + /** + * Constructs a hyperlink to some cells within this workbook + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param desc the contents of the cell which describe this hyperlink + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + protected HyperlinkRecord(int col, int row, + int lastcol, int lastrow, + String desc, + WritableSheet s, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + super(Type.HLINK); + + firstColumn = col; + firstRow = row; + + lastColumn = Math.max(firstColumn, lastcol); + lastRow = Math.max(firstRow, lastrow); + + setLocation(s, destcol, destrow, lastdestcol, lastdestrow); + contents = desc; + + linkType = workbookLink; + + modified = true; + } + + /** + * Copies a hyperlink read in from a read only sheet + */ + private void copyReadHyperlink(Hyperlink h, WritableSheet s) { + jxl.read.biff.HyperlinkRecord hl = (jxl.read.biff.HyperlinkRecord) h; + + data = hl.getRecord().getData(); + sheet = s; + + // Populate this hyperlink with the copied data + firstRow = hl.getRow(); + firstColumn = hl.getColumn(); + lastRow = hl.getLastRow(); + lastColumn = hl.getLastColumn(); + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + + linkType = unknown; + + if (hl.isFile()) { + linkType = fileLink; + file = hl.getFile(); + } else if (hl.isURL()) { + linkType = urlLink; + url = hl.getURL(); + } else if (hl.isLocation()) { + linkType = workbookLink; + location = hl.getLocation(); + } + + modified = false; + } + + /** + * Copies a hyperlink read in from a writable sheet. + * Used when copying writable sheets + * + * @param hl the hyperlink from the read spreadsheet + */ + private void copyWritableHyperlink(Hyperlink hl, WritableSheet s) { + HyperlinkRecord h = (HyperlinkRecord) hl; + + firstRow = h.firstRow; + lastRow = h.lastRow; + firstColumn = h.firstColumn; + lastColumn = h.lastColumn; + + if (h.url != null) { + try { + url = new URL(h.url.toString()); + } catch (MalformedURLException e) { + // should never get a malformed url as a result url.toString() + Assert.verify(false); + } + } + + if (h.file != null) { + file = new File(h.file.getPath()); + } + + location = h.location; + contents = h.contents; + linkType = h.linkType; + modified = true; + + sheet = s; + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + } + + /** + * Determines whether this is a hyperlink to a file + * + * @return TRUE if this is a hyperlink to a file, FALSE otherwise + */ + public boolean isFile() { + return linkType == fileLink; + } + + /** + * Determines whether this is a hyperlink to a UNC + * + * @return TRUE if this is a hyperlink to a UNC, FALSE otherwise + */ + public boolean isUNC() { + return linkType == uncLink; + } + + /** + * Determines whether this is a hyperlink to a web resource + * + * @return TRUE if this is a URL + */ + public boolean isURL() { + return linkType == urlLink; + } + + /** + * Determines whether this is a hyperlink to a location in this workbook + * + * @return TRUE if this is a link to an internal location + */ + public boolean isLocation() { + return linkType == workbookLink; + } + + /** + * Returns the row number of the top left cell + * + * @return the row number of this cell + */ + public int getRow() { + return firstRow; + } + + /** + * Returns the column number of the top left cell + * + * @return the column number of this cell + */ + public int getColumn() { + return firstColumn; + } + + /** + * Returns the row number of the bottom right cell + * + * @return the row number of this cell + */ + public int getLastRow() { + return lastRow; + } + + /** + * Returns the column number of the bottom right cell + * + * @return the column number of this cell + */ + public int getLastColumn() { + return lastColumn; + } + + /** + * Gets the URL referenced by this Hyperlink + * + * @return the URL, or NULL if this hyperlink is not a URL + */ + public URL getURL() { + return url; + } + + /** + * Sets the URL of this hyperlink + * + * @param url the url + */ + public void setURL(URL url) { + URL prevurl = this.url; + linkType = urlLink; + file = null; + location = null; + contents = null; + this.url = url; + modified = true; + + if (sheet == null) { + // hyperlink has not been added to the sheet yet, so simply return + return; + } + + // Change the label on the sheet if it was a string representation of the + // URL + WritableCell wc = sheet.getWritableCell(firstColumn, firstRow); + + if (wc.getType() == CellType.LABEL) { + Label l = (Label) wc; + String prevurlString = prevurl.toString(); + String prevurlString2 = ""; + if (prevurlString.charAt(prevurlString.length() - 1) == '/' || + prevurlString.charAt(prevurlString.length() - 1) == '\\') { + prevurlString2 = prevurlString.substring(0, + prevurlString.length() - 1); + } + + if (l.getString().equals(prevurlString) || + l.getString().equals(prevurlString2)) { + l.setString(url.toString()); + } + } + } + + /** + * Returns the local file eferenced by this Hyperlink + * + * @return the file, or NULL if this hyperlink is not a file + */ + public File getFile() { + return file; + } + + /** + * Sets the file activated by this hyperlink + * + * @param file the file + */ + public void setFile(File file) { + linkType = fileLink; + url = null; + location = null; + contents = null; + this.file = file; + modified = true; + + if (sheet == null) { + // hyperlink has not been added to the sheet yet, so simply return + return; + } + + // Change the label on the sheet + WritableCell wc = sheet.getWritableCell(firstColumn, firstRow); + + Assert.verify(wc.getType() == CellType.LABEL); + + Label l = (Label) wc; + l.setString(file.toString()); + } + + /** + * Gets the binary data to be written to the output file + * + * @return the data to write to file + */ + public byte[] getData() { + if (!modified) { + return data; + } + + // Build up the jxl.common.data + byte[] commonData = new byte[32]; + + // Set the range of cells this hyperlink applies to + IntegerHelper.getTwoBytes(firstRow, commonData, 0); + IntegerHelper.getTwoBytes(lastRow, commonData, 2); + IntegerHelper.getTwoBytes(firstColumn, commonData, 4); + IntegerHelper.getTwoBytes(lastColumn, commonData, 6); + + // Some inexplicable byte sequence + commonData[8] = (byte) 0xd0; + commonData[9] = (byte) 0xc9; + commonData[10] = (byte) 0xea; + commonData[11] = (byte) 0x79; + commonData[12] = (byte) 0xf9; + commonData[13] = (byte) 0xba; + commonData[14] = (byte) 0xce; + commonData[15] = (byte) 0x11; + commonData[16] = (byte) 0x8c; + commonData[17] = (byte) 0x82; + commonData[18] = (byte) 0x0; + commonData[19] = (byte) 0xaa; + commonData[20] = (byte) 0x0; + commonData[21] = (byte) 0x4b; + commonData[22] = (byte) 0xa9; + commonData[23] = (byte) 0x0b; + commonData[24] = (byte) 0x2; + commonData[25] = (byte) 0x0; + commonData[26] = (byte) 0x0; + commonData[27] = (byte) 0x0; + + // Set up the option flags to indicate the type of this URL. There + // is no description + int optionFlags = 0; + if (isURL()) { + optionFlags = 3; + + if (contents != null) { + optionFlags |= 0x14; + } + } else if (isFile()) { + optionFlags = 1; + + if (contents != null) { + optionFlags |= 0x14; + } + } else if (isLocation()) { + optionFlags = 8; + } else if (isUNC()) { + optionFlags = 259; + } + + IntegerHelper.getFourBytes(optionFlags, commonData, 28); + + if (isURL()) { + data = getURLData(commonData); + } else if (isFile()) { + data = getFileData(commonData); + } else if (isLocation()) { + data = getLocationData(commonData); + } else if (isUNC()) { + data = getUNCData(commonData); + } + + return data; + } + + /** + * A standard toString method + * + * @return the contents of this object as a string + */ + public String toString() { + if (isFile()) { + return file.toString(); + } else if (isURL()) { + return url.toString(); + } else if (isUNC()) { + return file.toString(); + } else { + return ""; + } + } + + /** + * Gets the range of cells which activate this hyperlink + * The get sheet index methods will all return -1, because the + * cells will all be present on the same sheet + * + * @return the range of cells which activate the hyperlink or NULL + * if this hyperlink has not been added to the sheet + */ + public Range getRange() { + return range; + } + + /** + * Sets the location of the cells to be linked to within this workbook + * + * @param desc the label describing the link + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + protected void setLocation(String desc, + WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + linkType = workbookLink; + url = null; + file = null; + modified = true; + contents = desc; + + setLocation(sheet, destcol, destrow, lastdestcol, lastdestrow); + + if (sheet == null) { + // hyperlink has not been added to the sheet yet, so simply return + return; + } + + // Change the label on the sheet + WritableCell wc = sheet.getWritableCell(firstColumn, firstRow); + + Assert.verify(wc.getType() == CellType.LABEL); + + Label l = (Label) wc; + l.setString(desc); + } + + /** + * Initializes the location from the data passed in + * + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + private void setLocation(WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + StringBuffer sb = new StringBuffer(); + sb.append('\''); + + if (sheet.getName().indexOf('\'') == -1) { + sb.append(sheet.getName()); + } else { + // sb.append(sheet.getName().replaceAll("'", "''")); + + // Can't use replaceAll as it is only 1.4 compatible, so have to + // do this the tedious way + String sheetName = sheet.getName(); + int pos = 0; + int nextPos = sheetName.indexOf('\'', pos); + + while (nextPos != -1 && pos < sheetName.length()) { + sb.append(sheetName, pos, nextPos); + sb.append("''"); + pos = nextPos + 1; + nextPos = sheetName.indexOf('\'', pos); + } + sb.append(sheetName.substring(pos)); + } + + sb.append('\''); + sb.append('!'); + + lastdestcol = Math.max(destcol, lastdestcol); + lastdestrow = Math.max(destrow, lastdestrow); + + CellReferenceHelper.getCellReference(destcol, destrow, sb); + sb.append(':'); + CellReferenceHelper.getCellReference(lastdestcol, lastdestrow, sb); + + location = sb.toString(); + } + + /** + * A row has been inserted, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + void insertRow(int r) { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (r > lastRow) { + return; + } + + if (r <= firstRow) { + firstRow++; + modified = true; + } + + if (r <= lastRow) { + lastRow++; + modified = true; + } + + if (modified) { + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * A column has been inserted, so adjust the range objects accordingly + * + * @param c the column which has been inserted + */ + void insertColumn(int c) { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (c > lastColumn) { + return; + } + + if (c <= firstColumn) { + firstColumn++; + modified = true; + } + + if (c <= lastColumn) { + lastColumn++; + modified = true; + } + + if (modified) { + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * A row has been removed, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + void removeRow(int r) { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (r > lastRow) { + return; + } + + if (r < firstRow) { + firstRow--; + modified = true; + } + + if (r < lastRow) { + lastRow--; + modified = true; + } + + if (modified) { + Assert.verify(range != null); + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * A column has been removed, so adjust the range objects accordingly + * + * @param c the column which has been removed + */ + void removeColumn(int c) { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (c > lastColumn) { + return; + } + + if (c < firstColumn) { + firstColumn--; + modified = true; + } + + if (c < lastColumn) { + lastColumn--; + modified = true; + } + + if (modified) { + Assert.verify(range != null); + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * Gets the hyperlink stream specific to a URL link + * + * @param cd the data jxl.common.for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getURLData(byte[] cd) { + String urlString = url.toString(); + + int dataLength = cd.length + 20 + (urlString.length() + 1) * 2; + + if (contents != null) { + dataLength += 4 + (contents.length() + 1) * 2; + } + + byte[] d = new byte[dataLength]; + + System.arraycopy(cd, 0, d, 0, cd.length); + + int urlPos = cd.length; + + if (contents != null) { + IntegerHelper.getFourBytes(contents.length() + 1, d, urlPos); + StringHelper.getUnicodeBytes(contents, d, urlPos + 4); + urlPos += (contents.length() + 1) * 2 + 4; + } + + // Inexplicable byte sequence + d[urlPos] = (byte) 0xe0; + d[urlPos + 1] = (byte) 0xc9; + d[urlPos + 2] = (byte) 0xea; + d[urlPos + 3] = (byte) 0x79; + d[urlPos + 4] = (byte) 0xf9; + d[urlPos + 5] = (byte) 0xba; + d[urlPos + 6] = (byte) 0xce; + d[urlPos + 7] = (byte) 0x11; + d[urlPos + 8] = (byte) 0x8c; + d[urlPos + 9] = (byte) 0x82; + d[urlPos + 10] = (byte) 0x0; + d[urlPos + 11] = (byte) 0xaa; + d[urlPos + 12] = (byte) 0x0; + d[urlPos + 13] = (byte) 0x4b; + d[urlPos + 14] = (byte) 0xa9; + d[urlPos + 15] = (byte) 0x0b; + + // Number of characters in the url, including a zero trailing character + IntegerHelper.getFourBytes((urlString.length() + 1) * 2, d, urlPos + 16); + + // Put the url into the data string + StringHelper.getUnicodeBytes(urlString, d, urlPos + 20); + + return d; + } + + /** + * Gets the hyperlink stream specific to a URL link + * + * @param cd the data jxl.common.for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getUNCData(byte[] cd) { + String uncString = file.getPath(); + + byte[] d = new byte[cd.length + uncString.length() * 2 + 2 + 4]; + System.arraycopy(cd, 0, d, 0, cd.length); + + int urlPos = cd.length; + + // The length of the unc string, including zero terminator + int length = uncString.length() + 1; + IntegerHelper.getFourBytes(length, d, urlPos); + + // Place the string into the stream + StringHelper.getUnicodeBytes(uncString, d, urlPos + 4); + + return d; + } + + /** + * Gets the hyperlink stream specific to a local file link + * + * @param cd the data jxl.common.for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getFileData(byte[] cd) { + // Build up the directory hierarchy in reverse order + ArrayList path = new ArrayList(); + ArrayList shortFileName = new ArrayList(); + path.add(file.getName()); + shortFileName.add(getShortName(file.getName())); + + File parent = file.getParentFile(); + while (parent != null) { + path.add(parent.getName()); + shortFileName.add(getShortName(parent.getName())); + parent = parent.getParentFile(); + } + + // Deduce the up directory level count and remove the directory from + // the path + int upLevelCount = 0; + int pos = path.size() - 1; + boolean upDir = true; + + while (upDir) { + String s = (String) path.get(pos); + if (s.equals("..")) { + upLevelCount++; + path.remove(pos); + shortFileName.remove(pos); + } else { + upDir = false; + } + + pos--; + } + + StringBuffer filePathSB = new StringBuffer(); + StringBuffer shortFilePathSB = new StringBuffer(); + + if (file.getPath().charAt(1) == ':') { + char driveLetter = file.getPath().charAt(0); + if (driveLetter != 'C' && driveLetter != 'c') { + filePathSB.append(driveLetter); + filePathSB.append(':'); + shortFilePathSB.append(driveLetter); + shortFilePathSB.append(':'); + } + } + + for (int i = path.size() - 1; i >= 0; i--) { + filePathSB.append((String) path.get(i)); + shortFilePathSB.append((String) shortFileName.get(i)); + + if (i != 0) { + filePathSB.append("\\"); + shortFilePathSB.append("\\"); + } + } + + + String filePath = filePathSB.toString(); + String shortFilePath = shortFilePathSB.toString(); + + int dataLength = cd.length + + 4 + (shortFilePath.length() + 1) + // short file name + 16 + // inexplicable byte sequence + 2 + // up directory level count + 8 + (filePath.length() + 1) * 2 + // long file name + 24; // inexplicable byte sequence + + + if (contents != null) { + dataLength += 4 + (contents.length() + 1) * 2; + } + + // Copy across the jxl.common.data into the new array + byte[] d = new byte[dataLength]; + + System.arraycopy(cd, 0, d, 0, cd.length); + + int filePos = cd.length; + + // Add in the description text + if (contents != null) { + IntegerHelper.getFourBytes(contents.length() + 1, d, filePos); + StringHelper.getUnicodeBytes(contents, d, filePos + 4); + filePos += (contents.length() + 1) * 2 + 4; + } + + int curPos = filePos; + + // Inexplicable byte sequence + d[curPos] = (byte) 0x03; + d[curPos + 1] = (byte) 0x03; + d[curPos + 2] = (byte) 0x0; + d[curPos + 3] = (byte) 0x0; + d[curPos + 4] = (byte) 0x0; + d[curPos + 5] = (byte) 0x0; + d[curPos + 6] = (byte) 0x0; + d[curPos + 7] = (byte) 0x0; + d[curPos + 8] = (byte) 0xc0; + d[curPos + 9] = (byte) 0x0; + d[curPos + 10] = (byte) 0x0; + d[curPos + 11] = (byte) 0x0; + d[curPos + 12] = (byte) 0x0; + d[curPos + 13] = (byte) 0x0; + d[curPos + 14] = (byte) 0x0; + d[curPos + 15] = (byte) 0x46; + + curPos += 16; + + // The directory up level count + IntegerHelper.getTwoBytes(upLevelCount, d, curPos); + curPos += 2; + + // The number of bytes in the short file name, including zero terminator + IntegerHelper.getFourBytes((shortFilePath.length() + 1), d, curPos); + + // The short file name + StringHelper.getBytes(shortFilePath, d, curPos + 4); + + curPos += 4 + (shortFilePath.length() + 1); + + // Inexplicable byte sequence + d[curPos] = (byte) 0xff; + d[curPos + 1] = (byte) 0xff; + d[curPos + 2] = (byte) 0xad; + d[curPos + 3] = (byte) 0xde; + d[curPos + 4] = (byte) 0x0; + d[curPos + 5] = (byte) 0x0; + d[curPos + 6] = (byte) 0x0; + d[curPos + 7] = (byte) 0x0; + d[curPos + 8] = (byte) 0x0; + d[curPos + 9] = (byte) 0x0; + d[curPos + 10] = (byte) 0x0; + d[curPos + 11] = (byte) 0x0; + d[curPos + 12] = (byte) 0x0; + d[curPos + 13] = (byte) 0x0; + d[curPos + 14] = (byte) 0x0; + d[curPos + 15] = (byte) 0x0; + d[curPos + 16] = (byte) 0x0; + d[curPos + 17] = (byte) 0x0; + d[curPos + 18] = (byte) 0x0; + d[curPos + 19] = (byte) 0x0; + d[curPos + 20] = (byte) 0x0; + d[curPos + 21] = (byte) 0x0; + d[curPos + 22] = (byte) 0x0; + d[curPos + 23] = (byte) 0x0; + + curPos += 24; + + // Size of the long file name data in bytes, including inexplicable data + // fields + int size = 6 + filePath.length() * 2; + IntegerHelper.getFourBytes(size, d, curPos); + curPos += 4; + + // The number of bytes in the long file name + // NOT including zero terminator + IntegerHelper.getFourBytes((filePath.length()) * 2, d, curPos); + curPos += 4; + + // Inexplicable bytes + d[curPos] = (byte) 0x3; + d[curPos + 1] = (byte) 0x0; + + curPos += 2; + + // The long file name + StringHelper.getUnicodeBytes(filePath, d, curPos); + curPos += (filePath.length() + 1) * 2; + + + /* + curPos += 24; + int nameLength = filePath.length() * 2; + + // Size of the file link + IntegerHelper.getFourBytes(nameLength+6, d, curPos); + + // Number of characters + IntegerHelper.getFourBytes(nameLength, d, curPos+4); + + // Inexplicable byte sequence + d[curPos+8] = 0x03; + + // The long file name + StringHelper.getUnicodeBytes(filePath, d, curPos+10); + */ + + return d; + } + + /** + * Gets the DOS short file name in 8.3 format of the name passed in + * + * @param s the name + * @return the dos short name + */ + private String getShortName(String s) { + int sep = s.indexOf('.'); + + String prefix = null; + String suffix = null; + + if (sep == -1) { + prefix = s; + suffix = ""; + } else { + prefix = s.substring(0, sep); + suffix = s.substring(sep + 1); + } + + if (prefix.length() > 8) { + prefix = prefix.substring(0, 6) + "~" + (prefix.length() - 8); + prefix = prefix.substring(0, 8); + } + + suffix = suffix.substring(0, Math.min(3, suffix.length())); + + if (suffix.length() > 0) { + return prefix + '.' + suffix; + } else { + return prefix; + } + } + + /** + * Gets the hyperlink stream specific to a location link + * + * @param cd the data jxl.common.for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getLocationData(byte[] cd) { + byte[] d = new byte[cd.length + 4 + (location.length() + 1) * 2]; + System.arraycopy(cd, 0, d, 0, cd.length); + + int locPos = cd.length; + + // The number of chars in the location string, plus a 0 terminator + IntegerHelper.getFourBytes(location.length() + 1, d, locPos); + + // Get the location + StringHelper.getUnicodeBytes(location, d, locPos + 4); + + return d; + } + + /** + * Initializes the range when this hyperlink is added to the sheet + * + * @param s the sheet containing this hyperlink + */ + void initialize(WritableSheet s) { + sheet = s; + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + } + + /** + * Called by the worksheet. Gets the string contents to put into the cell + * containing this hyperlink + * + * @return the string contents for the hyperlink cell + */ + String getContents() { + return contents; + } + + /** + * Sets the description + * + * @param desc the description + */ + protected void setContents(String desc) { + contents = desc; + modified = true; + } + + /** + * The excel type of hyperlink + */ + private static class LinkType { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/IndexRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/IndexRecord.java new file mode 100755 index 0000000..4337f96 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/IndexRecord.java @@ -0,0 +1,99 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Index into the cell rows in an worksheet + */ +class IndexRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * The numbe of rows served by this index record + */ + private final int rows; + /** + * The position of the BOF record in the excel output stream + */ + private final int bofPosition; + /** + * The number of blocks needed to hold all the rows + */ + private final int blocks; + + /** + * The position of the current 'pointer' within the byte array + */ + private int dataPos; + + /** + * Constructor + * + * @param pos the position of the BOF record + * @param bl the number of blocks + * @param r the number of rows + */ + public IndexRecord(int pos, int r, int bl) { + super(Type.INDEX); + bofPosition = pos; + rows = r; + blocks = bl; + + // Allocate the amount of bytes required to hold all the blocks + data = new byte[16 + 4 * blocks]; + dataPos = 16; + } + + /** + * Gets the binary data for output. This writes out an empty data block, and + * the information is filled in later on when the information becomes + * available + * + * @return the binary data + */ + protected byte[] getData() { + IntegerHelper.getFourBytes(rows, data, 8); + return data; + } + + /** + * Adds another index record into the array + * + * @param pos the position in the output file + */ + void addBlockPosition(int pos) { + IntegerHelper.getFourBytes(pos - bofPosition, data, dataPos); + dataPos += 4; + } + + /** + * Sets the position of the data start. This happens to be the position + * of the DEFCOLWIDTH record + */ + void setDataStartPosition(int pos) { + IntegerHelper.getFourBytes(pos - bofPosition, data, 12); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceEndRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceEndRecord.java new file mode 100755 index 0000000..8fb52af --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceEndRecord.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Marks the end of the user interface section of the Book stream. + * It has no data field + */ +class InterfaceEndRecord extends WritableRecordData { + /** + * Consructor + */ + public InterfaceEndRecord() { + super(Type.INTERFACEEND); + } + + /** + * Gets the binary data for output + * + * @return the binary data + */ + public byte[] getData() { + return new byte[0]; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceHeaderRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceHeaderRecord.java new file mode 100755 index 0000000..54acfd8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceHeaderRecord.java @@ -0,0 +1,49 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Marks the beginning of the user interface record + */ +class InterfaceHeaderRecord extends WritableRecordData { + /** + * Constructor + */ + public InterfaceHeaderRecord() { + super(Type.INTERFACEHDR); + } + + /** + * Gets the binary data + * + * @return the binary data + */ + public byte[] getData() { + // Return the character encoding + byte[] data = new byte[] + {(byte) 0xb0, (byte) 0x04}; + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/IterationRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/IterationRecord.java new file mode 100755 index 0000000..bad702f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/IterationRecord.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the iteration option from the dialog box + */ +class IterationRecord extends WritableRecordData { + /** + * The iterate flag + */ + private final boolean iterate; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param it the iterator flag + */ + public IterationRecord(boolean it) { + super(Type.ITERATION); + + iterate = it; + + data = new byte[2]; + + if (iterate) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/JxlWriteException.java b/datastructures-xslx/src/main/java/jxl/write/biff/JxlWriteException.java new file mode 100755 index 0000000..07c4575 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/JxlWriteException.java @@ -0,0 +1,71 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.write.WriteException; + +/** + * Exception thrown when reading a biff file + */ +public class JxlWriteException extends WriteException { + /** + * + */ + static WriteMessage formatInitialized = + new WriteMessage("Attempt to modify a referenced format"); + /** + * + */ + static WriteMessage cellReferenced = + new WriteMessage("Cell has already been added to a worksheet"); + static WriteMessage maxRowsExceeded = + new WriteMessage("The maximum number of rows permitted on a worksheet " + + "been exceeded"); + static WriteMessage maxColumnsExceeded = + new WriteMessage("The maximum number of columns permitted on a " + + "worksheet has been exceeded"); + static WriteMessage copyPropertySets = + new WriteMessage("Error encounted when copying additional property sets"); + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + public JxlWriteException(WriteMessage m) { + super(m.message); + } + + private static class WriteMessage { + /** + * + */ + public String message; + + /** + * Constructs this exception with the specified message + * + * @param m the messageA + */ + WriteMessage(String m) { + message = m; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/LabelRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/LabelRecord.java new file mode 100755 index 0000000..4e90a7e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/LabelRecord.java @@ -0,0 +1,214 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * A label record, used for writing out string + */ +public abstract class LabelRecord extends CellValue { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(LabelRecord.class); + + /** + * The string + */ + private String contents; + + /** + * A handle to the shared strings used within this workbook + */ + private SharedStrings sharedStrings; + + /** + * The index of the string in the shared string table + */ + private int index; + + /** + * Constructor used when creating a label from the user API + * + * @param c the column + * @param cont the contents + * @param r the row + */ + protected LabelRecord(int c, int r, String cont) { + super(Type.LABELSST, c, r); + contents = cont; + if (contents == null) { + contents = ""; + } + } + + /** + * Constructor used when creating a label from the API. This is + * overloaded to allow formatting information to be passed to the record + * + * @param c the column + * @param cont the contents + * @param r the row + * @param st the format applied to the cell + */ + protected LabelRecord(int c, int r, String cont, CellFormat st) { + super(Type.LABELSST, c, r, st); + contents = cont; + + if (contents == null) { + contents = ""; + } + } + + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param nr the record to copy + */ + protected LabelRecord(int c, int r, LabelRecord lr) { + super(Type.LABELSST, c, r, lr); + contents = lr.contents; + } + + /** + * Constructor used when copying a label from a read only + * spreadsheet + * + * @param lc the label to copy + */ + protected LabelRecord(LabelCell lc) { + super(Type.LABELSST, lc); + contents = lc.getString(); + if (contents == null) { + contents = ""; + } + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.LABEL; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 4]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + IntegerHelper.getFourBytes(index, data, celldata.length); + + return data; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() { + return contents; + } + + /** + * Gets the label for this cell. The value returned will be the same + * as for the getContents method in the base class + * + * @return the cell contents + */ + public String getString() { + return contents; + } + + /** + * Sets the string contents of this cell + * + * @param s the new string contents + */ + protected void setString(String s) { + if (s == null) { + s = ""; + } + + contents = s; + + // Don't bother doing anything if this cell has not been referenced + // yet - everything will be set up in due course + if (!isReferenced()) { + return; + } + + Assert.verify(sharedStrings != null); + + // Initalize the shared string index + index = sharedStrings.getIndex(contents); + + // Use the sharedStrings reference instead of this object's own + // handle - this means that the bespoke copy becomes eligible for + // garbage collection + contents = sharedStrings.get(index); + } + + /** + * Overrides the method in the base class in order to add the string + * content to the shared string table, and to store its shared string + * index + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) { + super.setCellDetails(fr, ss, s); + + sharedStrings = ss; + + index = sharedStrings.getIndex(contents); + + // Use the sharedStrings reference instead of this object's own + // handle - this means that the bespoke copy becomes eligible for + // garbage collection + contents = sharedStrings.get(index); + } + +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/LeftMarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/LeftMarginRecord.java new file mode 100755 index 0000000..329e9b8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/LeftMarginRecord.java @@ -0,0 +1,31 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class LeftMarginRecord extends MarginRecord { + LeftMarginRecord(double v) { + super(Type.LEFTMARGIN, v); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MMSRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/MMSRecord.java new file mode 100755 index 0000000..ce38048 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MMSRecord.java @@ -0,0 +1,68 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the number of addmen and delmenu groups in the book stream + */ +class MMSRecord extends WritableRecordData { + /** + * The number of menu items added + */ + private final byte numMenuItemsAdded; + /** + * The number of menu items deleted + */ + private final byte numMenuItemsDeleted; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param menuItemsAdded the number of menu items added + * @param menuItemsDeleted the number of menu items deleted + */ + public MMSRecord(int menuItemsAdded, int menuItemsDeleted) { + super(Type.MMS); + + numMenuItemsAdded = (byte) menuItemsAdded; + numMenuItemsDeleted = (byte) menuItemsDeleted; + + data = new byte[2]; + + data[0] = numMenuItemsAdded; + data[1] = numMenuItemsDeleted; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/MarginRecord.java new file mode 100755 index 0000000..7ead8e9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MarginRecord.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a margin value + */ +abstract class MarginRecord extends WritableRecordData { + /** + * The margin + */ + private final double margin; + + /** + * Constructor + */ + public MarginRecord(Type t, double v) { + super(t); + margin = v; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[8]; + + DoubleHelper.getIEEEBytes(margin, data, 0); + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MemoryDataOutput.java b/datastructures-xslx/src/main/java/jxl/write/biff/MemoryDataOutput.java new file mode 100755 index 0000000..1b6ccf3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MemoryDataOutput.java @@ -0,0 +1,108 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; +import jxl.common.Logger; + +/** + * Used to generate the excel biff data in memory. This class wraps a byte + * array + */ +class MemoryDataOutput implements ExcelDataOutput { + // The logger + private static final Logger logger = Logger.getLogger(MemoryDataOutput.class); + + /** + * The excel data + */ + private byte[] data; + + /** + * The grow size for the array + */ + private final int growSize; + + /** + * The current position within the array + */ + private int pos; + + /** + * Constructor + */ + public MemoryDataOutput(int initialSize, int gs) { + data = new byte[initialSize]; + growSize = gs; + pos = 0; + } + + /** + * Writes the bytes to the end of the array, growing the array + * as needs dictate + * + * @param d the data to write to the end of the array + */ + public void write(byte[] bytes) { + while (pos + bytes.length > data.length) { + // Grow the array + byte[] newdata = new byte[data.length + growSize]; + System.arraycopy(data, 0, newdata, 0, pos); + data = newdata; + } + + System.arraycopy(bytes, 0, data, pos, bytes.length); + pos += bytes.length; + } + + /** + * Gets the current position within the file + * + * @return the position within the file + */ + public int getPosition() { + return pos; + } + + /** + * Sets the data at the specified position to the contents of the array + * + * @param pos the position to alter + * @param newdata the data to modify + */ + public void setData(byte[] newdata, int pos) { + System.arraycopy(newdata, 0, data, pos, newdata.length); + } + + /** + * Writes the data to the output stream + */ + public void writeData(OutputStream out) throws IOException { + out.write(data, 0, pos); + } + + /** + * Called when the final compound file has been written. No cleanup is + * necessary for in-memory file generation + */ + public void close() throws IOException { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MergedCells.java b/datastructures-xslx/src/main/java/jxl/write/biff/MergedCells.java new file mode 100755 index 0000000..76573ac --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MergedCells.java @@ -0,0 +1,278 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.Cell; +import jxl.CellType; +import jxl.Range; +import jxl.WorkbookSettings; +import jxl.biff.SheetRangeImpl; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.Blank; +import jxl.write.WritableSheet; +import jxl.write.WriteException; + +/** + * Contains all the merged cells, and the necessary logic for checking + * for intersections and for handling very large amounts of merging + */ +class MergedCells { + /** + * The maximum number of ranges per sheet + */ + private static final int maxRangesPerSheet = 1020; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(MergedCells.class); + /** + * The list of merged cells + */ + private ArrayList ranges; + /** + * The sheet containing the cells + */ + private final WritableSheet sheet; + + /** + * Constructor + */ + public MergedCells(WritableSheet ws) { + ranges = new ArrayList(); + sheet = ws; + } + + /** + * Adds the range to the list of merged cells. Does no checking + * at this stage + * + * @param range the range to add + */ + void add(Range r) { + ranges.add(r); + } + + /** + * Used to adjust the merged cells following a row insertion + */ + void insertRow(int row) { + // Adjust any merged cells + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) { + sr = (SheetRangeImpl) i.next(); + sr.insertRow(row); + } + } + + /** + * Used to adjust the merged cells following a column insertion + */ + void insertColumn(int col) { + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) { + sr = (SheetRangeImpl) i.next(); + sr.insertColumn(col); + } + } + + /** + * Used to adjust the merged cells following a column removal + */ + void removeColumn(int col) { + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) { + sr = (SheetRangeImpl) i.next(); + if (sr.getTopLeft().getColumn() == col && + sr.getBottomRight().getColumn() == col) { + // The column with the merged cells on has been removed, so get + // rid of it from the list + i.remove(); + } else { + sr.removeColumn(col); + } + } + } + + /** + * Used to adjust the merged cells following a row removal + */ + void removeRow(int row) { + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) { + sr = (SheetRangeImpl) i.next(); + if (sr.getTopLeft().getRow() == row && + sr.getBottomRight().getRow() == row) { + // The row with the merged cells on has been removed, so get + // rid of it from the list + i.remove(); + } else { + sr.removeRow(row); + } + } + } + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + Range[] getMergedCells() { + Range[] cells = new Range[ranges.size()]; + + for (int i = 0; i < cells.length; i++) { + cells[i] = (Range) ranges.get(i); + } + + return cells; + } + + /** + * Unmerges the specified cells. The Range passed in should be one that + * has been previously returned as a result of the getMergedCells method + * + * @param r the range of cells to unmerge + */ + void unmergeCells(Range r) { + int index = ranges.indexOf(r); + + if (index != -1) { + ranges.remove(index); + } + } + + /** + * Called prior to writing out in order to check for intersections + */ + private void checkIntersections() { + ArrayList newcells = new ArrayList(ranges.size()); + + for (Iterator mci = ranges.iterator(); mci.hasNext(); ) { + SheetRangeImpl r = (SheetRangeImpl) mci.next(); + + // Check that the range doesn't intersect with any existing range + Iterator i = newcells.iterator(); + SheetRangeImpl range = null; + boolean intersects = false; + while (i.hasNext() && !intersects) { + range = (SheetRangeImpl) i.next(); + + if (range.intersects(r)) { + logger.warn("Could not merge cells " + r + + " as they clash with an existing set of merged cells."); + + intersects = true; + } + } + + if (!intersects) { + newcells.add(r); + } + } + + ranges = newcells; + } + + /** + * Checks the cell ranges for intersections, or if the merged cells + * contains more than one item of data + */ + private void checkRanges() { + try { + SheetRangeImpl range = null; + + // Check all the ranges to make sure they only contain one entry + for (int i = 0; i < ranges.size(); i++) { + range = (SheetRangeImpl) ranges.get(i); + + // Get the cell in the top left + Cell tl = range.getTopLeft(); + Cell br = range.getBottomRight(); + boolean found = false; + + for (int c = tl.getColumn(); c <= br.getColumn(); c++) { + for (int r = tl.getRow(); r <= br.getRow(); r++) { + Cell cell = sheet.getCell(c, r); + if (cell.getType() != CellType.EMPTY) { + if (!found) { + found = true; + } else { + logger.warn("Range " + range + + " contains more than one data cell. " + + "Setting the other cells to blank."); + Blank b = new Blank(c, r); + sheet.addCell(b); + } + } + } + } + } + } catch (WriteException e) { + // This should already have been checked - bomb out + Assert.verify(false); + } + } + + void write(File outputFile) throws IOException { + if (ranges.size() == 0) { + return; + } + + WorkbookSettings ws = + ((WritableSheetImpl) sheet).getWorkbookSettings(); + + if (!ws.getMergedCellCheckingDisabled()) { + checkIntersections(); + checkRanges(); + } + + // If they will all fit into one record, then create a single + // record, write them and get out + if (ranges.size() < maxRangesPerSheet) { + MergedCellsRecord mcr = new MergedCellsRecord(ranges); + outputFile.write(mcr); + return; + } + + int numRecordsRequired = ranges.size() / maxRangesPerSheet + 1; + int pos = 0; + + for (int i = 0; i < numRecordsRequired; i++) { + int numranges = Math.min(maxRangesPerSheet, ranges.size() - pos); + + ArrayList cells = new ArrayList(numranges); + for (int j = 0; j < numranges; j++) { + cells.add(ranges.get(pos + j)); + } + + MergedCellsRecord mcr = new MergedCellsRecord(cells); + outputFile.write(mcr); + + pos += numranges; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MergedCellsRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/MergedCellsRecord.java new file mode 100755 index 0000000..f06cba2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MergedCellsRecord.java @@ -0,0 +1,88 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import jxl.Cell; +import jxl.Range; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A number record. This is stored as 8 bytes, as opposed to the + * 4 byte RK record + */ +public class MergedCellsRecord extends WritableRecordData { + /** + * The ranges of all the cells which are merged on this sheet + */ + private final ArrayList ranges; + + /** + * Constructs a merged cell record + * + * @param ws the sheet containing the merged cells + */ + protected MergedCellsRecord(ArrayList mc) { + super(Type.MERGEDCELLS); + + ranges = mc; + } + + /** + * Gets the raw data for output to file + * + * @return the data to write to file + */ + public byte[] getData() { + byte[] data = new byte[ranges.size() * 8 + 2]; + + // Set the number of ranges + IntegerHelper.getTwoBytes(ranges.size(), data, 0); + + int pos = 2; + Range range = null; + for (int i = 0; i < ranges.size(); i++) { + range = (Range) ranges.get(i); + + // Set the various cell records + Cell tl = range.getTopLeft(); + Cell br = range.getBottomRight(); + + IntegerHelper.getTwoBytes(tl.getRow(), data, pos); + IntegerHelper.getTwoBytes(br.getRow(), data, pos + 2); + IntegerHelper.getTwoBytes(tl.getColumn(), data, pos + 4); + IntegerHelper.getTwoBytes(br.getColumn(), data, pos + 6); + + pos += 8; + } + + return data; + } + +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MulRKRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/MulRKRecord.java new file mode 100755 index 0000000..9e4a9b5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MulRKRecord.java @@ -0,0 +1,114 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.List; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.write.Number; + +/** + * Contains an array of RK numbers + */ +class MulRKRecord extends WritableRecordData { + /** + * The row containing these numbers + */ + private final int row; + /** + * The first column these rk number occur on + */ + private final int colFirst; + /** + * The last column these rk number occur on + */ + private final int colLast; + /** + * The array of rk numbers + */ + private final int[] rknumbers; + /** + * The array of xf indices + */ + private final int[] xfIndices; + + /** + * Constructs the rk numbers from the integer cells + * + * @param numbers A list of jxl.write.Number objects + */ + public MulRKRecord(List numbers) { + super(Type.MULRK); + row = ((Number) numbers.get(0)).getRow(); + colFirst = ((Number) numbers.get(0)).getColumn(); + colLast = colFirst + numbers.size() - 1; + + rknumbers = new int[numbers.size()]; + xfIndices = new int[numbers.size()]; + + for (int i = 0; i < numbers.size(); i++) { + rknumbers[i] = (int) ((Number) numbers.get(i)).getValue(); + xfIndices[i] = ((CellValue) numbers.get(i)).getXFIndex(); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[rknumbers.length * 6 + 6]; + + // Set up the row and the first column + IntegerHelper.getTwoBytes(row, data, 0); + IntegerHelper.getTwoBytes(colFirst, data, 2); + + // Add all the rk numbers + int pos = 4; + int rkValue = 0; + byte[] rkBytes = new byte[4]; + for (int i = 0; i < rknumbers.length; i++) { + IntegerHelper.getTwoBytes(xfIndices[i], data, pos); + + // To represent an int as an Excel RK value, we have to + // undergo some outrageous jiggery pokery, as follows: + + // Gets the bit representation of the number + rkValue = rknumbers[i] << 2; + + // Set the integer bit + rkValue |= 0x2; + IntegerHelper.getFourBytes(rkValue, data, pos + 2); + + pos += 6; + } + + // Write the number of rk numbers in this record + IntegerHelper.getTwoBytes(colLast, data, pos); + + return data; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/NameRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/NameRecord.java new file mode 100755 index 0000000..bec192c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/NameRecord.java @@ -0,0 +1,618 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.BuiltInName; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; + +/** + * A name record. Simply takes the binary data from the name + * record read in + */ +class NameRecord extends WritableRecordData { + // Constants which refer to the parse tokens after the string + private static final int cellReference = 0x3a; + private static final int areaReference = 0x3b; + private static final int subExpression = 0x29; + private static final int union = 0x10; + // An empty range + private static final NameRange EMPTY_RANGE = new NameRange(0, 0, 0, 0, 0); + // The logger + private static final Logger logger = Logger.getLogger(NameRecord.class); + /** + * The binary data for output to file + */ + private byte[] data; + /** + * The name + */ + private String name; + /** + * The built in name + */ + private BuiltInName builtInName; + /** + * The index into the name table + */ + private final int index; + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + */ + private int sheetRef = 0; + /** + * Modified flag + */ + private boolean modified; + /** + * The ranges covered by this name + */ + private NameRange[] ranges; + + /** + * Constructor - used when copying sheets + * + * @param index the index into the name table + */ + public NameRecord(jxl.read.biff.NameRecord sr, int ind) { + super(Type.NAME); + + data = sr.getData(); + name = sr.getName(); + sheetRef = sr.getSheetRef(); + index = ind; + modified = false; + + // Copy the ranges + jxl.read.biff.NameRecord.NameRange[] r = sr.getRanges(); + ranges = new NameRange[r.length]; + for (int i = 0; i < ranges.length; i++) { + ranges[i] = new NameRange(r[i]); + } + } + + /** + * Create a new name record with the given information. + * + * @param theName Name to be created. + * @param theIndex Index of this name. + * @param extSheet External sheet index this name refers to. + * @param theStartRow First row this name refers to. + * @param theEndRow Last row this name refers to. + * @param theStartCol First column this name refers to. + * @param theEndCol Last column this name refers to. + * @param global TRUE if this is a global name + */ + NameRecord(String theName, + int theIndex, + int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol, + boolean global) { + super(Type.NAME); + + name = theName; + index = theIndex; + sheetRef = global ? 0 : index + 1; // 0 indicates a global name, otherwise + // the 1-based index of the sheet + + ranges = new NameRange[1]; + ranges[0] = new NameRange(extSheet, + theStartRow, + theEndRow, + theStartCol, + theEndCol); + modified = true; + } + + /** + * Create a new name record with the given information. + * + * @param theName Name to be created. + * @param theIndex Index of this name. + * @param extSheet External sheet index this name refers to. + * @param theStartRow First row this name refers to. + * @param theEndRow Last row this name refers to. + * @param theStartCol First column this name refers to. + * @param theEndCol Last column this name refers to. + * @param global TRUE if this is a global name + */ + NameRecord(BuiltInName theName, + int theIndex, + int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol, + boolean global) { + super(Type.NAME); + + builtInName = theName; + index = theIndex; + sheetRef = global ? 0 : index + 1; // 0 indicates a global name, otherwise + // the 1-based index of the sheet + + ranges = new NameRange[1]; + ranges[0] = new NameRange(extSheet, + theStartRow, + theEndRow, + theStartCol, + theEndCol); + } + + /** + * Create a new name record with the given information for 2-range entities. + * + * @param theName Name to be created. + * @param theIndex Index of this name. + * @param extSheet External sheet index this name refers to. + * @param theStartRow First row this name refers to. + * @param theEndRow Last row this name refers to. + * @param theStartCol First column this name refers to. + * @param theEndCol Last column this name refers to. + * @param theStartRow2 First row this name refers to (2nd instance). + * @param theEndRow2 Last row this name refers to (2nd instance). + * @param theStartCol2 First column this name refers to (2nd instance). + * @param theEndCol2 Last column this name refers to (2nd instance). + * @param global TRUE if this is a global name + */ + NameRecord(BuiltInName theName, + int theIndex, + int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol, + int theStartRow2, + int theEndRow2, + int theStartCol2, + int theEndCol2, + boolean global) { + super(Type.NAME); + + builtInName = theName; + index = theIndex; + sheetRef = global ? 0 : index + 1; // 0 indicates a global name, otherwise + // the 1-based index of the sheet + + ranges = new NameRange[2]; + ranges[0] = new NameRange(extSheet, + theStartRow, + theEndRow, + theStartCol, + theEndCol); + ranges[1] = new NameRange(extSheet, + theStartRow2, + theEndRow2, + theStartCol2, + theEndCol2); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + if (data != null && !modified) { + // this is a copy + return data; + } + + final int NAME_HEADER_LENGTH = 15; + final byte AREA_RANGE_LENGTH = 11; + final byte AREA_REFERENCE = 0x3b; + + int detailLength; + + if (ranges.length > 1) { + detailLength = (ranges.length * AREA_RANGE_LENGTH) + 4; + } else { + detailLength = AREA_RANGE_LENGTH; + } + + int length = NAME_HEADER_LENGTH + detailLength; + length += builtInName != null ? 1 : name.length(); + data = new byte[length]; + + // Options + int options = 0; + + if (builtInName != null) { + options |= 0x20; + } + IntegerHelper.getTwoBytes(options, data, 0); + + // Keyboard shortcut + data[2] = 0; + + // Length of the name in chars + if (builtInName != null) { + data[3] = (byte) 0x1; + } else { + data[3] = (byte) name.length(); + } + + // Size of the definitions + IntegerHelper.getTwoBytes(detailLength, data, 4); + + // Sheet index + IntegerHelper.getTwoBytes(sheetRef, data, 6); + IntegerHelper.getTwoBytes(sheetRef, data, 8); + + // Byte 10-13 are optional lengths [0,0,0,0] + // Byte 14 is length of name which is not used. + + // The name + if (builtInName != null) { + data[15] = (byte) builtInName.getValue(); + } else { + StringHelper.getBytes(name, data, 15); + } + + // The actual range definition. + int pos = builtInName != null ? 16 : name.length() + 15; + + // If there are multiple ranges for the name, we must specify a + // subExpression type rather than areaReference and then put out + // multiple areaReference entries with an end byte. + if (ranges.length > 1) { + data[pos++] = subExpression; + // Length of remaining bytes excluding itself + IntegerHelper.getTwoBytes(detailLength - 3, data, pos); + pos += 2; + byte[] rd; + for (int i = 0; i < ranges.length; i++) { + data[pos++] = areaReference; + rd = ranges[i].getData(); + System.arraycopy(rd, 0, data, pos, rd.length); + pos += rd.length; + } + data[pos] = 0x10; + } else { + // Range format - area + data[pos] = areaReference; + + // The range data + byte[] rd = ranges[0].getData(); + System.arraycopy(rd, 0, data, pos + 1, rd.length); + } + + return data; + } + + /** + * Accessor for the name + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Accessor for the index of this name in the name table + * + * @return the index of this name in the name table + */ + public int getIndex() { + return index; + } + + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + * + * @return the sheet reference for name formula + */ + public int getSheetRef() { + return sheetRef; + } + + /** + * Set the index sheet reference for a record name + * 0 is for a global reference + */ + public void setSheetRef(int i) { + sheetRef = i; + IntegerHelper.getTwoBytes(sheetRef, data, 8); + } + + /** + * Gets the array of ranges for this name + * + * @return the ranges + */ + public NameRange[] getRanges() { + return ranges; + } + + /** + * Called when a row is inserted on the + * + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(int sheetIndex, int row) { + for (int i = 0; i < ranges.length; i++) { + if (sheetIndex != ranges[i].getExternalSheet()) { + continue; // shame on me - this is no better than a goto + } + + if (row <= ranges[i].getFirstRow()) { + ranges[i].incrementFirstRow(); + modified = true; + } + + if (row <= ranges[i].getLastRow()) { + ranges[i].incrementLastRow(); + modified = true; + } + } + } + + /** + * Called when a row is removed on the worksheet + * + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + * @reeturn TRUE if the name is to be removed entirely, FALSE otherwise + */ + boolean rowRemoved(int sheetIndex, int row) { + for (int i = 0; i < ranges.length; i++) { + if (sheetIndex != ranges[i].getExternalSheet()) { + continue; // shame on me - this is no better than a goto + } + + if (row == ranges[i].getFirstRow() && row == ranges[i].getLastRow()) { + // remove the range + ranges[i] = EMPTY_RANGE; + } + + if (row < ranges[i].getFirstRow() && row > 0) { + ranges[i].decrementFirstRow(); + modified = true; + } + + if (row <= ranges[i].getLastRow()) { + ranges[i].decrementLastRow(); + modified = true; + } + } + + // If all ranges are empty, then remove the name + int emptyRanges = 0; + for (int i = 0; i < ranges.length; i++) { + if (ranges[i] == EMPTY_RANGE) { + emptyRanges++; + } + } + + if (emptyRanges == ranges.length) { + return true; + } + + // otherwise just remove the empty ones + NameRange[] newRanges = new NameRange[ranges.length - emptyRanges]; + for (int i = 0; i < ranges.length; i++) { + if (ranges[i] != EMPTY_RANGE) { + newRanges[i] = ranges[i]; + } + } + + ranges = newRanges; + + return false; + } + + /** + * Called when a row is removed on the worksheet + * + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + * @reeturn TRUE if the name is to be removed entirely, FALSE otherwise + */ + boolean columnRemoved(int sheetIndex, int col) { + for (int i = 0; i < ranges.length; i++) { + if (sheetIndex != ranges[i].getExternalSheet()) { + continue; // shame on me - this is no better than a goto + } + + if (col == ranges[i].getFirstColumn() && col == + ranges[i].getLastColumn()) { + // remove the range + ranges[i] = EMPTY_RANGE; + } + + if (col < ranges[i].getFirstColumn() && col > 0) { + ranges[i].decrementFirstColumn(); + modified = true; + } + + if (col <= ranges[i].getLastColumn()) { + ranges[i].decrementLastColumn(); + modified = true; + } + } + + // If all ranges are empty, then remove the name + int emptyRanges = 0; + for (int i = 0; i < ranges.length; i++) { + if (ranges[i] == EMPTY_RANGE) { + emptyRanges++; + } + } + + if (emptyRanges == ranges.length) { + return true; + } + + // otherwise just remove the empty ones + NameRange[] newRanges = new NameRange[ranges.length - emptyRanges]; + for (int i = 0; i < ranges.length; i++) { + if (ranges[i] != EMPTY_RANGE) { + newRanges[i] = ranges[i]; + } + } + + ranges = newRanges; + + return false; + } + + /** + * Called when a row is inserted on the + * + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(int sheetIndex, int col) { + for (int i = 0; i < ranges.length; i++) { + if (sheetIndex != ranges[i].getExternalSheet()) { + continue; // shame on me - this is no better than a goto + } + + if (col <= ranges[i].getFirstColumn()) { + ranges[i].incrementFirstColumn(); + modified = true; + } + + if (col <= ranges[i].getLastColumn()) { + ranges[i].incrementLastColumn(); + modified = true; + } + } + } + + /** + * A nested class to hold range information + */ + static class NameRange { + private int columnFirst; + private int rowFirst; + private int columnLast; + private int rowLast; + private final int externalSheet; + + NameRange(jxl.read.biff.NameRecord.NameRange nr) { + columnFirst = nr.getFirstColumn(); + rowFirst = nr.getFirstRow(); + columnLast = nr.getLastColumn(); + rowLast = nr.getLastRow(); + externalSheet = nr.getExternalSheet(); + } + + /** + * Create a new range for the name record. + */ + NameRange(int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol) { + columnFirst = theStartCol; + rowFirst = theStartRow; + columnLast = theEndCol; + rowLast = theEndRow; + externalSheet = extSheet; + } + + int getFirstColumn() { + return columnFirst; + } + + int getFirstRow() { + return rowFirst; + } + + int getLastColumn() { + return columnLast; + } + + int getLastRow() { + return rowLast; + } + + int getExternalSheet() { + return externalSheet; + } + + void incrementFirstRow() { + rowFirst++; + } + + void incrementLastRow() { + rowLast++; + } + + void decrementFirstRow() { + rowFirst--; + } + + void decrementLastRow() { + rowLast--; + } + + void incrementFirstColumn() { + columnFirst++; + } + + void incrementLastColumn() { + columnLast++; + } + + void decrementFirstColumn() { + columnFirst--; + } + + void decrementLastColumn() { + columnLast--; + } + + byte[] getData() { + byte[] d = new byte[10]; + + // Sheet index + IntegerHelper.getTwoBytes(externalSheet, d, 0); + + // Starting row + IntegerHelper.getTwoBytes(rowFirst, d, 2); + + // End row + IntegerHelper.getTwoBytes(rowLast, d, 4); + + // Start column + IntegerHelper.getTwoBytes(columnFirst & 0xff, d, 6); + + // End columns + IntegerHelper.getTwoBytes(columnLast & 0xff, d, 8); + + return d; + } + } + +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/NineteenFourRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/NineteenFourRecord.java new file mode 100755 index 0000000..d87052b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/NineteenFourRecord.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A record which indicates whether or the 1904 date system is + * in use + */ +class NineteenFourRecord extends WritableRecordData { + /** + * Flag which indicates whether the 1904 date system is being used + */ + private final boolean nineteenFourDate; + + /** + * The binary data for output to file + */ + private final byte[] data; + + /** + * Constructor + * + * @param oldDate flag indicating whether the 1904 date system is in use + */ + public NineteenFourRecord(boolean oldDate) { + super(Type.NINETEENFOUR); + + nineteenFourDate = oldDate; + data = new byte[2]; + + if (nineteenFourDate) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * The binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/NumberFormatRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/NumberFormatRecord.java new file mode 100755 index 0000000..c918c83 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/NumberFormatRecord.java @@ -0,0 +1,131 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.FormatRecord; +import jxl.common.Logger; + +/** + * A class which contains a number format + */ +public class NumberFormatRecord extends FormatRecord { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NumberFormatRecord.class); + + /** + * Constructor. Replaces some of the characters in the java number + * format string with the appropriate excel format characters + * + * @param fmt the number format + */ + protected NumberFormatRecord(String fmt) { + super(); + + // Do the replacements in the format string + String fs = fmt; + + fs = replace(fs, "E0", "E+0"); + + fs = trimInvalidChars(fs); + + setFormatString(fs); + } + + + /** + * Constructor. Replaces some of the characters in the java number + * format string with the appropriate excel format characters + * + * @param fmt the number format + */ + protected NumberFormatRecord(String fmt, NonValidatingFormat dummy) { + super(); + + // Do the replacements in the format string + String fs = fmt; + + fs = replace(fs, "E0", "E+0"); + + setFormatString(fs); + } + + /** + * Remove all but the first characters preceding the # or the 0. + * Remove all characters after the # or the 0, unless it is a ) + * + * @param fs the candidate number format + * @return the string with spurious characters removed + */ + private String trimInvalidChars(String fs) { + int firstHash = fs.indexOf('#'); + int firstZero = fs.indexOf('0'); + int firstValidChar = 0; + + if (firstHash == -1 && firstZero == -1) { + // The string is complete nonsense. Return a default string + return "#.###"; + } + + if (firstHash != 0 && firstZero != 0 && + firstHash != 1 && firstZero != 1) { + // The string is dodgy. Find the first valid char + firstHash = firstHash == -1 ? firstHash = Integer.MAX_VALUE : firstHash; + firstZero = firstZero == -1 ? firstZero = Integer.MAX_VALUE : firstZero; + firstValidChar = Math.min(firstHash, firstZero); + + StringBuffer tmp = new StringBuffer(); + tmp.append(fs.charAt(0)); + tmp.append(fs.substring(firstValidChar)); + fs = tmp.toString(); + } + + // Now strip of everything at the end that isn't a # or 0 + int lastHash = fs.lastIndexOf('#'); + int lastZero = fs.lastIndexOf('0'); + + if (lastHash == fs.length() || + lastZero == fs.length()) { + return fs; + } + + // Find the last valid character + int lastValidChar = Math.max(lastHash, lastZero); + + // Check for the existence of a ) or % + while ((fs.length() > lastValidChar + 1) && + (fs.charAt(lastValidChar + 1) == ')' || + (fs.charAt(lastValidChar + 1) == '%'))) { + lastValidChar++; + } + + return fs.substring(0, lastValidChar + 1); + } + + // Dummy class to specify non validation + protected static class NonValidatingFormat { + public NonValidatingFormat() { + } + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/NumberRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/NumberRecord.java new file mode 100755 index 0000000..49f771f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/NumberRecord.java @@ -0,0 +1,167 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.biff.XFRecord; +import jxl.format.CellFormat; + +/** + * The record which contains numerical values. All values are stored + * as 64bit IEEE floating point values + */ +public abstract class NumberRecord extends CellValue { + /** + * The formatter to convert the value into a string + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The number + */ + private double value; + /** + * The java equivalent of the excel format + */ + private NumberFormat format; + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param val the value + */ + protected NumberRecord(int c, int r, double val) { + super(Type.NUMBER, c, r); + value = val; + } + + /** + * Overloaded constructor invoked from the API, which takes a cell + * format + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + protected NumberRecord(int c, int r, double val, CellFormat st) { + super(Type.NUMBER, c, r, st); + value = val; + } + + /** + * Constructor used when copying a workbook + * + * @param nc the number to copy + */ + protected NumberRecord(NumberCell nc) { + super(Type.NUMBER, nc); + value = nc.getValue(); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param nr the record to copy + */ + protected NumberRecord(int c, int r, NumberRecord nr) { + super(Type.NUMBER, c, r, nr); + value = nr.value; + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.NUMBER; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 8]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + DoubleHelper.getIEEEBytes(value, data, celldata.length); + + return data; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() { + if (format == null) { + format = ((XFRecord) getCellFormat()).getNumberFormat(); + if (format == null) { + format = defaultFormat; + } + } + return format.format(value); + } + + /** + * Gets the double contents for this cell. + * + * @return the cell contents + */ + public double getValue() { + return value; + } + + /** + * Sets the value of the contents for this cell + * + * @param val the new value + */ + public void setValue(double val) { + value = val; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return null; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ObjProjRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ObjProjRecord.java new file mode 100755 index 0000000..c5d7010 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ObjProjRecord.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record containing the obj proj record + */ +class ObjProjRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + */ + public ObjProjRecord() { + super(Type.OBJPROJ); + + data = new byte[4]; + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ObjectProtectRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ObjectProtectRecord.java new file mode 100755 index 0000000..dd58911 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ObjectProtectRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The protection state for a sheet or workbook + */ +class ObjectProtectRecord extends WritableRecordData { + /** + * The protection state + */ + private final boolean protection; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param prot the protection state + */ + public ObjectProtectRecord(boolean prot) { + super(Type.OBJPROTECT); + + protection = prot; + + data = new byte[2]; + + if (protection) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PLSRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PLSRecord.java new file mode 100755 index 0000000..45bf80e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PLSRecord.java @@ -0,0 +1,67 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a print header for a work sheet + */ +class PLSRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Consructor invoked when copying a spreadsheet + * + * @param hr the read header record + */ + public PLSRecord(jxl.read.biff.PLSRecord hr) { + super(Type.PLS); + + data = hr.getData(); + } + + /** + * Consructor invoked when copying a sheets + * + * @param hr the read header record + */ + public PLSRecord(PLSRecord hr) { + super(Type.PLS); + + data = new byte[hr.data.length]; + System.arraycopy(hr.data, 0, data, 0, data.length); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PaletteRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PaletteRecord.java new file mode 100755 index 0000000..f5b983a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PaletteRecord.java @@ -0,0 +1,53 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the colour palette + */ +class PaletteRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param p the palette record + */ + public PaletteRecord(jxl.read.biff.PaletteRecord p) { + super(Type.PALETTE); + + data = p.getData(); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PaneRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PaneRecord.java new file mode 100755 index 0000000..45da541 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PaneRecord.java @@ -0,0 +1,98 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the window attributes for a worksheet + */ +class PaneRecord extends WritableRecordData { + /** + * The pane codes + */ + private final static int topLeftPane = 0x3; + private final static int bottomLeftPane = 0x2; + private final static int topRightPane = 0x1; + private final static int bottomRightPane = 0x0; + /** + * The number of rows visible in the top left pane + */ + private final int rowsVisible; + /** + * The number of columns visible in the top left pane + */ + private final int columnsVisible; + + /** + * Code + *

+ * /** + * Constructor + */ + public PaneRecord(int cols, int rows) { + super(Type.PANE); + + rowsVisible = rows; + columnsVisible = cols; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[10]; + + // The x position + IntegerHelper.getTwoBytes(columnsVisible, data, 0); + + // The y position + IntegerHelper.getTwoBytes(rowsVisible, data, 2); + + // The top row visible in the bottom pane + if (rowsVisible > 0) { + IntegerHelper.getTwoBytes(rowsVisible, data, 4); + } + + // The left most column visible in the right pane + if (columnsVisible > 0) { + IntegerHelper.getTwoBytes(columnsVisible, data, 6); + } + + // The active pane + int activePane = topLeftPane; + + if (rowsVisible > 0 && columnsVisible == 0) { + activePane = bottomLeftPane; + } else if (rowsVisible == 0 && columnsVisible > 0) { + activePane = topRightPane; + } else if (rowsVisible > 0 && columnsVisible > 0) { + activePane = bottomRightPane; + } + // always present + IntegerHelper.getTwoBytes(activePane, data, 8); + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PasswordRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PasswordRecord.java new file mode 100755 index 0000000..958cae9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PasswordRecord.java @@ -0,0 +1,109 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A password record. Thanks to Michael Matthews for sending me the + * code to actually store the password for the sheet + */ +class PasswordRecord extends WritableRecordData { + /** + * The password + */ + private String password; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param pw the password + */ + public PasswordRecord(String pw) { + super(Type.PASSWORD); + + password = pw; + + if (pw == null) { + data = new byte[2]; + IntegerHelper.getTwoBytes(0, data, 0); + } else { + byte[] passwordBytes = pw.getBytes(); + int passwordHash = 0; + for (int a = 0; a < passwordBytes.length; a++) { + int shifted = rotLeft15Bit(passwordBytes[a], a + 1); + passwordHash ^= shifted; + } + passwordHash ^= passwordBytes.length; + passwordHash ^= 0xCE4B; + + data = new byte[2]; + IntegerHelper.getTwoBytes(passwordHash, data, 0); + } + } + + /** + * Constructor + * + * @param ph the password hash code + */ + public PasswordRecord(int ph) { + super(Type.PASSWORD); + + data = new byte[2]; + IntegerHelper.getTwoBytes(ph, data, 0); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } + + /** + * Rotate the value by 15 bits. Thanks to Michael for this + * + * @param val + * @param rotate + * @return int + */ + private int rotLeft15Bit(int val, int rotate) { + val = val & 0x7FFF; + + for (; rotate > 0; rotate--) { + if ((val & 0x4000) != 0) { + val = ((val << 1) & 0x7FFF) + 1; + } else { + val = (val << 1) & 0x7FFF; + } + } + + return val; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PrecisionRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PrecisionRecord.java new file mode 100755 index 0000000..c20ecfd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PrecisionRecord.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the Precision As Displayed option from the dialog box + */ +class PrecisionRecord extends WritableRecordData { + /** + * The precision as displayed flag + */ + private final boolean asDisplayed; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param disp the precision as displayed flag + */ + public PrecisionRecord(boolean disp) { + super(Type.PRECISION); + + asDisplayed = disp; + data = new byte[2]; + + if (!asDisplayed) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PrintGridLinesRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PrintGridLinesRecord.java new file mode 100755 index 0000000..a5c5489 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PrintGridLinesRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The grid lines option from the Page Setup dialog box + */ +class PrintGridLinesRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * The print grid lines option + */ + private final boolean printGridLines; + + /** + * Constructor + * + * @param pgl the grid lines option + */ + public PrintGridLinesRecord(boolean pgl) { + super(Type.PRINTGRIDLINES); + printGridLines = pgl; + + data = new byte[2]; + + if (printGridLines) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PrintHeadersRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PrintHeadersRecord.java new file mode 100755 index 0000000..253cd43 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PrintHeadersRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The headings options from the Page Setup dialog box + */ +class PrintHeadersRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * Flag to print headers + */ + private final boolean printHeaders; + + /** + * Constructor + * + * @param ph print headers flag + */ + public PrintHeadersRecord(boolean ph) { + super(Type.PRINTHEADERS); + printHeaders = ph; + + data = new byte[2]; + + if (printHeaders) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevPassRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevPassRecord.java new file mode 100755 index 0000000..fe785d0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevPassRecord.java @@ -0,0 +1,52 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the shared workbook protection flag + */ +class Prot4RevPassRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + */ + public Prot4RevPassRecord() { + super(Type.PROT4REVPASS); + + // Hard code in an unprotected workbook + data = new byte[2]; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevRecord.java new file mode 100755 index 0000000..133a4b6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevRecord.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The shared workbook protection flag + */ +class Prot4RevRecord extends WritableRecordData { + /** + * The protection flag + */ + private final boolean protection; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param prot protection flag + */ + public Prot4RevRecord(boolean prot) { + super(Type.PROT4REV); + + protection = prot; + + // Hard code in an unprotected workbook + data = new byte[2]; + + if (protection) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ProtectRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ProtectRecord.java new file mode 100755 index 0000000..e08e067 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ProtectRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The protection state for a sheet or workbook + */ +class ProtectRecord extends WritableRecordData { + /** + * The protection state + */ + private final boolean protection; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param prot the protection state + */ + public ProtectRecord(boolean prot) { + super(Type.PROTECT); + + protection = prot; + + data = new byte[2]; + + if (protection) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadBooleanFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadBooleanFormulaRecord.java new file mode 100755 index 0000000..ff700fe --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadBooleanFormulaRecord.java @@ -0,0 +1,47 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.BooleanFormulaCell; +import jxl.biff.FormulaData; + +/** + * Class for read number formula records + */ +class ReadBooleanFormulaRecord extends ReadFormulaRecord + implements BooleanFormulaCell { + /** + * Constructor + * + * @param f + */ + public ReadBooleanFormulaRecord(FormulaData f) { + super(f); + } + + /** + * Gets the boolean contents for this cell. + * + * @return the cell contents + */ + public boolean getValue() { + return ((BooleanFormulaCell) getReadFormula()).getValue(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadDateFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadDateFormulaRecord.java new file mode 100755 index 0000000..66580f5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadDateFormulaRecord.java @@ -0,0 +1,70 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.text.DateFormat; +import java.util.Date; +import jxl.DateFormulaCell; +import jxl.biff.FormulaData; + +/** + * Class for read number formula records + */ +class ReadDateFormulaRecord extends ReadFormulaRecord + implements DateFormulaCell { + /** + * Constructor + * + * @param f + */ + public ReadDateFormulaRecord(FormulaData f) { + super(f); + } + + /** + * Gets the Date contents for this cell. + * + * @return the cell contents + */ + public Date getDate() { + return ((DateFormulaCell) getReadFormula()).getDate(); + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + public boolean isTime() { + return ((DateFormulaCell) getReadFormula()).isTime(); + } + + + /** + * Gets the DateFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the DateFormat used to format the cell + */ + public DateFormat getDateFormat() { + return ((DateFormulaCell) getReadFormula()).getDateFormat(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadErrorFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadErrorFormulaRecord.java new file mode 100755 index 0000000..c03ce5e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadErrorFormulaRecord.java @@ -0,0 +1,118 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.ErrorFormulaCell; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.formula.FormulaErrorCode; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + + +/** + * Class for read number formula records + */ +class ReadErrorFormulaRecord extends ReadFormulaRecord + implements ErrorFormulaCell { + // The logger + private static final Logger logger = Logger.getLogger(ReadErrorFormulaRecord.class); + + /** + * Constructor + * + * @param f + */ + public ReadErrorFormulaRecord(FormulaData f) { + super(f); + } + + /** + * Gets the error code for this cell. + * + * @return the cell contents + */ + public int getErrorCode() { + return ((ErrorFormulaCell) getReadFormula()).getErrorCode(); + } + + /** + * Error formula specific exception handling. Can't really create + * a formula (as it will look for a cell of that name, so just + * create a STRING record containing the contents + * + * @return the bodged data + */ + protected byte[] handleFormulaException() { + byte[] expressiondata = null; + byte[] celldata = super.getCellData(); + + int errorCode = getErrorCode(); + String formulaString = null; + + if (errorCode == FormulaErrorCode.DIV0.getCode()) { + formulaString = "1/0"; + } else if (errorCode == FormulaErrorCode.VALUE.getCode()) { + formulaString = "\"\"/0"; + } else if (errorCode == FormulaErrorCode.REF.getCode()) { + formulaString = "\"#REF!\""; + } else { + formulaString = "\"ERROR\""; + } + + // Generate an appropriate dummy formula + WritableWorkbookImpl w = getSheet().getWorkbook(); + FormulaParser parser = new FormulaParser(formulaString, w, w, + w.getSettings()); + + // Get the bytes for the dummy formula + try { + parser.parse(); + } catch (FormulaException e2) { + logger.warn(e2.getMessage()); + } + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + + // Set the type bits to indicate an error + data[6] = 2; + data[12] = -1; + data[13] = -1; + + // Set the error code + data[8] = (byte) errorCode; + + return data; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadFormulaRecord.java new file mode 100755 index 0000000..9a5e075 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadFormulaRecord.java @@ -0,0 +1,402 @@ +/************************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.CellReferenceHelper; +import jxl.CellType; +import jxl.FormulaCell; +import jxl.Sheet; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.WritableCell; + +/** + * A formula record. This is invoked when copying a formula from a + * read only spreadsheet + * This method implements the FormulaData interface to allow the copying + * of writable sheets + */ +class ReadFormulaRecord extends CellValue implements FormulaData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ReadFormulaRecord.class); + + /** + * The underlying formula from the read sheet + */ + private final FormulaData formula; + + /** + * The formula parser + */ + private FormulaParser parser; + + /** + * Constructor + * + * @param f the formula to copy + */ + protected ReadFormulaRecord(FormulaData f) { + super(Type.FORMULA, f); + formula = f; + } + + protected final byte[] getCellData() { + return super.getData(); + } + + /** + * An exception has occurred, so produce some appropriate dummy + * cell contents. This may be overridden by subclasses + * if they require specific handling + * + * @return the bodged data + */ + protected byte[] handleFormulaException() { + byte[] expressiondata = null; + byte[] celldata = super.getData(); + + // Generate an appropriate dummy formula + WritableWorkbookImpl w = getSheet().getWorkbook(); + parser = new FormulaParser(getContents(), w, w, + w.getSettings()); + + // Get the bytes for the dummy formula + try { + parser.parse(); + } catch (FormulaException e2) { + logger.warn(e2.getMessage()); + parser = new FormulaParser("\"ERROR\"", w, w, w.getSettings()); + try { + parser.parse(); + } catch (FormulaException e3) { + Assert.verify(false); + } + } + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + return data; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + // Take the superclass cell data to take into account cell + // rationalization + byte[] celldata = super.getData(); + byte[] expressiondata = null; + + try { + if (parser == null) { + expressiondata = formula.getFormulaData(); + } else { + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + } + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + return data; + } catch (FormulaException e) { + // Something has gone wrong trying to read the formula data eg. it + // might be unsupported biff7 data + logger.warn + (CellReferenceHelper.getCellReference(getColumn(), getRow()) + + " " + e.getMessage()); + return handleFormulaException(); + } + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return formula.getType(); + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * + * @return the contents of this cell as a string + */ + public String getContents() { + return formula.getContents(); + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + byte[] d = formula.getFormulaData(); + byte[] data = new byte[d.length]; + + System.arraycopy(d, 0, data, 0, d.length); + + // Set the recalculate on load bit + data[8] |= 0x02; + + return data; + } + + /** + * Gets the formula bytes + * + * @return the formula bytes + */ + public byte[] getFormulaBytes() throws FormulaException { + // If the formula has been parsed, then get the parsed bytes + if (parser != null) { + return parser.getBytes(); + } + + // otherwise get the bytes from the original formula + byte[] readFormulaData = getFormulaData(); + byte[] formulaBytes = new byte[readFormulaData.length - 16]; + System.arraycopy(readFormulaData, 16, formulaBytes, 0, + formulaBytes.length); + return formulaBytes; + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new FormulaRecord(col, row, this); + } + + /** + * Overrides the method in the base class to add this to the Workbook's + * list of maintained formulas + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s the sheet this is being added to + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) { + super.setCellDetails(fr, ss, s); + s.getWorkbook().addRCIRCell(this); + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(Sheet s, int sheetIndex, int col) { + try { + if (parser == null) { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + getSheet().getWorkbook(), + getSheet().getWorkbook(), + getSheet().getWorkbookSettings()); + parser.parse(); + } + + parser.columnInserted(sheetIndex, col, s == getSheet()); + } catch (FormulaException e) { + logger.warn("cannot insert column within formula: " + e.getMessage()); + } + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnRemoved(Sheet s, int sheetIndex, int col) { + try { + if (parser == null) { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + getSheet().getWorkbook(), + getSheet().getWorkbook(), + getSheet().getWorkbookSettings()); + parser.parse(); + } + + parser.columnRemoved(sheetIndex, col, s == getSheet()); + } catch (FormulaException e) { + logger.warn("cannot remove column within formula: " + e.getMessage()); + } + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(Sheet s, int sheetIndex, int row) { + try { + if (parser == null) { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + getSheet().getWorkbook(), + getSheet().getWorkbook(), + getSheet().getWorkbookSettings()); + parser.parse(); + } + + parser.rowInserted(sheetIndex, row, s == getSheet()); + } catch (FormulaException e) { + logger.warn("cannot insert row within formula: " + e.getMessage()); + } + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the row was removed + * @param sheetIndex the sheet index on which the column was removed + * @param row the column number which was removed + */ + void rowRemoved(Sheet s, int sheetIndex, int row) { + try { + if (parser == null) { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + getSheet().getWorkbook(), + getSheet().getWorkbook(), + getSheet().getWorkbookSettings()); + parser.parse(); + } + + parser.rowRemoved(sheetIndex, row, s == getSheet()); + } catch (FormulaException e) { + logger.warn("cannot remove row within formula: " + e.getMessage()); + } + } + + /** + * Accessor for the read formula + * + * @return the read formula + */ + protected FormulaData getReadFormula() { + return formula; + } + + /** + * Accessor for the read formula + * + * @return the read formula + */ + public String getFormula() throws FormulaException { + return ((FormulaCell) formula).getFormula(); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if this formula was able to be imported, FALSE otherwise + */ + public boolean handleImportedCellReferences(ExternalSheet es, + WorkbookMethods mt, + WorkbookSettings ws) { + try { + if (parser == null) { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + es, mt, ws); + parser.parse(); + } + + return parser.handleImportedCellReferences(); + } catch (FormulaException e) { + logger.warn("cannot import formula: " + e.getMessage()); + return false; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadNumberFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadNumberFormulaRecord.java new file mode 100755 index 0000000..9304be6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadNumberFormulaRecord.java @@ -0,0 +1,110 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.text.NumberFormat; +import jxl.NumberFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + +/** + * Class for read number formula records + */ +class ReadNumberFormulaRecord extends ReadFormulaRecord + implements NumberFormulaCell { + // The logger + private static final Logger logger = Logger.getLogger(ReadNumberFormulaRecord.class); + + /** + * Constructor + * + * @param f + */ + public ReadNumberFormulaRecord(FormulaData f) { + super(f); + } + + /** + * Gets the double contents for this cell. + * + * @return the cell contents + */ + public double getValue() { + return ((NumberFormulaCell) getReadFormula()).getValue(); + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return ((NumberFormulaCell) getReadFormula()).getNumberFormat(); + } + + /** + * Error formula specific exception handling. Can't really create + * a formula (as it will look for a cell of that name, so just + * create a STRING record containing the contents + * + * @return the bodged data + */ + protected byte[] handleFormulaException() { + byte[] expressiondata = null; + byte[] celldata = super.getCellData(); + + // Generate an appropriate dummy formula + WritableWorkbookImpl w = getSheet().getWorkbook(); + FormulaParser parser = new FormulaParser(Double.toString(getValue()), w, w, + w.getSettings()); + + // Get the bytes for the dummy formula + try { + parser.parse(); + } catch (FormulaException e2) { + logger.warn(e2.getMessage()); + } + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + + // Store the value in the formula + DoubleHelper.getIEEEBytes(getValue(), data, 6); + + return data; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadStringFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadStringFormulaRecord.java new file mode 100755 index 0000000..b2fc7b3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadStringFormulaRecord.java @@ -0,0 +1,106 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.StringFormulaCell; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Class for read number formula records + */ +class ReadStringFormulaRecord extends ReadFormulaRecord + implements StringFormulaCell { + // the logger + private static final Logger logger = Logger.getLogger(ReadFormulaRecord.class); + + /** + * Constructor + * + * @param f + */ + public ReadStringFormulaRecord(FormulaData f) { + super(f); + } + + /** + * Gets the string contents for this cell. + * + * @return the cell contents + */ + public String getString() { + return ((StringFormulaCell) getReadFormula()).getString(); + } + + /** + * String formula specific exception handling. Can't really create + * a formula (as it will look for a cell of that name, so just + * create a STRING record containing the contents + * + * @return the bodged data + */ + protected byte[] handleFormulaException() { + byte[] expressiondata = null; + byte[] celldata = super.getCellData(); + + // Generate an appropriate dummy formula + WritableWorkbookImpl w = getSheet().getWorkbook(); + FormulaParser parser = new FormulaParser("\"" + getContents() + "\"", w, w, + w.getSettings()); + + // Get the bytes for the dummy formula + try { + parser.parse(); + } catch (FormulaException e2) { + logger.warn(e2.getMessage()); + parser = new FormulaParser("\"ERROR\"", w, w, w.getSettings()); + try { + parser.parse(); + } catch (FormulaException e3) { + Assert.verify(false); + } + } + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + + // Set the type bits to indicate a string formula + data[6] = 0; + data[12] = -1; + data[13] = -1; + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RefModeRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/RefModeRecord.java new file mode 100755 index 0000000..aff642b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/RefModeRecord.java @@ -0,0 +1,51 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the reference style option from the options dialog box + */ +class RefModeRecord extends WritableRecordData { + /** + * Constructor + */ + public RefModeRecord() { + super(Type.REFMODE); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[2]; + + // Hard code in the style A1 for now + data[0] = 0x1; + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RefreshAllRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/RefreshAllRecord.java new file mode 100755 index 0000000..3764fe9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/RefreshAllRecord.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates whether or not data ranges and pivot tables + * should be refreshed when the workbook is loaded + */ +class RefreshAllRecord extends WritableRecordData { + /** + * The refresh all flag + */ + private final boolean refreshall; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param refresh refresh all flag + */ + public RefreshAllRecord(boolean refresh) { + super(Type.REFRESHALL); + + refreshall = refresh; + + // Hard code in an unprotected workbook + data = new byte[2]; + + if (refreshall) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RightMarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/RightMarginRecord.java new file mode 100755 index 0000000..8a9b903 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/RightMarginRecord.java @@ -0,0 +1,31 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class RightMarginRecord extends MarginRecord { + RightMarginRecord(double v) { + super(Type.RIGHTMARGIN, v); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RowRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/RowRecord.java new file mode 100755 index 0000000..8a794ac --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/RowRecord.java @@ -0,0 +1,659 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.CellType; +import jxl.SheetSettings; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IndexMapping; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.biff.XFRecord; +import jxl.common.Logger; +import jxl.write.Number; +import jxl.write.WritableCellFeatures; +import jxl.write.WritableSheet; + +/** + * Contains all the cells for a given row in a sheet + */ +class RowRecord extends WritableRecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(RowRecord.class); + /** + * The amount to grow the cells array by + */ + private static final int growSize = 10; + /** + * The maximum integer value that can be squeezed into 30 bits + */ + private static final int maxRKValue = 0x1fffffff; + /** + * The minimum integer value that can be squeezed into 30 bits + */ + private static final int minRKValue = -0x20000000; + /** + * Indicates that the row is default height + */ + private static final int defaultHeightIndicator = 0xff; + /** + * The maximum number of columns + */ + private static final int maxColumns = 256; + /** + * The binary data + */ + private byte[] data; + /** + * The cells which comprise this row + */ + private CellValue[] cells; + /** + * The height of this row in 1/20ths of a point + */ + private int rowHeight; + /** + * Flag to indicate whether this row is outline collapsed or not + */ + private boolean collapsed; + /** + * The number of this row within the worksheet + */ + private int rowNumber; + /** + * The number of columns in this row. This is the largest column value + 1 + */ + private int numColumns; + /** + * The xfIndex for this row + */ + private int xfIndex; + /** + * The style for this row + */ + private XFRecord style; + /** + * Flag indicating that this row record has an default format + */ + private boolean defaultFormat; + /** + * Flag indicating whether this row matches the default font height + */ + private boolean matchesDefFontHeight; + /** + * The outline level of the row + */ + private int outlineLevel; + + /** + * Is this the icon indicator row of a group? + */ + private boolean groupStart; + + /** + * A handle back to the sheet + */ + private final WritableSheet sheet; + + /** + * Constructs an empty row which has the specified row number + * + * @param rn the row number of this row + */ + public RowRecord(int rn, WritableSheet ws) { + super(Type.ROW); + rowNumber = rn; + cells = new CellValue[0]; + numColumns = 0; + rowHeight = defaultHeightIndicator; + collapsed = false; + matchesDefFontHeight = true; + sheet = ws; + } + + /** + * Sets the row details based upon the readable row record passed in + * Called when copying spreadsheets + * + * @param height the height of the row record in 1/20ths of a point + * @param mdfh matches the default font height + * @param col the collapsed status of the row + * @param ol the outline level + * @param gs the group start + * @param xf the xfrecord for the row (NULL if no default is set) + */ + void setRowDetails(int height, + boolean mdfh, + boolean col, + int ol, + boolean gs, + XFRecord xfr) { + rowHeight = height; + collapsed = col; + matchesDefFontHeight = mdfh; + outlineLevel = ol; + groupStart = gs; + + if (xfr != null) { + defaultFormat = true; + style = xfr; + xfIndex = style.getXFIndex(); + } + } + + /** + * Gets the row number of this row + * + * @return the row number + */ + public int getRowNumber() { + return rowNumber; + } + + /** + * Adds a cell to this row, growing the array of cells as required + * + * @param cv the cell to add + */ + public void addCell(CellValue cv) { + int col = cv.getColumn(); + + if (col >= maxColumns) { + logger.warn("Could not add cell at " + + CellReferenceHelper.getCellReference(cv.getRow(), + cv.getColumn()) + + " because it exceeds the maximum column limit"); + return; + } + + // Grow the array if needs be + if (col >= cells.length) { + CellValue[] oldCells = cells; + cells = new CellValue[Math.max(oldCells.length + growSize, col + 1)]; + System.arraycopy(oldCells, 0, cells, 0, oldCells.length); + oldCells = null; + } + + // Remove any cell features from the cell being replaced + if (cells[col] != null) { + WritableCellFeatures wcf = cells[col].getWritableCellFeatures(); + if (wcf != null) { + wcf.removeComment(); + + // if the cell is part of a shared data validation,then don't remove + // the validation + if (wcf.getDVParser() != null && + !wcf.getDVParser().extendedCellsValidation()) { + wcf.removeDataValidation(); + } + } + + } + + cells[col] = cv; + + numColumns = Math.max(col + 1, numColumns); + } + + /** + * Removes a cell from this row + * + * @param col the column at which to remove the cell + */ + public void removeCell(int col) { + // Grow the array if needs be + if (col >= numColumns) { + return; + } + + cells[col] = null; + } + + /** + * Writes out the row information data (but not the individual cells) + * + * @param outputFile the output file + * @throws IOException + */ + public void write(File outputFile) throws IOException { + outputFile.write(this); + } + + /** + * Writes out all the cells in this row. If more than three integer + * values occur consecutively, then a MulRK record is used to group the + * numbers + * + * @param outputFile the output file + * @throws IOException + */ + public void writeCells(File outputFile) + throws IOException { + // This is the list for integer values + ArrayList integerValues = new ArrayList(); + boolean integerValue = false; + + // Write out all the records + for (int i = 0; i < numColumns; i++) { + integerValue = false; + if (cells[i] != null) { + // See if this cell is a 30-bit integer value (without additional + // cell features) + if (cells[i].getType() == CellType.NUMBER) { + Number nc = (Number) cells[i]; + if (nc.getValue() == (int) nc.getValue() && + nc.getValue() < maxRKValue && + nc.getValue() > minRKValue && + nc.getCellFeatures() == null) { + integerValue = true; + } + } + + if (integerValue) { + // This cell is an integer, add it to the list + integerValues.add(cells[i]); + } else { + // This cell is not an integer. Write out whatever integers we + // have, and then write out this cell + writeIntegerValues(integerValues, outputFile); + outputFile.write(cells[i]); + + // If the cell is a string formula, write out the string record + // immediately afterwards + if (cells[i].getType() == CellType.STRING_FORMULA) { + StringRecord sr = new StringRecord(cells[i].getContents()); + outputFile.write(sr); + } + } + } else { + // Cell does not exist. Write out the list of integers that + // we have + writeIntegerValues(integerValues, outputFile); + } + } + + // All done. Write out any remaining integer values + writeIntegerValues(integerValues, outputFile); + } + + /** + * Writes out the list of integer values. If there are more than three, + * a MulRK record is used, otherwise a sequence of Numbers is used + * + * @param outputFile the output file + * @param integerValues the array of integer values + * @throws IOException + */ + private void writeIntegerValues(ArrayList integerValues, File outputFile) + throws IOException { + if (integerValues.size() == 0) { + return; + } + + if (integerValues.size() >= 3) { + // Write out as a MulRK record + MulRKRecord mulrk = new MulRKRecord(integerValues); + outputFile.write(mulrk); + } else { + // Write out as number records + Iterator i = integerValues.iterator(); + while (i.hasNext()) { + outputFile.write((CellValue) i.next()); + } + } + + // Clear out the list of integerValues + integerValues.clear(); + } + + /** + * Gets the row data to output to file + * + * @return the binary data + */ + public byte[] getData() { + // Write out the row record + byte[] data = new byte[16]; + + // If the default row height has been changed in the sheet settings, + // then we need to set the rowHeight on this row explicitly, as + // specifying the "match default" flag doesn't work + int rh = rowHeight; + if (sheet.getSettings().getDefaultRowHeight() != + SheetSettings.DEFAULT_DEFAULT_ROW_HEIGHT) { + // the default row height has been changed. If this row does not + // have a specific row height set on it, then set it to the default + if (rh == defaultHeightIndicator) { + rh = sheet.getSettings().getDefaultRowHeight(); + } + } + + IntegerHelper.getTwoBytes(rowNumber, data, 0); + IntegerHelper.getTwoBytes(numColumns, data, 4); + IntegerHelper.getTwoBytes(rh, data, 6); + + int options = 0x100 + outlineLevel; + + if (groupStart) { + options |= 0x10; + } + + if (collapsed) { + options |= 0x20; + } + + if (!matchesDefFontHeight) { + options |= 0x40; + } + + if (defaultFormat) { + options |= 0x80; + options |= (xfIndex << 16); + } + + IntegerHelper.getFourBytes(options, data, 12); + + return data; + } + + /** + * Gets the maximum column value which occurs in this row + * + * @return the maximum column value + */ + public int getMaxColumn() { + return numColumns; + } + + /** + * Gets the cell which occurs at the specified column value + * + * @param col the colun for which to return the cell + * @return the cell value at the specified position, or null if the column + * is invalid + */ + public CellValue getCell(int col) { + return (col >= 0 && col < numColumns) ? cells[col] : null; + } + + /** + * Increments the row of this cell by one. Invoked by the sheet when + * inserting rows + */ + void incrementRow() { + rowNumber++; + + for (int i = 0; i < cells.length; i++) { + if (cells[i] != null) { + cells[i].incrementRow(); + } + } + } + + /** + * Decrements the row of this cell by one. Invoked by the sheet when + * removing rows + */ + void decrementRow() { + rowNumber--; + for (int i = 0; i < cells.length; i++) { + if (cells[i] != null) { + cells[i].decrementRow(); + } + } + } + + /** + * Inserts a new column at the position specified. If the max column length + * is already reached, then the last column simply gets dropped + * + * @param col the column to insert + */ + void insertColumn(int col) { + // Don't bother doing anything unless there are cells after the + // column to be inserted + if (col >= numColumns) { + return; + } + + // Create a new array to hold the new column. Grow it if need be + CellValue[] oldCells = cells; + + if (numColumns >= cells.length - 1) { + cells = new CellValue[oldCells.length + growSize]; + } else { + cells = new CellValue[oldCells.length]; + } + + // Copy in everything up to the new column + System.arraycopy(oldCells, 0, cells, 0, col); + + // Copy in the remaining cells + System.arraycopy(oldCells, col, cells, col + 1, numColumns - col); + + // Increment all the internal column numbers by one + for (int i = col + 1; i <= numColumns; i++) { + if (cells[i] != null) { + cells[i].incrementColumn(); + } + } + + // Adjust the maximum column record + numColumns = Math.min(numColumns + 1, maxColumns); + } + + /** + * Remove the new column at the position specified + * + * @param col the column to remove + */ + void removeColumn(int col) { + // Don't bother doing anything unless there are cells after the + // column to be inserted + if (col >= numColumns) { + return; + } + + // Create a new array to hold the new columns + CellValue[] oldCells = cells; + + cells = new CellValue[oldCells.length]; + + // Copy in everything up to the column + System.arraycopy(oldCells, 0, cells, 0, col); + + // Copy in the remaining cells after the column + System.arraycopy(oldCells, col + 1, cells, col, numColumns - (col + 1)); + + // Decrement all the internal column numbers by one + for (int i = col; i < numColumns; i++) { + if (cells[i] != null) { + cells[i].decrementColumn(); + } + } + + // Adjust the maximum column record + numColumns--; + } + + /** + * Interrogates whether this row is of default height + * + * @return TRUE if this is set to the default height, FALSE otherwise + */ + public boolean isDefaultHeight() { + return rowHeight == defaultHeightIndicator; + } + + /** + * Gets the height of the row + * + * @return the row height + */ + public int getRowHeight() { + return rowHeight; + } + + /** + * Sets the height of this row + * + * @param h the row height + */ + public void setRowHeight(int h) { + if (h == 0) { + setCollapsed(true); + matchesDefFontHeight = false; + } else { + rowHeight = h; + matchesDefFontHeight = false; + } + } + + /** + * Queries whether the row is collapsed + * + * @return the collapsed indicator + */ + public boolean isCollapsed() { + return collapsed; + } + + /** + * Sets the collapsed status of this row + * + * @param c the collapsed flag + */ + public void setCollapsed(boolean c) { + collapsed = c; + } + + /** + * Rationalizes the sheets xf index mapping + * + * @param xfmapping the index mapping + */ + void rationalize(IndexMapping xfmapping) { + if (defaultFormat) { + xfIndex = xfmapping.getNewIndex(xfIndex); + } + } + + /** + * Accessor for the style. The returned value is only non-null if the + * default style is overridden + * + * @return the style + */ + XFRecord getStyle() { + return style; + } + + /** + * Accessor for the default format flag + * + * @return TRUE if this row has its own default format + */ + boolean hasDefaultFormat() { + return defaultFormat; + } + + /** + * Accessor for the matches default font height flag + * + * @return TRUE if this row matches the default font height + */ + boolean matchesDefaultFontHeight() { + return matchesDefFontHeight; + } + + /** + * Accessor for the column's outline level + * + * @return the column's outline level + */ + public int getOutlineLevel() { + return outlineLevel; + } + + /** + * Sets the row's outline level + * + * @param level the row's outline level + */ + public void setOutlineLevel(int level) { + outlineLevel = level; + } + + /** + * Accessor for row's groupStart state + * + * @return the row's groupStart state + */ + public boolean getGroupStart() { + return groupStart; + } + + /** + * Sets the row's group start state + * + * @param value the group start state + */ + public void setGroupStart(boolean value) { + groupStart = value; + } + + /** + * Increments the row's outline level. This is how groups are made as well + */ + public void incrementOutlineLevel() { + outlineLevel++; + } + + /** + * Decrements the row's outline level. This removes it from a grouping + * level. If + * all outline levels are gone the uncollapse the row. + */ + public void decrementOutlineLevel() { + if (0 < outlineLevel) { + outlineLevel--; + } + + if (0 == outlineLevel) { + collapsed = false; + } + } +} + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RowsExceededException.java b/datastructures-xslx/src/main/java/jxl/write/biff/RowsExceededException.java new file mode 100755 index 0000000..b1762e4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/RowsExceededException.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +/** + * Exception thrown when attempting to add a row to a spreadsheet which + * has already reached the maximum amount + */ +public class RowsExceededException extends JxlWriteException { + /** + * Constructor + */ + public RowsExceededException() { + super(maxRowsExceeded); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SCLRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SCLRecord.java new file mode 100755 index 0000000..122e43c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SCLRecord.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan, Adam Caldwell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a margin value + */ +class SCLRecord extends WritableRecordData { + /** + * The zoom factor + */ + private final int zoomFactor; + + /** + * Constructor + * + * @param zf the zoom factor as a percentage + */ + public SCLRecord(int zf) { + super(Type.SCL); + + zoomFactor = zf; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[4]; + + int numerator = zoomFactor; + int denominator = 100; + + IntegerHelper.getTwoBytes(numerator, data, 0); + IntegerHelper.getTwoBytes(denominator, data, 2); + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SSTContinueRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SSTContinueRecord.java new file mode 100755 index 0000000..37d27b3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SSTContinueRecord.java @@ -0,0 +1,207 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A continuation of a shared string table record. + */ +class SSTContinueRecord extends WritableRecordData { + /** + * The maximum amount of bytes available for the SST record + */ + private static final int maxBytes = 8228 - // max length + 4; // standard biff record stuff + /** + * The first string is a special case + */ + private String firstString; + /** + * Indicates whether or not we need to include the length information + * for the first string + */ + private boolean includeLength; + /** + * The length of the first string + */ + private int firstStringLength; + /** + * The list of strings + */ + private final ArrayList strings; + /** + * The list of string lengths + */ + private final ArrayList stringLengths; + /** + * The binary data + */ + private byte[] data; + /** + * The count of bytes needed so far to contain this record + */ + private int byteCount; + + /** + * Constructor + * + * @param numRefs the number of string references in the workbook + * @param s the number of strings + */ + public SSTContinueRecord() { + super(Type.CONTINUE); + + byteCount = 0; + strings = new ArrayList(50); + stringLengths = new ArrayList(50); + } + + /** + * Adds the first string to this SST record + * + * @param s the string to add + * @param b include the length information for the first string + * @return the number of characters not added + */ + public int setFirstString(String s, boolean b) { + includeLength = b; + firstStringLength = s.length(); + + int bytes = 0; + + if (!includeLength) { + bytes = s.length() * 2 + 1; + } else { + bytes = s.length() * 2 + 3; + } + + if (bytes <= maxBytes) { + firstString = s; + byteCount += bytes; + return 0; + } + + // Calculate the number of characters we can add + // The bytes variable will always be an odd number + int charsAvailable = includeLength ? (maxBytes - 4) / 2 : + (maxBytes - 2) / 2; + + // Add what part of the string we can + firstString = s.substring(0, charsAvailable); + byteCount = maxBytes - 1; + + return s.length() - charsAvailable; + } + + /** + * Gets the current offset into this record, excluding the header fields + * + * @return the number of bytes after the header field + */ + public int getOffset() { + return byteCount; + } + + /** + * Adds a string to this record. It returns the number of string + * characters not added, due to space constraints. In the event + * of this being non-zero, a continue record will be needed + * + * @param s the string to add + * @return the number of characters not added + */ + public int add(String s) { + int bytes = s.length() * 2 + 3; + + // Must be able to add at least the first character of the string + // onto the SST + if (byteCount >= maxBytes - 5) { + return s.length(); + } + + stringLengths.add(new Integer(s.length())); + + if (bytes + byteCount < maxBytes) { + // add the string and return + strings.add(s); + byteCount += bytes; + return 0; + } + + // Calculate the number of characters we can add + int bytesLeft = maxBytes - 3 - byteCount; + int charsAvailable = bytesLeft % 2 == 0 ? bytesLeft / 2 : + (bytesLeft - 1) / 2; + + // Add what part of the string we can + strings.add(s.substring(0, charsAvailable)); + byteCount += charsAvailable * 2 + 3; + + return s.length() - charsAvailable; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + data = new byte[byteCount]; + + int pos = 0; + + // Write out the first string + if (includeLength) { + IntegerHelper.getTwoBytes(firstStringLength, data, 0); + data[2] = 0x01; + pos = 3; + } else { + // Just include the unicode indicator + data[0] = 0x01; + pos = 1; + } + + StringHelper.getUnicodeBytes(firstString, data, pos); + pos += firstString.length() * 2; + + // Now write out the remainder of the strings + Iterator i = strings.iterator(); + String s = null; + int length = 0; + int count = 0; + while (i.hasNext()) { + s = (String) i.next(); + length = ((Integer) stringLengths.get(count)).intValue(); + IntegerHelper.getTwoBytes(length, data, pos); + data[pos + 2] = 0x01; + StringHelper.getUnicodeBytes(s, data, pos + 3); + pos += s.length() * 2 + 3; + count++; + } + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SSTRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SSTRecord.java new file mode 100755 index 0000000..2dd5d9e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SSTRecord.java @@ -0,0 +1,156 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A shared string table record. + */ +class SSTRecord extends WritableRecordData { + /** + * The maximum amount of bytes available for the SST record + */ + private static final int maxBytes = 8228 - // max length + 8 - // bytes for string count fields + 4; // standard biff record header + /** + * The number of string references in the workbook + */ + private final int numReferences; + /** + * The number of strings in this table + */ + private final int numStrings; + /** + * The list of strings + */ + private final ArrayList strings; + /** + * The list of string lengths + */ + private final ArrayList stringLengths; + /** + * The binary data + */ + private byte[] data; + /** + * The count of bytes needed so far to contain this record + */ + private int byteCount; + + /** + * Constructor + * + * @param numRefs the number of string references in the workbook + * @param s the number of strings + */ + public SSTRecord(int numRefs, int s) { + super(Type.SST); + + numReferences = numRefs; + numStrings = s; + byteCount = 0; + strings = new ArrayList(50); + stringLengths = new ArrayList(50); + } + + /** + * Adds a string to this SST record. It returns the number of string + * characters not added, due to space constraints. In the event + * of this being non-zero, a continue record will be needed + * + * @param s the string to add + * @return the number of characters not added + */ + public int add(String s) { + int bytes = s.length() * 2 + 3; + + // Must be able to add at least the first character of the string + // onto the SST + if (byteCount >= maxBytes - 5) { + return s.length() > 0 ? s.length() : -1; // need to return some non-zero + // value in order to force the creation of a continue record + } + + stringLengths.add(new Integer(s.length())); + + if (bytes + byteCount < maxBytes) { + // add the string and return + strings.add(s); + byteCount += bytes; + return 0; + } + + // Calculate the number of characters we can add + int bytesLeft = maxBytes - 3 - byteCount; + int charsAvailable = bytesLeft % 2 == 0 ? bytesLeft / 2 : + (bytesLeft - 1) / 2; + + // Add what strings we can + strings.add(s.substring(0, charsAvailable)); + byteCount += charsAvailable * 2 + 3; + + return s.length() - charsAvailable; + } + + /** + * Gets the current offset into this record, excluding the header fields + * + * @return the number of bytes after the header field + */ + public int getOffset() { + return byteCount + 8; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + data = new byte[byteCount + 8]; + IntegerHelper.getFourBytes(numReferences, data, 0); + IntegerHelper.getFourBytes(numStrings, data, 4); + + int pos = 8; + int count = 0; + + Iterator i = strings.iterator(); + String s = null; + int length = 0; + while (i.hasNext()) { + s = (String) i.next(); + length = ((Integer) stringLengths.get(count)).intValue(); + IntegerHelper.getTwoBytes(length, data, pos); + data[pos + 2] = 0x01; + StringHelper.getUnicodeBytes(s, data, pos + 3); + pos += s.length() * 2 + 3; + count++; + } + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SaveRecalcRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SaveRecalcRecord.java new file mode 100755 index 0000000..dce67f2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SaveRecalcRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the recalculate before save flag as set in the options dialog box + */ +class SaveRecalcRecord extends WritableRecordData { + /** + * The binary data for output to file + */ + private final byte[] data; + /** + * The recalculate before save flag + */ + private final boolean recalc; + + /** + * Constructor + * + * @param r recalculate flag + */ + public SaveRecalcRecord(boolean r) { + super(Type.SAVERECALC); + recalc = r; + + data = new byte[2]; + + if (recalc) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ScenarioProtectRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ScenarioProtectRecord.java new file mode 100755 index 0000000..17dcd31 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ScenarioProtectRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The protection state for a sheet or workbook + */ +class ScenarioProtectRecord extends WritableRecordData { + /** + * The protection state + */ + private final boolean protection; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param prot the protection state + */ + public ScenarioProtectRecord(boolean prot) { + super(Type.SCENPROTECT); + + protection = prot; + + data = new byte[2]; + + if (protection) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SelectionRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SelectionRecord.java new file mode 100755 index 0000000..31acc7a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SelectionRecord.java @@ -0,0 +1,88 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the current selection + */ +class SelectionRecord extends WritableRecordData { + // The pane types + public final static PaneType lowerRight = new PaneType(0); + public final static PaneType upperRight = new PaneType(1); + public final static PaneType lowerLeft = new PaneType(2); + public final static PaneType upperLeft = new PaneType(3); + /** + * The pane type + */ + private final PaneType pane; + /** + * The top left column in this pane + */ + private final int column; + /** + * The top left row in this pane + */ + private final int row; + /** + * Constructor + */ + public SelectionRecord(PaneType pt, int col, int r) { + super(Type.SELECTION); + column = col; + row = r; + pane = pt; + } + + /** + * Gets the binary data + * + * @return the binary data + */ + public byte[] getData() { + // hard code the data in for now + byte[] data = new byte[15]; + + data[0] = (byte) pane.val; + IntegerHelper.getTwoBytes(row, data, 1); + IntegerHelper.getTwoBytes(column, data, 3); + + data[7] = (byte) 0x01; + + IntegerHelper.getTwoBytes(row, data, 9); + IntegerHelper.getTwoBytes(row, data, 11); + data[13] = (byte) column; + data[14] = (byte) column; + + return data; + } + + // Enumeration for the pane type + private static class PaneType { + int val; + + PaneType(int v) { + val = v; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SetupRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SetupRecord.java new file mode 100755 index 0000000..242d2a1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SetupRecord.java @@ -0,0 +1,235 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.SheetSettings; +import jxl.biff.DoubleHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; +import jxl.format.PageOrder; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; + +/** + * Stores the options and measurements from the Page Setup dialog box + */ +class SetupRecord extends WritableRecordData { + /** + * The logger + */ + Logger logger = Logger.getLogger(SetupRecord.class); + + /** + * The binary data for output to file + */ + private byte[] data; + + /** + * The header margin + */ + private double headerMargin; + + /** + * The footer margin + */ + private double footerMargin; + + /** + * The page orientation + */ + private PageOrientation orientation; + + /** + * The page order + */ + private PageOrder order; + + /** + * The paper size + */ + private int paperSize; + + /** + * The scale factor + */ + private final int scaleFactor; + + /** + * The page start + */ + private final int pageStart; + + /** + * The fit width + */ + private final int fitWidth; + + /** + * The fit height + */ + private final int fitHeight; + + /** + * The horizontal print resolution + */ + private final int horizontalPrintResolution; + + /** + * The vertical print resolution + */ + private final int verticalPrintResolution; + + /** + * The number of copies + */ + private final int copies; + + /** + * Indicates whether the setup data should be initiliazed in the setup + * box + */ + private final boolean initialized; + + /** + * Constructor, taking the sheet settings. This object just + * takes the various fields from the bean in which it is interested + * + * @param the sheet settings + */ + public SetupRecord(SheetSettings s) { + super(Type.SETUP); + + orientation = s.getOrientation(); + order = s.getPageOrder(); + headerMargin = s.getHeaderMargin(); + footerMargin = s.getFooterMargin(); + paperSize = s.getPaperSize().getValue(); + horizontalPrintResolution = s.getHorizontalPrintResolution(); + verticalPrintResolution = s.getVerticalPrintResolution(); + fitWidth = s.getFitWidth(); + fitHeight = s.getFitHeight(); + pageStart = s.getPageStart(); + scaleFactor = s.getScaleFactor(); + copies = s.getCopies(); + initialized = true; + } + + /** + * Sets the orientation + * + * @param o the orientation + */ + public void setOrientation(PageOrientation o) { + orientation = o; + } + + /** + * Sets the page order + * + * @param o + */ + public void setOrder(PageOrder o) { + order = o; + } + + /** + * Sets the header and footer margins + * + * @param hm the header margin + * @param fm the footer margin + */ + public void setMargins(double hm, double fm) { + headerMargin = hm; + footerMargin = fm; + } + + /** + * Sets the paper size + * + * @param ps the paper size + */ + public void setPaperSize(PaperSize ps) { + paperSize = ps.getValue(); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + data = new byte[34]; + + // Paper size + IntegerHelper.getTwoBytes(paperSize, data, 0); + + // Scale factor + IntegerHelper.getTwoBytes(scaleFactor, data, 2); + + // Page start + IntegerHelper.getTwoBytes(pageStart, data, 4); + + // Fit width + IntegerHelper.getTwoBytes(fitWidth, data, 6); + + // Fit height + IntegerHelper.getTwoBytes(fitHeight, data, 8); + + // grbit + int options = 0; + if (order == PageOrder.RIGHT_THEN_DOWN) { + options |= 0x01; + } + + if (orientation == PageOrientation.PORTRAIT) { + options |= 0x02; + } + + if (pageStart != 0) { + options |= 0x80; + } + + if (!initialized) { + options |= 0x04; + } + + IntegerHelper.getTwoBytes(options, data, 10); + + // print resolution + IntegerHelper.getTwoBytes(horizontalPrintResolution, data, 12); + + // vertical print resolution + IntegerHelper.getTwoBytes(verticalPrintResolution, data, 14); + + // header margin + DoubleHelper.getIEEEBytes(headerMargin, data, 16); + + // footer margin + DoubleHelper.getIEEEBytes(footerMargin, data, 24); + + // Number of copies + IntegerHelper.getTwoBytes(copies, data, 32); + + return data; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SharedStrings.java b/datastructures-xslx/src/main/java/jxl/write/biff/SharedStrings.java new file mode 100755 index 0000000..2b9a2cd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SharedStrings.java @@ -0,0 +1,171 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +/** + * The list of available shared strings. This class contains + * the labels used for the entire spreadsheet + */ +class SharedStrings { + /** + * All the strings in the spreadsheet, keyed on the string itself + */ + private final HashMap strings; + + /** + * Contains the same strings, held in a list + */ + private final ArrayList stringList; + + /** + * The total occurrence of strings in the workbook + */ + private int totalOccurrences; + + /** + * Constructor + */ + public SharedStrings() { + strings = new HashMap(100); + stringList = new ArrayList(100); + totalOccurrences = 0; + } + + /** + * Gets the index for the string passed in. If the string is already + * present, then returns the index of that string, otherwise + * creates a new key-index mapping + * + * @param s the string whose index we want + * @return the index of the string + */ + public int getIndex(String s) { + Integer i = (Integer) strings.get(s); + + if (i == null) { + i = new Integer(strings.size()); + strings.put(s, i); + stringList.add(s); + } + + totalOccurrences++; + + return i.intValue(); + } + + /** + * Gets the string at the specified index + * + * @param i the index of the string + * @return the string at the specified index + */ + public String get(int i) { + return (String) stringList.get(i); + } + + /** + * Writes out the shared string table + * + * @param outputFile the binary output file + * @throws IOException + */ + public void write(File outputFile) throws IOException { + // Thanks to Guenther for contributing the ExtSST implementation portion + // of this method + int charsLeft = 0; + String curString = null; + SSTRecord sst = new SSTRecord(totalOccurrences, stringList.size()); + ExtendedSSTRecord extsst = new ExtendedSSTRecord(stringList.size()); + int bucketSize = extsst.getNumberOfStringsPerBucket(); + + Iterator i = stringList.iterator(); + int stringIndex = 0; + while (i.hasNext() && charsLeft == 0) { + curString = (String) i.next(); + // offset + header bytes + int relativePosition = sst.getOffset() + 4; + charsLeft = sst.add(curString); + if ((stringIndex % bucketSize) == 0) { + extsst.addString(outputFile.getPos(), relativePosition); + } + stringIndex++; + } + outputFile.write(sst); + + if (charsLeft != 0 || i.hasNext()) { + // Add the remainder of the string to the continue record + SSTContinueRecord cont = createContinueRecord(curString, + charsLeft, + outputFile); + + // Carry on looping through the array until all the strings are done + while (i.hasNext()) { + curString = (String) i.next(); + int relativePosition = cont.getOffset() + 4; + charsLeft = cont.add(curString); + if ((stringIndex % bucketSize) == 0) { + extsst.addString(outputFile.getPos(), relativePosition); + } + stringIndex++; + + if (charsLeft != 0) { + outputFile.write(cont); + cont = createContinueRecord(curString, charsLeft, outputFile); + } + } + + outputFile.write(cont); + } + + outputFile.write(extsst); + } + + /** + * Creates and returns a continue record using the left over bits and + * pieces + */ + private SSTContinueRecord createContinueRecord + (String curString, int charsLeft, File outputFile) throws IOException { + // Set up the remainder of the string in the continue record + SSTContinueRecord cont = null; + while (charsLeft != 0) { + cont = new SSTContinueRecord(); + + if (charsLeft == curString.length() || curString.length() == 0) { + charsLeft = cont.setFirstString(curString, true); + } else { + charsLeft = cont.setFirstString + (curString.substring(curString.length() - charsLeft), false); + } + + if (charsLeft != 0) { + outputFile.write(cont); + cont = new SSTContinueRecord(); + } + } + + return cont; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SheetCopier.java b/datastructures-xslx/src/main/java/jxl/write/biff/SheetCopier.java new file mode 100755 index 0000000..3419945 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SheetCopier.java @@ -0,0 +1,965 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.TreeSet; +import jxl.BooleanCell; +import jxl.Cell; +import jxl.CellType; +import jxl.DateCell; +import jxl.Hyperlink; +import jxl.LabelCell; +import jxl.NumberCell; +import jxl.Range; +import jxl.Sheet; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.CellReferenceHelper; +import jxl.biff.ConditionalFormat; +import jxl.biff.DataValidation; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.SheetRangeImpl; +import jxl.biff.XFRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.biff.formula.FormulaException; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; +import jxl.read.biff.NameRecord; +import jxl.read.biff.SheetImpl; +import jxl.read.biff.WorkbookParser; +import jxl.write.Blank; +import jxl.write.Boolean; +import jxl.write.DateTime; +import jxl.write.Formula; +import jxl.write.Label; +import jxl.write.Number; +import jxl.write.WritableCell; +import jxl.write.WritableCellFormat; +import jxl.write.WritableHyperlink; +import jxl.write.WritableImage; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; + +/** + * A transient utility object used to copy sheets. This + * functionality has been farmed out to a different class + * in order to reduce the bloat of the WritableSheetImpl + */ +class SheetCopier { + private static final Logger logger = Logger.getLogger(SheetCopier.class); + + private final SheetImpl fromSheet; + private final WritableSheetImpl toSheet; + private final WorkbookSettings workbookSettings; + + // Objects used by the sheet + private TreeSet columnFormats; + private FormattingRecords formatRecords; + private ArrayList hyperlinks; + private MergedCells mergedCells; + private ArrayList rowBreaks; + private ArrayList columnBreaks; + private SheetWriter sheetWriter; + private ArrayList drawings; + private ArrayList images; + private ArrayList conditionalFormats; + private ArrayList validatedCells; + private AutoFilter autoFilter; + private DataValidation dataValidation; + private ComboBox comboBox; + private PLSRecord plsRecord; + private boolean chartOnly; + private ButtonPropertySetRecord buttonPropertySet; + private int numRows; + private int maxRowOutlineLevel; + private int maxColumnOutlineLevel; + + // Objects used to maintain state during the copy process + private HashMap xfRecords; + private HashMap fonts; + private HashMap formats; + + public SheetCopier(Sheet f, WritableSheet t) { + fromSheet = (SheetImpl) f; + toSheet = (WritableSheetImpl) t; + workbookSettings = toSheet.getWorkbook().getSettings(); + chartOnly = false; + } + + void setColumnFormats(TreeSet cf) { + columnFormats = cf; + } + + void setFormatRecords(FormattingRecords fr) { + formatRecords = fr; + } + + void setHyperlinks(ArrayList h) { + hyperlinks = h; + } + + void setMergedCells(MergedCells mc) { + mergedCells = mc; + } + + void setRowBreaks(ArrayList rb) { + rowBreaks = rb; + } + + void setColumnBreaks(ArrayList cb) { + columnBreaks = cb; + } + + void setSheetWriter(SheetWriter sw) { + sheetWriter = sw; + } + + void setDrawings(ArrayList d) { + drawings = d; + } + + void setImages(ArrayList i) { + images = i; + } + + void setConditionalFormats(ArrayList cf) { + conditionalFormats = cf; + } + + void setValidatedCells(ArrayList vc) { + validatedCells = vc; + } + + AutoFilter getAutoFilter() { + return autoFilter; + } + + DataValidation getDataValidation() { + return dataValidation; + } + + ComboBox getComboBox() { + return comboBox; + } + + PLSRecord getPLSRecord() { + return plsRecord; + } + + boolean isChartOnly() { + return chartOnly; + } + + ButtonPropertySetRecord getButtonPropertySet() { + return buttonPropertySet; + } + + /** + * Copies a sheet from a read-only version to the writable version. + * Performs shallow copies + */ + public void copySheet() { + shallowCopyCells(); + + // Copy the column info records + jxl.read.biff.ColumnInfoRecord[] readCirs = fromSheet.getColumnInfos(); + + for (int i = 0; i < readCirs.length; i++) { + jxl.read.biff.ColumnInfoRecord rcir = readCirs[i]; + for (int j = rcir.getStartColumn(); j <= rcir.getEndColumn(); j++) { + ColumnInfoRecord cir = new ColumnInfoRecord(rcir, j, + formatRecords); + cir.setHidden(rcir.getHidden()); + columnFormats.add(cir); + } + } + + // Copy the hyperlinks + Hyperlink[] hls = fromSheet.getHyperlinks(); + for (int i = 0; i < hls.length; i++) { + WritableHyperlink hr = new WritableHyperlink + (hls[i], toSheet); + hyperlinks.add(hr); + } + + // Copy the merged cells + Range[] merged = fromSheet.getMergedCells(); + + for (int i = 0; i < merged.length; i++) { + mergedCells.add(new SheetRangeImpl((SheetRangeImpl) merged[i], toSheet)); + } + + // Copy the row properties + try { + jxl.read.biff.RowRecord[] rowprops = fromSheet.getRowProperties(); + + for (int i = 0; i < rowprops.length; i++) { + RowRecord rr = toSheet.getRowRecord(rowprops[i].getRowNumber()); + XFRecord format = rowprops[i].hasDefaultFormat() ? + formatRecords.getXFRecord(rowprops[i].getXFIndex()) : null; + rr.setRowDetails(rowprops[i].getRowHeight(), + rowprops[i].matchesDefaultFontHeight(), + rowprops[i].isCollapsed(), + rowprops[i].getOutlineLevel(), + rowprops[i].getGroupStart(), + format); + numRows = Math.max(numRows, rowprops[i].getRowNumber() + 1); + } + } catch (RowsExceededException e) { + // Handle the rows exceeded exception - this cannot occur since + // the sheet we are copying from will have a valid number of rows + Assert.verify(false); + } + + // Copy the headers and footers + // sheetWriter.setHeader(new HeaderRecord(si.getHeader())); + // sheetWriter.setFooter(new FooterRecord(si.getFooter())); + + // Copy the page breaks + int[] rowbreaks = fromSheet.getRowPageBreaks(); + + if (rowbreaks != null) { + for (int i = 0; i < rowbreaks.length; i++) { + rowBreaks.add(new Integer(rowbreaks[i])); + } + } + + int[] columnbreaks = fromSheet.getColumnPageBreaks(); + + if (columnbreaks != null) { + for (int i = 0; i < columnbreaks.length; i++) { + columnBreaks.add(new Integer(columnbreaks[i])); + } + } + + // Copy the charts + sheetWriter.setCharts(fromSheet.getCharts()); + + // Copy the drawings + DrawingGroupObject[] dr = fromSheet.getDrawings(); + for (int i = 0; i < dr.length; i++) { + if (dr[i] instanceof jxl.biff.drawing.Drawing) { + WritableImage wi = new WritableImage + (dr[i], toSheet.getWorkbook().getDrawingGroup()); + drawings.add(wi); + images.add(wi); + } else if (dr[i] instanceof jxl.biff.drawing.Comment) { + jxl.biff.drawing.Comment c = + new jxl.biff.drawing.Comment(dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(c); + + // Set up the reference on the cell value + CellValue cv = (CellValue) toSheet.getWritableCell(c.getColumn(), + c.getRow()); + Assert.verify(cv.getCellFeatures() != null); + cv.getWritableCellFeatures().setCommentDrawing(c); + } else if (dr[i] instanceof jxl.biff.drawing.Button) { + jxl.biff.drawing.Button b = + new jxl.biff.drawing.Button + (dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(b); + } else if (dr[i] instanceof jxl.biff.drawing.ComboBox) { + jxl.biff.drawing.ComboBox cb = + new jxl.biff.drawing.ComboBox + (dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(cb); + } else if (dr[i] instanceof jxl.biff.drawing.CheckBox) { + jxl.biff.drawing.CheckBox cb = + new jxl.biff.drawing.CheckBox + (dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(cb); + } + + } + + // Copy the data validations + DataValidation rdv = fromSheet.getDataValidation(); + if (rdv != null) { + dataValidation = new DataValidation(rdv, + toSheet.getWorkbook(), + toSheet.getWorkbook(), + workbookSettings); + int objid = dataValidation.getComboBoxObjectId(); + + if (objid != 0) { + comboBox = (ComboBox) drawings.get(objid); + } + } + + // Copy the conditional formats + ConditionalFormat[] cf = fromSheet.getConditionalFormats(); + if (cf.length > 0) { + for (int i = 0; i < cf.length; i++) { + conditionalFormats.add(cf[i]); + } + } + + // Get the autofilter + autoFilter = fromSheet.getAutoFilter(); + + // Copy the workspace options + sheetWriter.setWorkspaceOptions(fromSheet.getWorkspaceOptions()); + + // Set a flag to indicate if it contains a chart only + if (fromSheet.getSheetBof().isChart()) { + chartOnly = true; + sheetWriter.setChartOnly(); + } + + // Copy the environment specific print record + if (fromSheet.getPLS() != null) { + if (fromSheet.getWorkbookBof().isBiff7()) { + logger.warn("Cannot copy Biff7 print settings record - ignoring"); + } else { + plsRecord = new PLSRecord(fromSheet.getPLS()); + } + } + + // Copy the button property set + if (fromSheet.getButtonPropertySet() != null) { + buttonPropertySet = new ButtonPropertySetRecord + (fromSheet.getButtonPropertySet()); + } + + // Copy the outline levels + maxRowOutlineLevel = fromSheet.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = fromSheet.getMaxColumnOutlineLevel(); + } + + /** + * Copies a sheet from a read-only version to the writable version. + * Performs shallow copies + */ + public void copyWritableSheet() { + shallowCopyCells(); + + /* + // Copy the column formats + Iterator cfit = fromWritableSheet.columnFormats.iterator(); + while (cfit.hasNext()) + { + ColumnInfoRecord cv = new ColumnInfoRecord + ((ColumnInfoRecord) cfit.next()); + columnFormats.add(cv); + } + + // Copy the merged cells + Range[] merged = fromWritableSheet.getMergedCells(); + + for (int i = 0; i < merged.length; i++) + { + mergedCells.add(new SheetRangeImpl((SheetRangeImpl)merged[i], this)); + } + + // Copy the row properties + try + { + RowRecord[] copyRows = fromWritableSheet.rows; + RowRecord row = null; + for (int i = 0; i < copyRows.length ; i++) + { + row = copyRows[i]; + + if (row != null && + (!row.isDefaultHeight() || + row.isCollapsed())) + { + RowRecord rr = getRowRecord(i); + rr.setRowDetails(row.getRowHeight(), + row.matchesDefaultFontHeight(), + row.isCollapsed(), + row.getStyle()); + } + } + } + catch (RowsExceededException e) + { + // Handle the rows exceeded exception - this cannot occur since + // the sheet we are copying from will have a valid number of rows + Assert.verify(false); + } + + // Copy the horizontal page breaks + rowBreaks = new ArrayList(fromWritableSheet.rowBreaks); + + // Copy the vertical page breaks + columnBreaks = new ArrayList(fromWritableSheet.columnBreaks); + + // Copy the data validations + DataValidation rdv = fromWritableSheet.dataValidation; + if (rdv != null) + { + dataValidation = new DataValidation(rdv, + workbook, + workbook, + workbookSettings); + } + + // Copy the charts + sheetWriter.setCharts(fromWritableSheet.getCharts()); + + // Copy the drawings + DrawingGroupObject[] dr = si.getDrawings(); + for (int i = 0 ; i < dr.length ; i++) + { + if (dr[i] instanceof jxl.biff.drawing.Drawing) + { + WritableImage wi = new WritableImage(dr[i], + workbook.getDrawingGroup()); + drawings.add(wi); + images.add(wi); + } + + // Not necessary to copy the comments, as they will be handled by + // the deep copy of the individual cells + } + + // Copy the workspace options + sheetWriter.setWorkspaceOptions(fromWritableSheet.getWorkspaceOptions()); + + // Copy the environment specific print record + if (fromWritableSheet.plsRecord != null) + { + plsRecord = new PLSRecord(fromWritableSheet.plsRecord); + } + + // Copy the button property set + if (fromWritableSheet.buttonPropertySet != null) + { + buttonPropertySet = new ButtonPropertySetRecord + (fromWritableSheet.buttonPropertySet); + } + */ + } + + /** + * Imports a sheet from a different workbook, doing a deep copy + */ + public void importSheet() { + xfRecords = new HashMap(); + fonts = new HashMap(); + formats = new HashMap(); + + deepCopyCells(); + + // Copy the column info records + jxl.read.biff.ColumnInfoRecord[] readCirs = fromSheet.getColumnInfos(); + + for (int i = 0; i < readCirs.length; i++) { + jxl.read.biff.ColumnInfoRecord rcir = readCirs[i]; + for (int j = rcir.getStartColumn(); j <= rcir.getEndColumn(); j++) { + ColumnInfoRecord cir = new ColumnInfoRecord(rcir, j); + int xfIndex = cir.getXfIndex(); + XFRecord cf = (WritableCellFormat) xfRecords.get(new Integer(xfIndex)); + + if (cf == null) { + CellFormat readFormat = fromSheet.getColumnView(j).getFormat(); + WritableCellFormat wcf = copyCellFormat(readFormat); + } + + cir.setCellFormat(cf); + cir.setHidden(rcir.getHidden()); + columnFormats.add(cir); + } + } + + // Copy the hyperlinks + Hyperlink[] hls = fromSheet.getHyperlinks(); + for (int i = 0; i < hls.length; i++) { + WritableHyperlink hr = new WritableHyperlink + (hls[i], toSheet); + hyperlinks.add(hr); + } + + // Copy the merged cells + Range[] merged = fromSheet.getMergedCells(); + + for (int i = 0; i < merged.length; i++) { + mergedCells.add(new SheetRangeImpl((SheetRangeImpl) merged[i], toSheet)); + } + + // Copy the row properties + try { + jxl.read.biff.RowRecord[] rowprops = fromSheet.getRowProperties(); + + for (int i = 0; i < rowprops.length; i++) { + RowRecord rr = toSheet.getRowRecord(rowprops[i].getRowNumber()); + XFRecord format = null; + jxl.read.biff.RowRecord rowrec = rowprops[i]; + if (rowrec.hasDefaultFormat()) { + format = (WritableCellFormat) + xfRecords.get(new Integer(rowrec.getXFIndex())); + + if (format == null) { + int rownum = rowrec.getRowNumber(); + CellFormat readFormat = fromSheet.getRowView(rownum).getFormat(); + WritableCellFormat wcf = copyCellFormat(readFormat); + } + } + + rr.setRowDetails(rowrec.getRowHeight(), + rowrec.matchesDefaultFontHeight(), + rowrec.isCollapsed(), + rowrec.getOutlineLevel(), + rowrec.getGroupStart(), + format); + numRows = Math.max(numRows, rowprops[i].getRowNumber() + 1); + } + } catch (RowsExceededException e) { + // Handle the rows exceeded exception - this cannot occur since + // the sheet we are copying from will have a valid number of rows + Assert.verify(false); + } + + // Copy the headers and footers + // sheetWriter.setHeader(new HeaderRecord(si.getHeader())); + // sheetWriter.setFooter(new FooterRecord(si.getFooter())); + + // Copy the page breaks + int[] rowbreaks = fromSheet.getRowPageBreaks(); + + if (rowbreaks != null) { + for (int i = 0; i < rowbreaks.length; i++) { + rowBreaks.add(new Integer(rowbreaks[i])); + } + } + + int[] columnbreaks = fromSheet.getColumnPageBreaks(); + + if (columnbreaks != null) { + for (int i = 0; i < columnbreaks.length; i++) { + columnBreaks.add(new Integer(columnbreaks[i])); + } + } + + // Copy the charts + Chart[] fromCharts = fromSheet.getCharts(); + if (fromCharts != null && fromCharts.length > 0) { + logger.warn("Importing of charts is not supported"); + /* + sheetWriter.setCharts(fromSheet.getCharts()); + IndexMapping xfMapping = new IndexMapping(200); + for (Iterator i = xfRecords.keySet().iterator(); i.hasNext();) + { + Integer key = (Integer) i.next(); + XFRecord xfmapping = (XFRecord) xfRecords.get(key); + xfMapping.setMapping(key.intValue(), xfmapping.getXFIndex()); + } + + IndexMapping fontMapping = new IndexMapping(200); + for (Iterator i = fonts.keySet().iterator(); i.hasNext();) + { + Integer key = (Integer) i.next(); + Integer fontmap = (Integer) fonts.get(key); + fontMapping.setMapping(key.intValue(), fontmap.intValue()); + } + + IndexMapping formatMapping = new IndexMapping(200); + for (Iterator i = formats.keySet().iterator(); i.hasNext();) + { + Integer key = (Integer) i.next(); + Integer formatmap = (Integer) formats.get(key); + formatMapping.setMapping(key.intValue(), formatmap.intValue()); + } + + // Now reuse the rationalization feature on each chart to + // handle the new fonts + for (int i = 0; i < fromCharts.length ; i++) + { + fromCharts[i].rationalize(xfMapping, fontMapping, formatMapping); + } + */ + } + + // Copy the drawings + DrawingGroupObject[] dr = fromSheet.getDrawings(); + + // Make sure the destination workbook has a drawing group + // created in it + if (dr.length > 0 && + toSheet.getWorkbook().getDrawingGroup() == null) { + toSheet.getWorkbook().createDrawingGroup(); + } + + for (int i = 0; i < dr.length; i++) { + if (dr[i] instanceof jxl.biff.drawing.Drawing) { + WritableImage wi = new WritableImage + (dr[i].getX(), dr[i].getY(), + dr[i].getWidth(), dr[i].getHeight(), + dr[i].getImageData()); + toSheet.getWorkbook().addDrawing(wi); + drawings.add(wi); + images.add(wi); + } else if (dr[i] instanceof jxl.biff.drawing.Comment) { + jxl.biff.drawing.Comment c = + new jxl.biff.drawing.Comment(dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(c); + + // Set up the reference on the cell value + CellValue cv = (CellValue) toSheet.getWritableCell(c.getColumn(), + c.getRow()); + Assert.verify(cv.getCellFeatures() != null); + cv.getWritableCellFeatures().setCommentDrawing(c); + } else if (dr[i] instanceof jxl.biff.drawing.Button) { + jxl.biff.drawing.Button b = + new jxl.biff.drawing.Button + (dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(b); + } else if (dr[i] instanceof jxl.biff.drawing.ComboBox) { + jxl.biff.drawing.ComboBox cb = + new jxl.biff.drawing.ComboBox + (dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(cb); + } + } + + // Copy the data validations + DataValidation rdv = fromSheet.getDataValidation(); + if (rdv != null) { + dataValidation = new DataValidation(rdv, + toSheet.getWorkbook(), + toSheet.getWorkbook(), + workbookSettings); + int objid = dataValidation.getComboBoxObjectId(); + if (objid != 0) { + comboBox = (ComboBox) drawings.get(objid); + } + } + + // Copy the workspace options + sheetWriter.setWorkspaceOptions(fromSheet.getWorkspaceOptions()); + + // Set a flag to indicate if it contains a chart only + if (fromSheet.getSheetBof().isChart()) { + chartOnly = true; + sheetWriter.setChartOnly(); + } + + // Copy the environment specific print record + if (fromSheet.getPLS() != null) { + if (fromSheet.getWorkbookBof().isBiff7()) { + logger.warn("Cannot copy Biff7 print settings record - ignoring"); + } else { + plsRecord = new PLSRecord(fromSheet.getPLS()); + } + } + + // Copy the button property set + if (fromSheet.getButtonPropertySet() != null) { + buttonPropertySet = new ButtonPropertySetRecord + (fromSheet.getButtonPropertySet()); + } + + importNames(); + + // Copy the outline levels + maxRowOutlineLevel = fromSheet.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = fromSheet.getMaxColumnOutlineLevel(); + } + + /** + * Performs a shallow copy of the specified cell + */ + private WritableCell shallowCopyCell(Cell cell) { + CellType ct = cell.getType(); + WritableCell newCell = null; + + if (ct == CellType.LABEL) { + newCell = new Label((LabelCell) cell); + } else if (ct == CellType.NUMBER) { + newCell = new Number((NumberCell) cell); + } else if (ct == CellType.DATE) { + newCell = new DateTime((DateCell) cell); + } else if (ct == CellType.BOOLEAN) { + newCell = new Boolean((BooleanCell) cell); + } else if (ct == CellType.NUMBER_FORMULA) { + newCell = new ReadNumberFormulaRecord((FormulaData) cell); + } else if (ct == CellType.STRING_FORMULA) { + newCell = new ReadStringFormulaRecord((FormulaData) cell); + } else if (ct == CellType.BOOLEAN_FORMULA) { + newCell = new ReadBooleanFormulaRecord((FormulaData) cell); + } else if (ct == CellType.DATE_FORMULA) { + newCell = new ReadDateFormulaRecord((FormulaData) cell); + } else if (ct == CellType.FORMULA_ERROR) { + newCell = new ReadErrorFormulaRecord((FormulaData) cell); + } else if (ct == CellType.EMPTY) { + if (cell.getCellFormat() != null) { + // It is a blank cell, rather than an empty cell, so + // it may have formatting information, so + // it must be copied + newCell = new Blank(cell); + } + } + + return newCell; + } + + /** + * Performs a deep copy of the specified cell, handling the cell format + * + * @param cell the cell to copy + */ + private WritableCell deepCopyCell(Cell cell) { + WritableCell c = shallowCopyCell(cell); + + if (c == null) { + return c; + } + + if (c instanceof ReadFormulaRecord) { + ReadFormulaRecord rfr = (ReadFormulaRecord) c; + boolean crossSheetReference = !rfr.handleImportedCellReferences + (fromSheet.getWorkbook(), + fromSheet.getWorkbook(), + workbookSettings); + + if (crossSheetReference) { + try { + logger.warn("Formula " + rfr.getFormula() + + " in cell " + + CellReferenceHelper.getCellReference(cell.getColumn(), + cell.getRow()) + + " cannot be imported because it references another " + + " sheet from the source workbook"); + } catch (FormulaException e) { + logger.warn("Formula in cell " + + CellReferenceHelper.getCellReference(cell.getColumn(), + cell.getRow()) + + " cannot be imported: " + e.getMessage()); + } + + // Create a new error formula and add it instead + c = new Formula(cell.getColumn(), cell.getRow(), "\"ERROR\""); + } + } + + // Copy the cell format + CellFormat cf = c.getCellFormat(); + int index = ((XFRecord) cf).getXFIndex(); + WritableCellFormat wcf = (WritableCellFormat) + xfRecords.get(new Integer(index)); + + if (wcf == null) { + wcf = copyCellFormat(cf); + } + + c.setCellFormat(wcf); + + return c; + } + + /** + * Perform a shallow copy of the cells from the specified sheet into this one + */ + void shallowCopyCells() { + // Copy the cells + int cells = fromSheet.getRows(); + Cell[] row = null; + Cell cell = null; + for (int i = 0; i < cells; i++) { + row = fromSheet.getRow(i); + + for (int j = 0; j < row.length; j++) { + cell = row[j]; + WritableCell c = shallowCopyCell(cell); + + // Encase the calls to addCell in a try-catch block + // These should not generate any errors, because we are + // copying from an existing spreadsheet. In the event of + // errors, catch the exception and then bomb out with an + // assertion + try { + if (c != null) { + toSheet.addCell(c); + + // Cell.setCellFeatures short circuits when the cell is copied, + // so make sure the copy logic handles the validated cells + if (c.getCellFeatures() != null && + c.getCellFeatures().hasDataValidation()) { + validatedCells.add(c); + } + } + } catch (WriteException e) { + Assert.verify(false); + } + } + } + numRows = toSheet.getRows(); + } + + /** + * Perform a deep copy of the cells from the specified sheet into this one + */ + void deepCopyCells() { + // Copy the cells + int cells = fromSheet.getRows(); + Cell[] row = null; + Cell cell = null; + for (int i = 0; i < cells; i++) { + row = fromSheet.getRow(i); + + for (int j = 0; j < row.length; j++) { + cell = row[j]; + WritableCell c = deepCopyCell(cell); + + // Encase the calls to addCell in a try-catch block + // These should not generate any errors, because we are + // copying from an existing spreadsheet. In the event of + // errors, catch the exception and then bomb out with an + // assertion + try { + if (c != null) { + toSheet.addCell(c); + + // Cell.setCellFeatures short circuits when the cell is copied, + // so make sure the copy logic handles the validated cells + if (c.getCellFeatures() != null & + c.getCellFeatures().hasDataValidation()) { + validatedCells.add(c); + } + } + } catch (WriteException e) { + Assert.verify(false); + } + } + } + } + + /** + * Returns an initialized copy of the cell format + * + * @param cf the cell format to copy + * @return a deep copy of the cell format + */ + private WritableCellFormat copyCellFormat(CellFormat cf) { + try { + // just do a deep copy of the cell format for now. This will create + // a copy of the format and font also - in the future this may + // need to be sorted out + XFRecord xfr = (XFRecord) cf; + WritableCellFormat f = new WritableCellFormat(xfr); + formatRecords.addStyle(f); + + // Maintain the local list of formats + int xfIndex = xfr.getXFIndex(); + xfRecords.put(new Integer(xfIndex), f); + + int fontIndex = xfr.getFontIndex(); + fonts.put(new Integer(fontIndex), new Integer(f.getFontIndex())); + + int formatIndex = xfr.getFormatRecord(); + formats.put(new Integer(formatIndex), new Integer(f.getFormatRecord())); + + return f; + } catch (NumFormatRecordsException e) { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + + return WritableWorkbook.NORMAL_STYLE; + } + } + + /** + * Imports any names defined on the source sheet to the destination workbook + */ + private void importNames() { + WorkbookParser fromWorkbook = fromSheet.getWorkbook(); + WritableWorkbook toWorkbook = toSheet.getWorkbook(); + int fromSheetIndex = fromWorkbook.getIndex(fromSheet); + NameRecord[] nameRecords = fromWorkbook.getNameRecords(); + String[] names = toWorkbook.getRangeNames(); + + for (int i = 0; i < nameRecords.length; i++) { + NameRecord.NameRange[] nameRanges = nameRecords[i].getRanges(); + + for (int j = 0; j < nameRanges.length; j++) { + int nameSheetIndex = fromWorkbook.getExternalSheetIndex + (nameRanges[j].getExternalSheet()); + + if (fromSheetIndex == nameSheetIndex) { + String name = nameRecords[i].getName(); + if (Arrays.binarySearch(names, name) < 0) { + toWorkbook.addNameArea(name, + toSheet, + nameRanges[j].getFirstColumn(), + nameRanges[j].getFirstRow(), + nameRanges[j].getLastColumn(), + nameRanges[j].getLastRow()); + } else { + logger.warn("Named range " + name + + " is already present in the destination workbook"); + } + + } + } + } + } + + /** + * Gets the number of rows - allows for the case where formatting has + * been applied to rows, even though the row has no data + * + * @return the number of rows + */ + int getRows() { + return numRows; + } + + /** + * Accessor for the maximum column outline level + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() { + return maxRowOutlineLevel; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SheetWriter.java b/datastructures-xslx/src/main/java/jxl/write/biff/SheetWriter.java new file mode 100755 index 0000000..062f326 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SheetWriter.java @@ -0,0 +1,1045 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.TreeSet; +import jxl.Cell; +import jxl.CellFeatures; +import jxl.Range; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.ConditionalFormat; +import jxl.biff.DataValidation; +import jxl.biff.DataValiditySettingsRecord; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.XFRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.SheetDrawingWriter; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.Colour; +import jxl.write.Blank; +import jxl.write.WritableCell; +import jxl.write.WritableCellFormat; +import jxl.write.WritableHyperlink; +import jxl.write.WriteException; + +/** + * Contains the functionality necessary for writing out a sheet. Originally + * this was incorporated in WritableSheetImpl, but was moved out into + * a dedicated class in order to reduce the over bloated nature of that + * class + */ +final class SheetWriter { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SheetWriter.class); + + /** + * A handle to the output file which the binary data is written to + */ + private final File outputFile; + + /** + * The rows within this sheet + */ + private RowRecord[] rows; + + /** + * A number of rows. This is a count of the maximum row number + 1 + */ + private int numRows; + + /** + * The number of columns. This is a count of the maximum column number + 1 + */ + private int numCols; + + /** + * The page header + */ + private HeaderRecord header; + /** + * The page footer + */ + private FooterRecord footer; + /** + * The settings for the sheet + */ + private SheetSettings settings; + /** + * The settings for the workbook + */ + private final WorkbookSettings workbookSettings; + /** + * Array of row page breaks + */ + private ArrayList rowBreaks; + /** + * Array of column page breaks + */ + private ArrayList columnBreaks; + /** + * Array of hyperlinks + */ + private ArrayList hyperlinks; + /** + * Array of conditional formats + */ + private ArrayList conditionalFormats; + /** + * The autofilter info + */ + private AutoFilter autoFilter; + /** + * Array of validated cells + */ + private ArrayList validatedCells; + /** + * The data validation validations + */ + private DataValidation dataValidation; + + /** + * The list of merged ranges + */ + private MergedCells mergedCells; + + /** + * The environment specific print record + */ + private PLSRecord plsRecord; + + /** + * The button property ste + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * The workspace options + */ + private WorkspaceInformationRecord workspaceOptions; + /** + * The column format overrides + */ + private TreeSet columnFormats; + + /** + * The list of drawings + */ + private final SheetDrawingWriter drawingWriter; + + /** + * Flag indicates that this sheet contains just a chart, and nothing + * else + */ + private boolean chartOnly; + + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + + /** + * A handle back to the writable sheet, in order for this class + * to invoke the get accessor methods + */ + private final WritableSheetImpl sheet; + + + /** + * Creates a new SheetWriter instance. + * + * @param of the output file + */ + public SheetWriter(File of, + WritableSheetImpl wsi, + WorkbookSettings ws) { + outputFile = of; + sheet = wsi; + workspaceOptions = new WorkspaceInformationRecord(); + workbookSettings = ws; + chartOnly = false; + drawingWriter = new SheetDrawingWriter(ws); + } + + /** + * Writes out this sheet. First writes out the standard sheet + * information then writes out each row in turn. + * Once all the rows have been written out, it retrospectively adjusts + * the offset references in the file + * + * @throws IOException + */ + public void write() throws IOException { + Assert.verify(rows != null); + + // This worksheet consists of just one chart, so write it and return + if (chartOnly) { + drawingWriter.write(outputFile); + return; + } + + BOFRecord bof = new BOFRecord(BOFRecord.sheet); + outputFile.write(bof); + + // Compute the number of blocks of 32 rows that will be needed + int numBlocks = numRows / 32; + if (numRows - numBlocks * 32 != 0) { + numBlocks++; + } + + int indexPos = outputFile.getPos(); + + // Write the index record out now in order to serve as a place holder + // The bof passed in is the bof of the workbook, not this sheet + IndexRecord indexRecord = new IndexRecord(0, numRows, numBlocks); + outputFile.write(indexRecord); + + if (settings.getAutomaticFormulaCalculation()) { + CalcModeRecord cmr = new CalcModeRecord(CalcModeRecord.automatic); + outputFile.write(cmr); + } else { + CalcModeRecord cmr = new CalcModeRecord(CalcModeRecord.manual); + outputFile.write(cmr); + } + + CalcCountRecord ccr = new CalcCountRecord(0x64); + outputFile.write(ccr); + + RefModeRecord rmr = new RefModeRecord(); + outputFile.write(rmr); + + IterationRecord itr = new IterationRecord(false); + outputFile.write(itr); + + DeltaRecord dtr = new DeltaRecord(0.001); + outputFile.write(dtr); + + SaveRecalcRecord srr = new SaveRecalcRecord + (settings.getRecalculateFormulasBeforeSave()); + outputFile.write(srr); + + PrintHeadersRecord phr = new PrintHeadersRecord + (settings.getPrintHeaders()); + outputFile.write(phr); + + PrintGridLinesRecord pglr = new PrintGridLinesRecord + (settings.getPrintGridLines()); + outputFile.write(pglr); + + GridSetRecord gsr = new GridSetRecord(true); + outputFile.write(gsr); + + GuttersRecord gutr = new GuttersRecord(); + gutr.setMaxColumnOutline(maxColumnOutlineLevel + 1); + gutr.setMaxRowOutline(maxRowOutlineLevel + 1); + + outputFile.write(gutr); + + DefaultRowHeightRecord drhr = new DefaultRowHeightRecord + (settings.getDefaultRowHeight(), + settings.getDefaultRowHeight() != + SheetSettings.DEFAULT_DEFAULT_ROW_HEIGHT); + outputFile.write(drhr); + + if (maxRowOutlineLevel > 0) { + workspaceOptions.setRowOutlines(true); + } + + if (maxColumnOutlineLevel > 0) { + workspaceOptions.setColumnOutlines(true); + } + + workspaceOptions.setFitToPages(settings.getFitToPages()); + outputFile.write(workspaceOptions); + + if (rowBreaks.size() > 0) { + int[] rb = new int[rowBreaks.size()]; + + for (int i = 0; i < rb.length; i++) { + rb[i] = ((Integer) rowBreaks.get(i)).intValue(); + } + + HorizontalPageBreaksRecord hpbr = new HorizontalPageBreaksRecord(rb); + outputFile.write(hpbr); + } + + if (columnBreaks.size() > 0) { + int[] rb = new int[columnBreaks.size()]; + + for (int i = 0; i < rb.length; i++) { + rb[i] = ((Integer) columnBreaks.get(i)).intValue(); + } + + VerticalPageBreaksRecord hpbr = new VerticalPageBreaksRecord(rb); + outputFile.write(hpbr); + } + + HeaderRecord header = new HeaderRecord(settings.getHeader().toString()); + outputFile.write(header); + + FooterRecord footer = new FooterRecord(settings.getFooter().toString()); + outputFile.write(footer); + + HorizontalCentreRecord hcr = new HorizontalCentreRecord + (settings.isHorizontalCentre()); + outputFile.write(hcr); + + VerticalCentreRecord vcr = new VerticalCentreRecord + (settings.isVerticalCentre()); + outputFile.write(vcr); + + // Write out the margins if they don't equal the default + if (settings.getLeftMargin() != settings.getDefaultWidthMargin()) { + MarginRecord mr = new LeftMarginRecord(settings.getLeftMargin()); + outputFile.write(mr); + } + + if (settings.getRightMargin() != settings.getDefaultWidthMargin()) { + MarginRecord mr = new RightMarginRecord(settings.getRightMargin()); + outputFile.write(mr); + } + + if (settings.getTopMargin() != settings.getDefaultHeightMargin()) { + MarginRecord mr = new TopMarginRecord(settings.getTopMargin()); + outputFile.write(mr); + } + + if (settings.getBottomMargin() != settings.getDefaultHeightMargin()) { + MarginRecord mr = new BottomMarginRecord(settings.getBottomMargin()); + outputFile.write(mr); + } + + if (plsRecord != null) { + outputFile.write(plsRecord); + } + + SetupRecord setup = new SetupRecord(settings); + outputFile.write(setup); + + if (settings.isProtected()) { + ProtectRecord pr = new ProtectRecord(settings.isProtected()); + outputFile.write(pr); + + ScenarioProtectRecord spr = new ScenarioProtectRecord + (settings.isProtected()); + outputFile.write(spr); + + ObjectProtectRecord opr = new ObjectProtectRecord + (settings.isProtected()); + outputFile.write(opr); + + if (settings.getPassword() != null) { + PasswordRecord pw = new PasswordRecord(settings.getPassword()); + outputFile.write(pw); + } else if (settings.getPasswordHash() != 0) { + PasswordRecord pw = new PasswordRecord(settings.getPasswordHash()); + outputFile.write(pw); + } + } + + indexRecord.setDataStartPosition(outputFile.getPos()); + DefaultColumnWidth dcw = + new DefaultColumnWidth(settings.getDefaultColumnWidth()); + outputFile.write(dcw); + + // Get a handle to the normal styles + WritableCellFormat normalStyle = + sheet.getWorkbook().getStyles().getNormalStyle(); + WritableCellFormat defaultDateFormat = + sheet.getWorkbook().getStyles().getDefaultDateFormat(); + + // Write out all the column formats + ColumnInfoRecord cir = null; + for (Iterator colit = columnFormats.iterator(); colit.hasNext(); ) { + cir = (ColumnInfoRecord) colit.next(); + + // Writing out the column info with index 0x100 causes excel to crash + if (cir.getColumn() < 0x100) { + outputFile.write(cir); + } + + XFRecord xfr = cir.getCellFormat(); + + if (xfr != normalStyle && cir.getColumn() < 0x100) { + // Make this the format for every cell in the column + Cell[] cells = getColumn(cir.getColumn()); + + for (int i = 0; i < cells.length; i++) { + if (cells[i] != null && + (cells[i].getCellFormat() == normalStyle || + cells[i].getCellFormat() == defaultDateFormat)) { + // The cell has no overriding format specified, so + // set it to the column default + ((WritableCell) cells[i]).setCellFormat(xfr); + } + } + } + } + + // Write out the auto filter + if (autoFilter != null) { + autoFilter.write(outputFile); + } + + DimensionRecord dr = new DimensionRecord(numRows, numCols); + outputFile.write(dr); + + // Write out all the rows, in blocks of 32 + for (int block = 0; block < numBlocks; block++) { + DBCellRecord dbcell = new DBCellRecord(outputFile.getPos()); + + int blockRows = Math.min(32, numRows - block * 32); + boolean firstRow = true; + + // First write out all the row records + for (int i = block * 32; i < block * 32 + blockRows; i++) { + if (rows[i] != null) { + rows[i].write(outputFile); + if (firstRow) { + dbcell.setCellOffset(outputFile.getPos()); + firstRow = false; + } + } + } + + // Now write out all the cells + for (int i = block * 32; i < block * 32 + blockRows; i++) { + if (rows[i] != null) { + dbcell.addCellRowPosition(outputFile.getPos()); + rows[i].writeCells(outputFile); + } + } + + // Now set the current file position in the index record + indexRecord.addBlockPosition(outputFile.getPos()); + + // Set the position of the file pointer and write out the DBCell + // record + dbcell.setPosition(outputFile.getPos()); + outputFile.write(dbcell); + } + + // Do the drawings and charts if enabled + if (!workbookSettings.getDrawingsDisabled()) { + drawingWriter.write(outputFile); + } + + Window2Record w2r = new Window2Record(settings); + outputFile.write(w2r); + + // Handle the frozen panes + if (settings.getHorizontalFreeze() != 0 || + settings.getVerticalFreeze() != 0) { + PaneRecord pr = new PaneRecord(settings.getHorizontalFreeze(), + settings.getVerticalFreeze()); + outputFile.write(pr); + + // Handle the selection record. First, there will always be a top left + SelectionRecord sr = new SelectionRecord + (SelectionRecord.upperLeft, 0, 0); + outputFile.write(sr); + + // Top right + if (settings.getHorizontalFreeze() != 0) { + sr = new SelectionRecord + (SelectionRecord.upperRight, settings.getHorizontalFreeze(), 0); + outputFile.write(sr); + } + + // Bottom left + if (settings.getVerticalFreeze() != 0) { + sr = new SelectionRecord + (SelectionRecord.lowerLeft, 0, settings.getVerticalFreeze()); + outputFile.write(sr); + } + + // Bottom right + if (settings.getHorizontalFreeze() != 0 && + settings.getVerticalFreeze() != 0) { + sr = new SelectionRecord + (SelectionRecord.lowerRight, + settings.getHorizontalFreeze(), + settings.getVerticalFreeze()); + outputFile.write(sr); + } + + Weird1Record w1r = new Weird1Record(); + outputFile.write(w1r); + } else { + // No frozen panes - just write out the selection record for the + // whole sheet + SelectionRecord sr = new SelectionRecord + (SelectionRecord.upperLeft, 0, 0); + outputFile.write(sr); + } + + // Handle the zoom factor + if (settings.getZoomFactor() != 100) { + SCLRecord sclr = new SCLRecord(settings.getZoomFactor()); + outputFile.write(sclr); + } + + // Now write out all the merged cells + mergedCells.write(outputFile); + + // Write out all the hyperlinks + Iterator hi = hyperlinks.iterator(); + WritableHyperlink hlr = null; + while (hi.hasNext()) { + hlr = (WritableHyperlink) hi.next(); + outputFile.write(hlr); + } + + if (buttonPropertySet != null) { + outputFile.write(buttonPropertySet); + } + + // Write out the data validations + if (dataValidation != null || validatedCells.size() > 0) { + writeDataValidation(); + } + + // Write out the conditional formats + if (conditionalFormats != null && conditionalFormats.size() > 0) { + for (Iterator i = conditionalFormats.iterator(); i.hasNext(); ) { + ConditionalFormat cf = (ConditionalFormat) i.next(); + cf.write(outputFile); + } + } + + EOFRecord eof = new EOFRecord(); + outputFile.write(eof); + + // Now the various cross reference offsets have been calculated, + // retrospectively set the values in the output file + outputFile.setData(indexRecord.getData(), indexPos + 4); + } + + /** + * Gets the header. Called when copying sheets + * + * @return the page header + */ + final HeaderRecord getHeader() { + return header; + } + + /** + * Gets the footer. Called when copying sheets + * + * @return the page footer + */ + final FooterRecord getFooter() { + return footer; + } + + /** + * Sets the data necessary for writing out the sheet. This method must + * be called immediately prior to writing + * + * @param rws the rows in the spreadsheet + */ + void setWriteData(RowRecord[] rws, + ArrayList rb, + ArrayList cb, + ArrayList hl, + MergedCells mc, + TreeSet cf, + int mrol, + int mcol) { + rows = rws; + rowBreaks = rb; + columnBreaks = cb; + hyperlinks = hl; + mergedCells = mc; + columnFormats = cf; + maxRowOutlineLevel = mrol; + maxColumnOutlineLevel = mcol; + } + + /** + * Sets the dimensions of this spreadsheet. This method must be called + * immediately prior to writing + * + * @param rws the number of rows + * @param cls the number of columns + */ + void setDimensions(int rws, int cls) { + numRows = rws; + numCols = cls; + } + + /** + * Sets the sheet settings for this particular sheet. Must be + * called immediately prior to writing + * + * @param sr the sheet settings + */ + void setSettings(SheetSettings sr) { + settings = sr; + } + + /** + * Accessor for the workspace options + * + * @return the workspace options + */ + WorkspaceInformationRecord getWorkspaceOptions() { + return workspaceOptions; + } + + /** + * Accessor for the workspace options + * + * @param wo the workspace options + */ + void setWorkspaceOptions(WorkspaceInformationRecord wo) { + if (wo != null) { + workspaceOptions = wo; + } + } + + /** + * Sets the drawings on this sheet + * + * @param dr the list of drawings + * @param mod a modified flag + */ + void setDrawings(ArrayList dr, boolean mod) { + drawingWriter.setDrawings(dr, mod); + } + + /** + * Accessor for the charts on this sheet + * + * @return the charts + */ + Chart[] getCharts() { + return drawingWriter.getCharts(); + } + + /** + * Sets the charts for this sheet + * + * @param ch the charts + */ + void setCharts(Chart[] ch) { + drawingWriter.setCharts(ch); + } + + /** + * Check all the merged cells for borders. If the merge record has + * borders, then we need to rejig the cell formats to take account of this. + * This is called by the write method of the WritableWorkbookImpl, so that + * any new XFRecords that are created may be written out with the others + */ + void checkMergedBorders() { + Range[] mcells = mergedCells.getMergedCells(); + ArrayList borderFormats = new ArrayList(); + for (int mci = 0; mci < mcells.length; mci++) { + Range range = mcells[mci]; + Cell topLeft = range.getTopLeft(); + XFRecord tlformat = (XFRecord) topLeft.getCellFormat(); + + if (tlformat != null && + tlformat.hasBorders() == true && + !tlformat.isRead()) { + try { + CellXFRecord cf1 = new CellXFRecord(tlformat); + Cell bottomRight = range.getBottomRight(); + + cf1.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf1.setBorder(Border.LEFT, + tlformat.getBorderLine(Border.LEFT), + tlformat.getBorderColour(Border.LEFT)); + cf1.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + + if (topLeft.getRow() == bottomRight.getRow()) { + cf1.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + } + + if (topLeft.getColumn() == bottomRight.getColumn()) { + cf1.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + } + + int index = borderFormats.indexOf(cf1); + if (index != -1) { + cf1 = (CellXFRecord) borderFormats.get(index); + } else { + borderFormats.add(cf1); + } + ((WritableCell) topLeft).setCellFormat(cf1); + + // Handle the bottom left corner + if (bottomRight.getRow() > topLeft.getRow()) { + // Handle the corner cell + if (bottomRight.getColumn() != topLeft.getColumn()) { + CellXFRecord cf2 = new CellXFRecord(tlformat); + cf2.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf2.setBorder(Border.LEFT, + tlformat.getBorderLine(Border.LEFT), + tlformat.getBorderColour(Border.LEFT)); + cf2.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + + index = borderFormats.indexOf(cf2); + if (index != -1) { + cf2 = (CellXFRecord) borderFormats.get(index); + } else { + borderFormats.add(cf2); + } + + sheet.addCell(new Blank(topLeft.getColumn(), + bottomRight.getRow(), cf2)); + } + + // Handle the cells down the left hand side (and along the + // right too, if necessary) + for (int i = topLeft.getRow() + 1; i < bottomRight.getRow(); i++) { + CellXFRecord cf3 = new CellXFRecord(tlformat); + cf3.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf3.setBorder(Border.LEFT, + tlformat.getBorderLine(Border.LEFT), + tlformat.getBorderColour(Border.LEFT)); + + if (topLeft.getColumn() == bottomRight.getColumn()) { + cf3.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + } + + index = borderFormats.indexOf(cf3); + if (index != -1) { + cf3 = (CellXFRecord) borderFormats.get(index); + } else { + borderFormats.add(cf3); + } + + sheet.addCell(new Blank(topLeft.getColumn(), i, cf3)); + } + } + + // Handle the top right corner + if (bottomRight.getColumn() > topLeft.getColumn()) { + if (bottomRight.getRow() != topLeft.getRow()) { + // Handle the corner cell + CellXFRecord cf6 = new CellXFRecord(tlformat); + cf6.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf6.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + cf6.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + index = borderFormats.indexOf(cf6); + if (index != -1) { + cf6 = (CellXFRecord) borderFormats.get(index); + } else { + borderFormats.add(cf6); + } + + sheet.addCell(new Blank(bottomRight.getColumn(), + topLeft.getRow(), cf6)); + } + + // Handle the cells along the right + for (int i = topLeft.getRow() + 1; + i < bottomRight.getRow(); i++) { + CellXFRecord cf7 = new CellXFRecord(tlformat); + cf7.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf7.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + + index = borderFormats.indexOf(cf7); + if (index != -1) { + cf7 = (CellXFRecord) borderFormats.get(index); + } else { + borderFormats.add(cf7); + } + + sheet.addCell(new Blank(bottomRight.getColumn(), i, cf7)); + } + + // Handle the cells along the top, and along the bottom too + for (int i = topLeft.getColumn() + 1; + i < bottomRight.getColumn(); i++) { + CellXFRecord cf8 = new CellXFRecord(tlformat); + cf8.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf8.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + + if (topLeft.getRow() == bottomRight.getRow()) { + cf8.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + } + + index = borderFormats.indexOf(cf8); + if (index != -1) { + cf8 = (CellXFRecord) borderFormats.get(index); + } else { + borderFormats.add(cf8); + } + + sheet.addCell(new Blank(i, topLeft.getRow(), cf8)); + } + } + + // Handle the bottom right corner + if (bottomRight.getColumn() > topLeft.getColumn() || + bottomRight.getRow() > topLeft.getRow()) { + // Handle the corner cell + CellXFRecord cf4 = new CellXFRecord(tlformat); + cf4.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf4.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + cf4.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + + if (bottomRight.getRow() == topLeft.getRow()) { + cf4.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + } + + if (bottomRight.getColumn() == topLeft.getColumn()) { + cf4.setBorder(Border.LEFT, + tlformat.getBorderLine(Border.LEFT), + tlformat.getBorderColour(Border.LEFT)); + } + + index = borderFormats.indexOf(cf4); + if (index != -1) { + cf4 = (CellXFRecord) borderFormats.get(index); + } else { + borderFormats.add(cf4); + } + + sheet.addCell(new Blank(bottomRight.getColumn(), + bottomRight.getRow(), cf4)); + + // Handle the cells along the bottom (and along the top + // as well, if appropriate) + for (int i = topLeft.getColumn() + 1; + i < bottomRight.getColumn(); i++) { + CellXFRecord cf5 = new CellXFRecord(tlformat); + cf5.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf5.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + + if (topLeft.getRow() == bottomRight.getRow()) { + cf5.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + } + + index = borderFormats.indexOf(cf5); + if (index != -1) { + cf5 = (CellXFRecord) borderFormats.get(index); + } else { + borderFormats.add(cf5); + } + + sheet.addCell(new Blank(i, bottomRight.getRow(), cf5)); + } + } + } catch (WriteException e) { + // just log e.toString(), not the whole stack trace + logger.warn(e.toString()); + } + } + } + } + + /** + * Get the cells in the column. Don't use the interface method + * getColumn for this as this will create loads of empty cells, + * and we could do without that overhead + */ + private Cell[] getColumn(int col) { + // Find the last non-null cell + boolean found = false; + int row = numRows - 1; + + while (row >= 0 && !found) { + if (rows[row] != null && + rows[row].getCell(col) != null) { + found = true; + } else { + row--; + } + } + + // Only create entries for non-empty cells + Cell[] cells = new Cell[row + 1]; + + for (int i = 0; i <= row; i++) { + cells[i] = rows[i] != null ? rows[i].getCell(col) : null; + } + + return cells; + } + + /** + * Sets a flag to indicate that this sheet contains a chart only + */ + void setChartOnly() { + chartOnly = true; + } + + /** + * Sets the environment specific print record + * + * @param pls the print record + */ + void setPLS(PLSRecord pls) { + plsRecord = pls; + } + + /** + * Sets the button property set record + * + * @param bps the button property set + */ + void setButtonPropertySet(ButtonPropertySetRecord bps) { + buttonPropertySet = bps; + } + + /** + * Sets the data validations + * + * @param dv the read-in list of data validations + * @param vc the api manipulated set of data validations + */ + void setDataValidation(DataValidation dv, ArrayList vc) { + dataValidation = dv; + validatedCells = vc; + } + + /** + * Sets the conditional formats + * + * @param cf the conditonal formats + */ + void setConditionalFormats(ArrayList cf) { + conditionalFormats = cf; + } + + /** + * Sets the auto filter + * + * @param af the autofilter + */ + void setAutoFilter(AutoFilter af) { + autoFilter = af; + } + + /** + * Writes out the data validations + */ + private void writeDataValidation() throws IOException { + if (dataValidation != null && validatedCells.size() == 0) { + // the only data validations are those read in - this should + // never be the case now that shared data validations add + // to the validatedCells list + dataValidation.write(outputFile); + return; + } + + if (dataValidation == null && validatedCells.size() > 0) { + // the only data validations are those which have been added by the + // write API. Need to sort out the combo box id + int comboBoxId = sheet.getComboBox() != null ? + sheet.getComboBox().getObjectId() : DataValidation.DEFAULT_OBJECT_ID; + dataValidation = new DataValidation(comboBoxId, + sheet.getWorkbook(), + sheet.getWorkbook(), + workbookSettings); + } + + for (Iterator i = validatedCells.iterator(); i.hasNext(); ) { + CellValue cv = (CellValue) i.next(); + CellFeatures cf = cv.getCellFeatures(); + + // Do not do anything if the DVParser has been copied, as it + // will already by on the DataValidation record as a result + // of the SheetCopier process + if (!cf.getDVParser().copied()) { + if (!cf.getDVParser().extendedCellsValidation()) { + // DVParser is specific for a single cell validation - just add it + DataValiditySettingsRecord dvsr = + new DataValiditySettingsRecord(cf.getDVParser()); + dataValidation.add(dvsr); + } else { + // Only add the DVParser once for shared validations + // only add it if it is the top left cell + if (cv.getColumn() == cf.getDVParser().getFirstColumn() && + cv.getRow() == cf.getDVParser().getFirstRow()) { + DataValiditySettingsRecord dvsr = + new DataValiditySettingsRecord(cf.getDVParser()); + dataValidation.add(dvsr); + } + } + } + } + dataValidation.write(outputFile); + } + /* + + // There is a mixture of read and write validations + for (Iterator i = validatedCells.iterator(); i.hasNext(); ) + { + CellValue cv = (CellValue) i.next(); + CellFeatures cf = cv.getCellFeatures(); + DataValiditySettingsRecord dvsr = + new DataValiditySettingsRecord(cf.getDVParser()); + dataValidation.add(dvsr); + } + dataValidation.write(outputFile); + return; + } + */ +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SortRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SortRecord.java new file mode 100755 index 0000000..12b8c12 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SortRecord.java @@ -0,0 +1,113 @@ +/********************************************************************* + * + * Copyright (C) 200r Andrew Khan, Al Mantei + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies sort dialog box values + */ +class SortRecord extends WritableRecordData { + private final String column1Name; + private final String column2Name; + private final String column3Name; + private final boolean sortColumns; + private final boolean sortKey1Desc; + private final boolean sortKey2Desc; + private final boolean sortKey3Desc; + private final boolean sortCaseSensitive; + + /** + * Constructor + * + * @param a Sort Column 1 Name + * @param b Sort Column 2 Name + * @param c Sort Column 3 Name + * @param sc Sort Columns + * @param sk1d Sort Key 1 Descending + * @param sk2d Sort Key 2 Descending + * @param sk3d Sort Key 3 Descending + * @param scs Sort Case Sensitive + */ + public SortRecord(String a, String b, String c, + boolean sc, boolean sk1d, + boolean sk2d, boolean sk3d, boolean scs) { + super(Type.SORT); + + column1Name = a; + column2Name = b; + column3Name = c; + sortColumns = sc; + sortKey1Desc = sk1d; + sortKey2Desc = sk2d; + sortKey3Desc = sk3d; + sortCaseSensitive = scs; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + int byteCount = 5 + (column1Name.length() * 2) + 1; + if (column2Name.length() > 0) + byteCount += (column2Name.length() * 2) + 1; + if (column3Name.length() > 0) + byteCount += (column3Name.length() * 2) + 1; + byte[] data = new byte[byteCount + 1]; + // there is supposed to be an extra "unused" byte at the end + int optionFlag = 0; + if (sortColumns) + optionFlag = optionFlag | 0x01; + if (sortKey1Desc) + optionFlag = optionFlag | 0x02; + if (sortKey2Desc) + optionFlag = optionFlag | 0x04; + if (sortKey3Desc) + optionFlag = optionFlag | 0x08; + if (sortCaseSensitive) + optionFlag = optionFlag | 0x10; + + data[0] = (byte) optionFlag; + // data[1] is an index for sorting by a list - not implemented + data[2] = (byte) column1Name.length(); + data[3] = (byte) column2Name.length(); + data[4] = (byte) column3Name.length(); + // always write the headings in unicode + data[5] = 0x01; + StringHelper.getUnicodeBytes(column1Name, data, 6); + int curPos = 6 + (column1Name.length() * 2); + if (column2Name.length() > 0) { + data[curPos++] = 0x01; + StringHelper.getUnicodeBytes(column2Name, data, curPos); + curPos += column2Name.length() * 2; + } + if (column3Name.length() > 0) { + data[curPos++] = 0x01; + StringHelper.getUnicodeBytes(column3Name, data, curPos); + curPos += column3Name.length() * 2; + } + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/StringRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/StringRecord.java new file mode 100755 index 0000000..ef3e3d5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/StringRecord.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the string result of a formula calculation. This record + * occurs immediately after the formula + */ +class StringRecord extends WritableRecordData { + /** + * The string value + */ + private final String value; + + /** + * Constructor + */ + public StringRecord(String val) { + super(Type.STRING); + + value = val; + } + + /** + * The binary data to be written out + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[value.length() * 2 + 3]; + IntegerHelper.getTwoBytes(value.length(), data, 0); + data[2] = 0x01; // unicode + StringHelper.getUnicodeBytes(value, data, 3); + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/StyleXFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/StyleXFRecord.java new file mode 100755 index 0000000..ee76c46 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/StyleXFRecord.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DisplayFormat; +import jxl.biff.FontRecord; +import jxl.biff.XFRecord; + +/** + * A style XF Record + */ +public class StyleXFRecord extends XFRecord { + /** + * Constructor + * + * @param fnt the font for this style + * @param form the format of this style + */ + public StyleXFRecord(FontRecord fnt, DisplayFormat form) { + super(fnt, form); + + setXFDetails(XFRecord.style, 0xfff0); + } + + + /** + * Sets the raw cell options. Called by WritableFormattingRecord + * when setting the built in cell formats + * + * @param opt the cell options + */ + public final void setCellOptions(int opt) { + super.setXFCellOptions(opt); + } + + /** + * Sets whether or not this XF record locks the cell + * + * @param l the locked flag + * @throws WriteException + */ + public void setLocked(boolean l) { + super.setXFLocked(l); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Styles.java b/datastructures-xslx/src/main/java/jxl/write/biff/Styles.java new file mode 100755 index 0000000..a7a468d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/Styles.java @@ -0,0 +1,193 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.XFRecord; +import jxl.common.Logger; +import jxl.write.DateFormat; +import jxl.write.DateFormats; +import jxl.write.NumberFormats; +import jxl.write.WritableCellFormat; +import jxl.write.WritableFont; +import jxl.write.WritableWorkbook; + +/** + * A structure containing the styles used by this workbook. This is used + * to enforce thread safety by tying the default styles to a workbook + * instance rather than by initializing them statically + */ +class Styles { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Styles.class); + + /** + * The default font for Cell formats + */ + private WritableFont arial10pt; + + /** + * The font used for hyperlinks + */ + private WritableFont hyperlinkFont; + + /** + * The default style for cells + */ + private WritableCellFormat normalStyle; + + /** + * The style used for hyperlinks + */ + private WritableCellFormat hyperlinkStyle; + + /** + * A cell format used to hide the cell contents + */ + private WritableCellFormat hiddenStyle; + + /** + * A cell format used for the default date format + */ + private WritableCellFormat defaultDateFormat; + + /** + * Constructor + */ + public Styles() { + arial10pt = null; + hyperlinkFont = null; + normalStyle = null; + hyperlinkStyle = null; + hiddenStyle = null; + } + + private synchronized void initNormalStyle() { + normalStyle = new WritableCellFormat(getArial10Pt(), + NumberFormats.DEFAULT); + normalStyle.setFont(getArial10Pt()); + } + + public WritableCellFormat getNormalStyle() { + if (normalStyle == null) { + initNormalStyle(); + } + + return normalStyle; + } + + private synchronized void initHiddenStyle() { + hiddenStyle = new WritableCellFormat + (getArial10Pt(), new DateFormat(";;;")); + } + + public WritableCellFormat getHiddenStyle() { + if (hiddenStyle == null) { + initHiddenStyle(); + } + + return hiddenStyle; + } + + private synchronized void initHyperlinkStyle() { + hyperlinkStyle = new WritableCellFormat(getHyperlinkFont(), + NumberFormats.DEFAULT); + } + + public WritableCellFormat getHyperlinkStyle() { + if (hyperlinkStyle == null) { + initHyperlinkStyle(); + } + + return hyperlinkStyle; + } + + private synchronized void initArial10Pt() { + arial10pt = new WritableFont(WritableWorkbook.ARIAL_10_PT); + } + + public WritableFont getArial10Pt() { + if (arial10pt == null) { + initArial10Pt(); + } + + return arial10pt; + } + + private synchronized void initHyperlinkFont() { + hyperlinkFont = new WritableFont(WritableWorkbook.HYPERLINK_FONT); + } + + public WritableFont getHyperlinkFont() { + if (hyperlinkFont == null) { + initHyperlinkFont(); + } + + return hyperlinkFont; + } + + private synchronized void initDefaultDateFormat() { + defaultDateFormat = new WritableCellFormat(DateFormats.DEFAULT); + } + + public WritableCellFormat getDefaultDateFormat() { + if (defaultDateFormat == null) { + initDefaultDateFormat(); + } + + return defaultDateFormat; + } + + /** + * Gets the thread safe version of the cell format passed in. If the + * format is already thread safe (ie. it doesn't use a statically initialized + * format or font) then the same object is simply returned + * This object is already tied to a workbook instance, so no synchronisation + * is necesasry + * + * @param wf a format to verify + * @return the thread safe format + */ + public XFRecord getFormat(XFRecord wf) { + XFRecord format = wf; + + // Check to see if the format is one of the shared Workbook defaults. If + // so, then get hold of the Workbook's specific instance + if (format == WritableWorkbook.NORMAL_STYLE) { + format = getNormalStyle(); + } else if (format == WritableWorkbook.HYPERLINK_STYLE) { + format = getHyperlinkStyle(); + } else if (format == WritableWorkbook.HIDDEN_STYLE) { + format = getHiddenStyle(); + } else if (format == DateRecord.defaultDateFormat) { + format = getDefaultDateFormat(); + } + + // Do the same with the statically shared fonts + if (format.getFont() == WritableWorkbook.ARIAL_10_PT) { + format.setFont(getArial10Pt()); + } else if (format.getFont() == WritableWorkbook.HYPERLINK_FONT) { + format.setFont(getHyperlinkFont()); + } + + return format; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SupbookRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SupbookRecord.java new file mode 100755 index 0000000..d2c130a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/SupbookRecord.java @@ -0,0 +1,301 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.WorkbookSettings; +import jxl.biff.EncodedURLHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Stores the supporting workbook information. For files written by + * JExcelApi this will only reference internal sheets + */ +class SupbookRecord extends WritableRecordData { + public final static SupbookType INTERNAL = new SupbookType(); + public final static SupbookType EXTERNAL = new SupbookType(); + public final static SupbookType ADDIN = new SupbookType(); + public final static SupbookType LINK = new SupbookType(); + public final static SupbookType UNKNOWN = new SupbookType(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SupbookRecord.class); + /** + * The type of this supbook record + */ + private SupbookType type; + /** + * The data to be written to the binary file + */ + private byte[] data; + + /** + * The number of sheets - internal & external supbooks only + */ + private int numSheets; + /** + * The name of the external file + */ + private String fileName; + /** + * The names of the external sheets + */ + private String[] sheetNames; + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + /** + * Constructor for add in function names + */ + public SupbookRecord() { + super(Type.SUPBOOK); + type = ADDIN; + } + + /** + * Constructor for internal sheets + */ + public SupbookRecord(int sheets, WorkbookSettings ws) { + super(Type.SUPBOOK); + + numSheets = sheets; + type = INTERNAL; + workbookSettings = ws; + } + + /** + * Constructor for external sheets + * + * @param fn the filename of the external supbook + * @param ws the workbook settings + */ + public SupbookRecord(String fn, WorkbookSettings ws) { + super(Type.SUPBOOK); + + fileName = fn; + numSheets = 1; + sheetNames = new String[0]; + workbookSettings = ws; + + type = EXTERNAL; + } + + /** + * Constructor used when copying from an external workbook + */ + public SupbookRecord(jxl.read.biff.SupbookRecord sr, WorkbookSettings ws) { + super(Type.SUPBOOK); + + workbookSettings = ws; + if (sr.getType() == jxl.read.biff.SupbookRecord.INTERNAL) { + type = INTERNAL; + numSheets = sr.getNumberOfSheets(); + } else if (sr.getType() == jxl.read.biff.SupbookRecord.EXTERNAL) { + type = EXTERNAL; + numSheets = sr.getNumberOfSheets(); + fileName = sr.getFileName(); + sheetNames = new String[numSheets]; + + for (int i = 0; i < numSheets; i++) { + sheetNames[i] = sr.getSheetName(i); + } + } + + if (sr.getType() == jxl.read.biff.SupbookRecord.ADDIN) { + logger.warn("Supbook type is addin"); + } + } + + /** + * Initializes an internal supbook record + * + * @param sr the read supbook record to copy from + */ + private void initInternal(jxl.read.biff.SupbookRecord sr) { + numSheets = sr.getNumberOfSheets(); + initInternal(); + } + + /** + * Initializes an internal supbook record + */ + private void initInternal() { + data = new byte[4]; + + IntegerHelper.getTwoBytes(numSheets, data, 0); + data[2] = 0x1; + data[3] = 0x4; + type = INTERNAL; + } + + /** + * Adjust the number of internal sheets. Called by WritableSheet when + * a sheet is added or or removed to the workbook + * + * @param sheets the new number of sheets + */ + void adjustInternal(int sheets) { + Assert.verify(type == INTERNAL); + numSheets = sheets; + initInternal(); + } + + /** + * Initializes an external supbook record + */ + private void initExternal() { + int totalSheetNameLength = 0; + for (int i = 0; i < numSheets; i++) { + totalSheetNameLength += sheetNames[i].length(); + } + + byte[] fileNameData = EncodedURLHelper.getEncodedURL(fileName, + workbookSettings); + int dataLength = 2 + // numsheets + 4 + fileNameData.length + + numSheets * 3 + totalSheetNameLength * 2; + + data = new byte[dataLength]; + + IntegerHelper.getTwoBytes(numSheets, data, 0); + + // Add in the file name. Precede with a byte denoting that it is a + // file name + int pos = 2; + IntegerHelper.getTwoBytes(fileNameData.length + 1, data, pos); + data[pos + 2] = 0; // ascii indicator + data[pos + 3] = 1; // file name indicator + System.arraycopy(fileNameData, 0, data, pos + 4, fileNameData.length); + + pos += 4 + fileNameData.length; + + // Get the sheet names + for (int i = 0; i < sheetNames.length; i++) { + IntegerHelper.getTwoBytes(sheetNames[i].length(), data, pos); + data[pos + 2] = 1; // unicode indicator + StringHelper.getUnicodeBytes(sheetNames[i], data, pos + 3); + pos += 3 + sheetNames[i].length() * 2; + } + } + + /** + * Initializes the supbook record for add in functions + */ + private void initAddin() { + data = new byte[]{0x1, 0x0, 0x1, 0x3a}; + } + + /** + * The binary data to be written out + * + * @return the binary data + */ + public byte[] getData() { + if (type == INTERNAL) { + initInternal(); + } else if (type == EXTERNAL) { + initExternal(); + } else if (type == ADDIN) { + initAddin(); + } else { + logger.warn("unsupported supbook type - defaulting to internal"); + initInternal(); + } + + return data; + } + + /** + * Gets the type of this supbook record + * + * @return the type of this supbook + */ + public SupbookType getType() { + return type; + } + + /** + * Gets the number of sheets. This will only be non-zero for internal + * and external supbooks + * + * @return the number of sheets + */ + public int getNumberOfSheets() { + return numSheets; + } + + /** + * Accessor for the file name + * + * @return the file name + */ + public String getFileName() { + return fileName; + } + + /** + * Adds the worksheet name to this supbook + * + * @param name the worksheet name + * @return the index of this sheet in the supbook record + */ + public int getSheetIndex(String s) { + boolean found = false; + int sheetIndex = 0; + for (int i = 0; i < sheetNames.length && !found; i++) { + if (sheetNames[i].equals(s)) { + found = true; + sheetIndex = 0; + } + } + + if (found) { + return sheetIndex; + } + + // Grow the array + String[] names = new String[sheetNames.length + 1]; + System.arraycopy(sheetNames, 0, names, 0, sheetNames.length); + names[sheetNames.length] = s; + sheetNames = names; + return sheetNames.length - 1; + } + + /** + * Accessor for the sheet name + * + * @param s the sheet index + */ + public String getSheetName(int s) { + return sheetNames[s]; + } + + /** + * The type of supbook this refers to + */ + private static class SupbookType { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/TabIdRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/TabIdRecord.java new file mode 100755 index 0000000..7a74d09 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/TabIdRecord.java @@ -0,0 +1,58 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains an array of sheet tab index numbers + */ +class TabIdRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param sheets the number of sheets + */ + public TabIdRecord(int sheets) { + super(Type.TABID); + + data = new byte[sheets * 2]; + + for (int i = 0; i < sheets; i++) { + IntegerHelper.getTwoBytes(i + 1, data, i * 2); + } + } + + /** + * Gets the data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/TemplateRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/TemplateRecord.java new file mode 100755 index 0000000..9501d55 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/TemplateRecord.java @@ -0,0 +1,47 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates whether or not this file is a template (Usually saved with .xlt file name extension) + * should be refreshed when the workbook is loaded + */ +class TemplateRecord extends WritableRecordData { + /** + * Constructor + * + * @param template flag + */ + public TemplateRecord() { + super(Type.TEMPLATE); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return new byte[0]; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/TopMarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/TopMarginRecord.java new file mode 100755 index 0000000..3c47191 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/TopMarginRecord.java @@ -0,0 +1,31 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class TopMarginRecord extends MarginRecord { + TopMarginRecord(double v) { + super(Type.TOPMARGIN, v); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/UsesElfsRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/UsesElfsRecord.java new file mode 100755 index 0000000..d2c31fc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/UsesElfsRecord.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the flag which indicates whether the version of excel can + * understand natural language input for formulae + */ +class UsesElfsRecord extends WritableRecordData { + /** + * The binary data for output to file + */ + private final byte[] data; + /** + * The uses ELFs flag + */ + private final boolean usesElfs; + + /** + * Constructor + */ + public UsesElfsRecord() { + super(Type.USESELFS); + + usesElfs = true; + + data = new byte[2]; + + if (usesElfs) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/VerticalCentreRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/VerticalCentreRecord.java new file mode 100755 index 0000000..9fd32e7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/VerticalCentreRecord.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Indicates whether the centre vertically on page option has been + * set from the options dialog box + */ +class VerticalCentreRecord extends WritableRecordData { + /** + * The binary data for output to file + */ + private final byte[] data; + /** + * The centre flag + */ + private final boolean centre; + + /** + * Constructor + * + * @param ce the centre flag + */ + public VerticalCentreRecord(boolean ce) { + super(Type.VCENTER); + + centre = ce; + + data = new byte[2]; + + if (centre) { + data[0] = 1; + } + } + + /** + * Gets the data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/VerticalPageBreaksRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/VerticalPageBreaksRecord.java new file mode 100755 index 0000000..e61d24f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/VerticalPageBreaksRecord.java @@ -0,0 +1,68 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the list of explicit horizontal page breaks on the current sheet + */ +class VerticalPageBreaksRecord extends WritableRecordData { + /** + * The row breaks + */ + private final int[] columnBreaks; + + /** + * Constructor + * + * @param break the row breaks + */ + public VerticalPageBreaksRecord(int[] breaks) { + super(Type.VERTICALPAGEBREAKS); + + columnBreaks = breaks; + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[columnBreaks.length * 6 + 2]; + + // The number of breaks on the list + IntegerHelper.getTwoBytes(columnBreaks.length, data, 0); + int pos = 2; + + for (int i = 0; i < columnBreaks.length; i++) { + IntegerHelper.getTwoBytes(columnBreaks[i], data, pos); + IntegerHelper.getTwoBytes(0xff, data, pos + 4); + pos += 6; + } + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Weird1Record.java b/datastructures-xslx/src/main/java/jxl/write/biff/Weird1Record.java new file mode 100755 index 0000000..5431987 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/Weird1Record.java @@ -0,0 +1,48 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Don't know what this does - something to do with freezing panes I think + */ +class Weird1Record extends WritableRecordData { + /** + * Constructor + */ + public Weird1Record() { + super(Type.WEIRD1); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[6]; + + data[2] = 0x37; + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Window1Record.java b/datastructures-xslx/src/main/java/jxl/write/biff/Window1Record.java new file mode 100755 index 0000000..2ed5ce5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/Window1Record.java @@ -0,0 +1,80 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains workbook level windowing attributes + */ +class Window1Record extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * The selected sheet + */ + private final int selectedSheet; + + /** + * Constructor + */ + public Window1Record(int selSheet) { + super(Type.WINDOW1); + + selectedSheet = selSheet; + + // hard code the data in for now + data = new byte[] + {(byte) 0x68, + (byte) 0x1, + (byte) 0xe, + (byte) 0x1, + (byte) 0x5c, + (byte) 0x3a, + (byte) 0xbe, + (byte) 0x23, + (byte) 0x38, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0x1, + (byte) 0, + (byte) 0x58, + (byte) 0x2}; + + IntegerHelper.getTwoBytes(selectedSheet, data, 10); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Window2Record.java b/datastructures-xslx/src/main/java/jxl/write/biff/Window2Record.java new file mode 100755 index 0000000..f0281c3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/Window2Record.java @@ -0,0 +1,98 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.SheetSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the window attributes for a worksheet + */ +class Window2Record extends WritableRecordData { + /** + * The binary data for output to file + */ + private final byte[] data; + + /** + * Constructor + */ + public Window2Record(SheetSettings settings) { + super(Type.WINDOW2); + + int options = 0; + + options |= 0x0; // display formula values, not formulas + + if (settings.getShowGridLines()) { + options |= 0x02; + } + + options |= 0x04; // display row and column headings + + options |= 0x0; // panes should be not frozen + + if (settings.getDisplayZeroValues()) { + options |= 0x10; + } + + options |= 0x20; // default header + + options |= 0x80; // display outline symbols + + // Handle the freeze panes + if (settings.getHorizontalFreeze() != 0 || + settings.getVerticalFreeze() != 0) { + options |= 0x08; + options |= 0x100; + } + + // Handle the selected flag + if (settings.isSelected()) { + options |= 0x600; + } + + // Handle the view mode + if (settings.getPageBreakPreviewMode()) { + options |= 0x800; + } + + // hard code the data in for now + data = new byte[18]; + IntegerHelper.getTwoBytes(options, data, 0); + IntegerHelper.getTwoBytes(0x40, data, 6); // grid line colour + IntegerHelper.getTwoBytes(settings.getPageBreakPreviewMagnification(), + data, 10); + IntegerHelper.getTwoBytes(settings.getNormalMagnification(), + data, 12); + + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WindowProtectRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/WindowProtectRecord.java new file mode 100755 index 0000000..2041ab7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/WindowProtectRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores an option from the Protect Workbook dialog box + */ +class WindowProtectRecord extends WritableRecordData { + /** + * Protect flag + */ + private final boolean protection; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param prot the protect flag + */ + public WindowProtectRecord(boolean prot) { + super(Type.WINDOWPROTECT); + + protection = prot; + + data = new byte[2]; + + if (protection) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableFontRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFontRecord.java new file mode 100755 index 0000000..d4d8304 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFontRecord.java @@ -0,0 +1,157 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.FontRecord; +import jxl.format.Font; +import jxl.write.WriteException; + +/** + * A writable Font record. This class intercepts any set accessor calls + * and throws and exception if the Font is already initialized + */ +public class WritableFontRecord extends FontRecord { + /** + * Constructor, used when creating a new font for writing out. + * + * @param bold the bold indicator + * @param ps the point size + * @param us the underline style + * @param fn the name + * @param it italicised indicator + * @param c the colour + * @param ss the script style + */ + protected WritableFontRecord(String fn, int ps, int bold, boolean it, + int us, int ci, int ss) { + super(fn, ps, bold, it, us, ci, ss); + } + + /** + * Publicly available copy constructor + * + * @param the font to copy + */ + protected WritableFontRecord(Font f) { + super(f); + } + + + /** + * Sets the point size for this font, if the font hasn't been initialized + * + * @param pointSize the point size + * @throws WriteException, if this font is already in use elsewhere + */ + protected void setPointSize(int pointSize) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontPointSize(pointSize); + } + + /** + * Sets the bold style for this font, if the font hasn't been initialized + * + * @param boldStyle the bold style + * @throws WriteException, if this font is already in use elsewhere + */ + protected void setBoldStyle(int boldStyle) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontBoldStyle(boldStyle); + } + + /** + * Sets the italic indicator for this font, if the font hasn't been + * initialized + * + * @param italic the italic flag + * @throws WriteException, if this font is already in use elsewhere + */ + protected void setItalic(boolean italic) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontItalic(italic); + } + + /** + * Sets the underline style for this font, if the font hasn't been + * initialized + * + * @param us the underline style + * @throws WriteException, if this font is already in use elsewhere + */ + protected void setUnderlineStyle(int us) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontUnderlineStyle(us); + } + + /** + * Sets the colour for this font, if the font hasn't been + * initialized + * + * @param colour the colour + * @throws WriteException, if this font is already in use elsewhere + */ + protected void setColour(int colour) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontColour(colour); + } + + /** + * Sets the script style (eg. superscript, subscript) for this font, + * if the font hasn't been initialized + * + * @param scriptStyle the colour + * @throws WriteException, if this font is already in use elsewhere + */ + protected void setScriptStyle(int scriptStyle) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontScriptStyle(scriptStyle); + } + + /** + * Sets the struck out flag + * + * @param so TRUE if the font is struck out, false otherwise + * @throws WriteException, if this font is already in use elsewhere + */ + protected void setStruckout(boolean os) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setFontStruckout(os); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableFonts.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFonts.java new file mode 100755 index 0000000..190f2b3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFonts.java @@ -0,0 +1,49 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Fonts; +import jxl.write.WritableFont; + +/** + * A container for the list of fonts used in this workbook The writable + * subclass instantiates the predetermined list of fonts available to + * users of the writable API + */ +public class WritableFonts extends Fonts { + /** + * Constructor. Creates the predetermined list of fonts + */ + public WritableFonts(WritableWorkbookImpl w) { + super(); + + addFont(w.getStyles().getArial10Pt()); + + // Create the default fonts + WritableFont f = new WritableFont(WritableFont.ARIAL); + addFont(f); + + f = new WritableFont(WritableFont.ARIAL); + addFont(f); + + f = new WritableFont(WritableFont.ARIAL); + addFont(f); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableFormattingRecords.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFormattingRecords.java new file mode 100755 index 0000000..294c560 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFormattingRecords.java @@ -0,0 +1,225 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Fonts; +import jxl.biff.FormattingRecords; +import jxl.biff.NumFormatRecordsException; +import jxl.common.Assert; +import jxl.write.NumberFormats; +import jxl.write.WritableCellFormat; + + +/** + * Handles the Format and XF record indexing. The writable subclass + * instantiates the predetermined list of XF records and formats + * present in every Excel Workbook + */ +public class WritableFormattingRecords extends FormattingRecords { + /** + * The statically defined normal style + */ + public static WritableCellFormat normalStyle; + + /** + * Constructor. Instantiates the prerequisite list of formats and + * styles required by all Excel workbooks + * + * @param f the list of Fonts + * @param styles the list of style clones + */ + public WritableFormattingRecords(Fonts f, Styles styles) { + super(f); + + try { + // Hard code all the styles + StyleXFRecord sxf = new StyleXFRecord + (styles.getArial10Pt(), NumberFormats.DEFAULT); + sxf.setLocked(true); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(2), NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(3), NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + // That's the end of the built ins. Write the normal style + // cell XF here + addStyle(styles.getNormalStyle()); + + // Continue with "user defined" styles + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.FORMAT7); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.FORMAT5); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.FORMAT8); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.FORMAT6); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.PERCENT_INTEGER); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + // Hard code in the pre-defined number formats for now + /* + FormatRecord fr = new FormatRecord + ("\"$\"#,##0_);\\(\"$\"#,##0\\)",5); + addFormat(fr); + + fr = new FormatRecord + ("\"$\"#,##0_);[Red]\\(\"$\"#,##0\\)", 6); + addFormat(fr); + + fr = new FormatRecord + ("\"$\"#,##0.00_);\\(\"$\"#,##0.00\\)", 7); + addFormat(fr); + + fr = new FormatRecord + ("\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)", 8); + addFormat(fr); + + fr = new FormatRecord + ("_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)", + 0x2a); + // outputFile.write(fr); + + fr = new FormatRecord + ("_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)", + 0x2e); + // outputFile.write(fr); + + fr = new FormatRecord + ("_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)", + 0x2c); + // outputFile.write(fr); + + fr = new FormatRecord + ("_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)", + 0x2b); + // outputFile.write(fr); + */ + } catch (NumFormatRecordsException e) { + // This should not happen yet, since we are just creating the file. + // Bomb out + Assert.verify(false, e.getMessage()); + } + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetCopier.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetCopier.java new file mode 100755 index 0000000..d43c8ca --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetCopier.java @@ -0,0 +1,513 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.TreeSet; +import jxl.BooleanCell; +import jxl.Cell; +import jxl.CellType; +import jxl.DateCell; +import jxl.LabelCell; +import jxl.NumberCell; +import jxl.Range; +import jxl.WorkbookSettings; +import jxl.biff.CellReferenceHelper; +import jxl.biff.DataValidation; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.SheetRangeImpl; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.XFRecord; +import jxl.biff.formula.FormulaException; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; +import jxl.write.Blank; +import jxl.write.Boolean; +import jxl.write.DateTime; +import jxl.write.Formula; +import jxl.write.Label; +import jxl.write.Number; +import jxl.write.WritableCell; +import jxl.write.WritableCellFormat; +import jxl.write.WritableHyperlink; +import jxl.write.WritableImage; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; + +/** + * A transient utility object used to copy sheets. This + * functionality has been farmed out to a different class + * in order to reduce the bloat of the WritableSheetImpl + */ +class WritableSheetCopier { + private static final Logger logger = Logger.getLogger(SheetCopier.class); + + private final WritableSheetImpl fromSheet; + private final WritableSheetImpl toSheet; + private final WorkbookSettings workbookSettings; + + // Objects used by the sheet + private TreeSet fromColumnFormats; + private TreeSet toColumnFormats; + private MergedCells fromMergedCells; + private MergedCells toMergedCells; + private RowRecord[] fromRows; + private ArrayList fromRowBreaks; + private ArrayList fromColumnBreaks; + private ArrayList toRowBreaks; + private ArrayList toColumnBreaks; + private DataValidation fromDataValidation; + private DataValidation toDataValidation; + private SheetWriter sheetWriter; + private ArrayList fromDrawings; + private ArrayList toDrawings; + private ArrayList toImages; + private WorkspaceInformationRecord fromWorkspaceOptions; + private PLSRecord fromPLSRecord; + private PLSRecord toPLSRecord; + private ButtonPropertySetRecord fromButtonPropertySet; + private ButtonPropertySetRecord toButtonPropertySet; + private ArrayList fromHyperlinks; + private ArrayList toHyperlinks; + private ArrayList validatedCells; + private int numRows; + private int maxRowOutlineLevel; + private int maxColumnOutlineLevel; + + + private final boolean chartOnly; + private FormattingRecords formatRecords; + + + // Objects used to maintain state during the copy process + private HashMap xfRecords; + private HashMap fonts; + private HashMap formats; + + public WritableSheetCopier(WritableSheet f, WritableSheet t) { + fromSheet = (WritableSheetImpl) f; + toSheet = (WritableSheetImpl) t; + workbookSettings = toSheet.getWorkbook().getSettings(); + chartOnly = false; + } + + void setColumnFormats(TreeSet fcf, TreeSet tcf) { + fromColumnFormats = fcf; + toColumnFormats = tcf; + } + + void setMergedCells(MergedCells fmc, MergedCells tmc) { + fromMergedCells = fmc; + toMergedCells = tmc; + } + + void setRows(RowRecord[] r) { + fromRows = r; + } + + void setValidatedCells(ArrayList vc) { + validatedCells = vc; + } + + void setRowBreaks(ArrayList frb, ArrayList trb) { + fromRowBreaks = frb; + toRowBreaks = trb; + } + + void setColumnBreaks(ArrayList fcb, ArrayList tcb) { + fromColumnBreaks = fcb; + toColumnBreaks = tcb; + } + + void setDrawings(ArrayList fd, ArrayList td, ArrayList ti) { + fromDrawings = fd; + toDrawings = td; + toImages = ti; + } + + void setHyperlinks(ArrayList fh, ArrayList th) { + fromHyperlinks = fh; + toHyperlinks = th; + } + + void setWorkspaceOptions(WorkspaceInformationRecord wir) { + fromWorkspaceOptions = wir; + } + + void setButtonPropertySetRecord(ButtonPropertySetRecord bpsr) { + fromButtonPropertySet = bpsr; + } + + void setSheetWriter(SheetWriter sw) { + sheetWriter = sw; + } + + DataValidation getDataValidation() { + return toDataValidation; + } + + void setDataValidation(DataValidation dv) { + fromDataValidation = dv; + } + + PLSRecord getPLSRecord() { + return toPLSRecord; + } + + void setPLSRecord(PLSRecord plsr) { + fromPLSRecord = plsr; + } + + boolean isChartOnly() { + return chartOnly; + } + + ButtonPropertySetRecord getButtonPropertySet() { + return toButtonPropertySet; + } + + /** + * Copies a sheet from a read-only version to the writable version. + * Performs shallow copies + */ + public void copySheet() { + shallowCopyCells(); + + // Copy the column formats + Iterator cfit = fromColumnFormats.iterator(); + while (cfit.hasNext()) { + ColumnInfoRecord cv = new ColumnInfoRecord + ((ColumnInfoRecord) cfit.next()); + toColumnFormats.add(cv); + } + + // Copy the merged cells + Range[] merged = fromMergedCells.getMergedCells(); + + for (int i = 0; i < merged.length; i++) { + toMergedCells.add(new SheetRangeImpl((SheetRangeImpl) merged[i], + toSheet)); + } + + try { + RowRecord row = null; + RowRecord newRow = null; + for (int i = 0; i < fromRows.length; i++) { + row = fromRows[i]; + + if (row != null && + (!row.isDefaultHeight() || + row.isCollapsed())) { + newRow = toSheet.getRowRecord(i); + newRow.setRowDetails(row.getRowHeight(), + row.matchesDefaultFontHeight(), + row.isCollapsed(), + row.getOutlineLevel(), + row.getGroupStart(), + row.getStyle()); + } + } + } catch (RowsExceededException e) { + // Handle the rows exceeded exception - this cannot occur since + // the sheet we are copying from will have a valid number of rows + Assert.verify(false); + } + + // Copy the horizontal page breaks + toRowBreaks = new ArrayList(fromRowBreaks); + + // Copy the vertical page breaks + toColumnBreaks = new ArrayList(fromColumnBreaks); + + // Copy the data validations + if (fromDataValidation != null) { + toDataValidation = new DataValidation + (fromDataValidation, + toSheet.getWorkbook(), + toSheet.getWorkbook(), + toSheet.getWorkbook().getSettings()); + } + + // Copy the charts + sheetWriter.setCharts(fromSheet.getCharts()); + + // Copy the drawings + for (Iterator i = fromDrawings.iterator(); i.hasNext(); ) { + Object o = i.next(); + if (o instanceof jxl.biff.drawing.Drawing) { + WritableImage wi = new WritableImage + ((jxl.biff.drawing.Drawing) o, + toSheet.getWorkbook().getDrawingGroup()); + toDrawings.add(wi); + toImages.add(wi); + } + + // Not necessary to copy the comments, as they will be handled by + // the deep copy of the individual cells + } + + // Copy the workspace options + sheetWriter.setWorkspaceOptions(fromWorkspaceOptions); + + // Copy the environment specific print record + if (fromPLSRecord != null) { + toPLSRecord = new PLSRecord(fromPLSRecord); + } + + // Copy the button property set + if (fromButtonPropertySet != null) { + toButtonPropertySet = new ButtonPropertySetRecord(fromButtonPropertySet); + } + + // Copy the hyperlinks + for (Iterator i = fromHyperlinks.iterator(); i.hasNext(); ) { + WritableHyperlink hr = new WritableHyperlink + ((WritableHyperlink) i.next(), toSheet); + toHyperlinks.add(hr); + } + } + + /** + * Performs a shallow copy of the specified cell + */ + private WritableCell shallowCopyCell(Cell cell) { + CellType ct = cell.getType(); + WritableCell newCell = null; + + if (ct == CellType.LABEL) { + newCell = new Label((LabelCell) cell); + } else if (ct == CellType.NUMBER) { + newCell = new Number((NumberCell) cell); + } else if (ct == CellType.DATE) { + newCell = new DateTime((DateCell) cell); + } else if (ct == CellType.BOOLEAN) { + newCell = new Boolean((BooleanCell) cell); + } else if (ct == CellType.NUMBER_FORMULA) { + newCell = new ReadNumberFormulaRecord((FormulaData) cell); + } else if (ct == CellType.STRING_FORMULA) { + newCell = new ReadStringFormulaRecord((FormulaData) cell); + } else if (ct == CellType.BOOLEAN_FORMULA) { + newCell = new ReadBooleanFormulaRecord((FormulaData) cell); + } else if (ct == CellType.DATE_FORMULA) { + newCell = new ReadDateFormulaRecord((FormulaData) cell); + } else if (ct == CellType.FORMULA_ERROR) { + newCell = new ReadErrorFormulaRecord((FormulaData) cell); + } else if (ct == CellType.EMPTY) { + if (cell.getCellFormat() != null) { + // It is a blank cell, rather than an empty cell, so + // it may have formatting information, so + // it must be copied + newCell = new Blank(cell); + } + } + + return newCell; + } + + /** + * Performs a deep copy of the specified cell, handling the cell format + * + * @param cell the cell to copy + */ + private WritableCell deepCopyCell(Cell cell) { + WritableCell c = shallowCopyCell(cell); + + if (c == null) { + return c; + } + + if (c instanceof ReadFormulaRecord) { + ReadFormulaRecord rfr = (ReadFormulaRecord) c; + boolean crossSheetReference = !rfr.handleImportedCellReferences + (fromSheet.getWorkbook(), + fromSheet.getWorkbook(), + workbookSettings); + + if (crossSheetReference) { + try { + logger.warn("Formula " + rfr.getFormula() + + " in cell " + + CellReferenceHelper.getCellReference(cell.getColumn(), + cell.getRow()) + + " cannot be imported because it references another " + + " sheet from the source workbook"); + } catch (FormulaException e) { + logger.warn("Formula in cell " + + CellReferenceHelper.getCellReference(cell.getColumn(), + cell.getRow()) + + " cannot be imported: " + e.getMessage()); + } + + // Create a new error formula and add it instead + c = new Formula(cell.getColumn(), cell.getRow(), "\"ERROR\""); + } + } + + // Copy the cell format + CellFormat cf = c.getCellFormat(); + int index = ((XFRecord) cf).getXFIndex(); + WritableCellFormat wcf = (WritableCellFormat) + xfRecords.get(new Integer(index)); + + if (wcf == null) { + wcf = copyCellFormat(cf); + } + + c.setCellFormat(wcf); + + return c; + } + + /** + * Perform a shallow copy of the cells from the specified sheet into this one + */ + void shallowCopyCells() { + // Copy the cells + int cells = fromSheet.getRows(); + Cell[] row = null; + Cell cell = null; + for (int i = 0; i < cells; i++) { + row = fromSheet.getRow(i); + + for (int j = 0; j < row.length; j++) { + cell = row[j]; + WritableCell c = shallowCopyCell(cell); + + // Encase the calls to addCell in a try-catch block + // These should not generate any errors, because we are + // copying from an existing spreadsheet. In the event of + // errors, catch the exception and then bomb out with an + // assertion + try { + if (c != null) { + toSheet.addCell(c); + + // Cell.setCellFeatures short circuits when the cell is copied, + // so make sure the copy logic handles the validated cells + if (c.getCellFeatures() != null & + c.getCellFeatures().hasDataValidation()) { + validatedCells.add(c); + } + } + } catch (WriteException e) { + Assert.verify(false); + } + } + } + numRows = toSheet.getRows(); + } + + /** + * Perform a deep copy of the cells from the specified sheet into this one + */ + void deepCopyCells() { + // Copy the cells + int cells = fromSheet.getRows(); + Cell[] row = null; + Cell cell = null; + for (int i = 0; i < cells; i++) { + row = fromSheet.getRow(i); + + for (int j = 0; j < row.length; j++) { + cell = row[j]; + WritableCell c = deepCopyCell(cell); + + // Encase the calls to addCell in a try-catch block + // These should not generate any errors, because we are + // copying from an existing spreadsheet. In the event of + // errors, catch the exception and then bomb out with an + // assertion + try { + if (c != null) { + toSheet.addCell(c); + + // Cell.setCellFeatures short circuits when the cell is copied, + // so make sure the copy logic handles the validated cells + if (c.getCellFeatures() != null & + c.getCellFeatures().hasDataValidation()) { + validatedCells.add(c); + } + } + } catch (WriteException e) { + Assert.verify(false); + } + } + } + } + + /** + * Returns an initialized copy of the cell format + * + * @param cf the cell format to copy + * @return a deep copy of the cell format + */ + private WritableCellFormat copyCellFormat(CellFormat cf) { + try { + // just do a deep copy of the cell format for now. This will create + // a copy of the format and font also - in the future this may + // need to be sorted out + XFRecord xfr = (XFRecord) cf; + WritableCellFormat f = new WritableCellFormat(xfr); + formatRecords.addStyle(f); + + // Maintain the local list of formats + int xfIndex = xfr.getXFIndex(); + xfRecords.put(new Integer(xfIndex), f); + + int fontIndex = xfr.getFontIndex(); + fonts.put(new Integer(fontIndex), new Integer(f.getFontIndex())); + + int formatIndex = xfr.getFormatRecord(); + formats.put(new Integer(formatIndex), new Integer(f.getFormatRecord())); + + return f; + } catch (NumFormatRecordsException e) { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + + return WritableWorkbook.NORMAL_STYLE; + } + } + + + /** + * Accessor for the maximum column outline level + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() { + return maxRowOutlineLevel; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetImpl.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetImpl.java new file mode 100755 index 0000000..e4478ba --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetImpl.java @@ -0,0 +1,2535 @@ +/********************************************************************** + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.TreeSet; +import java.util.regex.Pattern; +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellReferenceHelper; +import jxl.CellType; +import jxl.CellView; +import jxl.HeaderFooter; +import jxl.Hyperlink; +import jxl.Image; +import jxl.LabelCell; +import jxl.Range; +import jxl.Sheet; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.CellFinder; +import jxl.biff.ConditionalFormat; +import jxl.biff.DVParser; +import jxl.biff.DataValidation; +import jxl.biff.EmptyCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IndexMapping; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.SheetRangeImpl; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.XFRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; +import jxl.format.Font; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; +import jxl.write.Blank; +import jxl.write.Label; +import jxl.write.WritableCell; +import jxl.write.WritableCellFeatures; +import jxl.write.WritableCellFormat; +import jxl.write.WritableHyperlink; +import jxl.write.WritableImage; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; + +/** + * A writable sheet. This class contains implementation of all the + * writable sheet methods which may be invoke by the API + */ +class WritableSheetImpl implements WritableSheet { + /** + * The amount by which to grow the rows array + */ + private final static int rowGrowSize = 10; + /** + * The maximum number of rows excel allows in a worksheet + */ + private final static int numRowsPerSheet = 65536; + /** + * The maximum number of characters permissible for a sheet name + */ + private final static int maxSheetNameLength = 31; + /** + * The illegal characters for a sheet name + */ + private final static char[] illegalSheetNameCharacters = + new char[]{'*', ':', '?', '\\'}; + /** + * The supported file types + */ + private static final String[] imageTypes = new String[]{"png"}; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(WritableSheetImpl.class); + /** + * The name of this sheet + */ + private String name; + /** + * A handle to the output file which the binary data is written to + */ + private final File outputFile; + /** + * The rows within this sheet + */ + private RowRecord[] rows; + /** + * A handle to workbook format records + */ + private final FormattingRecords formatRecords; + /** + * A handle to the shared strings used by this workbook + */ + private final SharedStrings sharedStrings; + /** + * The list of non-default column formats + */ + private final TreeSet columnFormats; + /** + * The list of autosized columns + */ + private TreeSet autosizedColumns; + /** + * The list of hyperlinks + */ + private final ArrayList hyperlinks; + /** + * The list of merged ranged + */ + private final MergedCells mergedCells; + /** + * A number of rows. This is a count of the maximum row number + 1 + */ + private int numRows; + /** + * The number of columns. This is a count of the maximum column number + 1 + */ + private int numColumns; + /** + * The environment specific print record, copied from the read spreadsheet + */ + private PLSRecord plsRecord; + /** + * The buttons property set + */ + private ButtonPropertySetRecord buttonPropertySet; + /** + * A flag indicating that this sheet is a chart only + */ + private boolean chartOnly; + /** + * The data validations on this page. Used to store data validations + * from a read sheet + */ + private DataValidation dataValidation; + /** + * Array of row page breaks + */ + private ArrayList rowBreaks; + /** + * Array of column page breaks + */ + private ArrayList columnBreaks; + /** + * The drawings on this sheet + */ + private final ArrayList drawings; + /** + * The images on this sheet. This is a subset of the drawings list + */ + private final ArrayList images; + /** + * The conditional formats on this sheet + */ + private final ArrayList conditionalFormats; + /** + * The autofilter + */ + private AutoFilter autoFilter; + /** + * The writable cells on this sheet which may have validation added + * to them + */ + private final ArrayList validatedCells; + /** + * The combo box object used for list validations on this sheet + */ + private ComboBox comboBox; + /** + * Drawings modified flag. Set to true if the drawings list has + * been modified + */ + private boolean drawingsModified; + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + /** + * The settings for this sheet + */ + private SheetSettings settings; + /** + * The sheet writer engine + */ + private final SheetWriter sheetWriter; + /** + * The settings for the workbook + */ + private final WorkbookSettings workbookSettings; + /** + * The workbook + */ + private final WritableWorkbookImpl workbook; + + /** + * Constructor + * + * @param fr the formatting records used by the workbook + * @param of the output file to write the binary data + * @param f the fonts used by the workbook + * @param n the name of this sheet + * @param ss the shared strings used by the workbook + * @param ws the workbook settings + */ + public WritableSheetImpl(String n, + File of, + FormattingRecords fr, + SharedStrings ss, + WorkbookSettings ws, + WritableWorkbookImpl ww) { + name = validateName(n); + outputFile = of; + rows = new RowRecord[0]; + numRows = 0; + numColumns = 0; + chartOnly = false; + workbook = ww; + + formatRecords = fr; + sharedStrings = ss; + workbookSettings = ws; + drawingsModified = false; + columnFormats = new TreeSet(new ColumnInfoComparator()); + autosizedColumns = new TreeSet(); + hyperlinks = new ArrayList(); + mergedCells = new MergedCells(this); + rowBreaks = new ArrayList(); + columnBreaks = new ArrayList(); + drawings = new ArrayList(); + images = new ArrayList(); + conditionalFormats = new ArrayList(); + validatedCells = new ArrayList(); + settings = new SheetSettings(this); + + + sheetWriter = new SheetWriter(outputFile, + this, + workbookSettings); + } + + /** + * Returns the cell for the specified location eg. "A4", using the + * CellReferenceHelper + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + public Cell getCell(String loc) { + return getCell(CellReferenceHelper.getColumn(loc), + CellReferenceHelper.getRow(loc)); + } + + /** + * Returns the cell specified at this row and at this column + * + * @param column the column number + * @param row the row number + * @return the cell at the specified co-ordinates + */ + public Cell getCell(int column, int row) { + return getWritableCell(column, row); + } + + /** + * Returns the cell for the specified location eg. "A4". Note that this + * method is identical to calling getCell(CellReferenceHelper.getColumn(loc), + * CellReferenceHelper.getRow(loc)) and its implicit performance + * overhead for string parsing. As such,this method should therefore + * be used sparingly + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + public WritableCell getWritableCell(String loc) { + return getWritableCell(CellReferenceHelper.getColumn(loc), + CellReferenceHelper.getRow(loc)); + } + + /** + * Returns the cell specified at this row and at this column + * + * @param column the column number + * @param row the row number + * @return the cell at the specified co-ordinates + */ + public WritableCell getWritableCell(int column, int row) { + WritableCell c = null; + + if (row < rows.length && rows[row] != null) { + c = rows[row].getCell(column); + } + + if (c == null) { + c = new EmptyCell(column, row); + } + + return c; + } + + /** + * Returns the number of rows in this sheet + * + * @return the number of rows in this sheet + */ + public int getRows() { + return numRows; + } + + /** + * Returns the number of columns in this sheet + * + * @return the number of columns in this sheet + */ + public int getColumns() { + return numColumns; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public Cell findCell(String contents) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastRow the last row within the range + * @param lastCol the last column within the ranage + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(pattern, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell methods in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public LabelCell findLabelCell(String contents) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findLabelCell(contents); + } + + /** + * Gets all the cells on the specified row + * + * @param row the rows whose cells are to be returned + * @return the cells on the given row + */ + public Cell[] getRow(int row) { + // Find the last non-null cell + boolean found = false; + int col = numColumns - 1; + while (col >= 0 && !found) { + if (getCell(col, row).getType() != CellType.EMPTY) { + found = true; + } else { + col--; + } + } + + // Only create entries for non-empty cells + Cell[] cells = new Cell[col + 1]; + + for (int i = 0; i <= col; i++) { + cells[i] = getCell(i, row); + } + return cells; + } + + /** + * Gets all the cells on the specified column + * + * @param col the column whose cells are to be returned + * @return the cells on the specified column + */ + public Cell[] getColumn(int col) { + // Find the last non-null cell + boolean found = false; + int row = numRows - 1; + + while (row >= 0 && !found) { + if (getCell(col, row).getType() != CellType.EMPTY) { + found = true; + } else { + row--; + } + } + + // Only create entries for non-empty cells + Cell[] cells = new Cell[row + 1]; + + for (int i = 0; i <= row; i++) { + cells[i] = getCell(col, i); + } + return cells; + } + + /** + * Gets the name of this sheet + * + * @return the name of the sheet + */ + public String getName() { + return name; + } + + /** + * Sets the name of this worksheet + * + * @param n the name of this sheet + */ + public void setName(String n) { + name = n; + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) { + if (row < 0 || row >= numRows) { + return; + } + + // Create a new array to hold the new rows. Grow it if need be + RowRecord[] oldRows = rows; + + if (numRows == rows.length) { + rows = new RowRecord[oldRows.length + rowGrowSize]; + } else { + rows = new RowRecord[oldRows.length]; + } + + // Copy in everything up to the new row + System.arraycopy(oldRows, 0, rows, 0, row); + + // Copy in the remaining rows + System.arraycopy(oldRows, row, rows, row + 1, numRows - row); + + // Increment all the internal row number by one + for (int i = row + 1; i <= numRows; i++) { + if (rows[i] != null) { + rows[i].incrementRow(); + } + } + + // Adjust any hyperlinks + HyperlinkRecord hr = null; + Iterator i = hyperlinks.iterator(); + while (i.hasNext()) { + hr = (HyperlinkRecord) i.next(); + hr.insertRow(row); + } + + // Adjust any data validations + if (dataValidation != null) { + dataValidation.insertRow(row); + } + + if (validatedCells != null && validatedCells.size() > 0) { + for (Iterator vci = validatedCells.iterator(); vci.hasNext(); ) { + CellValue cv = (CellValue) vci.next(); + CellFeatures cf = cv.getCellFeatures(); + if (cf.getDVParser() != null) { + cf.getDVParser().insertRow(row); + } + } + } + + // Adjust any merged cells + mergedCells.insertRow(row); + + // Adjust any page breaks + ArrayList newRowBreaks = new ArrayList(); + Iterator ri = rowBreaks.iterator(); + while (ri.hasNext()) { + int val = ((Integer) ri.next()).intValue(); + if (val >= row) { + val++; + } + + newRowBreaks.add(new Integer(val)); + } + rowBreaks = newRowBreaks; + + // Adjust any conditional formats + for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext(); ) { + ConditionalFormat cf = (ConditionalFormat) cfit.next(); + cf.insertRow(row); + } + + // Handle interested cell references on the main workbook + if (workbookSettings.getFormulaAdjust()) { + workbook.rowInserted(this, row); + } + + // Adjust the maximum row record + numRows++; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken. If the + * max column on the sheet has been reached, then the last column entry + * gets dropped + * + * @param col the column to insert + */ + public void insertColumn(int col) { + if (col < 0 || col >= numColumns) { + return; + } + + // Iterate through all the row records adding in the column + for (int i = 0; i < numRows; i++) { + if (rows[i] != null) { + rows[i].insertColumn(col); + } + } + + // Adjust any hyperlinks + HyperlinkRecord hr = null; + Iterator i = hyperlinks.iterator(); + while (i.hasNext()) { + hr = (HyperlinkRecord) i.next(); + hr.insertColumn(col); + } + + // Iterate through the column views, incrementing the column number + i = columnFormats.iterator(); + while (i.hasNext()) { + ColumnInfoRecord cir = (ColumnInfoRecord) i.next(); + + if (cir.getColumn() >= col) { + cir.incrementColumn(); + } + } + + // Iterate through the autosized columns, incrementing the column number + if (autosizedColumns.size() > 0) { + TreeSet newAutosized = new TreeSet(); + i = autosizedColumns.iterator(); + while (i.hasNext()) { + Integer colnumber = (Integer) i.next(); + + if (colnumber.intValue() >= col) { + newAutosized.add(new Integer(colnumber.intValue() + 1)); + } else { + newAutosized.add(colnumber); + } + } + autosizedColumns = newAutosized; + } + + // Handle any data validations + if (dataValidation != null) { + dataValidation.insertColumn(col); + } + + if (validatedCells != null && validatedCells.size() > 0) { + for (Iterator vci = validatedCells.iterator(); vci.hasNext(); ) { + CellValue cv = (CellValue) vci.next(); + CellFeatures cf = cv.getCellFeatures(); + if (cf.getDVParser() != null) { + cf.getDVParser().insertColumn(col); + } + } + } + + // Adjust any merged cells + mergedCells.insertColumn(col); + + // Adjust any page breaks + ArrayList newColumnBreaks = new ArrayList(); + Iterator ri = columnBreaks.iterator(); + while (ri.hasNext()) { + int val = ((Integer) ri.next()).intValue(); + if (val >= col) { + val++; + } + + newColumnBreaks.add(new Integer(val)); + } + columnBreaks = newColumnBreaks; + + // Adjust any conditional formats + for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext(); ) { + ConditionalFormat cf = (ConditionalFormat) cfit.next(); + cf.insertColumn(col); + } + + // Handle interested cell references on the main workbook + if (workbookSettings.getFormulaAdjust()) { + workbook.columnInserted(this, col); + } + + numColumns++; + } + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + public void removeColumn(int col) { + if (col < 0 || col >= numColumns) { + return; + } + + // Iterate through all the row records removing the column + for (int i = 0; i < numRows; i++) { + if (rows[i] != null) { + rows[i].removeColumn(col); + } + } + + // Adjust any hyperlinks + HyperlinkRecord hr = null; + Iterator i = hyperlinks.iterator(); + while (i.hasNext()) { + hr = (HyperlinkRecord) i.next(); + + if (hr.getColumn() == col && + hr.getLastColumn() == col) { + // The row with the hyperlink on has been removed, so get + // rid of it from the list + i.remove(); + } else { + hr.removeColumn(col); + } + } + + // Adjust any data validations + if (dataValidation != null) { + dataValidation.removeColumn(col); + } + + if (validatedCells != null && validatedCells.size() > 0) { + for (Iterator vci = validatedCells.iterator(); vci.hasNext(); ) { + CellValue cv = (CellValue) vci.next(); + CellFeatures cf = cv.getCellFeatures(); + if (cf.getDVParser() != null) { + cf.getDVParser().removeColumn(col); + } + } + } + + // Adjust any merged cells + mergedCells.removeColumn(col); + + // Adjust any page breaks + ArrayList newColumnBreaks = new ArrayList(); + Iterator ri = columnBreaks.iterator(); + while (ri.hasNext()) { + int val = ((Integer) ri.next()).intValue(); + + if (val != col) { + if (val > col) { + val--; + } + + newColumnBreaks.add(new Integer(val)); + } + } + + columnBreaks = newColumnBreaks; + + + // Iterate through the column views, decrementing the column number + i = columnFormats.iterator(); + ColumnInfoRecord removeColumn = null; + while (i.hasNext()) { + ColumnInfoRecord cir = (ColumnInfoRecord) i.next(); + + if (cir.getColumn() == col) { + removeColumn = cir; + } else if (cir.getColumn() > col) { + cir.decrementColumn(); + } + } + + if (removeColumn != null) { + columnFormats.remove(removeColumn); + } + + // Iterate through the autosized columns, decrementing the column number + if (autosizedColumns.size() > 0) { + TreeSet newAutosized = new TreeSet(); + i = autosizedColumns.iterator(); + while (i.hasNext()) { + Integer colnumber = (Integer) i.next(); + + if (colnumber.intValue() == col) { + // do nothing + } else if (colnumber.intValue() > col) { + newAutosized.add(new Integer(colnumber.intValue() - 1)); + } else { + newAutosized.add(colnumber); + } + } + autosizedColumns = newAutosized; + } + + // Adjust any conditional formats + for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext(); ) { + ConditionalFormat cf = (ConditionalFormat) cfit.next(); + cf.removeColumn(col); + } + + // Handle interested cell references on the main workbook + if (workbookSettings.getFormulaAdjust()) { + workbook.columnRemoved(this, col); + } + + numColumns--; + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) { + if (row < 0 || row >= numRows) { + // Call rowRemoved anyway, to adjust the named cells + if (workbookSettings.getFormulaAdjust()) { + workbook.rowRemoved(this, row); + } + + return; + } + + // Create a new array to hold the new rows. Grow it if need be + RowRecord[] oldRows = rows; + + rows = new RowRecord[oldRows.length]; + + // Copy in everything up to the row to be removed + System.arraycopy(oldRows, 0, rows, 0, row); + + // Copy in the remaining rows + System.arraycopy(oldRows, row + 1, rows, row, numRows - (row + 1)); + + // Decrement all the internal row numbers by one + for (int i = row; i < numRows; i++) { + if (rows[i] != null) { + rows[i].decrementRow(); + } + } + + // Adjust any hyperlinks + HyperlinkRecord hr = null; + Iterator i = hyperlinks.iterator(); + while (i.hasNext()) { + hr = (HyperlinkRecord) i.next(); + + if (hr.getRow() == row && + hr.getLastRow() == row) { + // The row with the hyperlink on has been removed, so get + // rid of it from the list + i.remove(); + } else { + hr.removeRow(row); + } + } + + // Adjust any data validations + if (dataValidation != null) { + dataValidation.removeRow(row); + } + + if (validatedCells != null && validatedCells.size() > 0) { + for (Iterator vci = validatedCells.iterator(); vci.hasNext(); ) { + CellValue cv = (CellValue) vci.next(); + CellFeatures cf = cv.getCellFeatures(); + if (cf.getDVParser() != null) { + cf.getDVParser().removeRow(row); + } + } + } + + // Adjust any merged cells + mergedCells.removeRow(row); + + // Adjust any page breaks + ArrayList newRowBreaks = new ArrayList(); + Iterator ri = rowBreaks.iterator(); + while (ri.hasNext()) { + int val = ((Integer) ri.next()).intValue(); + + if (val != row) { + if (val > row) { + val--; + } + + newRowBreaks.add(new Integer(val)); + } + } + + rowBreaks = newRowBreaks; + + // Adjust any conditional formats + for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext(); ) { + ConditionalFormat cf = (ConditionalFormat) cfit.next(); + cf.removeRow(row); + } + + // Handle interested cell references on the main workbook + if (workbookSettings.getFormulaAdjust()) { + workbook.rowRemoved(this, row); + } + + // Adjust any drawings + /* + if (drawings != null) + { + for (Iterator drawingIt = drawings.iterator() ; drawingIt.hasNext() ; ) + { + DrawingGroupObject dgo = (DrawingGroupObject) drawingIt.next(); + dgo.removeRow(row); + } + } + */ + + // Adjust the maximum row record + numRows--; + } + + /** + * Adds the cell to this sheet. If the cell has already been added to + * this sheet or another sheet, a WriteException is thrown. If the + * position to be occupied by this cell is already taken, the incumbent + * cell is replaced. + * The cell is then marked as referenced, and its formatting information + * registered with the list of formatting records updated if necessary + * The RowsExceededException may be caught if client code wishes to + * explicitly trap the case where too many rows have been written + * to the current sheet. If this behaviour is not desired, it is + * sufficient simply to handle the WriteException, since this is a base + * class of RowsExceededException + * + * @param cell the cell to add + * @throws WriteException + * @throws RowsExceededException + */ + public void addCell(WritableCell cell) + throws WriteException, RowsExceededException { + if (cell.getType() == CellType.EMPTY) { + if (cell != null && cell.getCellFormat() == null) { + // return if it's a blank cell with no particular cell formatting + // information + return; + } + } + + CellValue cv = (CellValue) cell; + + if (cv.isReferenced()) { + throw new JxlWriteException(JxlWriteException.cellReferenced); + } + + int row = cell.getRow(); + RowRecord rowrec = getRowRecord(row); + + CellValue curcell = rowrec.getCell(cv.getColumn()); + boolean curSharedValidation = (curcell != null && + curcell.getCellFeatures() != null && + curcell.getCellFeatures().getDVParser() != null && + curcell.getCellFeatures().getDVParser().extendedCellsValidation()); + + // Check for shared data validations, but only if the cell being added + // has a data validation + if (cell.getCellFeatures() != null && + cell.getCellFeatures().hasDataValidation() && + curSharedValidation) { + DVParser dvp = curcell.getCellFeatures().getDVParser(); + logger.warn("Cannot add cell at " + + CellReferenceHelper.getCellReference(cv) + + " because it is part of the shared cell validation group " + + CellReferenceHelper.getCellReference(dvp.getFirstColumn(), + dvp.getFirstRow()) + + "-" + + CellReferenceHelper.getCellReference(dvp.getLastColumn(), + dvp.getLastRow())); + return; + } + + // Apply any shared validation from the current cell to this cell + if (curSharedValidation) { + WritableCellFeatures wcf = cell.getWritableCellFeatures(); + + if (wcf == null) { + wcf = new WritableCellFeatures(); + cell.setCellFeatures(wcf); + } + + wcf.shareDataValidation(curcell.getCellFeatures()); + } + + rowrec.addCell(cv); + + // Adjust the max rows and max columns accordingly + numRows = Math.max(row + 1, numRows); + numColumns = Math.max(numColumns, rowrec.getMaxColumn()); + + // Indicate this cell is now part of a worksheet, so that it can't be + // added anywhere else + cv.setCellDetails(formatRecords, sharedStrings, this); + } + + /** + * Gets the row record at the specified row number, growing the + * array as needs dictate + * + * @param row the row number we are interested in + * @return the row record at the specified row + * @throws RowsExceededException + */ + RowRecord getRowRecord(int row) throws RowsExceededException { + if (row >= numRowsPerSheet) { + throw new RowsExceededException(); + } + + // Grow the array of rows if needs be + // Thanks to Brendan for spotting the flaw in merely adding on the + // grow size + if (row >= rows.length) { + RowRecord[] oldRows = rows; + rows = new RowRecord[Math.max(oldRows.length + rowGrowSize, row + 1)]; + System.arraycopy(oldRows, 0, rows, 0, oldRows.length); + oldRows = null; + } + + RowRecord rowrec = rows[row]; + + if (rowrec == null) { + rowrec = new RowRecord(row, this); + rows[row] = rowrec; + } + + return rowrec; + } + + /** + * Gets the row record for the specified row + * + * @param r the row + * @return the row record + */ + RowRecord getRowInfo(int r) { + if (r < 0 || r > rows.length) { + return null; + } + + return rows[r]; + } + + /** + * Gets the column info record for the specified column + * + * @param c the column + * @return the column record + */ + ColumnInfoRecord getColumnInfo(int c) { + Iterator i = columnFormats.iterator(); + ColumnInfoRecord cir = null; + boolean stop = false; + + while (i.hasNext() && !stop) { + cir = (ColumnInfoRecord) i.next(); + + if (cir.getColumn() >= c) { + stop = true; + } + } + + if (!stop) { + return null; + } + + return cir.getColumn() == c ? cir : null; + } + + /** + * Sets this sheet as selected + * + * @deprecated Use the settings bean + */ + public void setSelected() { + settings.setSelected(); + } + + /** + * Retrieves the hidden status of this sheet + * + * @return TRUE if hidden, FALSE otherwise + * @deprecated Use the sheet settings bean instead + */ + public boolean isHidden() { + return settings.isHidden(); + } + + /** + * Sets the hidden status of this sheet + * + * @param h the hiden flag + * @deprecated Use the settings bean instead + */ + public void setHidden(boolean h) { + settings.setHidden(h); + } + + /** + * Sets the width (in characters) for a particular column in this sheet + * + * @param col the column whose width to set + * @param width the width of the column in characters + */ + public void setColumnView(int col, int width) { + CellView cv = new CellView(); + cv.setSize(width * 256); + setColumnView(col, cv); + } + + /** + * Sets the width (in characters) and format options for a + * particular column in this sheet + * + * @param col the column to set + * @param width the width in characters + * @param format the formt details for the column + */ + public void setColumnView(int col, int width, CellFormat format) { + CellView cv = new CellView(); + cv.setSize(width * 256); + cv.setFormat(format); + setColumnView(col, cv); + } + + /** + * Sets the view for this column + * + * @param col the column on which to set the view + * @param view the view to set + */ + public void setColumnView(int col, CellView view) { + XFRecord xfr = (XFRecord) view.getFormat(); + if (xfr == null) { + Styles styles = getWorkbook().getStyles(); + xfr = styles.getNormalStyle(); + } + + try { + if (!xfr.isInitialized()) { + formatRecords.addStyle(xfr); + } + + int width = view.depUsed() ? view.getDimension() * 256 : view.getSize(); + + if (view.isAutosize()) { + autosizedColumns.add(new Integer(col)); + } + + ColumnInfoRecord cir = new ColumnInfoRecord(col, + width, + xfr); + + if (view.isHidden()) { + cir.setHidden(true); + } + + if (!columnFormats.contains(cir)) { + columnFormats.add(cir); + } else { + columnFormats.remove(cir); + columnFormats.add(cir); + } + } catch (NumFormatRecordsException e) { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + + ColumnInfoRecord cir = new ColumnInfoRecord + (col, view.getDimension() * 256, WritableWorkbook.NORMAL_STYLE); + columnFormats.add(cir); + } + } + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in 1/20ths of a point + * @throws RowsExceededException + * @deprecated use the override which takes a CellView object + */ + public void setRowView(int row, int height) throws RowsExceededException { + CellView cv = new CellView(); + cv.setSize(height); + cv.setHidden(false); + setRowView(row, cv); + } + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param collapsed indicates whether the row is collapsed + * @throws RowsExceededException + * @deprecated use the override which takes a CellView object + */ + public void setRowView(int row, boolean collapsed) + throws RowsExceededException { + CellView cv = new CellView(); + cv.setHidden(collapsed); + setRowView(row, cv); + } + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in 1/20th of a point + * @param collapsed indicates whether the row is collapsed + * @param zeroHeight indicates that the row has zero height + * @throws RowsExceededException + * @deprecated use the override which takes a CellView object + */ + public void setRowView(int row, int height, + boolean collapsed) + throws RowsExceededException { + CellView cv = new CellView(); + cv.setSize(height); + cv.setHidden(collapsed); + setRowView(row, cv); + } + + /** + * Sets the view for this column + * + * @param row the column on which to set the view + * @param view the view to set + * @throws RowsExceededException + */ + public void setRowView(int row, CellView view) throws RowsExceededException { + RowRecord rowrec = getRowRecord(row); + + XFRecord xfr = (XFRecord) view.getFormat(); + + try { + if (xfr != null) { + if (!xfr.isInitialized()) { + formatRecords.addStyle(xfr); + } + } + } catch (NumFormatRecordsException e) { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + + xfr = null; + } + + rowrec.setRowDetails(view.getSize(), + false, + view.isHidden(), + 0, + false, + xfr); + numRows = Math.max(numRows, row + 1); + } + + /** + * Writes out this sheet. This functionality is delegated off to the + * SheetWriter class in order to reduce the bloated nature of this source + * file + * + * @throws IOException + */ + public void write() throws IOException { + boolean dmod = drawingsModified; + if (workbook.getDrawingGroup() != null) { + dmod |= workbook.getDrawingGroup().hasDrawingsOmitted(); + } + + if (autosizedColumns.size() > 0) { + autosizeColumns(); + } + + sheetWriter.setWriteData(rows, + rowBreaks, + columnBreaks, + hyperlinks, + mergedCells, + columnFormats, + maxRowOutlineLevel, + maxColumnOutlineLevel); + sheetWriter.setDimensions(getRows(), getColumns()); + sheetWriter.setSettings(settings); + sheetWriter.setPLS(plsRecord); + sheetWriter.setDrawings(drawings, dmod); + sheetWriter.setButtonPropertySet(buttonPropertySet); + sheetWriter.setDataValidation(dataValidation, validatedCells); + sheetWriter.setConditionalFormats(conditionalFormats); + sheetWriter.setAutoFilter(autoFilter); + + sheetWriter.write(); + } + + /** + * Copies the specified sheet, row by row and cell by cell + * + * @param s the sheet to copy + */ + void copy(Sheet s) { + // Copy the settings + settings = new SheetSettings(s.getSettings(), this); + + SheetCopier si = new SheetCopier(s, this); + si.setColumnFormats(columnFormats); + si.setFormatRecords(formatRecords); + si.setHyperlinks(hyperlinks); + si.setMergedCells(mergedCells); + si.setRowBreaks(rowBreaks); + si.setColumnBreaks(columnBreaks); + si.setSheetWriter(sheetWriter); + si.setDrawings(drawings); + si.setImages(images); + si.setConditionalFormats(conditionalFormats); + si.setValidatedCells(validatedCells); + + si.copySheet(); + + dataValidation = si.getDataValidation(); + comboBox = si.getComboBox(); + plsRecord = si.getPLSRecord(); + chartOnly = si.isChartOnly(); + buttonPropertySet = si.getButtonPropertySet(); + numRows = si.getRows(); + autoFilter = si.getAutoFilter(); + maxRowOutlineLevel = si.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = si.getMaxColumnOutlineLevel(); + } + + /** + * Copies the specified sheet, row by row and cell by cell + * + * @param s the sheet to copy + */ + void copy(WritableSheet s) { + settings = new SheetSettings(s.getSettings(), this); + WritableSheetImpl si = (WritableSheetImpl) s; + + WritableSheetCopier sc = new WritableSheetCopier(s, this); + sc.setColumnFormats(si.columnFormats, columnFormats); + sc.setMergedCells(si.mergedCells, mergedCells); + sc.setRows(si.rows); + sc.setRowBreaks(si.rowBreaks, rowBreaks); + sc.setColumnBreaks(si.columnBreaks, columnBreaks); + sc.setDataValidation(si.dataValidation); + sc.setSheetWriter(sheetWriter); + sc.setDrawings(si.drawings, drawings, images); + sc.setWorkspaceOptions(si.getWorkspaceOptions()); + sc.setPLSRecord(si.plsRecord); + sc.setButtonPropertySetRecord(si.buttonPropertySet); + sc.setHyperlinks(si.hyperlinks, hyperlinks); + sc.setValidatedCells(validatedCells); + + sc.copySheet(); + + dataValidation = sc.getDataValidation(); + plsRecord = sc.getPLSRecord(); + buttonPropertySet = sc.getButtonPropertySet(); + } + + /** + * Gets the header. Called when copying sheets + * + * @return the page header + */ + final HeaderRecord getHeader() { + return sheetWriter.getHeader(); + } + + /** + * Gets the footer. Called when copying sheets + * + * @return the page footer + */ + final FooterRecord getFooter() { + return sheetWriter.getFooter(); + } + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + * @deprecated Use the SheetSettings bean instead + */ + public boolean isProtected() { + return settings.isProtected(); + } + + /** + * Indicates whether or not this sheet is protected + * + * @param prot protected flag + * @deprecated Use the settings bean instead + */ + public void setProtected(boolean prot) { + settings.setProtected(prot); + } + + /** + * Gets the hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + public Hyperlink[] getHyperlinks() { + Hyperlink[] hl = new Hyperlink[hyperlinks.size()]; + + for (int i = 0; i < hyperlinks.size(); i++) { + hl[i] = (Hyperlink) hyperlinks.get(i); + } + + return hl; + } + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + public Range[] getMergedCells() { + return mergedCells.getMergedCells(); + } + + /** + * Gets the writable hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + public WritableHyperlink[] getWritableHyperlinks() { + WritableHyperlink[] hl = new WritableHyperlink[hyperlinks.size()]; + + for (int i = 0; i < hyperlinks.size(); i++) { + hl[i] = (WritableHyperlink) hyperlinks.get(i); + } + + return hl; + } + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. The contents of the cell which + * activate the hyperlink are removed. + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + * @param preserveLabel if TRUE preserves the label contents, if FALSE + * removes them + */ + public void removeHyperlink(WritableHyperlink h) { + removeHyperlink(h, false); + } + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. + * If the preserveLabel field is set, the cell contents of the + * hyperlink are preserved, although the hyperlink is deactivated. If + * this value is FALSE, the cell contents are removed + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + * @param preserveLabel if TRUE preserves the label contents, if FALSE + * removes them + */ + public void removeHyperlink(WritableHyperlink h, boolean preserveLabel) { + // Remove the hyperlink + hyperlinks.remove(h); + + if (!preserveLabel) { + // Set the cell contents for the hyperlink - including any formatting + // information - to be empty + Assert.verify(rows.length > h.getRow() && rows[h.getRow()] != null); + rows[h.getRow()].removeCell(h.getColumn()); + } + } + + /** + * Adds the specified hyperlink + * + * @param the hyperlink + * @throws WriteException + * @throws RowsExceededException + */ + public void addHyperlink(WritableHyperlink h) + throws WriteException, RowsExceededException { + // First set the label on the sheet + Cell c = getCell(h.getColumn(), h.getRow()); + + String contents = null; + if (h.isFile() || h.isUNC()) { + String cnts = ((HyperlinkRecord) h).getContents(); + if (cnts == null) { + contents = h.getFile().getPath(); + } else { + contents = cnts; + } + } else if (h.isURL()) { + String cnts = ((HyperlinkRecord) h).getContents(); + if (cnts == null) { + contents = h.getURL().toString(); + } else { + contents = cnts; + } + } else if (h.isLocation()) { + contents = ((HyperlinkRecord) h).getContents(); + } + + // If the cell type is a label, then preserve the cell contents + // and most of the format (apart from the font) + // otherwise overwrite the cell content and the format with the contents + // and the standard hyperlink format + if (c.getType() == CellType.LABEL) { + Label l = (Label) c; + l.setString(contents); + WritableCellFormat wcf = new WritableCellFormat(l.getCellFormat()); + wcf.setFont(WritableWorkbook.HYPERLINK_FONT); + l.setCellFormat(wcf); + } else { + Label l = new Label(h.getColumn(), h.getRow(), contents, + WritableWorkbook.HYPERLINK_STYLE); + addCell(l); + } + + // Set all other cells within range to be empty + for (int i = h.getRow(); i <= h.getLastRow(); i++) { + for (int j = h.getColumn(); j <= h.getLastColumn(); j++) { + if (i != h.getRow() && j != h.getColumn()) { + // Set the cell to be empty + if (rows.length < h.getLastColumn() && rows[i] != null) { + rows[i].removeCell(j); + } + } + } + } + + ((HyperlinkRecord) h).initialize(this); + hyperlinks.add(h); + } + + /** + * Merges the specified cells. Any clashes or intersections between + * merged cells are resolved when the spreadsheet is written out + * + * @param col1 the column number of the top left cell + * @param row1 the row number of the top left cell + * @param col2 the column number of the bottom right cell + * @param row2 the row number of the bottom right cell + * @return the Range object representing the merged cells + * @throws jxl.write..WriteException + * @throws RowsExceededException + */ + public Range mergeCells(int col1, int row1, int col2, int row2) + throws WriteException, RowsExceededException { + // First check that the cells make sense + if (col2 < col1 || row2 < row1) { + logger.warn("Cannot merge cells - top left and bottom right " + + "incorrectly specified"); + } + + // Make sure the spreadsheet is up to size + if (col2 >= numColumns || row2 >= numRows) { + addCell(new Blank(col2, row2)); + } + + SheetRangeImpl range = new SheetRangeImpl(this, col1, row1, col2, row2); + mergedCells.add(range); + + return range; + } + + /** + * Sets a row grouping + * + * @param row1 the first row of the group + * @param row2 the last row of the group + * @param collapsed should the group be collapsed? + * @throws WriteException + * @throws RowsExceededException + */ + public void setRowGroup(int row1, int row2, + boolean collapsed) + throws WriteException, RowsExceededException { + if (row2 < row1) { + logger.warn("Cannot merge cells - top and bottom rows incorrectly " + + "specified"); + } + + for (int i = row1; i <= row2; i++) { + RowRecord row = getRowRecord(i); + numRows = Math.max(i + 1, numRows); + row.incrementOutlineLevel(); + row.setCollapsed(collapsed); + maxRowOutlineLevel = Math.max(maxRowOutlineLevel, + row.getOutlineLevel()); + } + } + + /** + * Unsets a row grouping + * + * @param row1 the first row to unset + * @param row2 the last row to unset + * @throws WriteException + * @throws RowsExceededException + */ + public void unsetRowGroup(int row1, int row2) + throws WriteException, RowsExceededException { + if (row2 < row1) { + logger.warn("Cannot merge cells - top and bottom rows incorrectly " + + "specified"); + } + + // Make sure the spreadsheet is up to size + if (row2 >= numRows) { + logger.warn("" + row2 + + " is greater than the sheet bounds"); + row2 = numRows - 1; + } + + for (int i = row1; i <= row2; i++) { + rows[i].decrementOutlineLevel(); + } + + // Recalculate the max outline level + maxRowOutlineLevel = 0; + for (int i = rows.length; i-- > 0; ) { + maxRowOutlineLevel = Math.max(maxRowOutlineLevel, + rows[i].getOutlineLevel()); + } + } + + /** + * Sets a column grouping + * + * @param col1 the first column of the group + * @param col2 the last column of the group + * @param collapsed should the group be collapsed? + * @throws WriteException + * @throws RowsExceededException + */ + public void setColumnGroup(int col1, int col2, boolean collapsed) + throws WriteException, RowsExceededException { + if (col2 < col1) { + logger.warn("Cannot merge cells - top and bottom rows incorrectly " + + "specified"); + } + + for (int i = col1; i <= col2; i++) { + ColumnInfoRecord cir = getColumnInfo(i); + + // Create the column info record if not present using a default + // cell view + if (cir == null) { + setColumnView(i, new CellView()); + cir = getColumnInfo(i); + } + + cir.incrementOutlineLevel(); + cir.setCollapsed(collapsed); + maxColumnOutlineLevel = Math.max(maxColumnOutlineLevel, + cir.getOutlineLevel()); + } + } + + /** + * Unsets a column grouping + * + * @param col1 the first column to unset + * @param col2 the last column to unset + * @throws WriteException + * @throws RowsExceededException + */ + public void unsetColumnGroup(int col1, int col2) + throws WriteException, RowsExceededException { + if (col2 < col1) { + logger.warn("Cannot merge cells - top and bottom rows incorrectly " + + "specified"); + } + + for (int i = col1; i <= col2; i++) { + ColumnInfoRecord cir = getColumnInfo(i); + cir.decrementOutlineLevel(); + } + + // Recalculate the max outline level + maxColumnOutlineLevel = 0; + for (Iterator it = columnFormats.iterator(); it.hasNext(); ) { + ColumnInfoRecord cir = (ColumnInfoRecord) it.next(); + maxColumnOutlineLevel = Math.max(maxColumnOutlineLevel, + cir.getOutlineLevel()); + } + } + + /** + * Unmerges the specified cells. The Range passed in should be one that + * has been previously returned as a result of the getMergedCells method + * + * @param r the range of cells to unmerge + */ + public void unmergeCells(Range r) { + mergedCells.unmergeCells(r); + } + + /** + * Sets the header for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated Use the sheet settings bean + */ + public void setHeader(String l, String c, String r) { + HeaderFooter header = new HeaderFooter(); + header.getLeft().append(l); + header.getCentre().append(c); + header.getRight().append(r); + settings.setHeader(header); + } + + /** + * Sets the footer for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated Use the sheet settings bean + */ + public void setFooter(String l, String c, String r) { + HeaderFooter footer = new HeaderFooter(); + footer.getLeft().append(l); + footer.getCentre().append(c); + footer.getRight().append(r); + settings.setFooter(footer); + } + + /** + * Sets the page setup details + * + * @param p the page orientation + * @deprecated Use the SheetSettings bean + */ + public void setPageSetup(PageOrientation p) { + settings.setOrientation(p); + } + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + * @deprecated Use the SheetSettings bean + */ + public void setPageSetup(PageOrientation p, double hm, double fm) { + settings.setOrientation(p); + settings.setHeaderMargin(hm); + settings.setFooterMargin(fm); + } + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param ps the paper size + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + * @deprecated Use the SheetSettings bean + */ + public void setPageSetup(PageOrientation p, PaperSize ps, + double hm, double fm) { + settings.setPaperSize(ps); + settings.setOrientation(p); + settings.setHeaderMargin(hm); + settings.setFooterMargin(fm); + } + + /** + * Gets the settings for this sheet + * + * @return the page settings bean + */ + public SheetSettings getSettings() { + return settings; + } + + /** + * Gets the workbook settings + */ + WorkbookSettings getWorkbookSettings() { + return workbookSettings; + } + + /** + * Forces a page break at the specified row + * + * @param row the row to break at + */ + public void addRowPageBreak(int row) { + // First check that the row is not already present + Iterator i = rowBreaks.iterator(); + boolean found = false; + + while (i.hasNext() && !found) { + if (((Integer) i.next()).intValue() == row) { + found = true; + } + } + + if (!found) { + rowBreaks.add(new Integer(row)); + } + } + + /** + * Forces a page break at the specified column + * + * @param col the column to break at + */ + public void addColumnPageBreak(int col) { + // First check that the row is not already present + Iterator i = columnBreaks.iterator(); + boolean found = false; + + while (i.hasNext() && !found) { + if (((Integer) i.next()).intValue() == col) { + found = true; + } + } + + if (!found) { + columnBreaks.add(new Integer(col)); + } + } + + /** + * Accessor for the charts. Used when copying + * + * @return the charts on this sheet + */ + Chart[] getCharts() { + return sheetWriter.getCharts(); + } + + /** + * Accessor for the drawings. Used when copying + * + * @return the drawings on this sheet + */ + private DrawingGroupObject[] getDrawings() { + DrawingGroupObject[] dr = new DrawingGroupObject[drawings.size()]; + dr = (DrawingGroupObject[]) drawings.toArray(dr); + return dr; + } + + /** + * Check all the merged cells for borders. Although in an OO sense the + * logic should belong in this class, in order to reduce the bloated + * nature of the source code for this object this logic has been delegated + * to the SheetWriter + */ + void checkMergedBorders() { + sheetWriter.setWriteData(rows, + rowBreaks, + columnBreaks, + hyperlinks, + mergedCells, + columnFormats, + maxRowOutlineLevel, + maxColumnOutlineLevel); + sheetWriter.setDimensions(getRows(), getColumns()); + sheetWriter.checkMergedBorders(); + } + + /** + * Accessor for the workspace options + * + * @return the workspace options + */ + private WorkspaceInformationRecord getWorkspaceOptions() { + return sheetWriter.getWorkspaceOptions(); + } + + /** + * Rationalizes the sheets xf index mapping + * + * @param xfMapping the index mapping for XFRecords + * @param fontMapping the index mapping for fonts + * @param formatMapping the index mapping for formats + */ + void rationalize(IndexMapping xfMapping, + IndexMapping fontMapping, + IndexMapping formatMapping) { + // Rationalize the column formats + for (Iterator i = columnFormats.iterator(); i.hasNext(); ) { + ColumnInfoRecord cir = (ColumnInfoRecord) i.next(); + cir.rationalize(xfMapping); + } + + // Rationalize the row formats + for (int i = 0; i < rows.length; i++) { + if (rows[i] != null) { + rows[i].rationalize(xfMapping); + } + } + + // Rationalize any data that appears on the charts + Chart[] charts = getCharts(); + for (int c = 0; c < charts.length; c++) { + charts[c].rationalize(xfMapping, fontMapping, formatMapping); + } + } + + /** + * Accessor for the workbook + * + * @return the workbook + */ + WritableWorkbookImpl getWorkbook() { + return workbook; + } + + /** + * Gets the column format for the specified column + * + * @param col the column number + * @return the column format, or NULL if the column has no specific format + * @deprecated Use getColumnView instead + */ + public CellFormat getColumnFormat(int col) { + return getColumnView(col).getFormat(); + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column width, or the default width if the column has no + * specified format + * @deprecated Use getColumnView instead + */ + public int getColumnWidth(int col) { + return getColumnView(col).getDimension(); + } + + /** + * Gets the column width for the specified column + * + * @param row the column number + * @return the row height, or the default height if the column has no + * specified format + * @deprecated Use getRowView instead + */ + public int getRowHeight(int row) { + return getRowView(row).getDimension(); + } + + /** + * Accessor for the chart only method + * + * @return TRUE if this is a chart only, FALSE otherwise + */ + boolean isChartOnly() { + return chartOnly; + } + + /** + * Gets the row view for the specified row + * + * @param col the row number + * @return the row format, or the default format if no override is + * specified + */ + public CellView getRowView(int row) { + CellView cv = new CellView(); + + try { + RowRecord rr = getRowRecord(row); + + if (rr == null || rr.isDefaultHeight()) { + cv.setDimension(settings.getDefaultRowHeight()); + cv.setSize(settings.getDefaultRowHeight()); + } else if (rr.isCollapsed()) { + cv.setHidden(true); + } else { + cv.setDimension(rr.getRowHeight()); + cv.setSize(rr.getRowHeight()); + } + return cv; + } catch (RowsExceededException e) { + // Simple return the default + cv.setDimension(settings.getDefaultRowHeight()); + cv.setSize(settings.getDefaultRowHeight()); + return cv; + } + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column format, or the default format if no override is + * specified + */ + public CellView getColumnView(int col) { + ColumnInfoRecord cir = getColumnInfo(col); + CellView cv = new CellView(); + + if (cir != null) { + cv.setDimension(cir.getWidth() / 256); + cv.setSize(cir.getWidth()); + cv.setHidden(cir.getHidden()); + cv.setFormat(cir.getCellFormat()); + } else { + cv.setDimension(settings.getDefaultColumnWidth() / 256); + cv.setSize(settings.getDefaultColumnWidth() * 256); + } + + return cv; + } + + /** + * Adds an image to this sheet + * + * @param image the image to add + */ + public void addImage(WritableImage image) { + boolean supported = false; + java.io.File imageFile = image.getImageFile(); + String fileType = "?"; + + if (imageFile != null) { + String fileName = imageFile.getName(); + int fileTypeIndex = fileName.lastIndexOf('.'); + fileType = fileTypeIndex != -1 ? + fileName.substring(fileTypeIndex + 1) : ""; + + for (int i = 0; i < imageTypes.length && !supported; i++) { + if (fileType.equalsIgnoreCase(imageTypes[i])) { + supported = true; + } + } + } else { + supported = true; + } + + if (supported) { + workbook.addDrawing(image); + drawings.add(image); + images.add(image); + } else { + StringBuffer message = new StringBuffer("Image type "); + message.append(fileType); + message.append(" not supported. Supported types are "); + message.append(imageTypes[0]); + for (int i = 1; i < imageTypes.length; i++) { + message.append(", "); + message.append(imageTypes[i]); + } + logger.warn(message.toString()); + } + } + + /** + * Gets the number of images on this sheet + * + * @return the number of images on this sheet + */ + public int getNumberOfImages() { + return images.size(); + } + + /** + * Accessor for a particular image on this sheet + * + * @param i the 0-based image index number + * @return the image with the specified index number + */ + public WritableImage getImage(int i) { + return (WritableImage) images.get(i); + } + + /** + * Accessor for a particular image on this sheet + * + * @param i the 0-based image index number + * @return the image with the specified index number + */ + public Image getDrawing(int i) { + return (Image) images.get(i); + } + + /** + * Removes the specified image from this sheet. The image passed in + * must be the same instance as that retrieved from a getImage call + * + * @param wi the image to remove + */ + public void removeImage(WritableImage wi) { + drawings.remove(wi); + images.remove(wi); + drawingsModified = true; + workbook.removeDrawing(wi); + } + + /** + * Validates the sheet name + */ + private String validateName(String n) { + if (n.length() > maxSheetNameLength) { + logger.warn("Sheet name " + n + " too long - truncating"); + n = n.substring(0, maxSheetNameLength); + } + + if (n.charAt(0) == '\'') { + logger.warn("Sheet naming cannot start with ' - removing"); + n = n.substring(1); + } + + for (int i = 0; i < illegalSheetNameCharacters.length; i++) { + String newname = n.replace(illegalSheetNameCharacters[i], '@'); + if (n != newname) { + logger.warn(illegalSheetNameCharacters[i] + + " is not a valid character within a sheet name - replacing"); + } + n = newname; + } + + return n; + } + + /** + * Adds a drawing to the list - typically used for comments + * + * @param the drawing to add + */ + void addDrawing(DrawingGroupObject o) { + drawings.add(o); + Assert.verify(!(o instanceof Drawing)); + } + + /** + * Removes a drawing to the list - typically used for comments + * + * @param the drawing to add + */ + void removeDrawing(DrawingGroupObject o) { + int origSize = drawings.size(); + drawings.remove(o); + int newSize = drawings.size(); + drawingsModified = true; + Assert.verify(newSize == origSize - 1); + } + + /** + * Removes the data validation for the specified cell. Called from + * CellValue in response to a cell being replaced + * + * @param cv the cell being removed + */ + void removeDataValidation(CellValue cv) { + if (dataValidation != null) { + dataValidation.removeDataValidation(cv.getColumn(), cv.getRow()); + } + + if (validatedCells != null) { + boolean result = validatedCells.remove(cv); + + if (!result) { + logger.warn("Could not remove validated cell " + + CellReferenceHelper.getCellReference(cv)); + } + } + } + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + public int[] getRowPageBreaks() { + int[] rb = new int[rowBreaks.size()]; + int pos = 0; + for (Iterator i = rowBreaks.iterator(); i.hasNext(); pos++) { + rb[pos] = ((Integer) i.next()).intValue(); + } + return rb; + } + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + public int[] getColumnPageBreaks() { + int[] rb = new int[columnBreaks.size()]; + int pos = 0; + for (Iterator i = columnBreaks.iterator(); i.hasNext(); pos++) { + rb[pos] = ((Integer) i.next()).intValue(); + } + return rb; + } + + /** + * Flags the added cell as having data validation + * + * @param cell the cell with data validation + */ + void addValidationCell(CellValue cv) { + validatedCells.add(cv); + } + + /** + * Accessor for the combo box object used for list data validations on this + * sheet + * + * @return the combo box + */ + ComboBox getComboBox() { + return comboBox; + } + + /** + * Sets the combo box object used for list validations on this sheet + * + * @param cb the combo box + */ + void setComboBox(ComboBox cb) { + comboBox = cb; + } + + /** + * Gets the data validation. Retrieved by CellValue when copying sheets + */ + public DataValidation getDataValidation() { + return dataValidation; + } + + /** + * Performs the column autosizing + */ + private void autosizeColumns() { + Iterator i = autosizedColumns.iterator(); + while (i.hasNext()) { + Integer col = (Integer) i.next(); + autosizeColumn(col.intValue()); + } + } + + /** + * Autosizes the specified column + * + * @param col the column to autosize + */ + private void autosizeColumn(int col) { + int maxWidth = 0; + ColumnInfoRecord cir = getColumnInfo(col); + Font columnFont = cir.getCellFormat().getFont(); + Font defaultFont = WritableWorkbook.NORMAL_STYLE.getFont(); + + for (int i = 0; i < numRows; i++) { + Cell cell = null; + if (rows[i] != null) { + cell = rows[i].getCell(col); + } + + if (cell != null) { + String contents = cell.getContents(); + Font font = cell.getCellFormat().getFont(); + + Font activeFont = font.equals(defaultFont) ? columnFont : font; + + int pointSize = activeFont.getPointSize(); + int numChars = contents.length(); + + if (activeFont.isItalic() || + activeFont.getBoldWeight() > 400) // magic value for normal bold + { + numChars += 2; + } + + int points = numChars * pointSize; + maxWidth = Math.max(maxWidth, points * 256); + } + } + cir.setWidth(maxWidth / defaultFont.getPointSize()); + } + + /** + * Imports a sheet from a different workbook + * + * @param s the sheet to import + */ + void importSheet(Sheet s) { + // Copy the settings + settings = new SheetSettings(s.getSettings(), this); + + SheetCopier si = new SheetCopier(s, this); + si.setColumnFormats(columnFormats); + si.setFormatRecords(formatRecords); + si.setHyperlinks(hyperlinks); + si.setMergedCells(mergedCells); + si.setRowBreaks(rowBreaks); + si.setColumnBreaks(columnBreaks); + si.setSheetWriter(sheetWriter); + si.setDrawings(drawings); + si.setImages(images); + si.setValidatedCells(validatedCells); + + si.importSheet(); + + dataValidation = si.getDataValidation(); + comboBox = si.getComboBox(); + plsRecord = si.getPLSRecord(); + chartOnly = si.isChartOnly(); + buttonPropertySet = si.getButtonPropertySet(); + numRows = si.getRows(); + maxRowOutlineLevel = si.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = si.getMaxColumnOutlineLevel(); + } + + /** + * Extend the data validation contained in the specified cell across and + * downwards + * + * @param c the number of cells accross to apply this data validation + * @param r the number of cells downwards to apply this data validation + */ + public void applySharedDataValidation(WritableCell c, + int extraCols, + int extraRows) + throws WriteException { + // Check that the cell being applied has a data validation + if (c.getWritableCellFeatures() == null || + !c.getWritableCellFeatures().hasDataValidation()) { + logger.warn("Cannot extend data validation for " + + CellReferenceHelper.getCellReference(c.getColumn(), + c.getRow()) + + " as it has no data validation"); + return; + } + + // Check that none of the other cells in the range have any + // cell validations + int startColumn = c.getColumn(); + int startRow = c.getRow(); + int endRow = Math.min(numRows - 1, startRow + extraRows); + for (int y = startRow; y <= endRow; y++) { + if (rows[y] != null) { + int endCol = Math.min(rows[y].getMaxColumn() - 1, + startColumn + extraCols); + for (int x = startColumn; x <= endCol; x++) { + // Ignore the first cell + if (x == startColumn && y == startRow) { + continue; // continue statements - they're no better than gotos + } + + WritableCell c2 = rows[y].getCell(x); + + // Check that the target cell does not have any data validation + if (c2 != null && + c2.getWritableCellFeatures() != null && + c2.getWritableCellFeatures().hasDataValidation()) { + logger.warn("Cannot apply data validation from " + + CellReferenceHelper.getCellReference(startColumn, + startRow) + + " to " + + CellReferenceHelper.getCellReference + (startColumn + extraCols, + startRow + extraRows) + + " as cell " + + CellReferenceHelper.getCellReference(x, y) + + " already has a data validation"); + return; + } + } + } + } + + // Extend the range on the source data validation + WritableCellFeatures sourceDataValidation = c.getWritableCellFeatures(); + sourceDataValidation.getDVParser().extendCellValidation(extraCols, + extraRows); + + // Go through all the additional cells and add the data validation cell + for (int y = startRow; y <= startRow + extraRows; y++) { + RowRecord rowrec = getRowRecord(y); // create the row if it doesn't exist + for (int x = startColumn; x <= startColumn + extraCols; x++) { + // Ignore the first cell + if (x == startColumn && y == startRow) { + continue; // continue statements - they're no better than gotos + } + + WritableCell c2 = rowrec.getCell(x); + + // Check that the target cell does not have any data validation + if (c2 == null) { + Blank b = new Blank(x, y); + WritableCellFeatures validation = new WritableCellFeatures(); + validation.shareDataValidation(sourceDataValidation); + b.setCellFeatures(validation); + addCell(b); + } else { + // add the shared data validation to the existing cell + WritableCellFeatures validation = c2.getWritableCellFeatures(); + + if (validation != null) { + validation.shareDataValidation(sourceDataValidation); + } else { + validation = new WritableCellFeatures(); + validation.shareDataValidation(sourceDataValidation); + c2.setCellFeatures(validation); + } + } + } + } + } + + /** + * Remove the shared data validation from multiple cells. The cell passed + * in is the top left cell. The data validation is removed from this + * cell and all cells which share the same validation. + * + * @param cell the top left cell containing the shared data validation + */ + public void removeSharedDataValidation(WritableCell cell) + throws WriteException { + WritableCellFeatures wcf = cell.getWritableCellFeatures(); + if (wcf == null || + !wcf.hasDataValidation()) { + return; + } + + DVParser dvp = wcf.getDVParser(); + + // If the cell is not part of an extended validation, then simply call + // the atomic remove validation from the cell features + if (!dvp.extendedCellsValidation()) { + wcf.removeDataValidation(); + return; + } + + // Check that the cell validation being removed is in the top left of the + // validated area + if (dvp.extendedCellsValidation()) { + if (cell.getColumn() != dvp.getFirstColumn() || + cell.getRow() != dvp.getFirstRow()) { + logger.warn("Cannot remove data validation from " + + CellReferenceHelper.getCellReference(dvp.getFirstColumn(), + dvp.getFirstRow()) + + "-" + + CellReferenceHelper.getCellReference(dvp.getLastColumn(), + dvp.getLastRow()) + + " because the selected cell " + + CellReferenceHelper.getCellReference(cell) + + " is not the top left cell in the range"); + return; + } + } + + for (int y = dvp.getFirstRow(); y <= dvp.getLastRow(); y++) { + for (int x = dvp.getFirstColumn(); x <= dvp.getLastColumn(); x++) { + CellValue c2 = rows[y].getCell(x); + + // It's possible that some cells in the shared data range might + // be null eg. in the event of an insertRow or insertColumn + if (c2 != null) { + c2.getWritableCellFeatures().removeSharedDataValidation(); + c2.removeCellFeatures(); + } + } + } + + // Remove this shared validation from any data validations that were + // copied in + if (dataValidation != null) { + dataValidation.removeSharedDataValidation(dvp.getFirstColumn(), + dvp.getFirstRow(), + dvp.getLastColumn(), + dvp.getLastRow()); + } + } + + /** + * The comparator for column info record + */ + private static class ColumnInfoComparator implements Comparator { + /** + * Equals method + * + * @param o the object to compare + * @return TRUE if equal, FALSE otherwise + */ + public boolean equals(Object o) { + return o == this; + } + + /** + * Comparison function for to ColumnInfoRecords + * + * @param o2 first object to compare + * @param o1 second object to compare + * @return the result of the comparison + */ + public int compare(Object o1, Object o2) { + if (o1 == o2) { + return 0; + } + + Assert.verify(o1 instanceof ColumnInfoRecord); + Assert.verify(o2 instanceof ColumnInfoRecord); + + ColumnInfoRecord ci1 = (ColumnInfoRecord) o1; + ColumnInfoRecord ci2 = (ColumnInfoRecord) o2; + + return ci1.getColumn() - ci2.getColumn(); + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableWorkbookImpl.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableWorkbookImpl.java new file mode 100755 index 0000000..63d789c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableWorkbookImpl.java @@ -0,0 +1,1703 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.Range; +import jxl.Sheet; +import jxl.Workbook; +import jxl.WorkbookSettings; +import jxl.biff.BuiltInName; +import jxl.biff.CellReferenceHelper; +import jxl.biff.CountryCode; +import jxl.biff.Fonts; +import jxl.biff.FormattingRecords; +import jxl.biff.IndexMapping; +import jxl.biff.IntegerHelper; +import jxl.biff.RangeImpl; +import jxl.biff.WorkbookMethods; +import jxl.biff.XCTRecord; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroup; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.biff.drawing.Origin; +import jxl.biff.formula.ExternalSheet; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.Colour; +import jxl.format.RGB; +import jxl.read.biff.WorkbookParser; +import jxl.write.WritableCell; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; + + +/** + * A writable workbook + */ +public class WritableWorkbookImpl extends WritableWorkbook + implements ExternalSheet, WorkbookMethods { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(WritableWorkbookImpl.class); + // synchronizer object for static unitiatialization + private static final Object SYNCHRONIZER = new Object(); + /** + * The list of formats available within this workbook + */ + private final FormattingRecords formatRecords; + /** + * The output file to write the workbook to + */ + private final File outputFile; + /** + * The list of sheets within this workbook + */ + private final ArrayList sheets; + /** + * The list of fonts available within this workbook + */ + private final Fonts fonts; + /** + * The list of external sheets, used by cell references in formulas + */ + private ExternalSheetRecord externSheet; + /** + * The supbook records + */ + private ArrayList supbooks; + /** + * The name records + */ + private ArrayList names; + /** + * A lookup hash map of the name records + */ + private final HashMap nameRecords; + /** + * The shared strings used by this workbook + */ + private final SharedStrings sharedStrings; + /** + * Indicates whether or not the output stream should be closed. This + * depends on whether this Workbook was created with an output stream, + * or a flat file (flat file closes the stream + */ + private final boolean closeStream; + /** + * The workbook protection flag + */ + private boolean wbProtected; + /** + * The settings for the workbook + */ + private final WorkbookSettings settings; + /** + * The list of cells for the entire workbook which need to be updated + * following a row/column insert or remove + */ + private final ArrayList rcirCells; + /** + * The drawing group + */ + private DrawingGroup drawingGroup; + /** + * The jxl.common.workbook styles + */ + private final Styles styles; + /** + * Contains macros flag + */ + private boolean containsMacros; + /** + * The buttons property set + */ + private ButtonPropertySetRecord buttonPropertySet; + /** + * The country record, initialised when copying a workbook + */ + private CountryRecord countryRecord; + /** + * The names of any add in functions + */ + private String[] addInFunctionNames; + + /** + * The XCT records + */ + private XCTRecord[] xctRecords; + + /** + * Constructor. Writes the workbook direct to the existing output stream + * + * @param os the output stream + * @param cs TRUE if the workbook should close the output stream, FALSE + * @param ws the configuration for this workbook + * otherwise + * @throws IOException + */ + public WritableWorkbookImpl(OutputStream os, boolean cs, WorkbookSettings ws) + throws IOException { + super(); + outputFile = new File(os, ws, null); + sheets = new ArrayList(); + sharedStrings = new SharedStrings(); + nameRecords = new HashMap(); + closeStream = cs; + wbProtected = false; + containsMacros = false; + settings = ws; + rcirCells = new ArrayList(); + styles = new Styles(); + + // Reset the statically declared styles. These are no longer needed + // because the Styles class will intercept all calls within + // CellValue.setCellDetails and if it detects a standard format, then it + // will return a clone. In short, the static cell values will + // never get initialized anyway. Still, just to be extra sure... + synchronized (SYNCHRONIZER) { + WritableWorkbook.ARIAL_10_PT.uninitialize(); + WritableWorkbook.HYPERLINK_FONT.uninitialize(); + WritableWorkbook.NORMAL_STYLE.uninitialize(); + WritableWorkbook.HYPERLINK_STYLE.uninitialize(); + WritableWorkbook.HIDDEN_STYLE.uninitialize(); + DateRecord.defaultDateFormat.uninitialize(); + } + + WritableFonts wf = new WritableFonts(this); + fonts = wf; + + WritableFormattingRecords wfr = new WritableFormattingRecords(fonts, + styles); + formatRecords = wfr; + } + + /** + * A pseudo copy constructor. Takes the handles to the font and formatting + * records + * + * @param w the workbook to copy + * @param os the output stream to write the data to + * @param cs TRUE if the workbook should close the output stream, FALSE + * @param ws the configuration for this workbook + * @throws IOException + */ + public WritableWorkbookImpl(OutputStream os, + Workbook w, + boolean cs, + WorkbookSettings ws) throws IOException { + super(); + WorkbookParser wp = (WorkbookParser) w; + + // Reset the statically declared styles. These are no longer needed + // because the Styles class will intercept all calls within + // CellValue.setCellDetails and if it detects a standard format, then it + // will return a clone. In short, the static cell values will + // never get initialized anyway. Still, just to be extra sure... + synchronized (SYNCHRONIZER) { + WritableWorkbook.ARIAL_10_PT.uninitialize(); + WritableWorkbook.HYPERLINK_FONT.uninitialize(); + WritableWorkbook.NORMAL_STYLE.uninitialize(); + WritableWorkbook.HYPERLINK_STYLE.uninitialize(); + WritableWorkbook.HIDDEN_STYLE.uninitialize(); + DateRecord.defaultDateFormat.uninitialize(); + } + + closeStream = cs; + sheets = new ArrayList(); + sharedStrings = new SharedStrings(); + nameRecords = new HashMap(); + fonts = wp.getFonts(); + formatRecords = wp.getFormattingRecords(); + wbProtected = false; + settings = ws; + rcirCells = new ArrayList(); + styles = new Styles(); + outputFile = new File(os, ws, wp.getCompoundFile()); + + containsMacros = false; + if (!ws.getPropertySetsDisabled()) { + containsMacros = wp.containsMacros(); + } + + // Copy the country settings + if (wp.getCountryRecord() != null) { + countryRecord = new CountryRecord(wp.getCountryRecord()); + } + + // Copy any add in functions + addInFunctionNames = wp.getAddInFunctionNames(); + + // Copy XCT records + xctRecords = wp.getXCTRecords(); + + // Copy any external sheets + if (wp.getExternalSheetRecord() != null) { + externSheet = new ExternalSheetRecord(wp.getExternalSheetRecord()); + + // Get the associated supbooks + jxl.read.biff.SupbookRecord[] readsr = wp.getSupbookRecords(); + supbooks = new ArrayList(readsr.length); + + for (int i = 0; i < readsr.length; i++) { + jxl.read.biff.SupbookRecord readSupbook = readsr[i]; + if (readSupbook.getType() == jxl.read.biff.SupbookRecord.INTERNAL || + readSupbook.getType() == jxl.read.biff.SupbookRecord.EXTERNAL) { + supbooks.add(new SupbookRecord(readSupbook, settings)); + } else { + if (readSupbook.getType() != jxl.read.biff.SupbookRecord.ADDIN) { + logger.warn("unsupported supbook type - ignoring"); + } + } + } + } + + // Copy any drawings. These must be present before we try and copy + // the images from the read workbook + if (wp.getDrawingGroup() != null) { + drawingGroup = new DrawingGroup(wp.getDrawingGroup()); + } + + // Copy the property set references + if (containsMacros && wp.getButtonPropertySet() != null) { + buttonPropertySet = new ButtonPropertySetRecord + (wp.getButtonPropertySet()); + } + + // Copy any names + if (!settings.getNamesDisabled()) { + jxl.read.biff.NameRecord[] na = wp.getNameRecords(); + names = new ArrayList(na.length); + + for (int i = 0; i < na.length; i++) { + if (na[i].isBiff8()) { + NameRecord n = new NameRecord(na[i], i); + names.add(n); + String name = n.getName(); + nameRecords.put(name, n); + } else { + logger.warn("Cannot copy Biff7 name records - ignoring"); + } + } + } + + copyWorkbook(w); + + // The copy process may have caused some critical fields in the + // read drawing group to change. Make sure these updates are reflected + // in the writable drawing group + if (drawingGroup != null) { + drawingGroup.updateData(wp.getDrawingGroup()); + } + } + + /** + * Gets the sheets within this workbook. Use of this method for + * large worksheets can cause performance problems. + * + * @return an array of the individual sheets + */ + public WritableSheet[] getSheets() { + WritableSheet[] sheetArray = new WritableSheet[getNumberOfSheets()]; + + for (int i = 0; i < getNumberOfSheets(); i++) { + sheetArray[i] = getSheet(i); + } + return sheetArray; + } + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public String[] getSheetNames() { + String[] sheetNames = new String[getNumberOfSheets()]; + + for (int i = 0; i < sheetNames.length; i++) { + sheetNames[i] = getSheet(i).getName(); + } + + return sheetNames; + } + + /** + * Interface method from WorkbookMethods - gets the specified + * sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + public Sheet getReadSheet(int index) { + return getSheet(index); + } + + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the reQuired sheet + * @return The sheet specified by the index + */ + public WritableSheet getSheet(int index) { + return (WritableSheet) sheets.get(index); + } + + /** + * Gets the sheet with the specified name from within this workbook + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public WritableSheet getSheet(String name) { + // Iterate through the boundsheet records + boolean found = false; + Iterator i = sheets.iterator(); + WritableSheet s = null; + + while (i.hasNext() && !found) { + s = (WritableSheet) i.next(); + + if (s.getName().equals(name)) { + found = true; + } + } + + return found ? s : null; + } + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public int getNumberOfSheets() { + return sheets.size(); + } + + /** + * Closes this workbook, and frees makes any memory allocated available + * for garbage collection + * + * @throws IOException + * @throws JxlWriteException + */ + public void close() throws IOException, JxlWriteException { + outputFile.close(closeStream); + } + + /** + * Sets a new output file. This allows the smae workbook to be + * written to various different output files without having to + * read in any templates again + * + * @param fileName the file name + * @throws IOException + */ + public void setOutputFile(java.io.File fileName) throws IOException { + FileOutputStream fos = new FileOutputStream(fileName); + outputFile.setOutputFile(fos); + } + + + /** + * The internal method implementation for creating new sheets + * + * @param name + * @param index + * @param handleRefs flag indicating whether or not to handle external + * sheet references + * @return + */ + private WritableSheet createSheet(String name, int index, + boolean handleRefs) { + WritableSheet w = new WritableSheetImpl(name, + outputFile, + formatRecords, + sharedStrings, + settings, + this); + + int pos = index; + + if (index <= 0) { + pos = 0; + sheets.add(0, w); + } else if (index > sheets.size()) { + pos = sheets.size(); + sheets.add(w); + } else { + sheets.add(index, w); + } + + if (handleRefs && externSheet != null) { + externSheet.sheetInserted(pos); + } + + if (supbooks != null && supbooks.size() > 0) { + SupbookRecord supbook = (SupbookRecord) supbooks.get(0); + if (supbook.getType() == SupbookRecord.INTERNAL) { + supbook.adjustInternal(sheets.size()); + } + } + + return w; + } + + /** + * Creates a new sheet within the workbook, at the specified position. + * The new sheet is inserted at the specified position, or prepended/appended + * to the list of sheets if the index specified is somehow inappropriate + * + * @param name the name of the new sheet + * @param index the index at which to add the sheet + * @return the created sheet + */ + public WritableSheet createSheet(String name, int index) { + return createSheet(name, index, true); + } + + /** + * Removes a sheet from this workbook, the other sheets indices being + * altered accordingly. If the sheet referenced by the index + * does not exist, then no action is taken. + * + * @param index the index of the sheet to remove + */ + public void removeSheet(int index) { + int pos = index; + if (index <= 0) { + pos = 0; + sheets.remove(0); + } else if (index >= sheets.size()) { + pos = sheets.size() - 1; + sheets.remove(sheets.size() - 1); + } else { + sheets.remove(index); + } + + if (externSheet != null) { + externSheet.sheetRemoved(pos); + } + + if (supbooks != null && supbooks.size() > 0) { + SupbookRecord supbook = (SupbookRecord) supbooks.get(0); + if (supbook.getType() == SupbookRecord.INTERNAL) { + supbook.adjustInternal(sheets.size()); + } + } + + if (names != null && names.size() > 0) { + for (int i = 0; i < names.size(); i++) { + NameRecord n = (NameRecord) names.get(i); + int oldRef = n.getSheetRef(); + if (oldRef == (pos + 1)) { + n.setSheetRef(0); // make a global name reference + } else if (oldRef > (pos + 1)) { + if (oldRef < 1) { + oldRef = 1; + } + n.setSheetRef(oldRef - 1); // move one sheet + } + } + } + } + + /** + * Moves the specified sheet within this workbook to another index + * position. + * + * @param fromIndex the zero based index of the reQuired sheet + * @param toIndex the zero based index of the reQuired sheet + * @return the sheet that has been moved + */ + public WritableSheet moveSheet(int fromIndex, int toIndex) { + // Handle dodgy index + fromIndex = Math.max(fromIndex, 0); + fromIndex = Math.min(fromIndex, sheets.size() - 1); + toIndex = Math.max(toIndex, 0); + toIndex = Math.min(toIndex, sheets.size() - 1); + + WritableSheet sheet = (WritableSheet) sheets.remove(fromIndex); + sheets.add(toIndex, sheet); + + return sheet; + } + + /** + * Writes out this sheet to the output file. First it writes out + * the standard workbook information required by excel, before calling + * the write method on each sheet individually + * + * @throws IOException + */ + public void write() throws IOException { + // Perform some preliminary sheet check before we start writing out + // the workbook + WritableSheetImpl wsi = null; + for (int i = 0; i < getNumberOfSheets(); i++) { + wsi = (WritableSheetImpl) getSheet(i); + + // Check the merged records. This has to be done before the + // globals are written out because some more XF formats might be created + wsi.checkMergedBorders(); + + // Check to see if there are any predefined names + Range range = wsi.getSettings().getPrintArea(); + if (range != null) { + addNameArea(BuiltInName.PRINT_AREA, + wsi, + range.getTopLeft().getColumn(), + range.getTopLeft().getRow(), + range.getBottomRight().getColumn(), + range.getBottomRight().getRow(), + false); + } + + // Check to see if print titles by row were set + Range rangeR = wsi.getSettings().getPrintTitlesRow(); + Range rangeC = wsi.getSettings().getPrintTitlesCol(); + if (rangeR != null && rangeC != null) { + addNameArea(BuiltInName.PRINT_TITLES, + wsi, + rangeR.getTopLeft().getColumn(), + rangeR.getTopLeft().getRow(), + rangeR.getBottomRight().getColumn(), + rangeR.getBottomRight().getRow(), + rangeC.getTopLeft().getColumn(), + rangeC.getTopLeft().getRow(), + rangeC.getBottomRight().getColumn(), + rangeC.getBottomRight().getRow(), + false); + } + // Check to see if print titles by row were set + else if (rangeR != null) { + addNameArea(BuiltInName.PRINT_TITLES, + wsi, + rangeR.getTopLeft().getColumn(), + rangeR.getTopLeft().getRow(), + rangeR.getBottomRight().getColumn(), + rangeR.getBottomRight().getRow(), + false); + } + // Check to see if print titles by column were set + else if (rangeC != null) { + addNameArea(BuiltInName.PRINT_TITLES, + wsi, + rangeC.getTopLeft().getColumn(), + rangeC.getTopLeft().getRow(), + rangeC.getBottomRight().getColumn(), + rangeC.getBottomRight().getRow(), + false); + } + } + + // Rationalize all the XF and number formats + if (!settings.getRationalizationDisabled()) { + rationalize(); + } + + // Write the workbook globals + BOFRecord bof = new BOFRecord(BOFRecord.workbookGlobals); + outputFile.write(bof); + + // Must immediatly follow the BOF record + if (settings.getTemplate()) { + // Only write record if we are a template + TemplateRecord trec = new TemplateRecord(); + outputFile.write(trec); + } + + + InterfaceHeaderRecord ihr = new InterfaceHeaderRecord(); + outputFile.write(ihr); + + MMSRecord mms = new MMSRecord(0, 0); + outputFile.write(mms); + + InterfaceEndRecord ier = new InterfaceEndRecord(); + outputFile.write(ier); + + WriteAccessRecord wr = new WriteAccessRecord(settings.getWriteAccess()); + outputFile.write(wr); + + CodepageRecord cp = new CodepageRecord(); + outputFile.write(cp); + + DSFRecord dsf = new DSFRecord(); + outputFile.write(dsf); + + if (settings.getExcel9File()) { + // Only write record if we are a template + // We are not excel 2000, should we still set the flag + Excel9FileRecord e9rec = new Excel9FileRecord(); + outputFile.write(e9rec); + } + + TabIdRecord tabid = new TabIdRecord(getNumberOfSheets()); + outputFile.write(tabid); + + if (containsMacros) { + ObjProjRecord objproj = new ObjProjRecord(); + outputFile.write(objproj); + } + + if (buttonPropertySet != null) { + outputFile.write(buttonPropertySet); + } + + FunctionGroupCountRecord fgcr = new FunctionGroupCountRecord(); + outputFile.write(fgcr); + + // do not support password protected workbooks + WindowProtectRecord wpr = new WindowProtectRecord + (settings.getWindowProtected()); + outputFile.write(wpr); + + ProtectRecord pr = new ProtectRecord(wbProtected); + outputFile.write(pr); + + PasswordRecord pw = new PasswordRecord(null); + outputFile.write(pw); + + Prot4RevRecord p4r = new Prot4RevRecord(false); + outputFile.write(p4r); + + Prot4RevPassRecord p4rp = new Prot4RevPassRecord(); + outputFile.write(p4rp); + + // If no sheet is identified as being selected, then select + // the first one + boolean sheetSelected = false; + WritableSheetImpl wsheet = null; + int selectedSheetIndex = 0; + for (int i = 0; i < getNumberOfSheets() && !sheetSelected; i++) { + wsheet = (WritableSheetImpl) getSheet(i); + if (wsheet.getSettings().isSelected()) { + sheetSelected = true; + selectedSheetIndex = i; + } + } + + if (!sheetSelected) { + wsheet = (WritableSheetImpl) getSheet(0); + wsheet.getSettings().setSelected(true); + selectedSheetIndex = 0; + } + + Window1Record w1r = new Window1Record(selectedSheetIndex); + outputFile.write(w1r); + + BackupRecord bkr = new BackupRecord(false); + outputFile.write(bkr); + + HideobjRecord ho = new HideobjRecord(settings.getHideobj()); + outputFile.write(ho); + + NineteenFourRecord nf = new NineteenFourRecord(false); + outputFile.write(nf); + + PrecisionRecord pc = new PrecisionRecord(false); + outputFile.write(pc); + + RefreshAllRecord rar = new RefreshAllRecord(settings.getRefreshAll()); + outputFile.write(rar); + + BookboolRecord bb = new BookboolRecord(true); + outputFile.write(bb); + + // Write out all the fonts used + fonts.write(outputFile); + + // Write out the cell formats used within this workbook + formatRecords.write(outputFile); + + // Write out the palette, if it exists + if (formatRecords.getPalette() != null) { + outputFile.write(formatRecords.getPalette()); + } + + // Write out the uses elfs record + UsesElfsRecord uer = new UsesElfsRecord(); + outputFile.write(uer); + + // Write out the boundsheet records. Keep a handle to each one's + // position so we can write in the stream offset later + int[] boundsheetPos = new int[getNumberOfSheets()]; + Sheet sheet = null; + + for (int i = 0; i < getNumberOfSheets(); i++) { + boundsheetPos[i] = outputFile.getPos(); + sheet = getSheet(i); + BoundsheetRecord br = new BoundsheetRecord(sheet.getName()); + if (sheet.getSettings().isHidden()) { + br.setHidden(); + } + + if (((WritableSheetImpl) sheets.get(i)).isChartOnly()) { + br.setChartOnly(); + } + + outputFile.write(br); + } + + if (countryRecord == null) { + CountryCode lang = + CountryCode.getCountryCode(settings.getExcelDisplayLanguage()); + if (lang == CountryCode.UNKNOWN) { + logger.warn("Unknown country code " + + settings.getExcelDisplayLanguage() + + " using " + CountryCode.USA.getCode()); + lang = CountryCode.USA; + } + CountryCode region = + CountryCode.getCountryCode(settings.getExcelRegionalSettings()); + countryRecord = new CountryRecord(lang, region); + if (region == CountryCode.UNKNOWN) { + logger.warn("Unknown country code " + + settings.getExcelDisplayLanguage() + + " using " + CountryCode.UK.getCode()); + region = CountryCode.UK; + } + } + + outputFile.write(countryRecord); + + // Write out the names of any add in functions + if (addInFunctionNames != null && addInFunctionNames.length > 0) { + // Write out the supbook record + // SupbookRecord supbook = new SupbookRecord(); + // outputFile.write(supbook); + + for (int i = 0; i < addInFunctionNames.length; i++) { + ExternalNameRecord enr = new ExternalNameRecord(addInFunctionNames[i]); + outputFile.write(enr); + } + } + + if (xctRecords != null) { + for (int i = 0; i < xctRecords.length; i++) { + outputFile.write(xctRecords[i]); + } + } + + // Write out the external sheet record, if it exists + if (externSheet != null) { + //Write out all the supbook records + for (int i = 0; i < supbooks.size(); i++) { + SupbookRecord supbook = (SupbookRecord) supbooks.get(i); + outputFile.write(supbook); + } + outputFile.write(externSheet); + } + + // Write out the names, if any exists + if (names != null) { + for (int i = 0; i < names.size(); i++) { + NameRecord n = (NameRecord) names.get(i); + outputFile.write(n); + } + } + + // Write out the mso drawing group, if it exists + if (drawingGroup != null) { + drawingGroup.write(outputFile); + } + + sharedStrings.write(outputFile); + + EOFRecord eof = new EOFRecord(); + outputFile.write(eof); + + + // Write out the sheets + for (int i = 0; i < getNumberOfSheets(); i++) { + // first go back and modify the offset we wrote out for the + // boundsheet record + outputFile.setData + (IntegerHelper.getFourBytes(outputFile.getPos()), + boundsheetPos[i] + 4); + + wsheet = (WritableSheetImpl) getSheet(i); + wsheet.write(); + } + } + + /** + * Produces a writable copy of the workbook passed in by + * creating copies of each sheet in the specified workbook and adding + * them to its own record + * + * @param w the workbook to copy + */ + private void copyWorkbook(Workbook w) { + int numSheets = w.getNumberOfSheets(); + wbProtected = w.isProtected(); + Sheet s = null; + WritableSheetImpl ws = null; + for (int i = 0; i < numSheets; i++) { + s = w.getSheet(i); + ws = (WritableSheetImpl) createSheet(s.getName(), i, false); + ws.copy(s); + } + } + + /** + * Copies the specified sheet and places it at the index + * specified by the parameter + * + * @param s the index of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public void copySheet(int s, String name, int index) { + WritableSheet sheet = getSheet(s); + WritableSheetImpl ws = (WritableSheetImpl) createSheet(name, index); + ws.copy(sheet); + } + + /** + * Copies the specified sheet and places it at the index + * specified by the parameter + * + * @param s the name of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public void copySheet(String s, String name, int index) { + WritableSheet sheet = getSheet(s); + WritableSheetImpl ws = (WritableSheetImpl) createSheet(name, index); + ws.copy(sheet); + } + + /** + * Indicates whether or not this workbook is protected + * + * @param prot protected flag + */ + public void setProtected(boolean prot) { + wbProtected = prot; + } + + /** + * Rationalizes the cell formats, and then passes the resultant XF index + * mappings to each sheet in turn + */ + private void rationalize() { + IndexMapping fontMapping = formatRecords.rationalizeFonts(); + IndexMapping formatMapping = formatRecords.rationalizeDisplayFormats(); + IndexMapping xfMapping = formatRecords.rationalize(fontMapping, + formatMapping); + + WritableSheetImpl wsi = null; + for (int i = 0; i < sheets.size(); i++) { + wsi = (WritableSheetImpl) sheets.get(i); + wsi.rationalize(xfMapping, fontMapping, formatMapping); + } + } + + /** + * Gets the internal sheet index for a sheet name + * + * @param name the sheet name + * @return the internal sheet index + */ + private int getInternalSheetIndex(String name) { + int index = -1; + String[] names = getSheetNames(); + for (int i = 0; i < names.length; i++) { + if (name.equals(names[i])) { + index = i; + break; + } + } + + return index; + } + + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getExternalSheetName(int index) { + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int firstTab = externSheet.getFirstTabIndex(index); + + if (sr.getType() == SupbookRecord.INTERNAL) { + // It's an internal reference - get the name from the sheets list + WritableSheet ws = getSheet(firstTab); + + return ws.getName(); + } else if (sr.getType() == SupbookRecord.EXTERNAL) { + String name = sr.getFileName() + sr.getSheetName(firstTab); + return name; + } + + // An unknown supbook - return unkown + logger.warn("Unknown Supbook 1"); + return "[UNKNOWN]"; + } + + /** + * Gets the name of the last external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getLastExternalSheetName(int index) { + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int lastTab = externSheet.getLastTabIndex(index); + + if (sr.getType() == SupbookRecord.INTERNAL) { + // It's an internal reference - get the name from the sheets list + WritableSheet ws = getSheet(lastTab); + + return ws.getName(); + } else if (sr.getType() == SupbookRecord.EXTERNAL) { + Assert.verify(false); + } + + // An unknown supbook - return unkown + logger.warn("Unknown Supbook 2"); + return "[UNKNOWN]"; + } + + /** + * Parsing of formulas is only supported for a subset of the available + * biff version, so we need to test to see if this version is acceptable + * + * @return the BOF record, which + */ + public jxl.read.biff.BOFRecord getWorkbookBof() { + return null; + } + + + /** + * Gets the index of the external sheet for the name + * + * @param sheetName + * @return the sheet index of the external sheet index + */ + public int getExternalSheetIndex(int index) { + if (externSheet == null) { + return index; + } + + Assert.verify(externSheet != null); + + int firstTab = externSheet.getFirstTabIndex(index); + + return firstTab; + } + + /** + * Gets the index of the external sheet for the name + * + * @param sheetName + * @return the sheet index of the external sheet index + */ + public int getLastExternalSheetIndex(int index) { + if (externSheet == null) { + return index; + } + + Assert.verify(externSheet != null); + + int lastTab = externSheet.getLastTabIndex(index); + + return lastTab; + } + + /** + * Gets the external sheet index for the sheet name + * + * @param sheetName + * @return the sheet index or -1 if the sheet could not be found + */ + public int getExternalSheetIndex(String sheetName) { + if (externSheet == null) { + externSheet = new ExternalSheetRecord(); + supbooks = new ArrayList(); + supbooks.add(new SupbookRecord(getNumberOfSheets(), settings)); + } + + // Iterate through the sheets records + boolean found = false; + Iterator i = sheets.iterator(); + int sheetpos = 0; + WritableSheetImpl s = null; + + while (i.hasNext() && !found) { + s = (WritableSheetImpl) i.next(); + + if (s.getName().equals(sheetName)) { + found = true; + } else { + sheetpos++; + } + } + + if (found) { + // Check that the supbook record at position zero is internal and + // contains all the sheets + SupbookRecord supbook = (SupbookRecord) supbooks.get(0); + if (supbook.getType() != SupbookRecord.INTERNAL || + supbook.getNumberOfSheets() != getNumberOfSheets()) { + logger.warn("Cannot find sheet " + sheetName + " in supbook record"); + } + + return externSheet.getIndex(0, sheetpos); + } + + // Check for square brackets + int closeSquareBracketsIndex = sheetName.lastIndexOf(']'); + int openSquareBracketsIndex = sheetName.lastIndexOf('['); + + if (closeSquareBracketsIndex == -1 || + openSquareBracketsIndex == -1) { + logger.warn("Square brackets"); + return -1; + } + + String worksheetName = sheetName.substring(closeSquareBracketsIndex + 1); + String workbookName = sheetName.substring(openSquareBracketsIndex + 1, + closeSquareBracketsIndex); + String path = sheetName.substring(0, openSquareBracketsIndex); + String fileName = path + workbookName; + + boolean supbookFound = false; + SupbookRecord externalSupbook = null; + int supbookIndex = -1; + for (int ind = 0; ind < supbooks.size() && !supbookFound; ind++) { + externalSupbook = (SupbookRecord) supbooks.get(ind); + if (externalSupbook.getType() == SupbookRecord.EXTERNAL && + externalSupbook.getFileName().equals(fileName)) { + supbookFound = true; + supbookIndex = ind; + } + } + + if (!supbookFound) { + externalSupbook = new SupbookRecord(fileName, settings); + supbookIndex = supbooks.size(); + supbooks.add(externalSupbook); + } + + int sheetIndex = externalSupbook.getSheetIndex(worksheetName); + + return externSheet.getIndex(supbookIndex, sheetIndex); + } + + /** + * Gets the last external sheet index for the sheet name + * + * @param sheetName + * @return the sheet index or -1 if the sheet could not be found + */ + public int getLastExternalSheetIndex(String sheetName) { + if (externSheet == null) { + externSheet = new ExternalSheetRecord(); + supbooks = new ArrayList(); + supbooks.add(new SupbookRecord(getNumberOfSheets(), settings)); + } + + // Iterate through the sheets records + boolean found = false; + Iterator i = sheets.iterator(); + int sheetpos = 0; + WritableSheetImpl s = null; + + while (i.hasNext() && !found) { + s = (WritableSheetImpl) i.next(); + + if (s.getName().equals(sheetName)) { + found = true; + } else { + sheetpos++; + } + } + + if (!found) { + return -1; + } + + // Check that the supbook record at position zero is internal and contains + // all the sheets + SupbookRecord supbook = (SupbookRecord) supbooks.get(0); + Assert.verify(supbook.getType() == SupbookRecord.INTERNAL && + supbook.getNumberOfSheets() == getNumberOfSheets()); + + return externSheet.getIndex(0, sheetpos); + } + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public void setColourRGB(Colour c, int r, int g, int b) { + formatRecords.setColourRGB(c, r, g, b); + } + + /** + * Accessor for the RGB value for the specified colour + * + * @return the RGB for the specified colour + */ + public RGB getColourRGB(Colour c) { + return formatRecords.getColourRGB(c); + } + + /** + * Gets the name at the specified index + * + * @param index the index into the name table + * @return the name of the cell + */ + public String getName(int index) { + Assert.verify(index >= 0 && index < names.size()); + NameRecord n = (NameRecord) names.get(index); + return n.getName(); + } + + /** + * Gets the index of the name record for the name + * + * @param name + * @return the index in the name table + */ + public int getNameIndex(String name) { + NameRecord nr = (NameRecord) nameRecords.get(name); + return nr != null ? nr.getIndex() : -1; + } + + /** + * Adds a cell to workbook wide range of cells which need adjustment + * following a row/column insert or remove + * + * @param f the cell to add to the list + */ + void addRCIRCell(CellValue cv) { + rcirCells.add(cv); + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change + * + * @param s the sheet on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(WritableSheetImpl s, int col) { + int externalSheetIndex = getExternalSheetIndex(s.getName()); + for (Iterator i = rcirCells.iterator(); i.hasNext(); ) { + CellValue cv = (CellValue) i.next(); + cv.columnInserted(s, externalSheetIndex, col); + } + + // Adjust any named cells + if (names != null) { + for (Iterator i = names.iterator(); i.hasNext(); ) { + NameRecord nameRecord = (NameRecord) i.next(); + nameRecord.columnInserted(externalSheetIndex, col); + } + } + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change + * + * @param s the sheet on which the column was removed + * @param col the column number which was removed + */ + void columnRemoved(WritableSheetImpl s, int col) { + int externalSheetIndex = getExternalSheetIndex(s.getName()); + for (Iterator i = rcirCells.iterator(); i.hasNext(); ) { + CellValue cv = (CellValue) i.next(); + cv.columnRemoved(s, externalSheetIndex, col); + } + + // Adjust any named cells + ArrayList removedNames = new ArrayList(); + if (names != null) { + for (Iterator i = names.iterator(); i.hasNext(); ) { + NameRecord nameRecord = (NameRecord) i.next(); + boolean removeName = nameRecord.columnRemoved(externalSheetIndex, + col); + + if (removeName) { + removedNames.add(nameRecord); + } + } + + // Remove any names which have been deleted + for (Iterator i = removedNames.iterator(); i.hasNext(); ) { + NameRecord nameRecord = (NameRecord) i.next(); + boolean removed = names.remove(nameRecord); + Assert.verify(removed, "Could not remove name " + + nameRecord.getName()); + } + } + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change + * + * @param s the sheet on which the row was inserted + * @param row the row number which was inserted + */ + void rowInserted(WritableSheetImpl s, int row) { + int externalSheetIndex = getExternalSheetIndex(s.getName()); + + // Adjust the row infos + for (Iterator i = rcirCells.iterator(); i.hasNext(); ) { + CellValue cv = (CellValue) i.next(); + cv.rowInserted(s, externalSheetIndex, row); + } + + // Adjust any named cells + if (names != null) { + for (Iterator i = names.iterator(); i.hasNext(); ) { + NameRecord nameRecord = (NameRecord) i.next(); + nameRecord.rowInserted(externalSheetIndex, row); + } + } + } + + /** + * Called when a row is removed on the specified sheet. Notifies all + * RCIR cells of this change + * + * @param s the sheet on which the row was removed + * @param row the row number which was removed + */ + void rowRemoved(WritableSheetImpl s, int row) { + int externalSheetIndex = getExternalSheetIndex(s.getName()); + for (Iterator i = rcirCells.iterator(); i.hasNext(); ) { + CellValue cv = (CellValue) i.next(); + cv.rowRemoved(s, externalSheetIndex, row); + } + + // Adjust any named cells + ArrayList removedNames = new ArrayList(); + if (names != null) { + for (Iterator i = names.iterator(); i.hasNext(); ) { + NameRecord nameRecord = (NameRecord) i.next(); + boolean removeName = nameRecord.rowRemoved(externalSheetIndex, row); + + if (removeName) { + removedNames.add(nameRecord); + } + } + + // Remove any names which have been deleted + for (Iterator i = removedNames.iterator(); i.hasNext(); ) { + NameRecord nameRecord = (NameRecord) i.next(); + boolean removed = names.remove(nameRecord); + Assert.verify(removed, "Could not remove name " + + nameRecord.getName()); + } + } + } + + /** + * Gets the named cell from this workbook. If the name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be found, null is returned + * + * @param the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public WritableCell findCellByName(String name) { + NameRecord nr = (NameRecord) nameRecords.get(name); + + if (nr == null) { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + // Go and retrieve the first cell in the first range + int sheetIndex = getExternalSheetIndex(ranges[0].getExternalSheet()); + WritableSheet s = getSheet(sheetIndex); + WritableCell cell = s.getWritableCell(ranges[0].getFirstColumn(), + ranges[0].getFirstRow()); + + return cell; + } + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param the name of the cell/range to search for + * @return the range of cells + */ + public Range[] findByName(String name) { + NameRecord nr = (NameRecord) nameRecords.get(name); + + if (nr == null) { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + Range[] cellRanges = new Range[ranges.length]; + + for (int i = 0; i < ranges.length; i++) { + cellRanges[i] = new RangeImpl + (this, + getExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getFirstColumn(), + ranges[i].getFirstRow(), + getLastExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getLastColumn(), + ranges[i].getLastRow()); + } + + return cellRanges; + } + + /** + * Adds a drawing to this workbook + * + * @param d the drawing to add + */ + void addDrawing(DrawingGroupObject d) { + if (drawingGroup == null) { + drawingGroup = new DrawingGroup(Origin.WRITE); + } + + drawingGroup.add(d); + } + + /** + * Removes a drawing from this workbook + * + * @param d the drawing to remove + */ + void removeDrawing(Drawing d) { + Assert.verify(drawingGroup != null); + + drawingGroup.remove(d); + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + DrawingGroup getDrawingGroup() { + return drawingGroup; + } + + /** + * Create a drawing group for this workbook - used when importing sheets + * which contain drawings, but this workbook doesn't. + * We can't subsume this into the getDrawingGroup() method because the + * null-ness of the return value is used elsewhere to determine the + * origin of the workbook + */ + DrawingGroup createDrawingGroup() { + if (drawingGroup == null) { + drawingGroup = new DrawingGroup(Origin.WRITE); + } + + return drawingGroup; + } + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public String[] getRangeNames() { + if (names == null) { + return new String[0]; + } + + String[] n = new String[names.size()]; + for (int i = 0; i < names.size(); i++) { + NameRecord nr = (NameRecord) names.get(i); + n[i] = nr.getName(); + } + + return n; + } + + /** + * Removes the specified named range from the workbook + * + * @param name the name to remove + */ + public void removeRangeName(String name) { + int pos = 0; + boolean found = false; + for (Iterator i = names.iterator(); i.hasNext() && !found; ) { + NameRecord nr = (NameRecord) i.next(); + if (nr.getName().equals(name)) { + found = true; + } else { + pos++; + } + } + + // Remove the name from the list of names and the associated hashmap + // of names (used to retrieve the name index). If the name cannot + // be found, a warning is displayed + if (found) { + names.remove(pos); + if (nameRecords.remove(name) == null) { + logger.warn("Could not remove " + name + " from index lookups"); + } + } + } + + /** + * Accessor for the jxl.common.styles + * + * @return the standard styles for this workbook + */ + Styles getStyles() { + return styles; + } + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + */ + public void addNameArea(String name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow) { + addNameArea(name, sheet, firstCol, firstRow, lastCol, lastRow, true); + } + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + * @param global TRUE if this is a global name, FALSE if this is tied to + * the sheet + */ + void addNameArea(String name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean global) { + if (names == null) { + names = new ArrayList(); + } + + int externalSheetIndex = getExternalSheetIndex(sheet.getName()); + + // Create a new name record. + NameRecord nr = + new NameRecord(name, + names.size(), + externalSheetIndex, + firstRow, lastRow, + firstCol, lastCol, + global); + + // Add new name to name array. + names.add(nr); + + // Add new name to name hash table. + nameRecords.put(name, nr); + } + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + * @param global TRUE if this is a global name, FALSE if this is tied to + * the sheet + */ + void addNameArea(BuiltInName name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean global) { + if (names == null) { + names = new ArrayList(); + } + + int index = getInternalSheetIndex(sheet.getName()); + int externalSheetIndex = getExternalSheetIndex(sheet.getName()); + + // Create a new name record. + NameRecord nr = + new NameRecord(name, + index, + externalSheetIndex, + firstRow, lastRow, + firstCol, lastCol, + global); + + // Add new name to name array. + names.add(nr); + + // Add new name to name hash table. + nameRecords.put(name, nr); + } + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + * @param firstCol2 first column this name refers to. + * @param firstRow2 first row this name refers to. + * @param lastCol2 last column this name refers to. + * @param lastRow2 last row this name refers to. + * @param global TRUE if this is a global name, FALSE if this is tied to + * the sheet + */ + void addNameArea(BuiltInName name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + int firstCol2, + int firstRow2, + int lastCol2, + int lastRow2, + boolean global) { + if (names == null) { + names = new ArrayList(); + } + + int index = getInternalSheetIndex(sheet.getName()); + int externalSheetIndex = getExternalSheetIndex(sheet.getName()); + + // Create a new name record. + NameRecord nr = + new NameRecord(name, + index, + externalSheetIndex, + firstRow2, lastRow2, + firstCol2, lastCol2, + firstRow, lastRow, + firstCol, lastCol, + global); + + // Add new name to name array. + names.add(nr); + + // Add new name to name hash table. + nameRecords.put(name, nr); + } + + /** + * Accessor for the workbook settings + */ + WorkbookSettings getSettings() { + return settings; + } + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public WritableCell getWritableCell(String loc) { + WritableSheet s = getSheet(CellReferenceHelper.getSheet(loc)); + return s.getWritableCell(loc); + } + + /** + * Imports a sheet from a different workbook. Does a deep copy on all + * elements within that sheet + * + * @param name the name of the new sheet + * @param index the position for the new sheet within this workbook + * @param sheet the sheet (from another workbook) to merge into this one + * @return the new sheet + */ + public WritableSheet importSheet(String name, int index, Sheet sheet) { + WritableSheet ws = createSheet(name, index); + ((WritableSheetImpl) ws).importSheet(sheet); + + return ws; + } + +} + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WriteAccessRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/WriteAccessRecord.java new file mode 100755 index 0000000..9d0c840 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/WriteAccessRecord.java @@ -0,0 +1,73 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.Workbook; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The name used when Excel was installed. + * When writing worksheets, it uses the value from the WorkbookSettings object, + * if this is not set (null) this is hard coded as + * Java Excel API + Version number + */ +class WriteAccessRecord extends WritableRecordData { + /** + * The author of this workbook (ie. the Java Excel API) + */ + private final static String authorString = "Java Excel API"; + + // String of length 112 characters + /** + * The data to output to file + */ + private final byte[] data; + private String userName; + + /** + * Constructor + */ + public WriteAccessRecord(String userName) { + super(Type.WRITEACCESS); + + data = new byte[112]; + String astring = userName != null ? + userName : + authorString + " v" + Workbook.getVersion(); + + StringHelper.getBytes(astring, data, 0); + + // Pad out the record with space characters + for (int i = astring.length(); i < data.length; i++) { + data[i] = 0x20; + } + } + + /** + * Gets the data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/com/incesoft/tools/excel/xlsx/TestSJXLSX.java b/datastructures-xslx/src/test/java/org/xbib/datastructures/xslx/TestSJXLSX.java similarity index 94% rename from datastructures-xslx/src/main/java/com/incesoft/tools/excel/xlsx/TestSJXLSX.java rename to datastructures-xslx/src/test/java/org/xbib/datastructures/xslx/TestSJXLSX.java index 9acc9d1..95f7f24 100644 --- a/datastructures-xslx/src/main/java/com/incesoft/tools/excel/xlsx/TestSJXLSX.java +++ b/datastructures-xslx/src/test/java/org/xbib/datastructures/xslx/TestSJXLSX.java @@ -1,5 +1,12 @@ -package com.incesoft.tools.excel.xlsx; - +package org.xbib.datastructures.xslx; + +import com.incesoft.tools.excel.xlsx.Cell; +import com.incesoft.tools.excel.xlsx.CellStyle; +import com.incesoft.tools.excel.xlsx.Fill; +import com.incesoft.tools.excel.xlsx.Font; +import com.incesoft.tools.excel.xlsx.RichText; +import com.incesoft.tools.excel.xlsx.Sheet; +import com.incesoft.tools.excel.xlsx.SimpleXLSXWorkbook; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; diff --git a/gradle.properties b/gradle.properties index 4ae4df0..2ac09bf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib name = datastructures -version = 1.0.0 +version = 1.0.1 org.gradle.warning.mode = ALL diff --git a/settings.gradle b/settings.gradle index 11eb78a..72b2a75 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,7 +2,7 @@ dependencyResolutionManagement { versionCatalogs { libs { version('gradle', '7.5') - version('junit', '5.8.2') + version('junit', '5.9.0') version('jackson', '2.12.7') library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit') @@ -24,7 +24,6 @@ dependencyResolutionManagement { library('okio', 'com.squareup.okio', 'okio').version('1.13.0') library('assertj', 'org.assertj', 'assertj-core').version('3.22.0') library('compile-testing', 'com.google.testing.compile', 'compile-testing').version('0.19') - library('jxl', 'net.sourceforge.jexcelapi', 'jxl').version('2.6.12') } } }