diff --git a/src/java/org/apache/poi/hssf/record/FeatRecord.java b/src/java/org/apache/poi/hssf/record/FeatRecord.java index fd27f0226c..c9fed7d40d 100644 --- a/src/java/org/apache/poi/hssf/record/FeatRecord.java +++ b/src/java/org/apache/poi/hssf/record/FeatRecord.java @@ -17,7 +17,11 @@ package org.apache.poi.hssf.record; +import org.apache.poi.hssf.record.common.FeatFormulaErr2; +import org.apache.poi.hssf.record.common.FeatProtection; +import org.apache.poi.hssf.record.common.FeatSmartTag; import org.apache.poi.hssf.record.common.FtrHeader; +import org.apache.poi.hssf.record.common.SharedFeature; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.util.LittleEndianOutput; @@ -49,7 +53,7 @@ public final class FeatRecord extends StandardRecord { * ISFFEC2 -> FeatFormulaErr2 * ISFFACTOID -> FeatSmartTag */ - private byte[] rgbFeat; + private SharedFeature sharedFeature; public FeatRecord() { futureHeader = new FtrHeader(); @@ -75,7 +79,19 @@ public final class FeatRecord extends StandardRecord { cellRefs[i] = new CellRangeAddress(in); } - rgbFeat = in.readRemainder(); + switch(isf_sharedFeatureType) { + case FeatHdrRecord.SHAREDFEATURES_ISFPROTECTION: + sharedFeature = new FeatProtection(in); + break; + case FeatHdrRecord.SHAREDFEATURES_ISFFEC2: + sharedFeature = new FeatFormulaErr2(in); + break; + case FeatHdrRecord.SHAREDFEATURES_ISFFACTOID: + sharedFeature = new FeatSmartTag(in); + break; + default: + System.err.println("Unknown Shared Feature " + isf_sharedFeatureType + " found!"); + } } public String toString() { @@ -102,21 +118,18 @@ public final class FeatRecord extends StandardRecord { cellRefs[i].serialize(out); } - out.write(rgbFeat); + sharedFeature.serialize(out); } protected int getDataSize() { return 12 + 2+1+4+2+4+2+ (cellRefs.length * CellRangeAddress.ENCODED_SIZE) - +rgbFeat.length; + +sharedFeature.getDataSize(); } public int getIsf_sharedFeatureType() { return isf_sharedFeatureType; } - public void setIsf_sharedFeatureType(int isfSharedFeatureType) { - isf_sharedFeatureType = isfSharedFeatureType; - } public long getCbFeatData() { return cbFeatData; @@ -132,14 +145,24 @@ public final class FeatRecord extends StandardRecord { this.cellRefs = cellRefs; } - public byte[] getRgbFeat() { - return rgbFeat; + public SharedFeature getSharedFeature() { + return sharedFeature; } - public void setRgbFeat(byte[] rgbFeat) { - this.rgbFeat = rgbFeat; + public void setSharedFeature(SharedFeature feature) { + this.sharedFeature = feature; + + if(feature instanceof FeatProtection) { + isf_sharedFeatureType = FeatHdrRecord.SHAREDFEATURES_ISFPROTECTION; + } + if(feature instanceof FeatFormulaErr2) { + isf_sharedFeatureType = FeatHdrRecord.SHAREDFEATURES_ISFFEC2; + } + if(feature instanceof FeatSmartTag) { + isf_sharedFeatureType = FeatHdrRecord.SHAREDFEATURES_ISFFACTOID; + } if(isf_sharedFeatureType == FeatHdrRecord.SHAREDFEATURES_ISFFEC2) { - cbFeatData = rgbFeat.length; + cbFeatData = sharedFeature.getDataSize(); } else { cbFeatData = 0; } diff --git a/src/java/org/apache/poi/hssf/record/common/FeatFormulaErr2.java b/src/java/org/apache/poi/hssf/record/common/FeatFormulaErr2.java new file mode 100644 index 0000000000..adf1b888ab --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/common/FeatFormulaErr2.java @@ -0,0 +1,159 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.common; + +import org.apache.poi.hssf.record.FeatRecord; +//import org.apache.poi.hssf.record.Feat11Record; +//import org.apache.poi.hssf.record.Feat12Record; +import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.util.BitField; +import org.apache.poi.util.BitFieldFactory; +import org.apache.poi.util.LittleEndianOutput; + +/** + * Title: FeatFormulaErr2 (Formula Evaluation Shared Feature) common record part + *

+ * This record part specifies Formula Evaluation & Error Ignoring data + * for a sheet, stored as part of a Shared Feature. It can be found in + * records such as {@link FeatRecord}, {@link Feat11Record} or + * {@link Feat12Record}. + * For the full meanings of the flags, see pages 669 and 670 + * of the Excel binary file format documentation. + */ +public final class FeatFormulaErr2 implements SharedFeature { + static BitField checkCalculationErrors = + BitFieldFactory.getInstance(0x01); + static BitField checkEmptyCellRef = + BitFieldFactory.getInstance(0x02); + static BitField checkNumbersAsText = + BitFieldFactory.getInstance(0x04); + static BitField checkInconsistentRanges = + BitFieldFactory.getInstance(0x08); + static BitField checkInconsistentFormulas = + BitFieldFactory.getInstance(0x10); + static BitField checkDateTimeFormats = + BitFieldFactory.getInstance(0x20); + static BitField checkUnprotectedFormulas = + BitFieldFactory.getInstance(0x40); + static BitField performDataValidation = + BitFieldFactory.getInstance(0x80); + + /** + * What errors we should ignore + */ + private int errorCheck; + + + public FeatFormulaErr2() {} + + public FeatFormulaErr2(RecordInputStream in) { + errorCheck = in.readInt(); + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(" [FEATURE FORMULA ERRORS]\n"); + buffer.append(" checkCalculationErrors = "); + buffer.append(" checkEmptyCellRef = "); + buffer.append(" checkNumbersAsText = "); + buffer.append(" checkInconsistentRanges = "); + buffer.append(" checkInconsistentFormulas = "); + buffer.append(" checkDateTimeFormats = "); + buffer.append(" checkUnprotectedFormulas = "); + buffer.append(" performDataValidation = "); + buffer.append(" [/FEATURE FORMULA ERRORS]\n"); + return buffer.toString(); + } + + public void serialize(LittleEndianOutput out) { + out.writeInt(errorCheck); + } + + public int getDataSize() { + return 4; + } + + public int _getRawErrorCheckValue() { + return errorCheck; + } + + public boolean getCheckCalculationErrors() { + return checkCalculationErrors.isSet(errorCheck); + } + public void setCheckCalculationErrors(boolean checkCalculationErrors) { + FeatFormulaErr2.checkCalculationErrors.setBoolean( + errorCheck, checkCalculationErrors); + } + + public boolean getCheckEmptyCellRef() { + return checkEmptyCellRef.isSet(errorCheck); + } + public void setCheckEmptyCellRef(boolean checkEmptyCellRef) { + FeatFormulaErr2.checkEmptyCellRef.setBoolean( + errorCheck, checkEmptyCellRef); + } + + public boolean getCheckNumbersAsText() { + return checkNumbersAsText.isSet(errorCheck); + } + public void setCheckNumbersAsText(boolean checkNumbersAsText) { + FeatFormulaErr2.checkNumbersAsText.setBoolean( + errorCheck, checkNumbersAsText); + } + + public boolean getCheckInconsistentRanges() { + return checkInconsistentRanges.isSet(errorCheck); + } + public void setCheckInconsistentRanges(boolean checkInconsistentRanges) { + FeatFormulaErr2.checkInconsistentRanges.setBoolean( + errorCheck, checkInconsistentRanges); + } + + public boolean getCheckInconsistentFormulas() { + return checkInconsistentFormulas.isSet(errorCheck); + } + public void setCheckInconsistentFormulas( + boolean checkInconsistentFormulas) { + FeatFormulaErr2.checkInconsistentFormulas.setBoolean( + errorCheck, checkInconsistentFormulas); + } + + public boolean getCheckDateTimeFormats() { + return checkDateTimeFormats.isSet(errorCheck); + } + public void setCheckDateTimeFormats(boolean checkDateTimeFormats) { + FeatFormulaErr2.checkDateTimeFormats.setBoolean( + errorCheck, checkDateTimeFormats); + } + + public boolean getCheckUnprotectedFormulas() { + return checkUnprotectedFormulas.isSet(errorCheck); + } + public void setCheckUnprotectedFormulas(boolean checkUnprotectedFormulas) { + FeatFormulaErr2.checkUnprotectedFormulas.setBoolean( + errorCheck, checkUnprotectedFormulas); + } + + public boolean getPerformDataValidation() { + return performDataValidation.isSet(errorCheck); + } + public void setPerformDataValidation(boolean performDataValidation) { + FeatFormulaErr2.performDataValidation.setBoolean( + errorCheck, performDataValidation); + } +} diff --git a/src/java/org/apache/poi/hssf/record/common/FeatProtection.java b/src/java/org/apache/poi/hssf/record/common/FeatProtection.java new file mode 100644 index 0000000000..bab65e6218 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/common/FeatProtection.java @@ -0,0 +1,106 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.common; + +import org.apache.poi.hssf.record.FeatRecord; +import org.apache.poi.hssf.record.PasswordRecord; +import org.apache.poi.hssf.record.PasswordRev4Record; +//import org.apache.poi.hssf.record.Feat11Record; +//import org.apache.poi.hssf.record.Feat12Record; +import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.util.LittleEndianOutput; +import org.apache.poi.util.StringUtil; + +/** + * Title: FeatProtection (Protection Shared Feature) common record part + *

+ * This record part specifies Protection data for a sheet, stored + * as part of a Shared Feature. It can be found in records such + * as {@link FeatRecord}, {@link Feat11Record} or + * {@link Feat12Record} + */ +public final class FeatProtection implements SharedFeature { + public static long NO_SELF_RELATIVE_SECURITY_FEATURE = 0; + public static long HAS_SELF_RELATIVE_SECURITY_FEATURE = 1; + + private int fSD; + + /** + * 0 means no password. Otherwise indicates the + * password verifier algorithm (same kind as + * {@link PasswordRecord} and + * {@link PasswordRev4Record}) + */ + private int passwordVerifier; + + private String title; + private byte[] securityDescriptor; + + public FeatProtection() { + securityDescriptor = new byte[0]; + } + + public FeatProtection(RecordInputStream in) { + fSD = in.readInt(); + passwordVerifier = in.readInt(); + + title = StringUtil.readUnicodeString(in); + + securityDescriptor = in.readRemainder(); + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(" [FEATURE PROTECTION]\n"); + buffer.append(" Self Relative = " + fSD); + buffer.append(" Password Verifier = " + passwordVerifier); + buffer.append(" Title = " + title); + buffer.append(" Security Descriptor Size = " + securityDescriptor.length); + buffer.append(" [/FEATURE PROTECTION]\n"); + return buffer.toString(); + } + + public void serialize(LittleEndianOutput out) { + out.writeInt(fSD); + out.writeInt(passwordVerifier); + StringUtil.writeUnicodeString(out, title); + out.write(securityDescriptor); + } + + public int getDataSize() { + return 4 + 4 + StringUtil.getEncodedSize(title) + securityDescriptor.length; + } + + public int getPasswordVerifier() { + return passwordVerifier; + } + public void setPasswordVerifier(int passwordVerifier) { + this.passwordVerifier = passwordVerifier; + } + + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } + + public int getFSD() { + return fSD; + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/common/FeatSmartTag.java b/src/java/org/apache/poi/hssf/record/common/FeatSmartTag.java new file mode 100644 index 0000000000..2fc53e24f4 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/common/FeatSmartTag.java @@ -0,0 +1,63 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.common; + +import org.apache.poi.hssf.record.FeatRecord; +//import org.apache.poi.hssf.record.Feat11Record; +//import org.apache.poi.hssf.record.Feat12Record; +import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.util.LittleEndianOutput; + +/** + * Title: FeatSmartTag (Smart Tag Shared Feature) common record part + *

+ * This record part specifies Smart Tag data for a sheet, stored as part + * of a Shared Feature. It can be found in records such as + * {@link FeatRecord}, {@link Feat11Record} or {@link Feat12Record}. + * It is made up of a hash, and a set of Factoid Data that makes up + * the smart tags. + * For more details, see page 669 of the Excel binary file + * format documentation. + */ +public final class FeatSmartTag implements SharedFeature { + // TODO - process + private byte[] data; + + public FeatSmartTag() { + data = new byte[0]; + } + + public FeatSmartTag(RecordInputStream in) { + data = in.readRemainder(); + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(" [FEATURE SMART TAGS]\n"); + buffer.append(" [/FEATURE SMART TAGS]\n"); + return buffer.toString(); + } + + public int getDataSize() { + return data.length; + } + + public void serialize(LittleEndianOutput out) { + out.write(data); + } +} diff --git a/src/java/org/apache/poi/hssf/record/common/SharedFeature.java b/src/java/org/apache/poi/hssf/record/common/SharedFeature.java new file mode 100644 index 0000000000..1ce8bce1f2 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/common/SharedFeature.java @@ -0,0 +1,29 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.common; + +import org.apache.poi.util.LittleEndianOutput; + +/** + * Common Interface for all Shared Features + */ +public interface SharedFeature { + public String toString(); + public void serialize(LittleEndianOutput out); + public int getDataSize(); +} diff --git a/src/java/org/apache/poi/util/StringUtil.java b/src/java/org/apache/poi/util/StringUtil.java index 4a2cbabc72..b08a979fa4 100644 --- a/src/java/org/apache/poi/util/StringUtil.java +++ b/src/java/org/apache/poi/util/StringUtil.java @@ -130,6 +130,8 @@ public class StringUtil { *

  • byte[]/char[] characterData
  • * * For this encoding, the is16BitFlag is always present even if nChars==0. + * + * This structure is also known as a XLUnicodeString. */ public static String readUnicodeString(LittleEndianInput in) { diff --git a/src/testcases/org/apache/poi/hssf/record/TestFeatRecord.java b/src/testcases/org/apache/poi/hssf/record/TestFeatRecord.java index 7fe7c3bae0..288c7ea944 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestFeatRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestFeatRecord.java @@ -20,6 +20,7 @@ package org.apache.poi.hssf.record; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.model.InternalSheet; import org.apache.poi.hssf.model.InternalWorkbook; +import org.apache.poi.hssf.record.common.FeatFormulaErr2; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFTestHelper; import org.apache.poi.hssf.usermodel.HSSFWorkbook; @@ -152,8 +153,21 @@ public final class TestFeatRecord extends TestCase { assertEquals(0, fr.getCellRefs()[0].getFirstColumn()); assertEquals(0, fr.getCellRefs()[0].getLastColumn()); - // TODO - more checking of shared features stuff + // More checking of shared features stuff assertEquals(4, fr.getCbFeatData()); - assertEquals(4, fr.getRgbFeat().length); + assertEquals(4, fr.getSharedFeature().getDataSize()); + assertEquals(FeatFormulaErr2.class, fr.getSharedFeature().getClass()); + + FeatFormulaErr2 fferr2 = (FeatFormulaErr2)fr.getSharedFeature(); + assertEquals(0x04, fferr2._getRawErrorCheckValue()); + + assertFalse(fferr2.getCheckCalculationErrors()); + assertFalse(fferr2.getCheckDateTimeFormats()); + assertFalse(fferr2.getCheckEmptyCellRef()); + assertFalse(fferr2.getCheckInconsistentFormulas()); + assertFalse(fferr2.getCheckInconsistentRanges()); + assertTrue(fferr2.getCheckNumbersAsText()); + assertFalse(fferr2.getCheckUnprotectedFormulas()); + assertFalse(fferr2.getPerformDataValidation()); } }