git-svn-id: https://svn.apache.org/repos/asf/poi/branches/common_sl@1691843 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_13_FINAL
@@ -0,0 +1,2 @@ | |||
eclipse.preferences.version=1 | |||
encoding/<project>=UTF-8 |
@@ -152,6 +152,7 @@ org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line | |||
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line | |||
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line | |||
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line | |||
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line | |||
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line | |||
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line | |||
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line | |||
@@ -197,6 +198,7 @@ org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do | |||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert | |||
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert | |||
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert | |||
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert | |||
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert | |||
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert | |||
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert | |||
@@ -244,6 +246,7 @@ org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=inser | |||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert | |||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert | |||
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert | |||
org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert | |||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert | |||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert | |||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert | |||
@@ -321,6 +324,7 @@ org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do n | |||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert | |||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert | |||
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert | |||
org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert | |||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert | |||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert | |||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert |
@@ -0,0 +1,4 @@ | |||
eclipse.preferences.version=1 | |||
org.moreunit.preferences.version=2 | |||
org.moreunit.unitsourcefolder=ApachePOI\:src/java\:ApachePOI\:src/testcases\#ApachePOI\:src/java\:ApachePOI\:src/ooxml/testcases\#ApachePOI\:src/java\:ApachePOI\:src/scratchpad/testcases\#ApachePOI\:src/java\:ApachePOI\:src/excelant/testcases\#ApachePOI\:src/java\:ApachePOI\:src/integrationtest | |||
org.moreunit.useprojectsettings=true |
@@ -1796,3 +1796,348 @@ qoiVaXWfFHwg8u4bSHunrzNja17GyaZHdCEmM9vFzlaqBkoLWCMwIcaKnX9stQJp | |||
FZwpzgut7DanaPcCDk7LMBbntaJwRC72M0qcj16SUAdAuGt1yQ== | |||
=0DjT | |||
-----END PGP PUBLIC KEY BLOCK----- | |||
pub 4096R/B4812553 2014-02-26 [expires: 2019-02-25] | |||
uid David North <david@dnorth.net> | |||
uid David North (Oxford CompSoc) <north@ox.compsoc.net> | |||
uid David North (ASF Committer ID) <dnorth@apache.org> | |||
-----BEGIN PGP PUBLIC KEY BLOCK----- | |||
Version: GnuPG v1 | |||
mQINBFMOLVsBEADW2N1bQ6GFDDSbC+IKggdS/lwhaEo5Av4Z5B2es83A43boyJZ1 | |||
bn9xfd5TriHubI6VBgjbuB0peoCZye3PzEz9cfZYXsPiAqZngWk95or0N+vwrGoI | |||
9gJM6aBYjE06fxHZRTAi4ADPLW5sV0bT230/u1xpqg1lcko5eGV5sAjI9HaVdfvR | |||
68gQdNVK6TciOeM2EQcTHlRd8D9D2/XTp9aCFynNaFoKmBTOsc+VlczmIgd+1jzW | |||
qWcaGTkEtZKzAxUfxlWgO0xHjs3H4CGqtWWCqj8W1alkwIVHBeXIwHDoHlbmkXok | |||
65jfeQd9tWzTHGXETU7bBbxksbwRlrJrutgolLW+/v9F1je2aG/BKMwOLjF7dk8+ | |||
gvSKlu4PwoItGN0qraWsqAGDR4/bWLihqPluW+pUL544li702DUeZiVxFEb22yDb | |||
p2oWdFafEFhQz4FxRCam/4bfVt6bGLGE8GEd1jeahgmpkIAcbAoI9UOFAVaoSWFv | |||
AqYphVfONeZz7MMIcNlyHth5VAvQQF7/uBWCsNtlvBtVirR+nis+eEwoNRDwYx3N | |||
OKu1GTFPMMHUauB0ORD2ywsFQkIjDbnQMNhwQoC49bs46vCusBb42qmOpjP0viVs | |||
qGI3Ae78F4KEBpa5AVbYRbbicOPeV/tRrHFWMyXmH312898j29qO57e+awARAQAB | |||
tB5EYXZpZCBOb3J0aCA8ZGF2aWRAZG5vcnRoLm5ldD6JAkAEEwEIACoCGwMFCQlm | |||
AYAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AFAlMOL4ECGQEACgkQ+bj6w7SBJVML | |||
Mg//To6EADRjfp11ZGu3cARitfx67276xAkt017sxw76LBzHoF60RtPjcRBBRCYk | |||
0P1URdip6nC2M7Ol151YdTUIfKNWDRzAUVgpWrGfUIRPUgClyO2boTijblS3heZS | |||
gCKz9x3WwRn5elR3Qf6u/wZGIuHBjmh9Jwoa/c6G6ikIoLjoUvXBWR53FlLIpHIF | |||
/q6mzwhXdCwohDsfDI7ckSuSv5yMJsYLZRpEKB3zLgWo4CQP6edaewBd0bVb/Uzy | |||
/5aeolwEfwoNSJ2J4+tQ39U1vxPr1NLEALNl4qW4Gn2fSACnWyEjDHrLltDvolU/ | |||
iLXjgb//9ge93vZUfZmH6vPkrt4TzOfar4wLHTgiEX8nKaOwiMU72wgSL1DLJgry | |||
udf0zSIYDldm/Wy4ggywd/mSyp4oWR3pJFI926CyiQrnA4fSxKrE3yI4Pm5kZNBM | |||
5fhDwtPEtp4F/7kLxGK6MCxyeL94x9QnZdp4FHyRcO3XCCMhfAKT3qLiuHpTiNOt | |||
mOu3Ea1DsEVbSOw+gJzqEZN2ruB8z9DsOxNxlLsdW+sSQRSNgErnLlBLlkXpSawB | |||
imhbwfOHKqZ6eoEqz9ufbrJjIkOEDbIbW93hpyG7zdKSkKsXLR9HbOZ7kgMgzPVi | |||
KNAhc7axmvps55Jx7aXP7G+8c9d8iWhuwD9MemqvhRKyzeeJAj0EEwEIACcFAlMO | |||
LVsCGwMFCQlmAYAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQ+bj6w7SBJVPR | |||
6A/9Gk3J6BV0IPuybnmxrkKUicPsIoXgRnzU9LF2TIC6mYJBjb3Tllw0kahsTwLa | |||
vfXEEk971zushOUH/z/wLLgrQ8rzJq3RfbvWRMz9BurIJYGWCnkkINr+lURV0Od1 | |||
IzilZlLuCWCh+WS8w4sJuoziF71Ick3Pf0DCpPIte6PMqBWerrbmXlRfYZgP+vrH | |||
JZ6ZHUEkOVawuY91QRW5HKsM/8RDwgLP7zvXtt7BaVey0XW9pRVQM0fzYzW9wufc | |||
B9DMQeampdXv2R3iXVDQRo86zCRo+7UrqEpBGNXBNhWozucw5eidJo28U+76fWng | |||
7rpDrOPSOUWcg/IfWDrm+zrPyjX/9sFuyjP2w+/dsxmerYk4yXYOQaVHJYaBgAO4 | |||
O11WsjhZZ0bvYgxqJjHs9WGfg7ZfuobzIK2Pl1ZwU0lAcePwuHTBTBFlI6wUxmhG | |||
KM2V8vZUMopxUSw3AE4lWZV756ST8Tc9cVVllBg9eRGHdFN2NKullRzTvLMuUwLc | |||
IGkL6h7VrPmQUhgjyHO4mWgE0or4bm7pHRjP+1D+dxTlr/OZUAiCMgxaBo749Mas | |||
zgvDqcxJOtH/9J3dUeTzN8C1RoKPQPv1znmJ3OgpOKs0cNz1qNIQGQkYasl4awq3 | |||
nPz7QUqcKeZgy/6KIInOPJ8ys2+R54hFhOj/NUBBZID8EX+IRgQQEQIABgUCUw4x | |||
bwAKCRA7Ok8Kx55balZeAJ99U3htpjHcxLxLS9lkGT2+YiBIdgCbBLVVwOBCU7qt | |||
mdHezw/Xp2oxxNKIRgQQEQgABgUCUxNB9AAKCRCgJ0huCLbaUCjLAJ9Iht8ctGWu | |||
SiDFxodDIHmyJ2z8wACbBDGyGhDA5TVJphdJm9Qo1LYKgc2JBBwEEAEKAAYFAlMT | |||
McUACgkQsAucyC163ywE0CAAtVzeWwkCwf5ISyWjXosLnbxohXvmq7uh6bdTVoxW | |||
d1V882pi1UAxU/UZFhb7NSvLaz/hr6+WK0wldl27BF74d6j5rE/3r47KVeOEUuAw | |||
g/J/tLEfzhWtficG13qnLWyf/iTyP6fCIaFRNHL5PK6Br8xZHS0YF9zRHaCutwTm | |||
MHV5KinKktgA6Su7YmGwB3f1lirXxUa+RiGAkvg8tIs8Hn+V2x+FDD22QgW4FDh4 | |||
CFYrzz8NsUiIkYawlIHQdnCkQqiFii7MZOG+EsJ86P0lfYbfJfxEY+4nI737YfXH | |||
SCiWjDTiZG0Hkvrx3qbt0L+Ixydn3oGkrfP+ZngPBj6n7QkALAUFBA34+cylobOD | |||
jb1nKGM32iqPx50GpaS3KH3WmqqXaR2+6SbUUdZvmwac6vQvtRRfEhVtWB9irXUS | |||
wn218Ki2EgprAHwrsvLeMJYoR1U9v1UvSrFj5qtiagJYecxDhsM6ozHmcB2PKdE1 | |||
VkIv2vPRzVsfuWeEFnPOSOODsV1Zu00AfceotvAEZd+VdXNkJ3MfWEy7VT52FnG7 | |||
FntzqDXxGkYZLxK6zn0Qh6udvRz65Q3G0nd+USkAmhEBMTr1/La1LfMAibyUyiyL | |||
0bTRHkjXQc8SpibduFzpcWyrMELVqt1PXgaqtujcjVoHwo00NgJIedBosHVuGBXW | |||
PpUQx2DkTnIUn5Qkncf8SpgCCGFs6Z3qwcqrc6Pb1Sg6XQ6GpMTYMZO0yaLdSSrS | |||
vl6wdiJAzvlP1haafInnNFAo6qgaGNgdiSr1r+SpYycFHORCdqimLax5ggOxh1Y+ | |||
V5uZxDDvOSJQUNKO8bDfREke6gIsNRCj7hRutz9F2K566pu5Tn9UAiv1ZfqYxBub | |||
+mkjkctRw61DlqHepJkY1KQTLBphVM9CAADzWPKGRVZtjeYyLNPBap2GbciBQgzr | |||
/2UCCWAGraxpniCizgkFRPRFaOSp4p0p8DozR3/7xftLPXNgudUbRjxNlRX+uPj4 | |||
0jSEJT1Yrc1/hKgOWvmP13WTbT9/8F2tT+Gd8DTLHa2pXMePOX/dPmF7g7IiEUo9 | |||
Pmci3gFMd0MU1+tgjGZlFs771MZUBnq9rrsX6K5PcikE+UV354mTuF/GdL5Qvwbn | |||
7DQ0Z88FQ4jTatPucEghTLnO9ahB+5ZlE9ri5hWTKRMZNkX0KlCcgcNpPTJ9Ya3e | |||
vWUnuhPHmxq7jD0Fc8VLjiKMWSMMiqTrxdgit41ocZinCH54JKtYb41lsH55rx9q | |||
8crmWdsIKC/Dru7h0MXQW0iU92eMOxiCbXuwNqnzYxWkhjjydhUXmOcufgzfh9Ry | |||
S8zBC/rw5AkhmuMEZK9Ksskt7SvDPqdd0209JrHt+SA8G4kCHAQQAQIABgUCUxDx | |||
+gAKCRC9U3Jvvaau2NoJEACzaTZ8rR1F8+pIy+JdUfQxmZ80CI2CMCjUt+WieiRz | |||
3ZH2sgKqwx4T0c53n8938cwXmwdh5xG17sGWDS8VpXUQ3eq8nHPbc12zJFOWM+Ix | |||
fUIT6zapz18m9x4BjfP78QovU5OyqeE0KtvfwXNvp0OjdoEs70WCaGdmAAcpOd1N | |||
Einz71DOtDcvRdS7A6jqUwfohemu2iPGj7gvUoykb5gg3Nql4K1zQg+Ymo9RH8Bu | |||
MA+ebOvwaRaozi9q1Gjg5erXsN+H9TivoMwRiAHdlfeETuGWTFJl0EhtXMHiUjpz | |||
fA6UsR8LgRMehar8rcsWRY2x8oH6L7Wf2JLc+XnZhAe6wKij5BlbJrJBZc7FcoMJ | |||
KqxGlgLnMDXL5nmHfaIxiJUU6qe9DSRvGyMB1rr9xxeHp3s9DVR8G6Zaub6ufa7k | |||
8lfCKbJBru5UD/FrWPMNztiG8fgRg9krmU41ZgvhT5ksZmH5rcFgK7IOowiN9Bxg | |||
tAclbURX6uNc8CYmWVkR1f+zFS2ChPEebyK+R/VZm0WH3Rz6tacWUtzxr1GryLUU | |||
wTmxGnyXflD9ySAkjEO60NdwUpofeX9qMzza/sJPpxZsGmy3PHByS/AhK7wnPSlH | |||
52o9BTVqwY9b6B+oOnswjccgoNyQmtQu84CnGlUKG0y40ho3F1DCHH/l4jrYaxsY | |||
J4kCHAQQAQIABgUCUxHKygAKCRDABX5xTWlPsl7XEADlAVK6EiOs88azKcH6h/uY | |||
luxi8F97MOEcyjKrfliRxc5Wt2Ry417ngVelhrIzZPoUi4iT8znazQ+GQWBHS1SY | |||
51AEqPN0wrbLpMnhM7cHOZ+Yh8za/G8mIpXCInrsFoDsGTm6jBuwhsmD80HJ8ctq | |||
7DTxrxEfklPuKmQ0ZgbH4wwEmTeU8pzzLlBe9yiDOYVXscG6xffPp6Jml+H2buAb | |||
B7zZKD/yXuNKgs7AonMLrA6d297wslMAZd9lZ67jmLvWLUhd7LGaA2xVNO2+5yNx | |||
gMXrdL0wY6NdUY+yy/48b8izD4Hxc0+7wfnZgeND2tgg3av+hbX0sRbIoP7uZgrl | |||
JKuYuHpgmzQ0Y4eoMklWiWl1o7A8d/vVPuGSOztXUhlQF4nKfEAyn9pFyu4UXjfu | |||
NueBTQl8Hqii/wN2nMk9vZz2xFN2bnsqvIhjnMA2yoQl5bs576yj8Rbji9eK4ZUg | |||
WMAkuOkB675DX5rYrXUnDNo3aBS96NQG4CbG/jaM5i0a+ClVg9tVboGksBJZRzUs | |||
lUQxo8AXS+h74iipJrokigsrq20jO2t/WdxRdxduXu5IOs+hB6ae81JjjHjTFNDu | |||
uteDnnLNG1dBfebw/RkH3cjp+lsvnAks/mINStMoPEH7/Obvzuku32UvAxglkY47 | |||
QSCJfS4QpkD4G/NUvNFhVokCHAQTAQIABgUCUx5jgwAKCRCtltamwB49Z5zGD/9G | |||
Is/hfe5iQrSP9NFNChQjaeafjprCMfi30glYsaXM3y2rrXZJh7zKAvOhYcZwbWK2 | |||
+9meRzuMX2P2iG3WZ7iyaoPALb1WpPlSQef34g3SJRV1UDVKwYpkJSZCsqIX1bhB | |||
sr4qjY7pN46B7GX7RrGl79qDy/CUzLciJ1bX/BiB4L3+ls4qrrfomO3+r10ngqzT | |||
MMmn5hjVWE1BDcuViKYwhW0u/HcTulIVkIp3+Vs6nX54d7nG45YHdmBeLPA5fEFv | |||
Xe3Q3Q0YwK4r/bZwEqlg2GVg5npYIB7SB7MbU1znuHJvyUpTeO0z8lNWjYjKFedb | |||
YmO/tQSoJ82GQt9HMaENKxKP4Tk8Wr3SV3y177nD8+6jFWJQZ8lqsQ/GxZukFWPV | |||
0Eo9dRsQx5v+B5h2213hK4YcOhJaiVtBggUodIKiGz8sW89V2L8EATMb3lAJsvPu | |||
FiQkuQDOhDlbqAj0yQfyBxKmKCHOfAJ2Q3aYBhvFFXho+DBnPjS9r3QfL1z3UYNc | |||
Y8tCT9Um+MX6h/2iAodpxtu4xqHd2PavOxLsOA0IFKIcSNOp731VaMPdptgCOoCk | |||
r7JL7PrH+7QwBPQzCr74rQhiTfuDd1vbmgJGm/D/MuPX5+KemVqOiE+o6xotCb6Y | |||
KqAWu9CqW2m2L8QuD4hNR9NV+StJWmmNaCoOOXZQXIkCHAQTAQIABgUCUx5k3gAK | |||
CRAObqOOk0fwLFbMEACNkB4OlOW32GFzalq6uf2vmG9H99+/pBiKyGKvc50obLwK | |||
x4g8RM/dH6zduHUZ/uJJ08Qvx5h53JVYPOdxk7cXvWVnFJXRQNqgRotOLj9r8Hpa | |||
Rijh4GrTDXeVrNY1rw+Okw7v2iyNwWQDVOkAzrg/fZ5cZRp9e2Ti0cN4COcGJVQJ | |||
lHYfa13GuiSUSdc2OisYiQZWXdT8SnjHLb5cL6GRgmDDhOC/FISiu1L+LFIsyXoZ | |||
9pzFY/43sGTmZJjFnudoxEQHuXmbQZ4Z6o0yAncU2bow0QEQQ/949z4GHNcbXduz | |||
eXBWXO0kA6z4Lu8YLBk9ihrka3kw1Y4VtvIuWQDuLV7DR/k3/YHZzUUvzGH3c8Vn | |||
+DTK5f/dp2TrK63Py+x4/7E62V9uk/VB+TVy0E6vic0Tk3+KMGUHsVWGZiYXYnso | |||
ZpVw7/2vMmiPxai35RMe/yi8r7pAYrQc6su9o/WMS1Hxhp3VPa/g4S/mBfk8jse3 | |||
tHof6ZTYAG7t3FhxVGXgHRYa0nnmHf9bLtNOHrAJ4M07z1VB5wMkFJ491nf05oVr | |||
cyBA3swvliwAilskCfJFFy1yttnOlYY1hLVq7fUAoMfjux9BZglXlmCPp5HlaT2t | |||
rF6VThREOAI8/Es/zrYAgntLuPFLJcYvjqPEChttT8I0H5KlzoSMfHZybsyW04hG | |||
BBMRAgAGBQJTHmQGAAoJECKBkcFWfiwXbtEAoNyetrXS29ORsTlasGUPwumYgHaa | |||
AKCv7Ky87jcPLoQHLp5ugFOyMAXKc4kCHAQTAQIABgUCUx5kQwAKCRAo9QGWxDgC | |||
6wDkEADFhTXNCEedftkHSLl6GxWCGEi1qI418U+cn01hRCtIY0nVjzGYJaxHamtD | |||
2cepVSDDdIeXj9C4mcy3na0/mME7+H/tLuZiOv5d5rFIIK6gODmE/69LrCGSLJL/ | |||
1ULCQMqQ8IcKDEkZTmStEKJZPvE/lPmfjn1XGf5gC8zbTg59hN6op/bRuQpQHsIi | |||
LNn7jEW4X65Ygig/HcRK0Xj6iTEjPc0ITMthLPG5XHUjMeRt+syc65Hl+Q18zwsX | |||
i6bjp/DKLgDUdLvmXdBSxmsDZgZjJtfE5C/C0Yom/VpVesXCWCZqe++GT/K95dSq | |||
pZbu0DSVKtrSTqglRJf3gPeVwQUFixjj/tynZHiO/7cZq0pazRrX2qM2ZZB1hkhZ | |||
VkISzBMUFC8YPkbVDKVYisyUJLdUEKdSB18RcX/ZxLUOsYTo3CsUIwoTH6aL4yQB | |||
C8rB0OkbJeaZbU/3dlHnycAas4ze6hzvEdiYyb2jrg37qbJwg0CPDevsNkLu/Ie/ | |||
6BqqXcLszWS52aNcyLMPTpjvQIC2gV1f450chsiw9yeMUtsgVFXADO6+8zM1io9B | |||
1dtEjRXvaiI3jD/jqV+g8TV1i411NHw5sW9c/Jx0UuXDa7gmb6+6VvavIHCF0pLL | |||
FKUCMmoFePtp0dvdqN1Ysv1RBn7GKl4GGImE0dh5T8pPlXc57IhGBBMRAgAGBQJT | |||
HmRrAAoJEFk2rKVTkFoB0E0Aniydu8kZjiaob6/OT9hYtOkLaQRqAKCD+l2caV7i | |||
J/SkLCBgY2relojE2YkCGwQQAQIABgUCVGuZsAAKCRAXscp9ZAiVKMxYD/iZPK6P | |||
qwHxzEuQjquo8f9f0q1RkX4AZa1j5hvXcqAddUa3HpihHDFnW0UDpptlyonryxUE | |||
imhD9a22/eIsCz4sF8gAgrp9b1gRBJmJMvtRgVHaFn1u02/5IEvRj7JsWgVXzWFU | |||
yh9zLaiQtcpfvfFzmdZKtZ3hl0TGl8NFMLxUsownR2tx0iuc8r+NpzzKpxQeXlYS | |||
98DrDLjE3AKVSZcc8DUBTlwhCZLiXEaQjAKXW+Mxfg8C2E4hQIAsqLWhj8PRQEpG | |||
w9F0Wdrh9w4ePsKKnzsIaprkRve3vzEuDLvmHV+9DQd/sh+C5W1LjL4h+vC0eXhd | |||
42lIWrfgOhai2rrwgfSNEFnMAsB1zdWcwMgVSP97sSVfyrqMlDJWXuW8v2KR36ws | |||
zZnqVPsgQk0Uy53mItUnWblgabSb/udVa+ZI6NeOv1bMGMgKaFqXCn6aHD1X1/E3 | |||
0XtCy2dE6reTAU0zNaRc9ZJOLvxJj8ymP+pUqzkvxLMWAuqpCKsM6YNFlCFkzdfO | |||
LHxhqGp68jKPUfuG7z6yRkgDZF84k55SUK7td8lHmJpbeiL1+N8Sm9CujQ+TEAbA | |||
ZFAQjonwnzXWxzI4N+1TWM8BbhLmFpG814JNxyswYdmps6pdKCWHi8zC60dYZ6Mw | |||
IQZfNWpkeLh7W44NuSJ7pIXuSfEaFCKXX+RwtIpEYXZpZCBOb3J0aCAoT3hmb3Jk | |||
IENvbXBTb2MpIChJIGRvbid0IHVzZSB0aGlzIGFkZHJlc3MgbXVjaCwgYW5kIGl0 | |||
IHJvdXRlcyB0byB0aGUgc2FtZSBtYWlsYm94IGFzIG15IG1haW4gdXNlciBJRC4p | |||
IDxub3J0aEBveC5jb21wc29jLm5ldD6JAj0EEwEIACcFAlMOLj4CGwMFCQlmAYAF | |||
CwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQ+bj6w7SBJVMTXw/+PqnnuxB7vBpk | |||
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 | |||
=keWk | |||
-----END PGP PUBLIC KEY BLOCK----- | |||
pub 4096R/27B9F635 2015-06-22 | |||
uid Dominik Stadler <centic@apache.org> | |||
sub 4096R/EF9D5EA6 2015-06-22 | |||
-----BEGIN PGP PUBLIC KEY BLOCK----- | |||
Version: GnuPG v1 | |||
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 | |||
kg== | |||
=rPGm | |||
-----END PGP PUBLIC KEY BLOCK----- |
@@ -47,8 +47,8 @@ under the License. | |||
<description>The Apache POI project Ant build.</description> | |||
<property name="version.id" value="3.12-beta1"/> | |||
<property name="version.rel" value="REL_3_12_BETA1"/> | |||
<property name="version.id" value="3.13-beta1"/> | |||
<property name="version.rel" value="REL_3_13_BETA1"/> | |||
<property environment="env"/> | |||
<!-- the repository to download jars from --> | |||
@@ -56,7 +56,7 @@ under the License. | |||
<property name="main.lib" location="lib"/> | |||
<property name="ooxml.lib" location="ooxml-lib"/> | |||
<property name="compile.lib" location="compile-lib"/> | |||
<property name="compile.lib" location="compile-lib"/> | |||
<property name="forrest.home" value="${env.FORREST_HOME}"/> | |||
<!-- compiler options options --> | |||
@@ -82,7 +82,11 @@ under the License. | |||
<property name="POI.testdata.path" value="test-data"/> | |||
<property name="java.awt.headless" value="true"/> | |||
<property name="additionaljar" value=""/> | |||
<property name="http_proxy" value="${env.http_proxy}"/> | |||
<condition property="http_proxy" | |||
value="${env.http_proxy}" | |||
else=""> | |||
<isset property="env.http_proxy"/> | |||
</condition> | |||
<!-- Main: --> | |||
<property name="main.resource1.dir" value="src/resources/main"/> | |||
@@ -175,8 +179,8 @@ under the License. | |||
value="${repository.m2}/maven2/org/apache/xmlbeans/xmlbeans/2.6.0/xmlbeans-2.6.0.jar"/> | |||
<!-- coverage libs --> | |||
<property name="jacoco.zip" location="${main.lib}/jacoco-0.7.2.201409121644.zip"/> | |||
<property name="jacoco.url" value="${repository.m2}/maven2/org/jacoco/jacoco/0.7.2.201409121644/jacoco-0.7.2.201409121644.zip"/> | |||
<property name="jacoco.zip" location="${main.lib}/jacoco-0.7.4.201502262128.zip"/> | |||
<property name="jacoco.url" value="${repository.m2}/maven2/org/jacoco/jacoco/0.7.4.201502262128/jacoco-0.7.4.201502262128.zip"/> | |||
<property name="asm.jar" location="${main.lib}/asm-all-5.0.3.jar"/> | |||
<property name="asm.url" value="${repository.m2}/maven2/org/ow2/asm/asm-all/5.0.3/asm-all-5.0.3.jar"/> | |||
@@ -194,14 +198,14 @@ under the License. | |||
<property name="ooxml.xsds.src.jar" location="${ooxml.lib}/ooxml-schemas-1.1-sources.jar"/> | |||
<property name="ooxml.xsds.jar" location="${ooxml.lib}/ooxml-schemas-1.1.jar"/> | |||
<!-- additional schemas are packed into the poi schemas jar, --> | |||
<!-- so we don't have to care about a seperate versioning of the original ooxml schemas --> | |||
<property name="ooxml.xsds.dc.1" value="http://dublincore.org/schemas/xmls/qdc/2003/04/02/dc.xsd"/> | |||
<property name="ooxml.xsds.dc.2" value="http://dublincore.org/schemas/xmls/qdc/2003/04/02/dcterms.xsd"/> | |||
<property name="ooxml.xsds.dc.3" value="http://dublincore.org/schemas/xmls/qdc/2003/04/02/dcmitype.xsd"/> | |||
<property name="ooxml.xsds.dsig.1" value="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd"/> | |||
<property name="ooxml.xsds.dsig.2" value="http://uri.etsi.org/01903/v1.3.2/XAdES.xsd"/> | |||
<property name="ooxml.xsds.dsig.3" value="http://uri.etsi.org/01903/v1.4.1/XAdESv141.xsd"/> | |||
<!-- additional schemas are packed into the poi schemas jar, --> | |||
<!-- so we don't have to care about a seperate versioning of the original ooxml schemas --> | |||
<property name="ooxml.xsds.dc.1" value="http://dublincore.org/schemas/xmls/qdc/2003/04/02/dc.xsd"/> | |||
<property name="ooxml.xsds.dc.2" value="http://dublincore.org/schemas/xmls/qdc/2003/04/02/dcterms.xsd"/> | |||
<property name="ooxml.xsds.dc.3" value="http://dublincore.org/schemas/xmls/qdc/2003/04/02/dcmitype.xsd"/> | |||
<property name="ooxml.xsds.dsig.1" value="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd"/> | |||
<property name="ooxml.xsds.dsig.2" value="http://uri.etsi.org/01903/v1.3.2/XAdES.xsd"/> | |||
<property name="ooxml.xsds.dsig.3" value="http://uri.etsi.org/01903/v1.4.1/XAdESv141.xsd"/> | |||
<property name="ooxml.xsds.ozip.2" value="OfficeOpenXML-Part2.zip"/> | |||
<property name="ooxml.xsds.izip.2" value="OpenPackagingConventions-XMLSchema.zip"/> | |||
<property name="ooxml.xsds.url.2" | |||
@@ -263,17 +267,18 @@ under the License. | |||
<pathelement location="${ooxml.xsds.jar}"/> | |||
<path refid="main.classpath"/> | |||
<pathelement location="${main.output.dir}"/> | |||
<pathelement location="${scratchpad.output.dir}"/> | |||
<pathelement location="${ooxml.security.jar}"/> | |||
<!-- classes are omitted on test cases outside the xml-dsign area to avoid classpath poisioning --> | |||
<!-- classes are omitted on test cases outside the xml-dsign area to avoid classpath poisioning --> | |||
<!--path refid="ooxml.xmlsec.classpath"/--> | |||
<!-- Used only for ExtractorFactory, see #57963 --> | |||
<pathelement location="${scratchpad.output.dir}"/> | |||
</path> | |||
<path id="test.classpath"> | |||
<path refid="main.classpath"/> | |||
<pathelement location="${main.output.dir}"/> | |||
<pathelement location="${main.output.test.dir}"/> | |||
<pathelement location="${additionaljar}"/> | |||
<pathelement location="${additionaljar}"/> | |||
</path> | |||
<path id="test.scratchpad.classpath"> | |||
@@ -281,7 +286,7 @@ under the License. | |||
<pathelement location="${main.output.test.dir}"/> | |||
<pathelement location="${scratchpad.output.dir}"/> | |||
<pathelement location="${scratchpad.output.test.dir}"/> | |||
<pathelement location="${additionaljar}"/> | |||
<pathelement location="${additionaljar}"/> | |||
</path> | |||
<path id="test.ooxml.classpath"> | |||
@@ -289,14 +294,14 @@ under the License. | |||
<pathelement location="${ooxml.output.dir}"/> | |||
<pathelement location="${ooxml.output.test.dir}"/> | |||
<pathelement location="${main.output.test.dir}"/> | |||
<pathelement location="${additionaljar}"/> | |||
<pathelement location="${additionaljar}"/> | |||
</path> | |||
<path id="test.integration.classpath"> | |||
<path refid="scratchpad.classpath"/> | |||
<path refid="ooxml.classpath"/> | |||
<pathelement location="${main.output.test.dir}"/> | |||
<pathelement location="${ooxml.output.dir}"/> | |||
<pathelement location="${ooxml.output.dir}"/> | |||
<pathelement location="${integration.output.test.dir}"/> | |||
</path> | |||
@@ -331,7 +336,7 @@ under the License. | |||
<pathelement location="${excelant.output.dir}"/> | |||
<pathelement location="${excelant.output.test.dir}"/> | |||
<pathelement location="${main.output.test.dir}"/> | |||
<pathelement location="${additionaljar}"/> | |||
<pathelement location="${additionaljar}"/> | |||
</path> | |||
<path id="javadoc.classpath"> | |||
@@ -360,6 +365,8 @@ under the License. | |||
- compile Compile all files from main, ooxml and scratchpad | |||
- test Run all unit tests from main, ooxml and scratchpad | |||
- jar Produce jar files | |||
- jar-src Produce source-jar files | |||
- assemble Produce the zipped distribution files | |||
- site Generate all documentation (Requires Apache Forrest) | |||
- dist Create a distribution (Requires Apache Forrest) | |||
</echo> | |||
@@ -409,6 +416,7 @@ under the License. | |||
<!-- remove previous versions of third-party jars to prevent them from lingering around, | |||
we often had hard-to-find build/CI-problems because of these! --> | |||
<mkdir dir="${main.lib}"/> | |||
<mkdir dir="${compile.lib}"/> | |||
<delete verbose="true"> | |||
<fileset dir="${main.lib}"> | |||
<include name="ant-1.8*"/> | |||
@@ -417,9 +425,13 @@ under the License. | |||
<include name="commons-logging-1.1.jar"/> | |||
<include name="jacoco-0.6*"/> | |||
<include name="jacoco-0.7.1*"/> | |||
<include name="jacoco-0.7.2*"/> | |||
<include name="jacoco-0.7.3*"/> | |||
<include name="log4j-1.2.13*"/> | |||
<include name="org.jacoco.*-0.6.*"/> | |||
<include name="org.jacoco.*-0.7.1*"/> | |||
<include name="org.jacoco.*-0.7.2*"/> | |||
<include name="org.jacoco.*-0.7.3*"/> | |||
<include name="dom4j*"/> | |||
<include name="apache-rat-0.10*"/> | |||
<include name="xercesImpl-*.jar"/> | |||
@@ -721,7 +733,7 @@ under the License. | |||
<headfilter lines="16"/> | |||
</filterchain> | |||
</copy> | |||
<copy todir="${scratchpad.src}"> | |||
<copy todir="${main.src}"> | |||
<fileset dir="${geometry.output.tmpdir}" includes="**/*.java"/> | |||
<filterchain> | |||
<concatfilter prepend="${geometry.output.tmpdir}/apache-license.txt"/> | |||
@@ -976,7 +988,7 @@ under the License. | |||
<xml destfile="${coverage.dir}/coverage.xml"/> | |||
</jacoco:report> | |||
<echo message="Coverage results are available at coverage\index.html, coverage/coverage.xml" /> | |||
<echo message="Coverage results are available at ${coverage.dir}/index.html, ${coverage.dir}/coverage.xml" /> | |||
</target> | |||
<target name="-test-main-check"> | |||
@@ -1280,7 +1292,7 @@ under the License. | |||
description="Generates the API documentation"> | |||
<javadoc verbose="false" author="true" destdir="${apidocs.report.dir}" | |||
windowtitle="POI API Documentation" use="true" version="true" | |||
maxmemory="384M" additionalparam="-notimestamp" | |||
maxmemory="384M" additionalparam="-notimestamp" locale="en_US" | |||
classpathref="javadoc.classpath"> | |||
<packageset dir="${main.src}" defaultexcludes="yes"> | |||
<include name="org/apache/poi/**"/> | |||
@@ -1440,7 +1452,7 @@ under the License. | |||
</jar> | |||
</target> | |||
<target name="jar-src" description="Sources for Maven"> | |||
<target name="jar-src" depends="compile-all, compile-version, -manifest" description="Sources for Maven"> | |||
<jar destfile="${dist.dir}/${jar.name}-${version.id}-sources-${DSTAMP}.jar" | |||
manifest="build/poi-manifest.mf"> | |||
<fileset dir="${main.src}"/> | |||
@@ -1508,12 +1520,12 @@ under the License. | |||
<mkdir dir="${build.maven.javadocs}"/> | |||
<javadoc verbose="false" author="false" destdir="${build.maven.javadocs}" | |||
windowtitle="POI API Documentation" use="false" version="false" | |||
maxmemory="384M" additionalparam="-notimestamp -quiet" | |||
maxmemory="384M" additionalparam="-notimestamp -quiet" locale="en_US" | |||
classpathref="javadoc.classpath"> | |||
<packageset dir="${srcfolder}" defaultexcludes="yes"> | |||
<include name="org/apache/poi/**"/> | |||
</packageset> | |||
<link href="https://poi.apache.org/apidocs" packagelistLoc="build/tmp/site/build/site/apidocs"/> | |||
<link offline="true" href="https://poi.apache.org/apidocs" packagelistLoc="build/tmp/site/build/site/apidocs"/> | |||
</javadoc> | |||
<jar destfile="${dist.dir}/${jarname}-${version.id}-javadocs-${DSTAMP}.jar" | |||
manifest="build/poi-manifest.mf"> | |||
@@ -1628,6 +1640,14 @@ under the License. | |||
<echo>Use ${dist.dir}/multisign.sh to create md5 checksums and GPG signatures</echo> | |||
</target> | |||
<target name="osgi" depends="mvn-install"> | |||
<echo message="Building OSGi bundle via Maven" /> | |||
<mvn:mvn pom="osgi/pom.xml"> | |||
<arg value="-Dpoi.version=${version.id}" /> | |||
<arg value="install" /> | |||
</mvn:mvn> | |||
</target> | |||
<target name="dist" depends="clean, compile-all, test-all, site, jar, release-notes, assemble" | |||
description="Creates the entire distribution into build/dist, from scratch"> | |||
</target> | |||
@@ -1744,7 +1764,30 @@ under the License. | |||
<sourcePath path="src/java" /> | |||
<sourcePath path="src/ooxml/java" /> | |||
<sourcePath path="src/scratchpad/src" /> | |||
</findbugs> | |||
</findbugs> | |||
<findbugs home="${findbugs.home}" output="xml" outputFile="build/findbugs.xml" | |||
excludeFilter="src/resources/devtools/findbugs-filters.xml"> | |||
<fileset dir="${dist.dir}"> | |||
<include name="poi-${version.id}-*.jar"/> | |||
<include name="poi-scratchpad-${version.id}-*.jar"/> | |||
<include name="poi-ooxml-${version.id}-*.jar"/> | |||
<exclude name="poi-*${version.id}-sources-*.jar"/> | |||
<exclude name="poi-*${version.id}-javadocs-*.jar"/> | |||
</fileset> | |||
<auxClasspath path="${compile.lib}/bcpkix-jdk15on-1.51.jar" /> | |||
<auxClasspath path="${compile.lib}/bcprov-ext-jdk15on-1.51.jar" /> | |||
<auxClasspath path="${compile.lib}/slf4j-api-1.7.7.jar" /> | |||
<auxClasspath path="${compile.lib}/xmlsec-2.0.1.jar" /> | |||
<auxClasspath path="ooxml-lib/ooxml-schemas-1.1.jar" /> | |||
<auxClasspath path="ooxml-lib/ooxml-security-1.0.jar" /> | |||
<auxClasspath path="ooxml-lib/xmlbeans-2.6.0.jar" /> | |||
<auxClasspath path="lib/commons-codec-1.9.jar" /> | |||
<auxClasspath path="lib/commons-logging-1.1.3.jar" /> | |||
<auxClasspath path="lib/junit-4.12.jar" /> | |||
<sourcePath path="src/java" /> | |||
<sourcePath path="src/ooxml/java" /> | |||
<sourcePath path="src/scratchpad/src" /> | |||
</findbugs> | |||
</target> | |||
<target name="test-scratchpad-download-resources"> |
@@ -19,22 +19,56 @@ | |||
<programming-language>Java</programming-language> | |||
<category rdf:resource="http://projects.apache.org/category/content" /> | |||
<category rdf:resource="http://projects.apache.org/category/library" /> | |||
<release> | |||
<Version> | |||
<name>Apache POI 3.12</name> | |||
<created>2015-05-11</created> | |||
<revision>3.12</revision> | |||
</Version> | |||
</release> | |||
<release> | |||
<Version> | |||
<name>Apache POI 3.11</name> | |||
<created>2014-12-21</created> | |||
<revision>3.11</revision> | |||
</Version> | |||
</release> | |||
<release> | |||
<Version> | |||
<name>Apache POI 3.10.1</name> | |||
<created>2014-08-18</created> | |||
<revision>3.10.1</revision> | |||
</Version> | |||
</release> | |||
<release> | |||
<Version> | |||
<name>Apache POI 3.10</name> | |||
<created>2014-02-08</created> | |||
<revision>3.10</revision> | |||
</Version> | |||
</release> | |||
<release> | |||
<Version> | |||
<name>Apache POI 3.9</name> | |||
<created>2012-12-03</created> | |||
<revision>3.9</revision> | |||
</Version> | |||
</release> | |||
<release> | |||
<Version> | |||
<name>Apache POI 3.8</name> | |||
<created>2012-03-26</created> | |||
<revision>3.8</revision> | |||
</Version> | |||
</release> | |||
<release> | |||
<Version> | |||
<name>Apache POI 3.7</name> | |||
<created>2010-10-29</created> | |||
<revision>3.7</revision> | |||
</Version> | |||
</release> | |||
<release> | |||
<Version> | |||
<name>Apache POI 3.6</name> | |||
<created>2009-12-14</created> |
@@ -1,5 +1,5 @@ | |||
Apache POI | |||
Copyright 2003-2014 The Apache Software Foundation | |||
Copyright 2003-2015 The Apache Software Foundation | |||
This product includes software developed by | |||
The Apache Software Foundation (http://www.apache.org/). |
@@ -0,0 +1,229 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!-- | |||
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. | |||
--> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>org.apache</groupId> | |||
<artifactId>apache</artifactId> | |||
<version>10</version> | |||
<relativePath /> | |||
</parent> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi-bundle</artifactId> | |||
<packaging>bundle</packaging> | |||
<name>Apache POI OSGi bundle</name> | |||
<description> | |||
OSGi bundle that contains Apache POI, and the dependencies. | |||
</description> | |||
<url>http://poi.apache.org/</url> | |||
<version>${poi.version}</version> | |||
<!-- | |||
<version>3.12-beta2</version> | |||
<version>@VERSION@</version> | |||
--> | |||
<properties> | |||
<maven.compiler.source>1.6</maven.compiler.source> | |||
<maven.compiler.target>1.6</maven.compiler.target> | |||
<pax.exam.version>4.4.0</pax.exam.version> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>${project.groupId}</groupId> | |||
<artifactId>poi</artifactId> | |||
<version>${poi.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>${project.groupId}</groupId> | |||
<artifactId>poi-scratchpad</artifactId> | |||
<version>${poi.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>${project.groupId}</groupId> | |||
<artifactId>poi-ooxml</artifactId> | |||
<version>${poi.version}</version> | |||
</dependency> | |||
<!-- Test dependencies --> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<version>4.12</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.ops4j.pax.exam</groupId> | |||
<artifactId>pax-exam-junit4</artifactId> | |||
<version>${pax.exam.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.ops4j.pax.exam</groupId> | |||
<artifactId>pax-exam-container-native</artifactId> | |||
<version>${pax.exam.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.felix</groupId> | |||
<artifactId>org.apache.felix.framework</artifactId> | |||
<version>4.6.0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.ops4j.pax.exam</groupId> | |||
<artifactId>pax-exam-link-assembly</artifactId> | |||
<version>${pax.exam.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.ops4j.pax.url</groupId> | |||
<artifactId>pax-url-aether</artifactId> | |||
<version>2.3.0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>javax.inject</groupId> | |||
<artifactId>javax.inject</artifactId> | |||
<version>1</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.osgi</groupId> | |||
<artifactId>org.osgi.core</artifactId> | |||
<version>5.0.0</version> | |||
<scope>provided</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.felix</groupId> | |||
<artifactId>maven-bundle-plugin</artifactId> | |||
<extensions>true</extensions> | |||
<configuration> | |||
<instructions> | |||
<Bundle-Activator> | |||
org.apache.poi.osgi.Activator | |||
</Bundle-Activator> | |||
<Embed-Dependency> | |||
poi;inline=true, | |||
poi-scratchpad;inline=true, | |||
poi-ooxml;inline=true, | |||
poi-ooxml-schemas, | |||
xmlbeans | |||
</Embed-Dependency> | |||
<Embed-Transitive>true</Embed-Transitive> | |||
<Bundle-DocURL>${project.url}</Bundle-DocURL> | |||
<Export-Package> | |||
org.apache.poi.* | |||
</Export-Package> | |||
<Import-Package> | |||
!org.junit, | |||
*, | |||
org.apache.xmlbeans.impl.xpath.saxon;resolution:=optional, | |||
org.apache.xmlbeans.impl.xquery.saxon;resolution:=optional, | |||
org.bouncycastle.cert;resolution:=optional, | |||
org.bouncycastle.cert.ocsp;resolution:=optional, | |||
org.bouncycastle.cms.bc;resolution:=optional, | |||
org.bouncycastle.cert.jcajce;resolution:=optional, | |||
org.bouncycastle.operator;resolution:=optional, | |||
org.bouncycastle.operator.bc;resolution:=optional, | |||
org.bouncycastle.tsp;resolution:=optional, | |||
org.openxmlformats.schemas.officeDocument.x2006.math;resolution:=optional, | |||
org.openxmlformats.schemas.schemaLibrary.x2006.main;resolution:=optional, | |||
schemasMicrosoftComOfficePowerpoint;resolution:=optional, | |||
schemasMicrosoftComOfficeWord;resolution:=optional, | |||
</Import-Package> | |||
</instructions> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<artifactId>maven-compiler-plugin</artifactId> | |||
<version>3.2</version> | |||
<configuration> | |||
<source>${maven.compiler.source}</source> | |||
<target>${maven.compiler.target}</target> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
<profiles> | |||
<profile> | |||
<id>java6</id> | |||
<activation> | |||
<jdk>[1.6,)</jdk> | |||
</activation> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<artifactId>maven-assembly-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<phase>pre-integration-test</phase> | |||
<goals> | |||
<goal>single</goal> | |||
</goals> | |||
<configuration> | |||
<descriptor>test-bundles.xml</descriptor> | |||
<finalName>test</finalName> | |||
<attach>false</attach> | |||
</configuration> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<artifactId>maven-failsafe-plugin</artifactId> | |||
<version>2.10</version> | |||
<executions> | |||
<execution> | |||
<goals> | |||
<goal>integration-test</goal> | |||
<goal>verify</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
<configuration> | |||
<systemPropertyVariables> | |||
<org.ops4j.pax.logging.DefaultServiceLog.level> | |||
WARN | |||
</org.ops4j.pax.logging.DefaultServiceLog.level> | |||
</systemPropertyVariables> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</profile> | |||
</profiles> | |||
<organization> | |||
<name>The Apache Software Founation</name> | |||
<url>http://www.apache.org</url> | |||
</organization> | |||
<scm> | |||
<url>http://svn.apache.org/viewvc/poi/trunk/osgi</url> | |||
<connection>scm:svn:http://svn.apache.org/repos/asf/poi/trunk/osgi</connection> | |||
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/poi/trunk/osgi</developerConnection> | |||
</scm> | |||
</project> |
@@ -0,0 +1,28 @@ | |||
/* ==================================================================== | |||
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.osgi; | |||
import org.osgi.framework.BundleActivator; | |||
import org.osgi.framework.BundleContext; | |||
public class Activator implements BundleActivator { | |||
public void start(BundleContext context) throws Exception { | |||
} | |||
public void stop(BundleContext context) throws Exception { | |||
} | |||
} |
@@ -0,0 +1,84 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.osgi; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.ops4j.pax.exam.CoreOptions.bundle; | |||
import static org.ops4j.pax.exam.CoreOptions.junitBundles; | |||
import static org.ops4j.pax.exam.CoreOptions.options; | |||
import javax.inject.Inject; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.URISyntaxException; | |||
import org.apache.poi.hssf.usermodel.HSSFSheet; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
import org.ops4j.pax.exam.Configuration; | |||
import org.ops4j.pax.exam.Option; | |||
import org.ops4j.pax.exam.junit.PaxExam; | |||
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; | |||
import org.ops4j.pax.exam.spi.reactors.PerMethod; | |||
import org.osgi.framework.Bundle; | |||
import org.osgi.framework.BundleContext; | |||
import org.osgi.framework.ServiceReference; | |||
/** | |||
* Test to ensure that all our main formats can create, write | |||
* and read back in, when running under OSGi | |||
*/ | |||
@RunWith(PaxExam.class) | |||
@ExamReactorStrategy(PerMethod.class) | |||
public class TestOSGiBundle { | |||
private final File TARGET = new File("target"); | |||
@Inject | |||
private BundleContext bc; | |||
@Configuration | |||
public Option[] configuration() throws IOException, URISyntaxException { | |||
File base = new File(TARGET, "test-bundles"); | |||
return options( | |||
junitBundles(), | |||
bundle(new File(base, "poi-bundle.jar").toURI().toURL().toString())); | |||
} | |||
@Test | |||
public void testHSSF() throws Exception { | |||
HSSFWorkbook wb = new HSSFWorkbook(); | |||
HSSFSheet s = wb.createSheet("OSGi"); | |||
s.createRow(0).createCell(0).setCellValue("With OSGi"); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
wb.write(baos); | |||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |||
wb = new HSSFWorkbook(bais); | |||
assertEquals(1, wb.getNumberOfSheets()); | |||
s = wb.getSheet("OSGi"); | |||
assertEquals("With OSGi", s.getRow(0).getCell(0).toString()); | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
<!-- | |||
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. | |||
--> | |||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> | |||
<id>bundles</id> | |||
<formats> | |||
<format>dir</format> | |||
</formats> | |||
<includeBaseDirectory>false</includeBaseDirectory> | |||
<dependencySets> | |||
<dependencySet> | |||
<outputDirectory/> | |||
<outputFileNameMapping>${artifact.artifactId}.jar</outputFileNameMapping> | |||
<includes> | |||
<include>org.apache.poi:poi-bundle</include> | |||
</includes> | |||
</dependencySet> | |||
</dependencySets> | |||
</assembly> |
@@ -93,12 +93,12 @@ public class ReadCustomPropertySets | |||
out(" No. of sections: " + sectionCount); | |||
/* Print the list of sections: */ | |||
List sections = ps.getSections(); | |||
List<Section> sections = ps.getSections(); | |||
int nr = 0; | |||
for (Iterator i = sections.iterator(); i.hasNext();) | |||
for (Iterator<Section> i = sections.iterator(); i.hasNext();) | |||
{ | |||
/* Print a single section: */ | |||
Section sec = (Section) i.next(); | |||
Section sec = i.next(); | |||
out(" Section " + nr++ + ":"); | |||
String s = hex(sec.getFormatID().getBytes()); | |||
s = s.substring(0, s.length() - 1); |
@@ -50,5 +50,7 @@ public class CreateCells { | |||
FileOutputStream fileOut = new FileOutputStream("workbook.xls"); | |||
wb.write(fileOut); | |||
fileOut.close(); | |||
wb.close(); | |||
} | |||
} |
@@ -21,6 +21,8 @@ package org.apache.poi.ss.examples; | |||
import org.apache.poi.hssf.usermodel.*; | |||
import org.apache.poi.ss.usermodel.*; | |||
import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold.RangeType; | |||
import org.apache.poi.ss.usermodel.IconMultiStateFormatting.IconSet; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||
@@ -31,10 +33,9 @@ import java.io.IOException; | |||
* Excel Conditional Formatting -- Examples | |||
* | |||
* <p> | |||
* Based on the code snippets from http://www.contextures.com/xlcondformat03.html | |||
* Partly based on the code snippets from | |||
* http://www.contextures.com/xlcondformat03.html | |||
* </p> | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class ConditionalFormats { | |||
@@ -46,6 +47,7 @@ public class ConditionalFormats { | |||
sameCell(wb.createSheet("Same Cell")); | |||
multiCell(wb.createSheet("MultiCell")); | |||
overlapping(wb.createSheet("Overlapping")); | |||
errors(wb.createSheet("Errors")); | |||
hideDupplicates(wb.createSheet("Hide Dups")); | |||
formatDuplicates(wb.createSheet("Duplicates")); | |||
@@ -53,6 +55,9 @@ public class ConditionalFormats { | |||
expiry(wb.createSheet("Expiry")); | |||
shadeAlt(wb.createSheet("Shade Alt")); | |||
shadeBands(wb.createSheet("Shade Bands")); | |||
iconSets(wb.createSheet("Icon Sets")); | |||
// TODO Add colour scales, data bars etc, see bug #58130 | |||
// Write the output to a file | |||
String file = "cf-poi.xls"; | |||
@@ -60,7 +65,7 @@ public class ConditionalFormats { | |||
FileOutputStream out = new FileOutputStream(file); | |||
wb.write(out); | |||
out.close(); | |||
System.out.println("Generated: " + file); | |||
} | |||
/** | |||
@@ -139,6 +144,71 @@ public class ConditionalFormats { | |||
sheet.getRow(2).createCell(4).setCellValue("<== Condition 1: Formula Is =$B2>75 (Blue Fill)"); | |||
} | |||
/** | |||
* Multiple conditional formatting rules can apply to | |||
* one cell, some combining, some beating others. | |||
* Done in order of the rules added to the | |||
* SheetConditionalFormatting object | |||
*/ | |||
static void overlapping(Sheet sheet) { | |||
for (int i=0; i<40; i++) { | |||
int rn = i+1; | |||
Row r = sheet.createRow(i); | |||
r.createCell(0).setCellValue("This is row " + rn + " (" + i + ")"); | |||
String str = ""; | |||
if (rn%2 == 0) str = str + "even "; | |||
if (rn%3 == 0) str = str + "x3 "; | |||
if (rn%5 == 0) str = str + "x5 "; | |||
if (rn%10 == 0) str = str + "x10 "; | |||
if (str.length() == 0) str = "nothing special..."; | |||
r.createCell(1).setCellValue("It is " + str); | |||
} | |||
sheet.autoSizeColumn(0); | |||
sheet.autoSizeColumn(1); | |||
sheet.getRow(1).createCell(3).setCellValue("Even rows are blue"); | |||
sheet.getRow(2).createCell(3).setCellValue("Multiples of 3 have a grey background"); | |||
sheet.getRow(4).createCell(3).setCellValue("Multiples of 5 are bold"); | |||
sheet.getRow(9).createCell(3).setCellValue("Multiples of 10 are red (beats even)"); | |||
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting(); | |||
// Condition 1: Row divides by 10, red (will beat #1) | |||
ConditionalFormattingRule rule1 = | |||
sheetCF.createConditionalFormattingRule("MOD(ROW(),10)=0"); | |||
FontFormatting font1 = rule1.createFontFormatting(); | |||
font1.setFontColorIndex(IndexedColors.RED.index); | |||
// Condition 2: Row is even, blue | |||
ConditionalFormattingRule rule2 = | |||
sheetCF.createConditionalFormattingRule("MOD(ROW(),2)=0"); | |||
FontFormatting font2 = rule2.createFontFormatting(); | |||
font2.setFontColorIndex(IndexedColors.BLUE.index); | |||
// Condition 3: Row divides by 5, bold | |||
ConditionalFormattingRule rule3 = | |||
sheetCF.createConditionalFormattingRule("MOD(ROW(),5)=0"); | |||
FontFormatting font3 = rule3.createFontFormatting(); | |||
font3.setFontStyle(false, true); | |||
// Condition 4: Row divides by 3, grey background | |||
ConditionalFormattingRule rule4 = | |||
sheetCF.createConditionalFormattingRule("MOD(ROW(),3)=0"); | |||
PatternFormatting fill4 = rule4.createPatternFormatting(); | |||
fill4.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.index); | |||
fill4.setFillPattern(PatternFormatting.SOLID_FOREGROUND); | |||
// Apply | |||
CellRangeAddress[] regions = { | |||
CellRangeAddress.valueOf("A1:F41") | |||
}; | |||
sheetCF.addConditionalFormatting(regions, rule1); | |||
sheetCF.addConditionalFormatting(regions, rule2); | |||
sheetCF.addConditionalFormatting(regions, rule3); | |||
sheetCF.addConditionalFormatting(regions, rule4); | |||
} | |||
/** | |||
* Use Excel conditional formatting to check for errors, | |||
@@ -346,4 +416,64 @@ public class ConditionalFormats { | |||
sheet.createRow(0).createCell(1).setCellValue("Shade Bands of Rows"); | |||
sheet.createRow(1).createCell(1).setCellValue("Condition: Formula Is =MOD(ROW(),6)<2 (Light Grey Fill)"); | |||
} | |||
/** | |||
* Icon Sets / Multi-States allow you to have icons shown which vary | |||
* based on the values, eg Red traffic light / Yellow traffic light / | |||
* Green traffic light | |||
*/ | |||
static void iconSets(Sheet sheet) { | |||
sheet.createRow(0).createCell(0).setCellValue("Icon Sets"); | |||
Row r = sheet.createRow(1); | |||
r.createCell(0).setCellValue("Reds"); | |||
r.createCell(1).setCellValue(0); | |||
r.createCell(2).setCellValue(0); | |||
r.createCell(3).setCellValue(0); | |||
r = sheet.createRow(2); | |||
r.createCell(0).setCellValue("Yellows"); | |||
r.createCell(1).setCellValue(5); | |||
r.createCell(2).setCellValue(5); | |||
r.createCell(3).setCellValue(5); | |||
r = sheet.createRow(3); | |||
r.createCell(0).setCellValue("Greens"); | |||
r.createCell(1).setCellValue(10); | |||
r.createCell(2).setCellValue(10); | |||
r.createCell(3).setCellValue(10); | |||
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting(); | |||
CellRangeAddress[] regions = { CellRangeAddress.valueOf("B1:B4") }; | |||
ConditionalFormattingRule rule1 = | |||
sheetCF.createConditionalFormattingRule(IconSet.GYR_3_TRAFFIC_LIGHTS); | |||
IconMultiStateFormatting im1 = rule1.getMultiStateFormatting(); | |||
im1.getThresholds()[0].setRangeType(RangeType.MIN); | |||
im1.getThresholds()[1].setRangeType(RangeType.PERCENT); | |||
im1.getThresholds()[1].setValue(33d); | |||
im1.getThresholds()[2].setRangeType(RangeType.MAX); | |||
sheetCF.addConditionalFormatting(regions, rule1); | |||
regions = new CellRangeAddress[] { CellRangeAddress.valueOf("C1:C4") }; | |||
ConditionalFormattingRule rule2 = | |||
sheetCF.createConditionalFormattingRule(IconSet.GYR_3_FLAGS); | |||
IconMultiStateFormatting im2 = rule1.getMultiStateFormatting(); | |||
im2.getThresholds()[0].setRangeType(RangeType.PERCENT); | |||
im2.getThresholds()[0].setValue(0d); | |||
im2.getThresholds()[1].setRangeType(RangeType.PERCENT); | |||
im2.getThresholds()[1].setValue(33d); | |||
im2.getThresholds()[2].setRangeType(RangeType.PERCENT); | |||
im2.getThresholds()[2].setValue(67d); | |||
sheetCF.addConditionalFormatting(regions, rule2); | |||
regions = new CellRangeAddress[] { CellRangeAddress.valueOf("D1:D4") }; | |||
ConditionalFormattingRule rule3 = | |||
sheetCF.createConditionalFormattingRule(IconSet.GYR_3_SYMBOLS_CIRCLE); | |||
IconMultiStateFormatting im3 = rule1.getMultiStateFormatting(); | |||
im3.setIconOnly(true); | |||
im3.getThresholds()[0].setRangeType(RangeType.MIN); | |||
im3.getThresholds()[1].setRangeType(RangeType.NUMBER); | |||
im3.getThresholds()[1].setValue(3d); | |||
im3.getThresholds()[2].setRangeType(RangeType.NUMBER); | |||
im3.getThresholds()[2].setValue(7d); | |||
sheetCF.addConditionalFormatting(regions, rule3); | |||
} | |||
} |
@@ -289,7 +289,7 @@ public class ToHtml { | |||
private void fontStyle(CellStyle style) { | |||
Font font = wb.getFontAt(style.getFontIndex()); | |||
if (font.getBoldweight() >= HSSFFont.BOLDWEIGHT_NORMAL) | |||
if (font.getBoldweight() >= HSSFFont.BOLDWEIGHT_BOLD) | |||
out.format(" font-weight: bold;%n"); | |||
if (font.getItalic()) | |||
out.format(" font-style: italic;%n"); | |||
@@ -309,8 +309,12 @@ public class ToHtml { | |||
style = wb.getCellStyleAt((short) 0); | |||
StringBuilder sb = new StringBuilder(); | |||
Formatter fmt = new Formatter(sb); | |||
fmt.format("style_%02x", style.getIndex()); | |||
return fmt.toString(); | |||
try { | |||
fmt.format("style_%02x", style.getIndex()); | |||
return fmt.toString(); | |||
} finally { | |||
fmt.close(); | |||
} | |||
} | |||
private <K> void styleOut(String attr, K key, Map<K, String> mapping) { |
@@ -114,16 +114,17 @@ public class AligningCells { | |||
// Make the selection | |||
CTRowImpl ctRow = (CTRowImpl) row.getCTRow(); | |||
List spanList = new ArrayList(); | |||
// Add object with format start_coll:end_coll. For example 1:3 will span from | |||
// cell 1 to cell 3, where the column index starts with 0 | |||
// | |||
// You can add multiple spans for one row | |||
Object span = start_column + ":" + end_column; | |||
List<Object> spanList = new ArrayList<Object>(); | |||
spanList.add(span); | |||
//add spns to the row | |||
ctRow.setSpans(spanList); | |||
} | |||
} | |||
} |
@@ -23,6 +23,7 @@ import java.util.zip.ZipEntry; | |||
import java.util.zip.ZipFile; | |||
import java.util.zip.ZipOutputStream; | |||
import org.apache.poi.openxml4j.opc.internal.ZipHelper; | |||
import org.apache.poi.ss.usermodel.DateUtil; | |||
import org.apache.poi.ss.usermodel.IndexedColors; | |||
import org.apache.poi.ss.util.CellReference; | |||
@@ -165,27 +166,31 @@ public class BigGridDemo { | |||
* @param out the stream to write the result to | |||
*/ | |||
private static void substitute(File zipfile, File tmpfile, String entry, OutputStream out) throws IOException { | |||
ZipFile zip = new ZipFile(zipfile); | |||
ZipOutputStream zos = new ZipOutputStream(out); | |||
@SuppressWarnings("unchecked") | |||
Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) zip.entries(); | |||
while (en.hasMoreElements()) { | |||
ZipEntry ze = en.nextElement(); | |||
if(!ze.getName().equals(entry)){ | |||
zos.putNextEntry(new ZipEntry(ze.getName())); | |||
InputStream is = zip.getInputStream(ze); | |||
copyStream(is, zos); | |||
is.close(); | |||
ZipFile zip = ZipHelper.openZipFile(zipfile); | |||
try { | |||
ZipOutputStream zos = new ZipOutputStream(out); | |||
@SuppressWarnings("unchecked") | |||
Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) zip.entries(); | |||
while (en.hasMoreElements()) { | |||
ZipEntry ze = en.nextElement(); | |||
if(!ze.getName().equals(entry)){ | |||
zos.putNextEntry(new ZipEntry(ze.getName())); | |||
InputStream is = zip.getInputStream(ze); | |||
copyStream(is, zos); | |||
is.close(); | |||
} | |||
} | |||
zos.putNextEntry(new ZipEntry(entry)); | |||
InputStream is = new FileInputStream(tmpfile); | |||
copyStream(is, zos); | |||
is.close(); | |||
zos.close(); | |||
} finally { | |||
zip.close(); | |||
} | |||
zos.putNextEntry(new ZipEntry(entry)); | |||
InputStream is = new FileInputStream(tmpfile); | |||
copyStream(is, zos); | |||
is.close(); | |||
zos.close(); | |||
} | |||
private static void copyStream(InputStream in, OutputStream out) throws IOException { |
@@ -0,0 +1,145 @@ | |||
<?xml version="1.0"?> | |||
<!-- | |||
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. | |||
--> | |||
<project name="POI Testbuild" default="run" basedir="."> | |||
<description>Test-Ant file which verifies that the Apache POI distribution build sources can be compiled successfully. | |||
Before running this, you should execute the "assemble" target in the main build.xml to have the packaged files | |||
created correctly. | |||
</description> | |||
<property name="dist" value="../../build/dist"/> | |||
<property name="build" value="../../build/distsourcebuild"/> | |||
<target name="init" depends=""> | |||
</target> | |||
<target name="run" depends="init,runSourceBuild,runCompileTest"/> | |||
<target name="runSourceBuild" depends="init"> | |||
<!-- clean out old stuff in build-dir --> | |||
<delete dir="${build}"/> | |||
<mkdir dir="${build}"/> | |||
<!-- select latest built source zip --> | |||
<pathconvert property="srcpackage"> | |||
<last> | |||
<sort> | |||
<date xmlns="antlib:org.apache.tools.ant.types.resources.comparators"/> | |||
<resources> | |||
<fileset dir="${dist}"> | |||
<include name="poi-src-*.zip" /> | |||
</fileset> | |||
</resources> | |||
</sort> | |||
</last> | |||
</pathconvert> | |||
<echo message="Found source package at ${srcpackage}"/> | |||
<unzip src="${srcpackage}" dest="${build}" failOnEmptyArchive="true"/> | |||
<!-- look for name of sub-dir, do this dynamically as it changes with every (beta|rc)-release --> | |||
<pathconvert property="dirversion"> | |||
<dirset dir="${build}"> | |||
<include name="*" /> | |||
</dirset> | |||
</pathconvert> | |||
<!-- finally call Ant on the extracted source to check if we can build the packages --> | |||
<echo message="Building in temporary dir ${dirversion}/"/> | |||
<ant dir="${dirversion}" target="jar" inheritAll="false" inheritRefs="false" useNativeBasedir="true"/> | |||
</target> | |||
<target name="runCompileTest" depends="init" description="Verify that we can compile most examples without including excelant or scratchpad jars"> | |||
<!-- clean out old stuff in build-dir --> | |||
<delete dir="${build}"/> | |||
<mkdir dir="${build}"/> | |||
<!-- select latest built jar files without scratchpad.jar --> | |||
<pathconvert property="jarpackage"> | |||
<sort> | |||
<resources> | |||
<fileset dir="${dist}"> | |||
<include name="poi-3.*.jar" /> | |||
<include name="poi-ooxml-3.*.jar" /> | |||
<include name="poi-ooxml-schemas-3.*.jar" /> | |||
<exclude name="*-javadocs-*" /> | |||
<exclude name="*-sources-*" /> | |||
</fileset> | |||
</resources> | |||
</sort> | |||
</pathconvert> | |||
<echo message="Found jar packages at ${jarpackage}"/> | |||
<path id="libs"> | |||
<fileset dir="../../lib"> | |||
<include name="junit*.jar" /> | |||
</fileset> | |||
<fileset dir="../../ooxml-lib"> | |||
<include name="ooxml-schemas-*.jar" /> | |||
<include name="xmlbeans-*.jar" /> | |||
<exclude name="xmlbeans-2.3.*.jar" /> | |||
</fileset> | |||
</path> | |||
<echo message="Compiling examples without linking to scratchpad.jar to ensure that only some specific ones require this jar" /> | |||
<javac srcdir="../examples/src" destdir="${build}" | |||
target="1.6" | |||
source="1.6" | |||
debug="trye" | |||
encoding="ASCII" | |||
fork="yes" | |||
includeantruntime="false" | |||
excludes="org/apache/poi/hslf/**,org/apache/poi/hsmf/**,**/EmbeddedObjects.java,**/EmeddedObjects.java,**/Word2Forrest.java" | |||
classpath="${jarpackage}" | |||
classpathref="libs"> | |||
</javac> | |||
<!-- select latest built jar files with additionally scratchpad.jar --> | |||
<pathconvert property="jarpackagescratchpad"> | |||
<sort> | |||
<resources> | |||
<fileset dir="${dist}"> | |||
<include name="poi-3.*.jar" /> | |||
<include name="poi-ooxml-3.*.jar" /> | |||
<include name="poi-ooxml-schemas-3.*.jar" /> | |||
<include name="poi-scratchpad-3.*.jar" /> | |||
<exclude name="*-javadocs-*" /> | |||
<exclude name="*-sources-*" /> | |||
</fileset> | |||
</resources> | |||
</sort> | |||
</pathconvert> | |||
<echo message="Compiling all examples with the additinal scratchpad.jar" /> | |||
<javac srcdir="../examples/src" destdir="${build}" | |||
target="1.6" | |||
source="1.6" | |||
debug="trye" | |||
encoding="ASCII" | |||
fork="yes" | |||
includeantruntime="false" | |||
classpath="${jarpackagescratchpad}" | |||
classpathref="libs"> | |||
</javac> | |||
</target> | |||
</project> |
@@ -31,6 +31,7 @@ import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.apache.poi.hwpf.OldWordFileFormatException; | |||
import org.apache.poi.stress.*; | |||
import org.apache.tools.ant.DirectoryScanner; | |||
import org.junit.Test; | |||
@@ -65,83 +66,91 @@ import org.junit.runners.Parameterized.Parameters; | |||
*/ | |||
@RunWith(Parameterized.class) | |||
public class TestAllFiles { | |||
private static final File ROOT_DIR = new File("test-data"); | |||
private static final File ROOT_DIR = new File("test-data"); | |||
// map file extensions to the actual mappers | |||
private static final Map<String, FileHandler> HANDLERS = new HashMap<String, FileHandler>(); | |||
static { | |||
// Excel | |||
HANDLERS.put(".xls", new HSSFFileHandler()); | |||
HANDLERS.put(".xlsx", new XSSFFileHandler()); | |||
HANDLERS.put(".xlsm", new XSSFFileHandler()); | |||
HANDLERS.put(".xltx", new XSSFFileHandler()); | |||
HANDLERS.put(".xlsb", new XSSFFileHandler()); | |||
// Word | |||
HANDLERS.put(".doc", new HWPFFileHandler()); | |||
HANDLERS.put(".docx", new XWPFFileHandler()); | |||
HANDLERS.put(".dotx", new XWPFFileHandler()); | |||
HANDLERS.put(".docm", new XWPFFileHandler()); | |||
HANDLERS.put(".ooxml", new XWPFFileHandler()); // OPCPackage | |||
// Powerpoint | |||
HANDLERS.put(".ppt", new HSLFFileHandler()); | |||
HANDLERS.put(".pptx", new XSLFFileHandler()); | |||
HANDLERS.put(".pptm", new XSLFFileHandler()); | |||
HANDLERS.put(".ppsm", new XSLFFileHandler()); | |||
HANDLERS.put(".ppsx", new XSLFFileHandler()); | |||
HANDLERS.put(".thmx", new XSLFFileHandler()); | |||
// Outlook | |||
HANDLERS.put(".msg", new HSMFFileHandler()); | |||
// Publisher | |||
HANDLERS.put(".pub", new HPBFFileHandler()); | |||
// Visio | |||
HANDLERS.put(".vsd", new HDGFFileHandler()); | |||
// POIFS | |||
HANDLERS.put(".ole2", new POIFSFileHandler()); | |||
// Microsoft Admin Template? | |||
HANDLERS.put(".adm", new HPSFFileHandler()); | |||
// Microsoft TNEF | |||
HANDLERS.put(".dat", new HMEFFileHandler()); | |||
// TODO: are these readable by some of the formats? | |||
HANDLERS.put(".shw", new NullFileHandler()); | |||
HANDLERS.put(".zvi", new NullFileHandler()); | |||
HANDLERS.put(".mpp", new NullFileHandler()); | |||
HANDLERS.put(".qwp", new NullFileHandler()); | |||
HANDLERS.put(".wps", new NullFileHandler()); | |||
HANDLERS.put(".bin", new NullFileHandler()); | |||
HANDLERS.put(".xps", new NullFileHandler()); | |||
HANDLERS.put(".sldprt", new NullFileHandler()); | |||
HANDLERS.put(".mdb", new NullFileHandler()); | |||
HANDLERS.put(".vml", new NullFileHandler()); | |||
// ignore some file types, images, other formats, ... | |||
HANDLERS.put(".txt", new NullFileHandler()); | |||
HANDLERS.put(".pdf", new NullFileHandler()); | |||
HANDLERS.put(".rtf", new NullFileHandler()); | |||
HANDLERS.put(".gif", new NullFileHandler()); | |||
HANDLERS.put(".html", new NullFileHandler()); | |||
HANDLERS.put(".png", new NullFileHandler()); | |||
HANDLERS.put(".wmf", new NullFileHandler()); | |||
HANDLERS.put(".emf", new NullFileHandler()); | |||
HANDLERS.put(".dib", new NullFileHandler()); | |||
HANDLERS.put(".svg", new NullFileHandler()); | |||
HANDLERS.put(".pict", new NullFileHandler()); | |||
HANDLERS.put(".jpg", new NullFileHandler()); | |||
HANDLERS.put(".wav", new NullFileHandler()); | |||
HANDLERS.put(".pfx", new NullFileHandler()); | |||
HANDLERS.put(".xml", new NullFileHandler()); | |||
HANDLERS.put(".csv", new NullFileHandler()); | |||
// map some files without extension | |||
HANDLERS.put("spreadsheet/BigSSTRecord", new NullFileHandler()); | |||
private static final Map<String, FileHandler> HANDLERS = new HashMap<String, FileHandler>(); | |||
static { | |||
// Excel | |||
HANDLERS.put(".xls", new HSSFFileHandler()); | |||
HANDLERS.put(".xlsx", new XSSFFileHandler()); | |||
HANDLERS.put(".xlsm", new XSSFFileHandler()); | |||
HANDLERS.put(".xltx", new XSSFFileHandler()); | |||
HANDLERS.put(".xlsb", new XSSFFileHandler()); | |||
// Word | |||
HANDLERS.put(".doc", new HWPFFileHandler()); | |||
HANDLERS.put(".docx", new XWPFFileHandler()); | |||
HANDLERS.put(".dotx", new XWPFFileHandler()); | |||
HANDLERS.put(".docm", new XWPFFileHandler()); | |||
HANDLERS.put(".ooxml", new XWPFFileHandler()); // OPCPackage | |||
// Powerpoint | |||
HANDLERS.put(".ppt", new HSLFFileHandler()); | |||
HANDLERS.put(".pptx", new XSLFFileHandler()); | |||
HANDLERS.put(".pptm", new XSLFFileHandler()); | |||
HANDLERS.put(".ppsm", new XSLFFileHandler()); | |||
HANDLERS.put(".ppsx", new XSLFFileHandler()); | |||
HANDLERS.put(".thmx", new XSLFFileHandler()); | |||
// Outlook | |||
HANDLERS.put(".msg", new HSMFFileHandler()); | |||
// Publisher | |||
HANDLERS.put(".pub", new HPBFFileHandler()); | |||
// Visio - binary | |||
HANDLERS.put(".vsd", new HDGFFileHandler()); | |||
// Visio - ooxml (currently unsupported) | |||
HANDLERS.put(".vsdm", new NullFileHandler()); | |||
HANDLERS.put(".vsdx", new NullFileHandler()); | |||
HANDLERS.put(".vssm", new NullFileHandler()); | |||
HANDLERS.put(".vssx", new NullFileHandler()); | |||
HANDLERS.put(".vstm", new NullFileHandler()); | |||
HANDLERS.put(".vstx", new NullFileHandler()); | |||
// POIFS | |||
HANDLERS.put(".ole2", new POIFSFileHandler()); | |||
// Microsoft Admin Template? | |||
HANDLERS.put(".adm", new HPSFFileHandler()); | |||
// Microsoft TNEF | |||
HANDLERS.put(".dat", new HMEFFileHandler()); | |||
// TODO: are these readable by some of the formats? | |||
HANDLERS.put(".shw", new NullFileHandler()); | |||
HANDLERS.put(".zvi", new NullFileHandler()); | |||
HANDLERS.put(".mpp", new NullFileHandler()); | |||
HANDLERS.put(".qwp", new NullFileHandler()); | |||
HANDLERS.put(".wps", new NullFileHandler()); | |||
HANDLERS.put(".bin", new NullFileHandler()); | |||
HANDLERS.put(".xps", new NullFileHandler()); | |||
HANDLERS.put(".sldprt", new NullFileHandler()); | |||
HANDLERS.put(".mdb", new NullFileHandler()); | |||
HANDLERS.put(".vml", new NullFileHandler()); | |||
// ignore some file types, images, other formats, ... | |||
HANDLERS.put(".txt", new NullFileHandler()); | |||
HANDLERS.put(".pdf", new NullFileHandler()); | |||
HANDLERS.put(".rtf", new NullFileHandler()); | |||
HANDLERS.put(".gif", new NullFileHandler()); | |||
HANDLERS.put(".html", new NullFileHandler()); | |||
HANDLERS.put(".png", new NullFileHandler()); | |||
HANDLERS.put(".wmf", new NullFileHandler()); | |||
HANDLERS.put(".emf", new NullFileHandler()); | |||
HANDLERS.put(".dib", new NullFileHandler()); | |||
HANDLERS.put(".svg", new NullFileHandler()); | |||
HANDLERS.put(".pict", new NullFileHandler()); | |||
HANDLERS.put(".jpg", new NullFileHandler()); | |||
HANDLERS.put(".wav", new NullFileHandler()); | |||
HANDLERS.put(".pfx", new NullFileHandler()); | |||
HANDLERS.put(".xml", new NullFileHandler()); | |||
HANDLERS.put(".csv", new NullFileHandler()); | |||
// map some files without extension | |||
HANDLERS.put("spreadsheet/BigSSTRecord", new NullFileHandler()); | |||
HANDLERS.put("spreadsheet/BigSSTRecord2", new NullFileHandler()); | |||
HANDLERS.put("spreadsheet/BigSSTRecord2CR1", new NullFileHandler()); | |||
HANDLERS.put("spreadsheet/BigSSTRecord2CR2", new NullFileHandler()); | |||
@@ -151,88 +160,104 @@ public class TestAllFiles { | |||
HANDLERS.put("spreadsheet/BigSSTRecord2CR6", new NullFileHandler()); | |||
HANDLERS.put("spreadsheet/BigSSTRecord2CR7", new NullFileHandler()); | |||
HANDLERS.put("spreadsheet/BigSSTRecordCR", new NullFileHandler()); | |||
HANDLERS.put("spreadsheet/test_properties1", new NullFileHandler()); | |||
} | |||
private static final Set<String> EXPECTED_FAILURES = new HashSet<String>(); | |||
static { | |||
// password protected files | |||
EXPECTED_FAILURES.add("spreadsheet/password.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/51832.xls"); | |||
EXPECTED_FAILURES.add("document/PasswordProtected.doc"); | |||
EXPECTED_FAILURES.add("slideshow/Password_Protected-hello.ppt"); | |||
EXPECTED_FAILURES.add("slideshow/Password_Protected-56-hello.ppt"); | |||
EXPECTED_FAILURES.add("slideshow/Password_Protected-np-hello.ppt"); | |||
EXPECTED_FAILURES.add("slideshow/cryptoapi-proc2356.ppt"); | |||
//EXPECTED_FAILURES.add("document/bug53475-password-is-pass.docx"); | |||
//EXPECTED_FAILURES.add("document/bug53475-password-is-solrcell.docx"); | |||
EXPECTED_FAILURES.add("spreadsheet/xor-encryption-abc.xls"); | |||
HANDLERS.put("spreadsheet/test_properties1", new NullFileHandler()); | |||
} | |||
// Old Word Documents where we can at least extract some text | |||
private static final Set<String> OLD_FILES = new HashSet<String>(); | |||
static { | |||
OLD_FILES.add("document/Bug49933.doc"); | |||
OLD_FILES.add("document/Bug51944.doc"); | |||
OLD_FILES.add("document/Word6.doc"); | |||
OLD_FILES.add("document/Word6_sections.doc"); | |||
OLD_FILES.add("document/Word6_sections2.doc"); | |||
OLD_FILES.add("document/Word95.doc"); | |||
OLD_FILES.add("document/word95err.doc"); | |||
OLD_FILES.add("hpsf/TestMickey.doc"); | |||
OLD_FILES.add("document/52117.doc"); | |||
} | |||
private static final Set<String> EXPECTED_FAILURES = new HashSet<String>(); | |||
static { | |||
// password protected files | |||
EXPECTED_FAILURES.add("spreadsheet/password.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/protected_passtika.xlsx"); | |||
EXPECTED_FAILURES.add("spreadsheet/51832.xls"); | |||
EXPECTED_FAILURES.add("document/PasswordProtected.doc"); | |||
EXPECTED_FAILURES.add("slideshow/Password_Protected-hello.ppt"); | |||
EXPECTED_FAILURES.add("slideshow/Password_Protected-56-hello.ppt"); | |||
EXPECTED_FAILURES.add("slideshow/Password_Protected-np-hello.ppt"); | |||
EXPECTED_FAILURES.add("slideshow/cryptoapi-proc2356.ppt"); | |||
//EXPECTED_FAILURES.add("document/bug53475-password-is-pass.docx"); | |||
//EXPECTED_FAILURES.add("document/bug53475-password-is-solrcell.docx"); | |||
EXPECTED_FAILURES.add("spreadsheet/xor-encryption-abc.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/35897-type4.xls"); | |||
//EXPECTED_FAILURES.add("poifs/protect.xlsx"); | |||
//EXPECTED_FAILURES.add("poifs/protected_sha512.xlsx"); | |||
//EXPECTED_FAILURES.add("poifs/extenxls_pwd123.xlsx"); | |||
//EXPECTED_FAILURES.add("poifs/protected_agile.docx"); | |||
// TODO: fails XMLExportTest, is this ok? | |||
EXPECTED_FAILURES.add("spreadsheet/CustomXMLMapping-singleattributenamespace.xlsx"); | |||
EXPECTED_FAILURES.add("spreadsheet/55864.xlsx"); | |||
// TODO: these fail now with some NPE/file read error because we now try to compute every value via Cell.toString()! | |||
EXPECTED_FAILURES.add("spreadsheet/44958.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/44958_1.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/testArraysAndTables.xls"); | |||
// TODO: good to ignore? | |||
EXPECTED_FAILURES.add("spreadsheet/sample-beta.xlsx"); | |||
EXPECTED_FAILURES.add("spreadsheet/49931.xls"); | |||
EXPECTED_FAILURES.add("openxml4j/ContentTypeHasParameters.ooxml"); | |||
// This is actually a spreadsheet! | |||
EXPECTED_FAILURES.add("hpsf/TestRobert_Flaherty.doc"); | |||
// some files that are broken, Excel 5.0/95, Word 95, ... | |||
EXPECTED_FAILURES.add("spreadsheet/43493.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/46904.xls"); | |||
EXPECTED_FAILURES.add("document/56880.doc"); | |||
EXPECTED_FAILURES.add("document/Bug49933.doc"); | |||
EXPECTED_FAILURES.add("document/Bug50955.doc"); | |||
EXPECTED_FAILURES.add("document/Bug51944.doc"); | |||
EXPECTED_FAILURES.add("document/Word6.doc"); | |||
EXPECTED_FAILURES.add("document/Word6_sections.doc"); | |||
EXPECTED_FAILURES.add("document/Word6_sections2.doc"); | |||
EXPECTED_FAILURES.add("document/Word95.doc"); | |||
EXPECTED_FAILURES.add("document/word95err.doc"); | |||
EXPECTED_FAILURES.add("hpsf/TestMickey.doc"); | |||
EXPECTED_FAILURES.add("slideshow/PPT95.ppt"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_DerivedPartNameFAIL.docx"); | |||
EXPECTED_FAILURES.add("spreadsheet/54764-2.xlsx"); // see TestXSSFBugs.bug54764() | |||
EXPECTED_FAILURES.add("spreadsheet/54764.xlsx"); // see TestXSSFBugs.bug54764() | |||
// TODO: fails XMLExportTest, is this ok? | |||
EXPECTED_FAILURES.add("spreadsheet/CustomXMLMapping-singleattributenamespace.xlsx"); | |||
EXPECTED_FAILURES.add("spreadsheet/55864.xlsx"); | |||
// TODO: these fail now with some NPE/file read error because we now try to compute every value via Cell.toString()! | |||
EXPECTED_FAILURES.add("spreadsheet/44958.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/44958_1.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/testArraysAndTables.xls"); | |||
// TODO: good to ignore? | |||
EXPECTED_FAILURES.add("spreadsheet/sample-beta.xlsx"); | |||
EXPECTED_FAILURES.add("spreadsheet/49931.xls"); | |||
EXPECTED_FAILURES.add("openxml4j/ContentTypeHasParameters.ooxml"); | |||
// This is actually a spreadsheet! | |||
EXPECTED_FAILURES.add("hpsf/TestRobert_Flaherty.doc"); | |||
// some files that are broken, eg Word 95, ... | |||
EXPECTED_FAILURES.add("spreadsheet/43493.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/46904.xls"); | |||
EXPECTED_FAILURES.add("document/56880.doc"); | |||
EXPECTED_FAILURES.add("document/Bug50955.doc"); | |||
EXPECTED_FAILURES.add("slideshow/PPT95.ppt"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx"); | |||
EXPECTED_FAILURES.add("openxml4j/OPCCompliance_DerivedPartNameFAIL.docx"); | |||
EXPECTED_FAILURES.add("spreadsheet/54764-2.xlsx"); // see TestXSSFBugs.bug54764() | |||
EXPECTED_FAILURES.add("spreadsheet/54764.xlsx"); // see TestXSSFBugs.bug54764() | |||
EXPECTED_FAILURES.add("spreadsheet/Simple.xlsb"); | |||
EXPECTED_FAILURES.add("poifs/unknown_properties.msg"); // POIFS properties corrupted | |||
EXPECTED_FAILURES.add("poifs/only-zero-byte-streams.ole2"); // No actual contents | |||
// old Excel files, which we only support simple text extraction of | |||
EXPECTED_FAILURES.add("spreadsheet/testEXCEL_2.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/testEXCEL_3.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/testEXCEL_4.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/testEXCEL_5.xls"); | |||
EXPECTED_FAILURES.add("spreadsheet/testEXCEL_95.xls"); | |||
// OOXML Strict is not yet supported, see bug #57699 | |||
EXPECTED_FAILURES.add("spreadsheet/SampleSS.strict.xlsx"); | |||
EXPECTED_FAILURES.add("spreadsheet/SimpleStrict.xlsx"); | |||
EXPECTED_FAILURES.add("spreadsheet/sample.strict.xlsx"); | |||
// non-TNEF files | |||
EXPECTED_FAILURES.add("ddf/Container.dat"); | |||
EXPECTED_FAILURES.add("ddf/47143.dat"); | |||
} | |||
// non-TNEF files | |||
EXPECTED_FAILURES.add("ddf/Container.dat"); | |||
EXPECTED_FAILURES.add("ddf/47143.dat"); | |||
} | |||
@Parameters(name="{index}: {0} using {1}") | |||
public static Iterable<Object[]> files() { | |||
DirectoryScanner scanner = new DirectoryScanner(); | |||
scanner.setBasedir(ROOT_DIR); | |||
scanner.setExcludes(new String[] { "**/.svn/**" }); | |||
scanner.scan(); | |||
System.out.println("Handling " + scanner.getIncludedFiles().length + " files"); | |||
List<Object[]> files = new ArrayList<Object[]>(); | |||
@@ -240,47 +265,72 @@ public class TestAllFiles { | |||
file = file.replace('\\', '/'); // ... failures/handlers lookup doesn't work on windows otherwise | |||
files.add(new Object[] { file, HANDLERS.get(getExtension(file)) }); | |||
} | |||
return files; | |||
} | |||
} | |||
@Parameter(value=0) | |||
public String file; | |||
@Parameter(value=1) | |||
public FileHandler handler; | |||
@Test | |||
public void testAllFiles() throws Exception { | |||
assertNotNull("Unknown file extension for file: " + file + ": " + getExtension(file), handler); | |||
InputStream stream = new BufferedInputStream(new FileInputStream(new File(ROOT_DIR, file)),100); | |||
try { | |||
handler.handleFile(stream); | |||
assertFalse("Expected to fail for file " + file + " and handler " + handler + ", but did not fail!", | |||
EXPECTED_FAILURES.contains(file)); | |||
} catch (Exception e) { | |||
// check if we expect failure for this file | |||
if(!EXPECTED_FAILURES.contains(file)) { | |||
throw new Exception("While handling " + file, e); | |||
} | |||
} finally { | |||
stream.close(); | |||
} | |||
} | |||
private static String getExtension(String file) { | |||
int pos = file.lastIndexOf('.'); | |||
if(pos == -1 || pos == file.length()-1) { | |||
return file; | |||
} | |||
return file.substring(pos); | |||
} | |||
private static class NullFileHandler implements FileHandler { | |||
@Override | |||
assertNotNull("Unknown file extension for file: " + file + ": " + getExtension(file), handler); | |||
File inputFile = new File(ROOT_DIR, file); | |||
try { | |||
InputStream stream = new BufferedInputStream(new FileInputStream(inputFile), 64*1024); | |||
try { | |||
handler.handleFile(stream); | |||
assertFalse("Expected to fail for file " + file + " and handler " + handler + ", but did not fail!", | |||
OLD_FILES.contains(file)); | |||
} finally { | |||
stream.close(); | |||
} | |||
handler.handleExtracting(inputFile); | |||
assertFalse("Expected to fail for file " + file + " and handler " + handler + ", but did not fail!", | |||
EXPECTED_FAILURES.contains(file)); | |||
} catch (OldWordFileFormatException e) { | |||
// for old word files we should still support extracting text | |||
if(OLD_FILES.contains(file)) { | |||
handler.handleExtracting(inputFile); | |||
} else { | |||
// check if we expect failure for this file | |||
if(!EXPECTED_FAILURES.contains(file) && !AbstractFileHandler.EXPECTED_EXTRACTOR_FAILURES.contains(file)) { | |||
System.out.println("Failed: " + file); | |||
throw new Exception("While handling " + file, e); | |||
} | |||
} | |||
} catch (Exception e) { | |||
// check if we expect failure for this file | |||
if(!EXPECTED_FAILURES.contains(file) && !AbstractFileHandler.EXPECTED_EXTRACTOR_FAILURES.contains(file)) { | |||
System.out.println("Failed: " + file); | |||
throw new Exception("While handling " + file, e); | |||
} | |||
} | |||
} | |||
private static String getExtension(String file) { | |||
int pos = file.lastIndexOf('.'); | |||
if(pos == -1 || pos == file.length()-1) { | |||
return file; | |||
} | |||
return file.substring(pos); | |||
} | |||
private static class NullFileHandler implements FileHandler { | |||
@Override | |||
public void handleFile(InputStream stream) throws Exception { | |||
} | |||
} | |||
} | |||
@Override | |||
public void handleExtracting(File file) throws Exception { | |||
} | |||
} | |||
} |
@@ -0,0 +1,143 @@ | |||
/* ==================================================================== | |||
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.stress; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertNotNull; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileNotFoundException; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import java.util.zip.ZipException; | |||
import org.apache.poi.POIOLE2TextExtractor; | |||
import org.apache.poi.POITextExtractor; | |||
import org.apache.poi.dev.OOXMLPrettyPrint; | |||
import org.apache.poi.extractor.ExtractorFactory; | |||
import org.apache.poi.hpsf.extractor.HPSFPropertiesExtractor; | |||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||
import org.apache.poi.openxml4j.exceptions.OpenXML4JException; | |||
import org.apache.xmlbeans.XmlException; | |||
public abstract class AbstractFileHandler implements FileHandler { | |||
public static final Set<String> EXPECTED_EXTRACTOR_FAILURES = new HashSet<String>(); | |||
static { | |||
// password protected files | |||
EXPECTED_EXTRACTOR_FAILURES.add("document/bug53475-password-is-pass.docx"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("poifs/extenxls_pwd123.xlsx"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("poifs/protect.xlsx"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("poifs/protected_agile.docx"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("poifs/protected_sha512.xlsx"); | |||
// unsupported file-types, no supported OLE2 parts | |||
EXPECTED_EXTRACTOR_FAILURES.add("hmef/quick-winmail.dat"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("hmef/winmail-sample1.dat"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("hmef/bug52400-winmail-simple.dat"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("hmef/bug52400-winmail-with-attachments.dat"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("hpsf/Test0313rur.adm"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("hsmf/attachment_msg_pdf.msg"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("poifs/Notes.ole2"); | |||
EXPECTED_EXTRACTOR_FAILURES.add("slideshow/testPPT.thmx"); | |||
} | |||
public void handleExtracting(File file) throws Exception { | |||
boolean before = ExtractorFactory.getThreadPrefersEventExtractors(); | |||
try { | |||
ExtractorFactory.setThreadPrefersEventExtractors(true); | |||
handleExtractingInternal(file); | |||
ExtractorFactory.setThreadPrefersEventExtractors(false); | |||
handleExtractingInternal(file); | |||
} finally { | |||
ExtractorFactory.setThreadPrefersEventExtractors(before); | |||
} | |||
/* Did fail for some documents with special XML contents... | |||
try { | |||
OOXMLPrettyPrint.main(new String[] { file.getAbsolutePath(), | |||
"/tmp/pretty-" + file.getName() }); | |||
} catch (ZipException e) { | |||
// ignore, not a Zip/OOXML file | |||
}*/ | |||
} | |||
private void handleExtractingInternal(File file) throws Exception { | |||
long length = file.length(); | |||
long modified = file.lastModified(); | |||
POITextExtractor extractor = ExtractorFactory.createExtractor(file); | |||
try { | |||
assertNotNull(extractor); | |||
assertNotNull(extractor.getText()); | |||
// also try metadata | |||
POITextExtractor metadataExtractor = extractor.getMetadataTextExtractor(); | |||
assertNotNull(metadataExtractor.getText()); | |||
assertFalse("Expected Extraction to fail for file " + file + " and handler " + this + ", but did not fail!", | |||
EXPECTED_EXTRACTOR_FAILURES.contains(file)); | |||
assertEquals("File should not be modified by extractor", length, file.length()); | |||
assertEquals("File should not be modified by extractor", modified, file.lastModified()); | |||
handleExtractingAsStream(file); | |||
if(extractor instanceof POIOLE2TextExtractor) { | |||
HPSFPropertiesExtractor hpsfExtractor = new HPSFPropertiesExtractor((POIOLE2TextExtractor)extractor); | |||
try { | |||
assertNotNull(hpsfExtractor.getDocumentSummaryInformationText()); | |||
assertNotNull(hpsfExtractor.getSummaryInformationText()); | |||
String text = hpsfExtractor.getText(); | |||
//System.out.println(text); | |||
assertNotNull(text); | |||
} finally { | |||
hpsfExtractor.close(); | |||
} | |||
} | |||
} catch (IllegalArgumentException e) { | |||
if(!EXPECTED_EXTRACTOR_FAILURES.contains(file)) { | |||
throw new Exception("While handling " + file, e); | |||
} | |||
} finally { | |||
extractor.close(); | |||
} | |||
} | |||
private void handleExtractingAsStream(File file) throws FileNotFoundException, | |||
IOException, InvalidFormatException, OpenXML4JException, | |||
XmlException { | |||
InputStream stream = new FileInputStream(file); | |||
try { | |||
POITextExtractor streamExtractor = ExtractorFactory.createExtractor(stream); | |||
try { | |||
assertNotNull(streamExtractor); | |||
assertNotNull(streamExtractor.getText()); | |||
} finally { | |||
streamExtractor.close(); | |||
} | |||
} finally { | |||
stream.close(); | |||
} | |||
} | |||
} |
@@ -16,6 +16,7 @@ | |||
==================================================================== */ | |||
package org.apache.poi.stress; | |||
import java.io.File; | |||
import java.io.InputStream; | |||
/** | |||
@@ -34,4 +35,10 @@ public interface FileHandler { | |||
* @throws Exception | |||
*/ | |||
void handleFile(InputStream stream) throws Exception; | |||
/** | |||
* Ensures that extracting text from the given file | |||
* is returning some text. | |||
*/ | |||
void handleExtracting(File file) throws Exception; | |||
} |
@@ -19,10 +19,12 @@ package org.apache.poi.stress; | |||
import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertTrue; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.InputStream; | |||
import org.apache.poi.hdgf.HDGFDiagram; | |||
import org.apache.poi.hdgf.extractor.VisioTextExtractor; | |||
import org.apache.poi.hdgf.streams.Stream; | |||
import org.apache.poi.hdgf.streams.TrailerStream; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
@@ -48,11 +50,27 @@ public class HDGFFileHandler extends POIFSFileHandler { | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Test | |||
public void test() throws Exception { | |||
InputStream stream = new FileInputStream("test-data/diagram/44501.vsd"); | |||
File file = new File("test-data/diagram/44501.vsd"); | |||
InputStream stream = new FileInputStream(file); | |||
try { | |||
handleFile(stream); | |||
} finally { | |||
stream.close(); | |||
} | |||
handleExtracting(file); | |||
stream = new FileInputStream(file); | |||
try { | |||
VisioTextExtractor extractor = new VisioTextExtractor(stream); | |||
try { | |||
assertNotNull(extractor.getText()); | |||
} finally { | |||
extractor.close(); | |||
} | |||
} finally { | |||
stream.close(); | |||
} | |||
} | |||
} |
@@ -26,7 +26,7 @@ import org.apache.poi.hmef.attribute.MAPIAttribute; | |||
import org.apache.poi.hmef.attribute.MAPIStringAttribute; | |||
import org.junit.Test; | |||
public class HMEFFileHandler implements FileHandler { | |||
public class HMEFFileHandler extends AbstractFileHandler { | |||
@Override | |||
public void handleFile(InputStream stream) throws Exception { |
@@ -18,10 +18,12 @@ package org.apache.poi.stress; | |||
import static org.junit.Assert.assertNotNull; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.InputStream; | |||
import org.apache.poi.hpbf.HPBFDocument; | |||
import org.apache.poi.hpbf.extractor.PublisherTextExtractor; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.junit.Test; | |||
@@ -39,11 +41,28 @@ public class HPBFFileHandler extends POIFSFileHandler { | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Test | |||
public void test() throws Exception { | |||
InputStream stream = new FileInputStream("test-data/publisher/SampleBrochure.pub"); | |||
File file = new File("test-data/publisher/SampleBrochure.pub"); | |||
InputStream stream = new FileInputStream(file); | |||
try { | |||
handleFile(stream); | |||
} finally { | |||
stream.close(); | |||
} | |||
handleExtracting(file); | |||
stream = new FileInputStream(file); | |||
try { | |||
PublisherTextExtractor extractor = new PublisherTextExtractor(stream); | |||
try { | |||
assertNotNull(extractor.getText()); | |||
} finally { | |||
extractor.close(); | |||
} | |||
} finally { | |||
stream.close(); | |||
} | |||
} | |||
} |
@@ -18,6 +18,7 @@ package org.apache.poi.stress; | |||
import static org.junit.Assert.assertNotNull; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.InputStream; | |||
@@ -25,7 +26,7 @@ import org.apache.poi.hpsf.HPSFPropertiesOnlyDocument; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.junit.Test; | |||
public class HPSFFileHandler implements FileHandler { | |||
public class HPSFFileHandler extends AbstractFileHandler { | |||
@Override | |||
public void handleFile(InputStream stream) throws Exception { | |||
HPSFPropertiesOnlyDocument hpsf = new HPSFPropertiesOnlyDocument(new POIFSFileSystem(stream)); | |||
@@ -43,4 +44,10 @@ public class HPSFFileHandler implements FileHandler { | |||
stream.close(); | |||
} | |||
} | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Test | |||
public void testExtractor() throws Exception { | |||
handleExtracting(new File("test-data/hpsf/TestBug44375.xls")); | |||
} | |||
} |
@@ -22,6 +22,8 @@ import java.io.FileInputStream; | |||
import java.io.InputStream; | |||
import org.apache.poi.hsmf.MAPIMessage; | |||
import org.apache.poi.hsmf.datatypes.AttachmentChunks; | |||
import org.apache.poi.hsmf.datatypes.DirectoryChunk; | |||
import org.junit.Test; | |||
public class HSMFFileHandler extends POIFSFileHandler { | |||
@@ -32,6 +34,19 @@ public class HSMFFileHandler extends POIFSFileHandler { | |||
assertNotNull(mapi.getDisplayBCC()); | |||
assertNotNull(mapi.getMessageDate()); | |||
AttachmentChunks[] attachments = mapi.getAttachmentFiles(); | |||
for(AttachmentChunks attachment : attachments) { | |||
DirectoryChunk chunkDirectory = attachment.attachmentDirectory; | |||
if(chunkDirectory != null) { | |||
MAPIMessage attachmentMSG = chunkDirectory.getAsEmbededMessage(); | |||
assertNotNull(attachmentMSG); | |||
String body = attachmentMSG.getTextBody(); | |||
assertNotNull(body); | |||
} | |||
} | |||
/* => Writing isn't yet supported... | |||
// write out the file | |||
File file = TempFile.createTempFile("StressTest", ".msg"); |
@@ -16,6 +16,7 @@ | |||
==================================================================== */ | |||
package org.apache.poi.stress; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.InputStream; | |||
@@ -49,4 +50,10 @@ public class HSSFFileHandler extends SpreadsheetHandler { | |||
stream.close(); | |||
} | |||
} | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Test | |||
public void testExtractor() throws Exception { | |||
handleExtracting(new File("test-data/spreadsheet/BOOK_in_capitals.xls")); | |||
} | |||
} |
@@ -18,12 +18,21 @@ package org.apache.poi.stress; | |||
import static org.junit.Assert.assertNotNull; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.PrintWriter; | |||
import java.io.StringWriter; | |||
import org.apache.poi.hdf.extractor.WordDocument; | |||
import org.apache.poi.hwpf.HWPFDocument; | |||
import org.apache.poi.hwpf.extractor.WordExtractor; | |||
import org.junit.Test; | |||
@SuppressWarnings("deprecation") | |||
public class HWPFFileHandler extends POIFSFileHandler { | |||
@Override | |||
public void handleFile(InputStream stream) throws Exception { | |||
@@ -33,16 +42,57 @@ public class HWPFFileHandler extends POIFSFileHandler { | |||
assertNotNull(doc.getEndnotes()); | |||
handlePOIDocument(doc); | |||
// fails for many documents, but is deprecated anyway... | |||
// handleWordDocument(doc); | |||
} | |||
protected void handleWordDocument(HWPFDocument doc) throws IOException { | |||
ByteArrayOutputStream outStream = new ByteArrayOutputStream(); | |||
doc.write(outStream); | |||
WordDocument wordDoc = new WordDocument(new ByteArrayInputStream(outStream.toByteArray())); | |||
StringWriter docTextWriter = new StringWriter(); | |||
PrintWriter out = new PrintWriter(docTextWriter); | |||
try { | |||
wordDoc.writeAllText(out); | |||
} finally { | |||
out.close(); | |||
} | |||
docTextWriter.close(); | |||
} | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Test | |||
public void test() throws Exception { | |||
InputStream stream = new FileInputStream("test-data/document/HeaderFooterUnicode.doc"); | |||
File file = new File("test-data/document/52117.doc"); | |||
InputStream stream = new FileInputStream(file); | |||
try { | |||
handleFile(stream); | |||
} finally { | |||
stream.close(); | |||
} | |||
handleExtracting(file); | |||
stream = new FileInputStream(file); | |||
try { | |||
WordExtractor extractor = new WordExtractor(stream); | |||
try { | |||
assertNotNull(extractor.getText()); | |||
} finally { | |||
extractor.close(); | |||
} | |||
} finally { | |||
stream.close(); | |||
} | |||
} | |||
@Test | |||
public void testExtractingOld() throws Exception { | |||
File file = new File("test-data/document/52117.doc"); | |||
handleExtracting(file); | |||
} | |||
} | |||
} |
@@ -25,7 +25,7 @@ import java.io.InputStream; | |||
import org.apache.poi.POIDocument; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
public class POIFSFileHandler implements FileHandler { | |||
public class POIFSFileHandler extends AbstractFileHandler { | |||
@Override | |||
public void handleFile(InputStream stream) throws Exception { |
@@ -20,9 +20,17 @@ import static org.junit.Assert.assertNotNull; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.List; | |||
import org.apache.poi.POIXMLDocument; | |||
import org.apache.poi.openxml4j.exceptions.OpenXML4JException; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.openxml4j.opc.PackageAccess; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.poifs.crypt.Decryptor; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.junit.Ignore; | |||
import org.junit.Test; | |||
public final class POIXMLDocumentHandler { | |||
protected void handlePOIXMLDocument(POIXMLDocument doc) throws Exception { | |||
@@ -36,11 +44,33 @@ public final class POIXMLDocumentHandler { | |||
protected static boolean isEncrypted(InputStream stream) throws IOException { | |||
if (POIFSFileSystem.hasPOIFSHeader(stream)) { | |||
POIFSFileSystem poifs = new POIFSFileSystem(stream); | |||
if (poifs.getRoot().hasEntry("EncryptedPackage")) { | |||
if (poifs.getRoot().hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)) { | |||
return true; | |||
} | |||
throw new IOException("wrong file format or file extension for OO XML file"); | |||
} | |||
return false; | |||
} | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Ignore("POIXMLDocument cannot handle this Visio file currently...") | |||
@Test | |||
public void test() throws Exception { | |||
OPCPackage pkg = OPCPackage.open("test-data/diagram/test.vsdx", PackageAccess.READ); | |||
try { | |||
handlePOIXMLDocument(new TestPOIXMLDocument(pkg)); | |||
} finally { | |||
pkg.close(); | |||
} | |||
} | |||
private final static class TestPOIXMLDocument extends POIXMLDocument { | |||
public TestPOIXMLDocument(OPCPackage pkg) { | |||
super(pkg); | |||
} | |||
public List<PackagePart> getAllEmbedds() throws OpenXML4JException { | |||
return null; | |||
} | |||
} | |||
} |
@@ -30,7 +30,7 @@ import org.apache.poi.ss.usermodel.Sheet; | |||
import org.apache.poi.ss.usermodel.Workbook; | |||
import org.apache.poi.ss.usermodel.WorkbookFactory; | |||
public abstract class SpreadsheetHandler implements FileHandler { | |||
public abstract class SpreadsheetHandler extends AbstractFileHandler { | |||
public void handleWorkbook(Workbook wb, String extension) throws IOException { | |||
// try to access some of the content | |||
readContent(wb); |
@@ -18,35 +18,97 @@ package org.apache.poi.stress; | |||
import static org.junit.Assert.assertNotNull; | |||
import java.awt.Dimension; | |||
import java.awt.Graphics2D; | |||
import java.awt.image.BufferedImage; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.sl.draw.DrawFactory; | |||
import org.apache.poi.sl.draw.Drawable; | |||
import org.apache.poi.xslf.XSLFSlideShow; | |||
import org.apache.poi.xslf.usermodel.XMLSlideShow; | |||
import org.apache.poi.xslf.usermodel.XSLFNotes; | |||
import org.apache.poi.xslf.usermodel.XSLFShape; | |||
import org.apache.poi.xslf.usermodel.XSLFSlide; | |||
import org.apache.poi.xslf.usermodel.XSLFTextParagraph; | |||
import org.apache.poi.xslf.usermodel.XSLFTextShape; | |||
import org.junit.Test; | |||
public class XSLFFileHandler implements FileHandler { | |||
public class XSLFFileHandler extends AbstractFileHandler { | |||
@Override | |||
public void handleFile(InputStream stream) throws Exception { | |||
// ignore password protected files | |||
if (POIXMLDocumentHandler.isEncrypted(stream)) return; | |||
XSLFSlideShow slide = new XSLFSlideShow(OPCPackage.open(stream)); | |||
assertNotNull(slide.getPresentation()); | |||
assertNotNull(slide.getSlideMasterReferences()); | |||
assertNotNull(slide.getSlideReferences()); | |||
new POIXMLDocumentHandler().handlePOIXMLDocument(slide); | |||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
try { | |||
slide.write(out); | |||
} finally { | |||
out.close(); | |||
} | |||
createBitmaps(out); | |||
} | |||
private void createBitmaps(ByteArrayOutputStream out) throws IOException { | |||
XMLSlideShow ppt = new XMLSlideShow(new ByteArrayInputStream(out.toByteArray())); | |||
Dimension pgsize = ppt.getPageSize(); | |||
for (XSLFSlide xmlSlide : ppt.getSlides()) { | |||
// System.out.println("slide-" + (i + 1)); | |||
// System.out.println("" + xmlSlide[i].getTitle()); | |||
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB); | |||
Graphics2D graphics = img.createGraphics(); | |||
// draw stuff | |||
xmlSlide.draw(graphics); | |||
// Also try to read notes | |||
XSLFNotes notes = xmlSlide.getNotes(); | |||
if(notes != null) { | |||
for (XSLFShape note : notes) { | |||
DrawFactory df = DrawFactory.getInstance(graphics); | |||
Drawable d = df.getDrawable(note); | |||
d.draw(graphics); | |||
if (note instanceof XSLFTextShape) { | |||
XSLFTextShape txShape = (XSLFTextShape) note; | |||
for (XSLFTextParagraph xslfParagraph : txShape.getTextParagraphs()) { | |||
xslfParagraph.getText(); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
ppt.close(); | |||
} | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Test | |||
public void test() throws Exception { | |||
InputStream stream = new FileInputStream("test-data/slideshow/testPPT.pptx"); | |||
InputStream stream = new FileInputStream("test-data/slideshow/SampleShow.pptx"); | |||
try { | |||
handleFile(stream); | |||
} finally { | |||
stream.close(); | |||
} | |||
} | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Test | |||
public void testExtractor() throws Exception { | |||
handleExtracting(new File("test-data/slideshow/testPPT.thmx")); | |||
} | |||
} |
@@ -17,6 +17,7 @@ | |||
package org.apache.poi.stress; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.InputStream; | |||
@@ -71,4 +72,10 @@ public class XSSFFileHandler extends SpreadsheetHandler { | |||
stream.close(); | |||
} | |||
} | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Test | |||
public void testExtractor() throws Exception { | |||
handleExtracting(new File("test-data/spreadsheet/56278.xlsx")); | |||
} | |||
} |
@@ -16,13 +16,15 @@ | |||
==================================================================== */ | |||
package org.apache.poi.stress; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.InputStream; | |||
import java.io.PushbackInputStream; | |||
import org.apache.poi.xwpf.usermodel.XWPFDocument; | |||
import org.junit.Test; | |||
public class XWPFFileHandler implements FileHandler { | |||
public class XWPFFileHandler extends AbstractFileHandler { | |||
@Override | |||
public void handleFile(InputStream stream) throws Exception { | |||
// ignore password protected files | |||
@@ -36,12 +38,16 @@ public class XWPFFileHandler implements FileHandler { | |||
// a test-case to test this locally without executing the full TestAllFiles | |||
@Test | |||
public void test() throws Exception { | |||
InputStream stream = new FileInputStream("test-data/document/footnotes.docx"); | |||
File file = new File("test-data/document/51921-Word-Crash067.docx"); | |||
InputStream stream = new PushbackInputStream(new FileInputStream(file), 100000); | |||
try { | |||
handleFile(stream); | |||
} finally { | |||
stream.close(); | |||
} | |||
handleExtracting(file); | |||
} | |||
} |
@@ -0,0 +1,28 @@ | |||
/* ==================================================================== | |||
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; | |||
/** | |||
* Exception thrown if an Empty (zero byte) file is supplied | |||
*/ | |||
public class EmptyFileException extends IllegalArgumentException { | |||
private static final long serialVersionUID = 1536449292174360166L; | |||
public EmptyFileException() { | |||
super("The supplied file was empty (zero bytes long)"); | |||
} | |||
} |
@@ -16,9 +16,10 @@ | |||
==================================================================== */ | |||
package org.apache.poi; | |||
@SuppressWarnings("serial") | |||
public class EncryptedDocumentException extends IllegalStateException | |||
{ | |||
private static final long serialVersionUID = 7276950444540469193L; | |||
public EncryptedDocumentException(String s) { | |||
super(s); | |||
} |
@@ -30,14 +30,11 @@ import org.apache.poi.hpsf.PropertySet; | |||
import org.apache.poi.hpsf.PropertySetFactory; | |||
import org.apache.poi.hpsf.SummaryInformation; | |||
import org.apache.poi.poifs.crypt.EncryptionInfo; | |||
import org.apache.poi.poifs.filesystem.DirectoryEntry; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
import org.apache.poi.poifs.filesystem.DocumentInputStream; | |||
import org.apache.poi.poifs.filesystem.Entry; | |||
import org.apache.poi.poifs.filesystem.EntryUtils; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.OPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
@@ -45,8 +42,6 @@ import org.apache.poi.util.POILogger; | |||
* This holds the common functionality for all POI | |||
* Document classes. | |||
* Currently, this relates to Document Information Properties | |||
* | |||
* @author Nick Burch | |||
*/ | |||
public abstract class POIDocument { | |||
/** Holds metadata on our document */ | |||
@@ -72,20 +67,23 @@ public abstract class POIDocument { | |||
} | |||
/** | |||
* @deprecated use {@link POIDocument#POIDocument(DirectoryNode)} instead | |||
* Constructs from an old-style OPOIFS | |||
*/ | |||
@Deprecated | |||
protected POIDocument(DirectoryNode dir, POIFSFileSystem fs) { | |||
this.directory = dir; | |||
} | |||
protected POIDocument(POIFSFileSystem fs) { | |||
protected POIDocument(OPOIFSFileSystem fs) { | |||
this(fs.getRoot()); | |||
} | |||
/** | |||
* Constructs from an old-style OPOIFS | |||
*/ | |||
protected POIDocument(NPOIFSFileSystem fs) { | |||
this(fs.getRoot()); | |||
} | |||
/** | |||
* Constructs from the default POIFS | |||
*/ | |||
protected POIDocument(POIFSFileSystem fs) { | |||
this(fs.getRoot()); | |||
} | |||
/** | |||
* Fetch the Document Summary Information of the document | |||
@@ -179,12 +177,13 @@ public abstract class POIDocument { | |||
protected PropertySet getPropertySet(String setName, EncryptionInfo encryptionInfo) { | |||
DirectoryNode dirNode = directory; | |||
NPOIFSFileSystem encPoifs = null; | |||
if (encryptionInfo != null) { | |||
try { | |||
InputStream is = encryptionInfo.getDecryptor().getDataStream(directory); | |||
POIFSFileSystem poifs = new POIFSFileSystem(is); | |||
encPoifs = new NPOIFSFileSystem(is); | |||
is.close(); | |||
dirNode = poifs.getRoot(); | |||
dirNode = encPoifs.getRoot(); | |||
} catch (Exception e) { | |||
logger.log(POILogger.ERROR, "Error getting encrypted property set with name " + setName, e); | |||
return null; | |||
@@ -208,6 +207,11 @@ public abstract class POIDocument { | |||
try { | |||
// Create the Property Set | |||
PropertySet set = PropertySetFactory.create(dis); | |||
// Tidy up if needed | |||
if (encPoifs != null) { | |||
encPoifs.close(); | |||
} | |||
// Return the properties | |||
return set; | |||
} catch(IOException ie) { | |||
// Must be corrupt or something like that | |||
@@ -218,26 +222,39 @@ public abstract class POIDocument { | |||
} | |||
return null; | |||
} | |||
/** | |||
* Writes out the updated standard Document Information Properties (HPSF) | |||
* into the currently open NPOIFSFileSystem | |||
* TODO Implement in-place update | |||
* | |||
* @throws IOException if an error when writing to the open | |||
* {@link NPOIFSFileSystem} occurs | |||
* TODO throws exception if open from stream not file | |||
*/ | |||
protected void writeProperties() throws IOException { | |||
throw new IllegalStateException("In-place write is not yet supported"); | |||
} | |||
/** | |||
* Writes out the standard Documment Information Properties (HPSF) | |||
* Writes out the standard Document Information Properties (HPSF) | |||
* @param outFS the POIFSFileSystem to write the properties into | |||
* | |||
* @throws IOException if an error when writing to the | |||
* {@link POIFSFileSystem} occurs | |||
* {@link NPOIFSFileSystem} occurs | |||
*/ | |||
protected void writeProperties(POIFSFileSystem outFS) throws IOException { | |||
protected void writeProperties(NPOIFSFileSystem outFS) throws IOException { | |||
writeProperties(outFS, null); | |||
} | |||
/** | |||
* Writes out the standard Documment Information Properties (HPSF) | |||
* @param outFS the POIFSFileSystem to write the properties into | |||
* Writes out the standard Document Information Properties (HPSF) | |||
* @param outFS the NPOIFSFileSystem to write the properties into | |||
* @param writtenEntries a list of POIFS entries to add the property names too | |||
* | |||
* @throws IOException if an error when writing to the | |||
* {@link POIFSFileSystem} occurs | |||
* {@link NPOIFSFileSystem} occurs | |||
*/ | |||
protected void writeProperties(POIFSFileSystem outFS, List<String> writtenEntries) throws IOException { | |||
protected void writeProperties(NPOIFSFileSystem outFS, List<String> writtenEntries) throws IOException { | |||
SummaryInformation si = getSummaryInformation(); | |||
if (si != null) { | |||
writePropertySet(SummaryInformation.DEFAULT_STREAM_NAME, si, outFS); | |||
@@ -258,12 +275,12 @@ public abstract class POIDocument { | |||
* Writes out a given ProperySet | |||
* @param name the (POIFS Level) name of the property to write | |||
* @param set the PropertySet to write out | |||
* @param outFS the POIFSFileSystem to write the property into | |||
* @param outFS the NPOIFSFileSystem to write the property into | |||
* | |||
* @throws IOException if an error when writing to the | |||
* {@link POIFSFileSystem} occurs | |||
* {@link NPOIFSFileSystem} occurs | |||
*/ | |||
protected void writePropertySet(String name, PropertySet set, POIFSFileSystem outFS) throws IOException { | |||
protected void writePropertySet(String name, PropertySet set, NPOIFSFileSystem outFS) throws IOException { | |||
try { | |||
MutablePropertySet mSet = new MutablePropertySet(set); | |||
ByteArrayOutputStream bOut = new ByteArrayOutputStream(); | |||
@@ -288,55 +305,4 @@ public abstract class POIDocument { | |||
* @throws IOException thrown on errors writing to the stream | |||
*/ | |||
public abstract void write(OutputStream out) throws IOException; | |||
/** | |||
* Copies nodes from one POIFS to the other minus the excepts | |||
* @param source is the source POIFS to copy from | |||
* @param target is the target POIFS to copy to | |||
* @param excepts is a list of Strings specifying what nodes NOT to copy | |||
* | |||
* @throws IOException thrown on errors writing to the target file system. | |||
* | |||
* @deprecated Use {@link EntryUtils#copyNodes(DirectoryEntry, DirectoryEntry, List)} instead | |||
*/ | |||
@Deprecated | |||
protected void copyNodes( POIFSFileSystem source, POIFSFileSystem target, | |||
List<String> excepts ) throws IOException { | |||
EntryUtils.copyNodes( source, target, excepts ); | |||
} | |||
/** | |||
* Copies nodes from one POIFS to the other minus the excepts | |||
* @param sourceRoot is the source POIFS to copy from | |||
* @param targetRoot is the target POIFS to copy to | |||
* @param excepts is a list of Strings specifying what nodes NOT to copy | |||
* | |||
* @throws IOException thrown on errors writing to the target directory node. | |||
* | |||
* @deprecated Use {@link EntryUtils#copyNodes(DirectoryEntry, DirectoryEntry, List)} instead | |||
*/ | |||
@Deprecated | |||
protected void copyNodes( DirectoryNode sourceRoot, | |||
DirectoryNode targetRoot, List<String> excepts ) throws IOException | |||
{ | |||
EntryUtils.copyNodes( sourceRoot, targetRoot, excepts ); | |||
} | |||
/** | |||
* Copies an Entry into a target POIFS directory, recursively | |||
* | |||
* @param entry the entry to copy from | |||
* @param target the entry to write to | |||
* | |||
* @throws IOException thrown on errors writing to the target directory entry. | |||
* | |||
* @deprecated Use {@link EntryUtils#copyNodeRecursively(Entry, DirectoryEntry)} instead | |||
*/ | |||
@Internal | |||
@Deprecated | |||
protected void copyNodeRecursively( Entry entry, DirectoryEntry target ) | |||
throws IOException | |||
{ | |||
EntryUtils.copyNodeRecursively( entry, target ); | |||
} | |||
} |
@@ -20,7 +20,6 @@ import org.apache.poi.hpsf.DocumentSummaryInformation; | |||
import org.apache.poi.hpsf.SummaryInformation; | |||
import org.apache.poi.hpsf.extractor.HPSFPropertiesExtractor; | |||
import org.apache.poi.poifs.filesystem.DirectoryEntry; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
/** | |||
* Common Parent for OLE2 based Text Extractors | |||
@@ -34,15 +33,27 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
* @see org.apache.poi.hwpf.extractor.WordExtractor | |||
*/ | |||
public abstract class POIOLE2TextExtractor extends POITextExtractor { | |||
/** The POIDocument that's open */ | |||
protected POIDocument document; | |||
/** | |||
* Creates a new text extractor for the given document | |||
* | |||
* @param document The POIDocument to use in this extractor. | |||
*/ | |||
public POIOLE2TextExtractor(POIDocument document) { | |||
super(document); | |||
this.document = document; | |||
} | |||
/** | |||
* Creates a new text extractor, using the same | |||
* document as another text extractor. Normally | |||
* only used by properties extractors. | |||
*/ | |||
protected POIOLE2TextExtractor(POIOLE2TextExtractor otherExtractor) { | |||
this.document = otherExtractor.document; | |||
} | |||
/** | |||
* Returns the document information metadata for the document | |||
* | |||
@@ -81,17 +92,4 @@ public abstract class POIOLE2TextExtractor extends POITextExtractor { | |||
{ | |||
return document.directory; | |||
} | |||
/** | |||
* Return the underlying POIFS FileSystem of this document. | |||
* | |||
* @return the POIFSFileSystem that is associated with the POIDocument of this extractor. | |||
* | |||
* @deprecated Use {@link #getRoot()} instead | |||
*/ | |||
@Deprecated | |||
public POIFSFileSystem getFileSystem() | |||
{ | |||
return document.directory.getFileSystem(); | |||
} | |||
} |
@@ -31,24 +31,6 @@ import java.io.IOException; | |||
* @see org.apache.poi.hwpf.extractor.WordExtractor | |||
*/ | |||
public abstract class POITextExtractor implements Closeable { | |||
/** The POIDocument that's open */ | |||
protected POIDocument document; | |||
/** | |||
* Creates a new text extractor for the given document | |||
*/ | |||
public POITextExtractor(POIDocument document) { | |||
this.document = document; | |||
} | |||
/** | |||
* Creates a new text extractor, using the same | |||
* document as another text extractor. Normally | |||
* only used by properties extractors. | |||
*/ | |||
protected POITextExtractor(POITextExtractor otherExtractor) { | |||
this.document = otherExtractor.document; | |||
} | |||
/** | |||
* Retrieves all the text from the document. | |||
* How cells, paragraphs etc are separated in the text |
@@ -82,6 +82,13 @@ public final class EscherPropertyFactory { | |||
pos += ((EscherArrayProperty)p).setArrayData(data, pos); | |||
} else { | |||
byte[] complexData = ((EscherComplexProperty)p).getComplexData(); | |||
int leftover = data.length-pos; | |||
if(leftover < complexData.length){ | |||
throw new IllegalStateException("Could not read complex escher property, lenght was " + complexData.length + ", but had only " + | |||
leftover + " bytes left"); | |||
} | |||
System.arraycopy(data, pos, complexData, 0, complexData.length); | |||
pos += complexData.length; | |||
} |
@@ -24,6 +24,7 @@ import java.util.List; | |||
import org.apache.poi.POIDocument; | |||
import org.apache.poi.poifs.filesystem.EntryUtils; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.OPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
/** | |||
@@ -36,6 +37,9 @@ public class HPSFPropertiesOnlyDocument extends POIDocument { | |||
public HPSFPropertiesOnlyDocument(NPOIFSFileSystem fs) { | |||
super(fs.getRoot()); | |||
} | |||
public HPSFPropertiesOnlyDocument(OPOIFSFileSystem fs) { | |||
super(fs); | |||
} | |||
public HPSFPropertiesOnlyDocument(POIFSFileSystem fs) { | |||
super(fs); | |||
} | |||
@@ -44,7 +48,7 @@ public class HPSFPropertiesOnlyDocument extends POIDocument { | |||
* Write out, with any properties changes, but nothing else | |||
*/ | |||
public void write(OutputStream out) throws IOException { | |||
POIFSFileSystem fs = new POIFSFileSystem(); | |||
NPOIFSFileSystem fs = new NPOIFSFileSystem(); | |||
// For tracking what we've written out, so far | |||
List<String> excepts = new ArrayList<String>(1); |
@@ -285,7 +285,7 @@ public class Section | |||
/* | |||
* Extract the dictionary (if available). | |||
*/ | |||
dictionary = (Map) getProperty(0); | |||
dictionary = (Map<Long,String>) getProperty(0); | |||
} | |||
@@ -23,6 +23,7 @@ import java.io.IOException; | |||
import java.util.Iterator; | |||
import org.apache.poi.POIDocument; | |||
import org.apache.poi.POIOLE2TextExtractor; | |||
import org.apache.poi.POITextExtractor; | |||
import org.apache.poi.hpsf.CustomProperties; | |||
import org.apache.poi.hpsf.DocumentSummaryInformation; | |||
@@ -39,10 +40,10 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
* build in and custom, returning them in | |||
* textual form. | |||
*/ | |||
public class HPSFPropertiesExtractor extends POITextExtractor { | |||
public class HPSFPropertiesExtractor extends POIOLE2TextExtractor { | |||
private Closeable toClose; | |||
public HPSFPropertiesExtractor(POITextExtractor mainExtractor) { | |||
public HPSFPropertiesExtractor(POIOLE2TextExtractor mainExtractor) { | |||
super(mainExtractor); | |||
} | |||
public HPSFPropertiesExtractor(POIDocument doc) { | |||
@@ -57,6 +58,10 @@ public class HPSFPropertiesExtractor extends POITextExtractor { | |||
} | |||
public String getDocumentSummaryInformationText() { | |||
if(document == null) { // event based extractor does not have a document | |||
return ""; | |||
} | |||
DocumentSummaryInformation dsi = document.getDocumentSummaryInformation(); | |||
StringBuffer text = new StringBuffer(); | |||
@@ -78,6 +83,10 @@ public class HPSFPropertiesExtractor extends POITextExtractor { | |||
return text.toString(); | |||
} | |||
public String getSummaryInformationText() { | |||
if(document == null) { // event based extractor does not have a document | |||
return ""; | |||
} | |||
SummaryInformation si = document.getSummaryInformation(); | |||
// Just normal properties |
@@ -34,7 +34,7 @@ import org.apache.poi.hssf.record.DrawingGroupRecord; | |||
import org.apache.poi.hssf.record.EscherAggregate; | |||
import org.apache.poi.hssf.usermodel.HSSFPatriarch; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
/** | |||
* Utility for representing drawings contained in a binary Excel file as a XML tree | |||
@@ -138,7 +138,7 @@ public class BiffDrawingToXml { | |||
} | |||
public static void writeToFile(OutputStream fos, InputStream xlsWorkbook, boolean excludeWorkbookRecords, String[] params) throws IOException { | |||
POIFSFileSystem fs = new POIFSFileSystem(xlsWorkbook); | |||
NPOIFSFileSystem fs = new NPOIFSFileSystem(xlsWorkbook); | |||
HSSFWorkbook workbook = new HSSFWorkbook(fs); | |||
InternalWorkbook internalWorkbook = getInternalWorkbook(workbook); | |||
DrawingGroupRecord r = (DrawingGroupRecord) internalWorkbook.findFirstRecordBySid(DrawingGroupRecord.sid); |
@@ -17,13 +17,63 @@ | |||
package org.apache.poi.hssf.dev; | |||
import java.io.*; | |||
import java.io.DataInputStream; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileNotFoundException; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.io.OutputStreamWriter; | |||
import java.io.PrintStream; | |||
import java.io.Writer; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.hssf.record.*; | |||
import org.apache.poi.hssf.record.RecordInputStream.LeftoverDataException; | |||
import org.apache.poi.hssf.record.chart.*; | |||
import org.apache.poi.hssf.record.chart.AreaFormatRecord; | |||
import org.apache.poi.hssf.record.chart.AreaRecord; | |||
import org.apache.poi.hssf.record.chart.AxisLineFormatRecord; | |||
import org.apache.poi.hssf.record.chart.AxisOptionsRecord; | |||
import org.apache.poi.hssf.record.chart.AxisParentRecord; | |||
import org.apache.poi.hssf.record.chart.AxisRecord; | |||
import org.apache.poi.hssf.record.chart.AxisUsedRecord; | |||
import org.apache.poi.hssf.record.chart.BarRecord; | |||
import org.apache.poi.hssf.record.chart.BeginRecord; | |||
import org.apache.poi.hssf.record.chart.CatLabRecord; | |||
import org.apache.poi.hssf.record.chart.CategorySeriesAxisRecord; | |||
import org.apache.poi.hssf.record.chart.ChartEndBlockRecord; | |||
import org.apache.poi.hssf.record.chart.ChartEndObjectRecord; | |||
import org.apache.poi.hssf.record.chart.ChartFRTInfoRecord; | |||
import org.apache.poi.hssf.record.chart.ChartFormatRecord; | |||
import org.apache.poi.hssf.record.chart.ChartRecord; | |||
import org.apache.poi.hssf.record.chart.ChartStartBlockRecord; | |||
import org.apache.poi.hssf.record.chart.ChartStartObjectRecord; | |||
import org.apache.poi.hssf.record.chart.DatRecord; | |||
import org.apache.poi.hssf.record.chart.DataFormatRecord; | |||
import org.apache.poi.hssf.record.chart.DefaultDataLabelTextPropertiesRecord; | |||
import org.apache.poi.hssf.record.chart.EndRecord; | |||
import org.apache.poi.hssf.record.chart.FontBasisRecord; | |||
import org.apache.poi.hssf.record.chart.FontIndexRecord; | |||
import org.apache.poi.hssf.record.chart.FrameRecord; | |||
import org.apache.poi.hssf.record.chart.LegendRecord; | |||
import org.apache.poi.hssf.record.chart.LineFormatRecord; | |||
import org.apache.poi.hssf.record.chart.LinkedDataRecord; | |||
import org.apache.poi.hssf.record.chart.ObjectLinkRecord; | |||
import org.apache.poi.hssf.record.chart.PlotAreaRecord; | |||
import org.apache.poi.hssf.record.chart.PlotGrowthRecord; | |||
import org.apache.poi.hssf.record.chart.SeriesIndexRecord; | |||
import org.apache.poi.hssf.record.chart.SeriesListRecord; | |||
import org.apache.poi.hssf.record.chart.SeriesRecord; | |||
import org.apache.poi.hssf.record.chart.SeriesTextRecord; | |||
import org.apache.poi.hssf.record.chart.SeriesToChartGroupRecord; | |||
import org.apache.poi.hssf.record.chart.SheetPropertiesRecord; | |||
import org.apache.poi.hssf.record.chart.TextRecord; | |||
import org.apache.poi.hssf.record.chart.TickRecord; | |||
import org.apache.poi.hssf.record.chart.UnitsRecord; | |||
import org.apache.poi.hssf.record.chart.ValueRangeRecord; | |||
import org.apache.poi.hssf.record.pivottable.DataItemRecord; | |||
import org.apache.poi.hssf.record.pivottable.ExtendedPivotTableViewFieldsRecord; | |||
import org.apache.poi.hssf.record.pivottable.PageItemRecord; | |||
@@ -32,330 +82,330 @@ import org.apache.poi.hssf.record.pivottable.ViewDefinitionRecord; | |||
import org.apache.poi.hssf.record.pivottable.ViewFieldsRecord; | |||
import org.apache.poi.hssf.record.pivottable.ViewSourceRecord; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.LittleEndian; | |||
/** | |||
* Utillity for reading in BIFF8 records and displaying data from them. | |||
* | |||
*@author Andrew C. Oliver (acoliver at apache dot org) | |||
*@author Glen Stampoultzis (glens at apache.org) | |||
*@see #main | |||
* @see #main | |||
*/ | |||
public final class BiffViewer { | |||
static final char[] NEW_LINE_CHARS = System.getProperty("line.separator").toCharArray(); | |||
static final char[] NEW_LINE_CHARS = System.getProperty("line.separator").toCharArray(); | |||
private BiffViewer() { | |||
// no instances of this class | |||
} | |||
/** | |||
* Create an array of records from an input stream | |||
* | |||
*@param is the InputStream from which the records will be obtained | |||
*@return an array of Records created from the InputStream | |||
*@exception RecordFormatException on error processing the InputStream | |||
*/ | |||
public static Record[] createRecords(InputStream is, PrintStream ps, BiffRecordListener recListener, boolean dumpInterpretedRecords) | |||
throws RecordFormatException { | |||
List<Record> temp = new ArrayList<Record>(); | |||
RecordInputStream recStream = new RecordInputStream(is); | |||
while (true) { | |||
boolean hasNext; | |||
try { | |||
hasNext = recStream.hasNextRecord(); | |||
} catch (LeftoverDataException e) { | |||
e.printStackTrace(); | |||
System.err.println("Discarding " + recStream.remaining() + " bytes and continuing"); | |||
recStream.readRemainder(); | |||
hasNext = recStream.hasNextRecord(); | |||
} | |||
if (!hasNext) { | |||
break; | |||
} | |||
recStream.nextRecord(); | |||
if (recStream.getSid() == 0) { | |||
continue; | |||
} | |||
Record record; | |||
if (dumpInterpretedRecords) { | |||
record = createRecord (recStream); | |||
if (record.getSid() == ContinueRecord.sid) { | |||
continue; | |||
} | |||
temp.add(record); | |||
if (dumpInterpretedRecords) { | |||
for (String header : recListener.getRecentHeaders()) { | |||
ps.println(header); | |||
} | |||
ps.print(record.toString()); | |||
} | |||
} else { | |||
recStream.readRemainder(); | |||
} | |||
ps.println(); | |||
} | |||
Record[] result = new Record[temp.size()]; | |||
temp.toArray(result); | |||
return result; | |||
} | |||
/** | |||
* Essentially a duplicate of RecordFactory. Kept separate as not to screw | |||
* up non-debug operations. | |||
* | |||
*/ | |||
private static Record createRecord(RecordInputStream in) { | |||
switch (in.getSid()) { | |||
case AreaFormatRecord.sid: return new AreaFormatRecord(in); | |||
case AreaRecord.sid: return new AreaRecord(in); | |||
case ArrayRecord.sid: return new ArrayRecord(in); | |||
case AxisLineFormatRecord.sid: return new AxisLineFormatRecord(in); | |||
case AxisOptionsRecord.sid: return new AxisOptionsRecord(in); | |||
case AxisParentRecord.sid: return new AxisParentRecord(in); | |||
case AxisRecord.sid: return new AxisRecord(in); | |||
case AxisUsedRecord.sid: return new AxisUsedRecord(in); | |||
case AutoFilterInfoRecord.sid: return new AutoFilterInfoRecord(in); | |||
case BOFRecord.sid: return new BOFRecord(in); | |||
case BackupRecord.sid: return new BackupRecord(in); | |||
case BarRecord.sid: return new BarRecord(in); | |||
case BeginRecord.sid: return new BeginRecord(in); | |||
case BlankRecord.sid: return new BlankRecord(in); | |||
case BookBoolRecord.sid: return new BookBoolRecord(in); | |||
case BoolErrRecord.sid: return new BoolErrRecord(in); | |||
case BottomMarginRecord.sid: return new BottomMarginRecord(in); | |||
case BoundSheetRecord.sid: return new BoundSheetRecord(in); | |||
case CFHeaderRecord.sid: return new CFHeaderRecord(in); | |||
case CFRuleRecord.sid: return new CFRuleRecord(in); | |||
case CalcCountRecord.sid: return new CalcCountRecord(in); | |||
case CalcModeRecord.sid: return new CalcModeRecord(in); | |||
case CategorySeriesAxisRecord.sid: return new CategorySeriesAxisRecord(in); | |||
case ChartFormatRecord.sid: return new ChartFormatRecord(in); | |||
case ChartRecord.sid: return new ChartRecord(in); | |||
case CodepageRecord.sid: return new CodepageRecord(in); | |||
case ColumnInfoRecord.sid: return new ColumnInfoRecord(in); | |||
case ContinueRecord.sid: return new ContinueRecord(in); | |||
case CountryRecord.sid: return new CountryRecord(in); | |||
case DBCellRecord.sid: return new DBCellRecord(in); | |||
case DSFRecord.sid: return new DSFRecord(in); | |||
case DatRecord.sid: return new DatRecord(in); | |||
case DataFormatRecord.sid: return new DataFormatRecord(in); | |||
case DateWindow1904Record.sid: return new DateWindow1904Record(in); | |||
case DConRefRecord.sid: return new DConRefRecord(in); | |||
case DefaultColWidthRecord.sid:return new DefaultColWidthRecord(in); | |||
case DefaultDataLabelTextPropertiesRecord.sid: return new DefaultDataLabelTextPropertiesRecord(in); | |||
case DefaultRowHeightRecord.sid: return new DefaultRowHeightRecord(in); | |||
case DeltaRecord.sid: return new DeltaRecord(in); | |||
case DimensionsRecord.sid: return new DimensionsRecord(in); | |||
case DrawingGroupRecord.sid: return new DrawingGroupRecord(in); | |||
case DrawingRecordForBiffViewer.sid: return new DrawingRecordForBiffViewer(in); | |||
case DrawingSelectionRecord.sid: return new DrawingSelectionRecord(in); | |||
case DVRecord.sid: return new DVRecord(in); | |||
case DVALRecord.sid: return new DVALRecord(in); | |||
case EOFRecord.sid: return new EOFRecord(in); | |||
case EndRecord.sid: return new EndRecord(in); | |||
case ExtSSTRecord.sid: return new ExtSSTRecord(in); | |||
case ExtendedFormatRecord.sid: return new ExtendedFormatRecord(in); | |||
case ExternSheetRecord.sid: return new ExternSheetRecord(in); | |||
case ExternalNameRecord.sid: return new ExternalNameRecord(in); | |||
case FeatRecord.sid: return new FeatRecord(in); | |||
case FeatHdrRecord.sid: return new FeatHdrRecord(in); | |||
case FilePassRecord.sid: return new FilePassRecord(in); | |||
case FileSharingRecord.sid: return new FileSharingRecord(in); | |||
case FnGroupCountRecord.sid: return new FnGroupCountRecord(in); | |||
case FontBasisRecord.sid: return new FontBasisRecord(in); | |||
case FontIndexRecord.sid: return new FontIndexRecord(in); | |||
case FontRecord.sid: return new FontRecord(in); | |||
case FooterRecord.sid: return new FooterRecord(in); | |||
case FormatRecord.sid: return new FormatRecord(in); | |||
case FormulaRecord.sid: return new FormulaRecord(in); | |||
case FrameRecord.sid: return new FrameRecord(in); | |||
case GridsetRecord.sid: return new GridsetRecord(in); | |||
case GutsRecord.sid: return new GutsRecord(in); | |||
case HCenterRecord.sid: return new HCenterRecord(in); | |||
case HeaderRecord.sid: return new HeaderRecord(in); | |||
case HideObjRecord.sid: return new HideObjRecord(in); | |||
case HorizontalPageBreakRecord.sid: return new HorizontalPageBreakRecord(in); | |||
case HyperlinkRecord.sid: return new HyperlinkRecord(in); | |||
case IndexRecord.sid: return new IndexRecord(in); | |||
case InterfaceEndRecord.sid: return InterfaceEndRecord.create(in); | |||
case InterfaceHdrRecord.sid: return new InterfaceHdrRecord(in); | |||
case IterationRecord.sid: return new IterationRecord(in); | |||
case LabelRecord.sid: return new LabelRecord(in); | |||
case LabelSSTRecord.sid: return new LabelSSTRecord(in); | |||
case LeftMarginRecord.sid: return new LeftMarginRecord(in); | |||
case LegendRecord.sid: return new LegendRecord(in); | |||
case LineFormatRecord.sid: return new LineFormatRecord(in); | |||
case LinkedDataRecord.sid: return new LinkedDataRecord(in); | |||
case MMSRecord.sid: return new MMSRecord(in); | |||
case MergeCellsRecord.sid: return new MergeCellsRecord(in); | |||
case MulBlankRecord.sid: return new MulBlankRecord(in); | |||
case MulRKRecord.sid: return new MulRKRecord(in); | |||
case NameRecord.sid: return new NameRecord(in); | |||
case NameCommentRecord.sid: return new NameCommentRecord(in); | |||
case NoteRecord.sid: return new NoteRecord(in); | |||
case NumberRecord.sid: return new NumberRecord(in); | |||
case ObjRecord.sid: return new ObjRecord(in); | |||
case ObjectLinkRecord.sid: return new ObjectLinkRecord(in); | |||
case PaletteRecord.sid: return new PaletteRecord(in); | |||
case PaneRecord.sid: return new PaneRecord(in); | |||
case PasswordRecord.sid: return new PasswordRecord(in); | |||
case PasswordRev4Record.sid: return new PasswordRev4Record(in); | |||
case PlotAreaRecord.sid: return new PlotAreaRecord(in); | |||
case PlotGrowthRecord.sid: return new PlotGrowthRecord(in); | |||
case PrecisionRecord.sid: return new PrecisionRecord(in); | |||
case PrintGridlinesRecord.sid: return new PrintGridlinesRecord(in); | |||
case PrintHeadersRecord.sid: return new PrintHeadersRecord(in); | |||
case PrintSetupRecord.sid: return new PrintSetupRecord(in); | |||
case ProtectRecord.sid: return new ProtectRecord(in); | |||
case ProtectionRev4Record.sid: return new ProtectionRev4Record(in); | |||
case RKRecord.sid: return new RKRecord(in); | |||
case RecalcIdRecord.sid: return new RecalcIdRecord(in); | |||
case RefModeRecord.sid: return new RefModeRecord(in); | |||
case RefreshAllRecord.sid: return new RefreshAllRecord(in); | |||
case RightMarginRecord.sid: return new RightMarginRecord(in); | |||
case RowRecord.sid: return new RowRecord(in); | |||
case SCLRecord.sid: return new SCLRecord(in); | |||
case SSTRecord.sid: return new SSTRecord(in); | |||
case SaveRecalcRecord.sid: return new SaveRecalcRecord(in); | |||
case SelectionRecord.sid: return new SelectionRecord(in); | |||
case SeriesIndexRecord.sid: return new SeriesIndexRecord(in); | |||
case SeriesListRecord.sid: return new SeriesListRecord(in); | |||
case SeriesRecord.sid: return new SeriesRecord(in); | |||
case SeriesTextRecord.sid: return new SeriesTextRecord(in); | |||
case SeriesToChartGroupRecord.sid: return new SeriesToChartGroupRecord(in); | |||
case SharedFormulaRecord.sid: return new SharedFormulaRecord(in); | |||
case SheetPropertiesRecord.sid:return new SheetPropertiesRecord(in); | |||
case StringRecord.sid: return new StringRecord(in); | |||
case StyleRecord.sid: return new StyleRecord(in); | |||
case SupBookRecord.sid: return new SupBookRecord(in); | |||
case TabIdRecord.sid: return new TabIdRecord(in); | |||
case TableStylesRecord.sid: return new TableStylesRecord(in); | |||
case TableRecord.sid: return new TableRecord(in); | |||
case TextObjectRecord.sid: return new TextObjectRecord(in); | |||
case TextRecord.sid: return new TextRecord(in); | |||
case TickRecord.sid: return new TickRecord(in); | |||
case TopMarginRecord.sid: return new TopMarginRecord(in); | |||
case UncalcedRecord.sid: return new UncalcedRecord(in); | |||
case UnitsRecord.sid: return new UnitsRecord(in); | |||
case UseSelFSRecord.sid: return new UseSelFSRecord(in); | |||
case VCenterRecord.sid: return new VCenterRecord(in); | |||
case ValueRangeRecord.sid: return new ValueRangeRecord(in); | |||
case VerticalPageBreakRecord.sid: return new VerticalPageBreakRecord(in); | |||
case WSBoolRecord.sid: return new WSBoolRecord(in); | |||
case WindowOneRecord.sid: return new WindowOneRecord(in); | |||
case WindowProtectRecord.sid: return new WindowProtectRecord(in); | |||
case WindowTwoRecord.sid: return new WindowTwoRecord(in); | |||
case WriteAccessRecord.sid: return new WriteAccessRecord(in); | |||
case WriteProtectRecord.sid: return new WriteProtectRecord(in); | |||
// chart | |||
case CatLabRecord.sid: return new CatLabRecord(in); | |||
case ChartEndBlockRecord.sid: return new ChartEndBlockRecord(in); | |||
case ChartEndObjectRecord.sid: return new ChartEndObjectRecord(in); | |||
case ChartFRTInfoRecord.sid: return new ChartFRTInfoRecord(in); | |||
case ChartStartBlockRecord.sid: return new ChartStartBlockRecord(in); | |||
case ChartStartObjectRecord.sid: return new ChartStartObjectRecord(in); | |||
// pivot table | |||
case StreamIDRecord.sid: return new StreamIDRecord(in); | |||
case ViewSourceRecord.sid: return new ViewSourceRecord(in); | |||
case PageItemRecord.sid: return new PageItemRecord(in); | |||
case ViewDefinitionRecord.sid: return new ViewDefinitionRecord(in); | |||
case ViewFieldsRecord.sid: return new ViewFieldsRecord(in); | |||
case DataItemRecord.sid: return new DataItemRecord(in); | |||
case ExtendedPivotTableViewFieldsRecord.sid: return new ExtendedPivotTableViewFieldsRecord(in); | |||
} | |||
return new UnknownRecord(in); | |||
} | |||
private static final class CommandArgs { | |||
private final boolean _biffhex; | |||
private final boolean _noint; | |||
private final boolean _out; | |||
private final boolean _rawhex; | |||
private final boolean _noHeader; | |||
private final File _file; | |||
private CommandArgs(boolean biffhex, boolean noint, boolean out, boolean rawhex, boolean noHeader, File file) { | |||
_biffhex = biffhex; | |||
_noint = noint; | |||
_out = out; | |||
_rawhex = rawhex; | |||
_file = file; | |||
_noHeader = noHeader; | |||
} | |||
public static CommandArgs parse(String[] args) throws CommandParseException { | |||
int nArgs = args.length; | |||
boolean biffhex = false; | |||
boolean noint = false; | |||
boolean out = false; | |||
boolean rawhex = false; | |||
boolean noheader = false; | |||
File file = null; | |||
for (int i=0; i<nArgs; i++) { | |||
String arg = args[i]; | |||
if (arg.startsWith("--")) { | |||
if ("--biffhex".equals(arg)) { | |||
biffhex = true; | |||
} else if ("--noint".equals(arg)) { | |||
noint = true; | |||
} else if ("--out".equals(arg)) { | |||
out = true; | |||
} else if ("--escher".equals(arg)) { | |||
System.setProperty("poi.deserialize.escher", "true"); | |||
} else if ("--rawhex".equals(arg)) { | |||
rawhex = true; | |||
} else if ("--noheader".equals(arg)) { | |||
noheader = true; | |||
} else { | |||
throw new CommandParseException("Unexpected option '" + arg + "'"); | |||
} | |||
continue; | |||
} | |||
file = new File(arg); | |||
if (!file.exists()) { | |||
throw new CommandParseException("Specified file '" + arg + "' does not exist"); | |||
} | |||
if (i+1<nArgs) { | |||
throw new CommandParseException("File name must be the last arg"); | |||
} | |||
} | |||
if (file == null) { | |||
throw new CommandParseException("Biff viewer needs a filename"); | |||
} | |||
return new CommandArgs(biffhex, noint, out, rawhex, noheader, file); | |||
} | |||
public boolean shouldDumpBiffHex() { | |||
return _biffhex; | |||
} | |||
public boolean shouldDumpRecordInterpretations() { | |||
return !_noint; | |||
} | |||
public boolean shouldOutputToFile() { | |||
return _out; | |||
} | |||
public boolean shouldOutputRawHexOnly() { | |||
return _rawhex; | |||
} | |||
public boolean suppressHeader() { | |||
return _noHeader; | |||
} | |||
public File getFile() { | |||
return _file; | |||
} | |||
} | |||
private static final class CommandParseException extends Exception { | |||
public CommandParseException(String msg) { | |||
super(msg); | |||
} | |||
} | |||
// no instances of this class | |||
} | |||
/** | |||
* Create an array of records from an input stream | |||
* | |||
*@param is the InputStream from which the records will be obtained | |||
*@return an array of Records created from the InputStream | |||
*@exception RecordFormatException on error processing the InputStream | |||
*/ | |||
public static Record[] createRecords(InputStream is, PrintStream ps, BiffRecordListener recListener, boolean dumpInterpretedRecords) | |||
throws RecordFormatException { | |||
List<Record> temp = new ArrayList<Record>(); | |||
RecordInputStream recStream = new RecordInputStream(is); | |||
while (true) { | |||
boolean hasNext; | |||
try { | |||
hasNext = recStream.hasNextRecord(); | |||
} catch (LeftoverDataException e) { | |||
e.printStackTrace(); | |||
System.err.println("Discarding " + recStream.remaining() + " bytes and continuing"); | |||
recStream.readRemainder(); | |||
hasNext = recStream.hasNextRecord(); | |||
} | |||
if (!hasNext) { | |||
break; | |||
} | |||
recStream.nextRecord(); | |||
if (recStream.getSid() == 0) { | |||
continue; | |||
} | |||
Record record; | |||
if (dumpInterpretedRecords) { | |||
record = createRecord (recStream); | |||
if (record.getSid() == ContinueRecord.sid) { | |||
continue; | |||
} | |||
temp.add(record); | |||
if (dumpInterpretedRecords) { | |||
for (String header : recListener.getRecentHeaders()) { | |||
ps.println(header); | |||
} | |||
ps.print(record.toString()); | |||
} | |||
} else { | |||
recStream.readRemainder(); | |||
} | |||
ps.println(); | |||
} | |||
Record[] result = new Record[temp.size()]; | |||
temp.toArray(result); | |||
return result; | |||
} | |||
/** | |||
* Essentially a duplicate of RecordFactory. Kept separate as not to screw | |||
* up non-debug operations. | |||
* | |||
*/ | |||
private static Record createRecord(RecordInputStream in) { | |||
switch (in.getSid()) { | |||
case AreaFormatRecord.sid: return new AreaFormatRecord(in); | |||
case AreaRecord.sid: return new AreaRecord(in); | |||
case ArrayRecord.sid: return new ArrayRecord(in); | |||
case AxisLineFormatRecord.sid: return new AxisLineFormatRecord(in); | |||
case AxisOptionsRecord.sid: return new AxisOptionsRecord(in); | |||
case AxisParentRecord.sid: return new AxisParentRecord(in); | |||
case AxisRecord.sid: return new AxisRecord(in); | |||
case AxisUsedRecord.sid: return new AxisUsedRecord(in); | |||
case AutoFilterInfoRecord.sid: return new AutoFilterInfoRecord(in); | |||
case BOFRecord.sid: return new BOFRecord(in); | |||
case BackupRecord.sid: return new BackupRecord(in); | |||
case BarRecord.sid: return new BarRecord(in); | |||
case BeginRecord.sid: return new BeginRecord(in); | |||
case BlankRecord.sid: return new BlankRecord(in); | |||
case BookBoolRecord.sid: return new BookBoolRecord(in); | |||
case BoolErrRecord.sid: return new BoolErrRecord(in); | |||
case BottomMarginRecord.sid: return new BottomMarginRecord(in); | |||
case BoundSheetRecord.sid: return new BoundSheetRecord(in); | |||
case CFHeaderRecord.sid: return new CFHeaderRecord(in); | |||
case CFHeader12Record.sid: return new CFHeader12Record(in); | |||
case CFRuleRecord.sid: return new CFRuleRecord(in); | |||
case CFRule12Record.sid: return new CFRule12Record(in); | |||
// TODO Add CF Ex, and remove from UnknownRecord | |||
case CalcCountRecord.sid: return new CalcCountRecord(in); | |||
case CalcModeRecord.sid: return new CalcModeRecord(in); | |||
case CategorySeriesAxisRecord.sid:return new CategorySeriesAxisRecord(in); | |||
case ChartFormatRecord.sid: return new ChartFormatRecord(in); | |||
case ChartRecord.sid: return new ChartRecord(in); | |||
case CodepageRecord.sid: return new CodepageRecord(in); | |||
case ColumnInfoRecord.sid: return new ColumnInfoRecord(in); | |||
case ContinueRecord.sid: return new ContinueRecord(in); | |||
case CountryRecord.sid: return new CountryRecord(in); | |||
case DBCellRecord.sid: return new DBCellRecord(in); | |||
case DSFRecord.sid: return new DSFRecord(in); | |||
case DatRecord.sid: return new DatRecord(in); | |||
case DataFormatRecord.sid: return new DataFormatRecord(in); | |||
case DateWindow1904Record.sid: return new DateWindow1904Record(in); | |||
case DConRefRecord.sid: return new DConRefRecord(in); | |||
case DefaultColWidthRecord.sid: return new DefaultColWidthRecord(in); | |||
case DefaultDataLabelTextPropertiesRecord.sid: return new DefaultDataLabelTextPropertiesRecord(in); | |||
case DefaultRowHeightRecord.sid: return new DefaultRowHeightRecord(in); | |||
case DeltaRecord.sid: return new DeltaRecord(in); | |||
case DimensionsRecord.sid: return new DimensionsRecord(in); | |||
case DrawingGroupRecord.sid: return new DrawingGroupRecord(in); | |||
case DrawingRecordForBiffViewer.sid: return new DrawingRecordForBiffViewer(in); | |||
case DrawingSelectionRecord.sid: return new DrawingSelectionRecord(in); | |||
case DVRecord.sid: return new DVRecord(in); | |||
case DVALRecord.sid: return new DVALRecord(in); | |||
case EOFRecord.sid: return new EOFRecord(in); | |||
case EndRecord.sid: return new EndRecord(in); | |||
case ExtSSTRecord.sid: return new ExtSSTRecord(in); | |||
case ExtendedFormatRecord.sid: return new ExtendedFormatRecord(in); | |||
case ExternSheetRecord.sid: return new ExternSheetRecord(in); | |||
case ExternalNameRecord.sid: return new ExternalNameRecord(in); | |||
case FeatRecord.sid: return new FeatRecord(in); | |||
case FeatHdrRecord.sid: return new FeatHdrRecord(in); | |||
case FilePassRecord.sid: return new FilePassRecord(in); | |||
case FileSharingRecord.sid: return new FileSharingRecord(in); | |||
case FnGroupCountRecord.sid: return new FnGroupCountRecord(in); | |||
case FontBasisRecord.sid: return new FontBasisRecord(in); | |||
case FontIndexRecord.sid: return new FontIndexRecord(in); | |||
case FontRecord.sid: return new FontRecord(in); | |||
case FooterRecord.sid: return new FooterRecord(in); | |||
case FormatRecord.sid: return new FormatRecord(in); | |||
case FormulaRecord.sid: return new FormulaRecord(in); | |||
case FrameRecord.sid: return new FrameRecord(in); | |||
case GridsetRecord.sid: return new GridsetRecord(in); | |||
case GutsRecord.sid: return new GutsRecord(in); | |||
case HCenterRecord.sid: return new HCenterRecord(in); | |||
case HeaderRecord.sid: return new HeaderRecord(in); | |||
case HideObjRecord.sid: return new HideObjRecord(in); | |||
case HorizontalPageBreakRecord.sid: return new HorizontalPageBreakRecord(in); | |||
case HyperlinkRecord.sid: return new HyperlinkRecord(in); | |||
case IndexRecord.sid: return new IndexRecord(in); | |||
case InterfaceEndRecord.sid: return InterfaceEndRecord.create(in); | |||
case InterfaceHdrRecord.sid: return new InterfaceHdrRecord(in); | |||
case IterationRecord.sid: return new IterationRecord(in); | |||
case LabelRecord.sid: return new LabelRecord(in); | |||
case LabelSSTRecord.sid: return new LabelSSTRecord(in); | |||
case LeftMarginRecord.sid: return new LeftMarginRecord(in); | |||
case LegendRecord.sid: return new LegendRecord(in); | |||
case LineFormatRecord.sid: return new LineFormatRecord(in); | |||
case LinkedDataRecord.sid: return new LinkedDataRecord(in); | |||
case MMSRecord.sid: return new MMSRecord(in); | |||
case MergeCellsRecord.sid: return new MergeCellsRecord(in); | |||
case MulBlankRecord.sid: return new MulBlankRecord(in); | |||
case MulRKRecord.sid: return new MulRKRecord(in); | |||
case NameRecord.sid: return new NameRecord(in); | |||
case NameCommentRecord.sid: return new NameCommentRecord(in); | |||
case NoteRecord.sid: return new NoteRecord(in); | |||
case NumberRecord.sid: return new NumberRecord(in); | |||
case ObjRecord.sid: return new ObjRecord(in); | |||
case ObjectLinkRecord.sid: return new ObjectLinkRecord(in); | |||
case PaletteRecord.sid: return new PaletteRecord(in); | |||
case PaneRecord.sid: return new PaneRecord(in); | |||
case PasswordRecord.sid: return new PasswordRecord(in); | |||
case PasswordRev4Record.sid: return new PasswordRev4Record(in); | |||
case PlotAreaRecord.sid: return new PlotAreaRecord(in); | |||
case PlotGrowthRecord.sid: return new PlotGrowthRecord(in); | |||
case PrecisionRecord.sid: return new PrecisionRecord(in); | |||
case PrintGridlinesRecord.sid: return new PrintGridlinesRecord(in); | |||
case PrintHeadersRecord.sid: return new PrintHeadersRecord(in); | |||
case PrintSetupRecord.sid: return new PrintSetupRecord(in); | |||
case ProtectRecord.sid: return new ProtectRecord(in); | |||
case ProtectionRev4Record.sid: return new ProtectionRev4Record(in); | |||
case RKRecord.sid: return new RKRecord(in); | |||
case RecalcIdRecord.sid: return new RecalcIdRecord(in); | |||
case RefModeRecord.sid: return new RefModeRecord(in); | |||
case RefreshAllRecord.sid: return new RefreshAllRecord(in); | |||
case RightMarginRecord.sid: return new RightMarginRecord(in); | |||
case RowRecord.sid: return new RowRecord(in); | |||
case SCLRecord.sid: return new SCLRecord(in); | |||
case SSTRecord.sid: return new SSTRecord(in); | |||
case SaveRecalcRecord.sid: return new SaveRecalcRecord(in); | |||
case SelectionRecord.sid: return new SelectionRecord(in); | |||
case SeriesIndexRecord.sid: return new SeriesIndexRecord(in); | |||
case SeriesListRecord.sid: return new SeriesListRecord(in); | |||
case SeriesRecord.sid: return new SeriesRecord(in); | |||
case SeriesTextRecord.sid: return new SeriesTextRecord(in); | |||
case SeriesToChartGroupRecord.sid:return new SeriesToChartGroupRecord(in); | |||
case SharedFormulaRecord.sid: return new SharedFormulaRecord(in); | |||
case SheetPropertiesRecord.sid: return new SheetPropertiesRecord(in); | |||
case StringRecord.sid: return new StringRecord(in); | |||
case StyleRecord.sid: return new StyleRecord(in); | |||
case SupBookRecord.sid: return new SupBookRecord(in); | |||
case TabIdRecord.sid: return new TabIdRecord(in); | |||
case TableStylesRecord.sid: return new TableStylesRecord(in); | |||
case TableRecord.sid: return new TableRecord(in); | |||
case TextObjectRecord.sid: return new TextObjectRecord(in); | |||
case TextRecord.sid: return new TextRecord(in); | |||
case TickRecord.sid: return new TickRecord(in); | |||
case TopMarginRecord.sid: return new TopMarginRecord(in); | |||
case UncalcedRecord.sid: return new UncalcedRecord(in); | |||
case UnitsRecord.sid: return new UnitsRecord(in); | |||
case UseSelFSRecord.sid: return new UseSelFSRecord(in); | |||
case VCenterRecord.sid: return new VCenterRecord(in); | |||
case ValueRangeRecord.sid: return new ValueRangeRecord(in); | |||
case VerticalPageBreakRecord.sid: return new VerticalPageBreakRecord(in); | |||
case WSBoolRecord.sid: return new WSBoolRecord(in); | |||
case WindowOneRecord.sid: return new WindowOneRecord(in); | |||
case WindowProtectRecord.sid: return new WindowProtectRecord(in); | |||
case WindowTwoRecord.sid: return new WindowTwoRecord(in); | |||
case WriteAccessRecord.sid: return new WriteAccessRecord(in); | |||
case WriteProtectRecord.sid: return new WriteProtectRecord(in); | |||
// chart | |||
case CatLabRecord.sid: return new CatLabRecord(in); | |||
case ChartEndBlockRecord.sid: return new ChartEndBlockRecord(in); | |||
case ChartEndObjectRecord.sid: return new ChartEndObjectRecord(in); | |||
case ChartFRTInfoRecord.sid: return new ChartFRTInfoRecord(in); | |||
case ChartStartBlockRecord.sid: return new ChartStartBlockRecord(in); | |||
case ChartStartObjectRecord.sid: return new ChartStartObjectRecord(in); | |||
// pivot table | |||
case StreamIDRecord.sid: return new StreamIDRecord(in); | |||
case ViewSourceRecord.sid: return new ViewSourceRecord(in); | |||
case PageItemRecord.sid: return new PageItemRecord(in); | |||
case ViewDefinitionRecord.sid: return new ViewDefinitionRecord(in); | |||
case ViewFieldsRecord.sid: return new ViewFieldsRecord(in); | |||
case DataItemRecord.sid: return new DataItemRecord(in); | |||
case ExtendedPivotTableViewFieldsRecord.sid: return new ExtendedPivotTableViewFieldsRecord(in); | |||
} | |||
return new UnknownRecord(in); | |||
} | |||
private static final class CommandArgs { | |||
private final boolean _biffhex; | |||
private final boolean _noint; | |||
private final boolean _out; | |||
private final boolean _rawhex; | |||
private final boolean _noHeader; | |||
private final File _file; | |||
private CommandArgs(boolean biffhex, boolean noint, boolean out, boolean rawhex, boolean noHeader, File file) { | |||
_biffhex = biffhex; | |||
_noint = noint; | |||
_out = out; | |||
_rawhex = rawhex; | |||
_file = file; | |||
_noHeader = noHeader; | |||
} | |||
public static CommandArgs parse(String[] args) throws CommandParseException { | |||
int nArgs = args.length; | |||
boolean biffhex = false; | |||
boolean noint = false; | |||
boolean out = false; | |||
boolean rawhex = false; | |||
boolean noheader = false; | |||
File file = null; | |||
for (int i=0; i<nArgs; i++) { | |||
String arg = args[i]; | |||
if (arg.startsWith("--")) { | |||
if ("--biffhex".equals(arg)) { | |||
biffhex = true; | |||
} else if ("--noint".equals(arg)) { | |||
noint = true; | |||
} else if ("--out".equals(arg)) { | |||
out = true; | |||
} else if ("--escher".equals(arg)) { | |||
System.setProperty("poi.deserialize.escher", "true"); | |||
} else if ("--rawhex".equals(arg)) { | |||
rawhex = true; | |||
} else if ("--noheader".equals(arg)) { | |||
noheader = true; | |||
} else { | |||
throw new CommandParseException("Unexpected option '" + arg + "'"); | |||
} | |||
continue; | |||
} | |||
file = new File(arg); | |||
if (!file.exists()) { | |||
throw new CommandParseException("Specified file '" + arg + "' does not exist"); | |||
} | |||
if (i+1<nArgs) { | |||
throw new CommandParseException("File name must be the last arg"); | |||
} | |||
} | |||
if (file == null) { | |||
throw new CommandParseException("Biff viewer needs a filename"); | |||
} | |||
return new CommandArgs(biffhex, noint, out, rawhex, noheader, file); | |||
} | |||
public boolean shouldDumpBiffHex() { | |||
return _biffhex; | |||
} | |||
public boolean shouldDumpRecordInterpretations() { | |||
return !_noint; | |||
} | |||
public boolean shouldOutputToFile() { | |||
return _out; | |||
} | |||
public boolean shouldOutputRawHexOnly() { | |||
return _rawhex; | |||
} | |||
public boolean suppressHeader() { | |||
return _noHeader; | |||
} | |||
public File getFile() { | |||
return _file; | |||
} | |||
} | |||
private static final class CommandParseException extends Exception { | |||
public CommandParseException(String msg) { | |||
super(msg); | |||
} | |||
} | |||
/** | |||
* Method main with 1 argument just run straight biffview against given | |||
@@ -420,7 +470,7 @@ public final class BiffViewer { | |||
protected static InputStream getPOIFSInputStream(File file) | |||
throws IOException, FileNotFoundException { | |||
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(file)); | |||
NPOIFSFileSystem fs = new NPOIFSFileSystem(new FileInputStream(file)); | |||
String workbookName = HSSFWorkbook.getWorkbookDirEntryName(fs.getRoot()); | |||
return fs.createDocumentInputStream(workbookName); | |||
} |
@@ -19,6 +19,7 @@ package org.apache.poi.hssf.eventusermodel; | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
import java.util.Set; | |||
import org.apache.poi.hssf.eventusermodel.HSSFUserException; | |||
import org.apache.poi.hssf.record.*; | |||
@@ -56,11 +57,24 @@ public class HSSFEventFactory { | |||
* @param req an Instance of HSSFRequest which has your registered listeners | |||
* @param dir a DirectoryNode containing your workbook | |||
*/ | |||
public void processWorkbookEvents(HSSFRequest req, DirectoryNode dir) throws IOException { | |||
InputStream in = dir.createDocumentInputStream("Workbook"); | |||
processEvents(req, in); | |||
} | |||
public void processWorkbookEvents(HSSFRequest req, DirectoryNode dir) throws IOException { | |||
// some old documents have "WORKBOOK" or "BOOK" | |||
final String name; | |||
Set<String> entryNames = dir.getEntryNames(); | |||
if (entryNames.contains("Workbook")) { | |||
name = "Workbook"; | |||
} else if (entryNames.contains("WORKBOOK")) { | |||
name = "WORKBOOK"; | |||
} else if (entryNames.contains("BOOK")) { | |||
name = "BOOK"; | |||
} else { | |||
name = "Workbook"; | |||
} | |||
InputStream in = dir.createDocumentInputStream(name); | |||
processEvents(req, in); | |||
} | |||
/** | |||
* Processes a file into essentially record events. |
@@ -21,6 +21,7 @@ import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.POIDocument; | |||
import org.apache.poi.POIOLE2TextExtractor; | |||
import org.apache.poi.hpsf.DocumentSummaryInformation; | |||
import org.apache.poi.hpsf.SummaryInformation; | |||
@@ -75,7 +76,7 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor implements or | |||
public EventBasedExcelExtractor( DirectoryNode dir ) | |||
{ | |||
super( null ); | |||
super( (POIDocument)null ); | |||
_dir = dir; | |||
} | |||
@@ -83,16 +84,6 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor implements or | |||
this(fs.getRoot()); | |||
} | |||
/** | |||
* Return the underlying POIFS FileSystem of | |||
* this document. | |||
* | |||
* @deprecated Use {@link #getRoot()} instead | |||
*/ | |||
public POIFSFileSystem getFileSystem() { | |||
return _dir.getFileSystem(); | |||
} | |||
/** | |||
* Would return the document information metadata for the document, | |||
* if we supported it |
@@ -774,11 +774,12 @@ public final class InternalWorkbook { | |||
} | |||
} | |||
// also tell the LinkTable about the removed sheet | |||
// +1 because we already removed it from the count of sheets! | |||
for(int i = sheetIndex+1;i < getNumSheets()+1;i++) { | |||
// also update the link-table as otherwise references might point at invalid sheets | |||
linkTable.removeSheet(i); | |||
if (linkTable != null) { | |||
// also tell the LinkTable about the removed sheet | |||
// +1 because we already removed it from the count of sheets! | |||
for(int i = sheetIndex+1;i < getNumSheets()+1;i++) { | |||
linkTable.removeSheet(i); | |||
} | |||
} | |||
} | |||
@@ -0,0 +1,88 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.record; | |||
import org.apache.poi.hssf.record.common.FtrHeader; | |||
import org.apache.poi.hssf.record.common.FutureRecord; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Conditional Formatting Header v12 record CFHEADER12 (0x0879), | |||
* for conditional formattings introduced in Excel 2007 and newer. | |||
*/ | |||
public final class CFHeader12Record extends CFHeaderBase implements FutureRecord { | |||
public static final short sid = 0x0879; | |||
private FtrHeader futureHeader; | |||
/** Creates new CFHeaderRecord */ | |||
public CFHeader12Record() { | |||
createEmpty(); | |||
futureHeader = new FtrHeader(); | |||
futureHeader.setRecordType(sid); | |||
} | |||
public CFHeader12Record(CellRangeAddress[] regions, int nRules) { | |||
super(regions, nRules); | |||
futureHeader = new FtrHeader(); | |||
futureHeader.setRecordType(sid); | |||
} | |||
public CFHeader12Record(RecordInputStream in) { | |||
futureHeader = new FtrHeader(in); | |||
read(in); | |||
} | |||
@Override | |||
protected String getRecordName() { | |||
return "CFHEADER12"; | |||
} | |||
protected int getDataSize() { | |||
return FtrHeader.getDataSize() + super.getDataSize(); | |||
} | |||
public void serialize(LittleEndianOutput out) { | |||
// Sync the associated range | |||
futureHeader.setAssociatedRange(getEnclosingCellRange()); | |||
// Write the future header first | |||
futureHeader.serialize(out); | |||
// Then the rest of the CF Header details | |||
super.serialize(out); | |||
} | |||
public short getSid() { | |||
return sid; | |||
} | |||
public short getFutureRecordType() { | |||
return futureHeader.getRecordType(); | |||
} | |||
public FtrHeader getFutureHeader() { | |||
return futureHeader; | |||
} | |||
public CellRangeAddress getAssociatedRange() { | |||
return futureHeader.getAssociatedRange(); | |||
} | |||
public Object clone() { | |||
CFHeader12Record result = new CFHeader12Record(); | |||
result.futureHeader = (FtrHeader)futureHeader.clone(); | |||
super.copyTo(result); | |||
return result; | |||
} | |||
} |
@@ -0,0 +1,153 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.record; | |||
import org.apache.poi.hssf.record.cf.CellRangeUtil; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.ss.util.CellRangeAddressList; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Parent of Conditional Formatting Header records, | |||
* {@link CFHeaderRecord} and {@link CFHeader12Record}. | |||
*/ | |||
public abstract class CFHeaderBase extends StandardRecord { | |||
private int field_1_numcf; | |||
private int field_2_need_recalculation_and_id; | |||
private CellRangeAddress field_3_enclosing_cell_range; | |||
private CellRangeAddressList field_4_cell_ranges; | |||
/** Creates new CFHeaderBase */ | |||
protected CFHeaderBase() { | |||
} | |||
protected CFHeaderBase(CellRangeAddress[] regions, int nRules) { | |||
CellRangeAddress[] unmergedRanges = regions; | |||
CellRangeAddress[] mergeCellRanges = CellRangeUtil.mergeCellRanges(unmergedRanges); | |||
setCellRanges(mergeCellRanges); | |||
field_1_numcf = nRules; | |||
} | |||
protected void createEmpty() { | |||
field_3_enclosing_cell_range = new CellRangeAddress(0, 0, 0, 0); | |||
field_4_cell_ranges = new CellRangeAddressList(); | |||
} | |||
protected void read(RecordInputStream in) { | |||
field_1_numcf = in.readShort(); | |||
field_2_need_recalculation_and_id = in.readShort(); | |||
field_3_enclosing_cell_range = new CellRangeAddress(in); | |||
field_4_cell_ranges = new CellRangeAddressList(in); | |||
} | |||
public int getNumberOfConditionalFormats() { | |||
return field_1_numcf; | |||
} | |||
public void setNumberOfConditionalFormats(int n) { | |||
field_1_numcf=n; | |||
} | |||
public boolean getNeedRecalculation() { | |||
// Held on the 1st bit | |||
return field_2_need_recalculation_and_id % 2 == 1; | |||
} | |||
public void setNeedRecalculation(boolean b) { | |||
// held on the first bit | |||
if (b == getNeedRecalculation()) return; | |||
if (b) field_2_need_recalculation_and_id++; | |||
else field_2_need_recalculation_and_id--; | |||
} | |||
public int getID() { | |||
// Remaining 15 bits of field 2 | |||
return field_2_need_recalculation_and_id>>1; | |||
} | |||
public void setID(int id) { | |||
// Remaining 15 bits of field 2 | |||
boolean needsRecalc = getNeedRecalculation(); | |||
field_2_need_recalculation_and_id = (id<<1); | |||
if (needsRecalc) field_2_need_recalculation_and_id++; | |||
} | |||
public CellRangeAddress getEnclosingCellRange() { | |||
return field_3_enclosing_cell_range; | |||
} | |||
public void setEnclosingCellRange(CellRangeAddress cr) { | |||
field_3_enclosing_cell_range = cr; | |||
} | |||
/** | |||
* Set cell ranges list to a single cell range and | |||
* modify the enclosing cell range accordingly. | |||
* @param cellRanges - list of CellRange objects | |||
*/ | |||
public void setCellRanges(CellRangeAddress[] cellRanges) { | |||
if(cellRanges == null) { | |||
throw new IllegalArgumentException("cellRanges must not be null"); | |||
} | |||
CellRangeAddressList cral = new CellRangeAddressList(); | |||
CellRangeAddress enclosingRange = null; | |||
for (int i = 0; i < cellRanges.length; i++) { | |||
CellRangeAddress cr = cellRanges[i]; | |||
enclosingRange = CellRangeUtil.createEnclosingCellRange(cr, enclosingRange); | |||
cral.addCellRangeAddress(cr); | |||
} | |||
field_3_enclosing_cell_range = enclosingRange; | |||
field_4_cell_ranges = cral; | |||
} | |||
public CellRangeAddress[] getCellRanges() { | |||
return field_4_cell_ranges.getCellRangeAddresses(); | |||
} | |||
protected abstract String getRecordName(); | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[").append(getRecordName()).append("]\n"); | |||
buffer.append("\t.numCF = ").append(getNumberOfConditionalFormats()).append("\n"); | |||
buffer.append("\t.needRecalc = ").append(getNeedRecalculation()).append("\n"); | |||
buffer.append("\t.id = ").append(getID()).append("\n"); | |||
buffer.append("\t.enclosingCellRange= ").append(getEnclosingCellRange()).append("\n"); | |||
buffer.append("\t.cfranges=["); | |||
for( int i=0; i<field_4_cell_ranges.countRanges(); i++) { | |||
buffer.append(i==0?"":",").append(field_4_cell_ranges.getCellRangeAddress(i).toString()); | |||
} | |||
buffer.append("]\n"); | |||
buffer.append("[/").append(getRecordName()).append("]\n"); | |||
return buffer.toString(); | |||
} | |||
protected int getDataSize() { | |||
return 4 // 2 short fields | |||
+ CellRangeAddress.ENCODED_SIZE | |||
+ field_4_cell_ranges.getSize(); | |||
} | |||
public void serialize(LittleEndianOutput out) { | |||
out.writeShort(field_1_numcf); | |||
out.writeShort(field_2_need_recalculation_and_id); | |||
field_3_enclosing_cell_range.serialize(out); | |||
field_4_cell_ranges.serialize(out); | |||
} | |||
protected void copyTo(CFHeaderBase result) { | |||
result.field_1_numcf = field_1_numcf; | |||
result.field_2_need_recalculation_and_id = field_2_need_recalculation_and_id; | |||
result.field_3_enclosing_cell_range = field_3_enclosing_cell_range.copy(); | |||
result.field_4_cell_ranges = field_4_cell_ranges.copy(); | |||
} | |||
} |
@@ -17,144 +17,39 @@ | |||
package org.apache.poi.hssf.record; | |||
import org.apache.poi.hssf.record.cf.CellRangeUtil; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.ss.util.CellRangeAddressList; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Conditional Formatting Header record CFHEADER (0x01B0) | |||
* | |||
* @author Dmitriy Kumshayev | |||
* Conditional Formatting Header record CFHEADER (0x01B0). | |||
* Used to describe a {@link CFRuleRecord}. | |||
* @see CFHeader12Record | |||
*/ | |||
public final class CFHeaderRecord extends StandardRecord { | |||
public static final short sid = 0x01B0; | |||
private int field_1_numcf; | |||
private int field_2_need_recalculation; | |||
private CellRangeAddress field_3_enclosing_cell_range; | |||
private CellRangeAddressList field_4_cell_ranges; | |||
/** Creates new CFHeaderRecord */ | |||
public CFHeaderRecord() | |||
{ | |||
field_4_cell_ranges = new CellRangeAddressList(); | |||
} | |||
public CFHeaderRecord(CellRangeAddress[] regions, int nRules) { | |||
CellRangeAddress[] unmergedRanges = regions; | |||
CellRangeAddress[] mergeCellRanges = CellRangeUtil.mergeCellRanges(unmergedRanges); | |||
setCellRanges(mergeCellRanges); | |||
field_1_numcf = nRules; | |||
} | |||
public CFHeaderRecord(RecordInputStream in) | |||
{ | |||
field_1_numcf = in.readShort(); | |||
field_2_need_recalculation = in.readShort(); | |||
field_3_enclosing_cell_range = new CellRangeAddress(in); | |||
field_4_cell_ranges = new CellRangeAddressList(in); | |||
} | |||
public int getNumberOfConditionalFormats() | |||
{ | |||
return field_1_numcf; | |||
} | |||
public void setNumberOfConditionalFormats(int n) | |||
{ | |||
field_1_numcf=n; | |||
} | |||
public boolean getNeedRecalculation() | |||
{ | |||
return field_2_need_recalculation==1?true:false; | |||
} | |||
public void setNeedRecalculation(boolean b) | |||
{ | |||
field_2_need_recalculation=b?1:0; | |||
} | |||
public CellRangeAddress getEnclosingCellRange() | |||
{ | |||
return field_3_enclosing_cell_range; | |||
} | |||
public void setEnclosingCellRange(CellRangeAddress cr) | |||
{ | |||
field_3_enclosing_cell_range = cr; | |||
} | |||
/** | |||
* Set cell ranges list to a single cell range and | |||
* modify the enclosing cell range accordingly. | |||
* @param cellRanges - list of CellRange objects | |||
*/ | |||
public void setCellRanges(CellRangeAddress[] cellRanges) | |||
{ | |||
if(cellRanges == null) | |||
{ | |||
throw new IllegalArgumentException("cellRanges must not be null"); | |||
} | |||
CellRangeAddressList cral = new CellRangeAddressList(); | |||
CellRangeAddress enclosingRange = null; | |||
for (int i = 0; i < cellRanges.length; i++) | |||
{ | |||
CellRangeAddress cr = cellRanges[i]; | |||
enclosingRange = CellRangeUtil.createEnclosingCellRange(cr, enclosingRange); | |||
cral.addCellRangeAddress(cr); | |||
} | |||
field_3_enclosing_cell_range = enclosingRange; | |||
field_4_cell_ranges = cral; | |||
} | |||
public CellRangeAddress[] getCellRanges() { | |||
return field_4_cell_ranges.getCellRangeAddresses(); | |||
} | |||
public String toString() | |||
{ | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[CFHEADER]\n"); | |||
buffer.append(" .id = ").append(Integer.toHexString(sid)).append("\n"); | |||
buffer.append(" .numCF = ").append(getNumberOfConditionalFormats()).append("\n"); | |||
buffer.append(" .needRecalc = ").append(getNeedRecalculation()).append("\n"); | |||
buffer.append(" .enclosingCellRange= ").append(getEnclosingCellRange()).append("\n"); | |||
buffer.append(" .cfranges=["); | |||
for( int i=0; i<field_4_cell_ranges.countRanges(); i++) | |||
{ | |||
buffer.append(i==0?"":",").append(field_4_cell_ranges.getCellRangeAddress(i).toString()); | |||
} | |||
buffer.append("]\n"); | |||
buffer.append("[/CFHEADER]\n"); | |||
return buffer.toString(); | |||
} | |||
protected int getDataSize() { | |||
return 4 // 2 short fields | |||
+ CellRangeAddress.ENCODED_SIZE | |||
+ field_4_cell_ranges.getSize(); | |||
} | |||
public void serialize(LittleEndianOutput out) { | |||
out.writeShort(field_1_numcf); | |||
out.writeShort(field_2_need_recalculation); | |||
field_3_enclosing_cell_range.serialize(out); | |||
field_4_cell_ranges.serialize(out); | |||
} | |||
public short getSid() { | |||
return sid; | |||
} | |||
public Object clone() | |||
{ | |||
CFHeaderRecord result = new CFHeaderRecord(); | |||
result.field_1_numcf = field_1_numcf; | |||
result.field_2_need_recalculation = field_2_need_recalculation; | |||
result.field_3_enclosing_cell_range = field_3_enclosing_cell_range; | |||
result.field_4_cell_ranges = field_4_cell_ranges.copy(); | |||
return result; | |||
} | |||
public final class CFHeaderRecord extends CFHeaderBase { | |||
public static final short sid = 0x01B0; | |||
/** Creates new CFHeaderRecord */ | |||
public CFHeaderRecord() { | |||
createEmpty(); | |||
} | |||
public CFHeaderRecord(CellRangeAddress[] regions, int nRules) { | |||
super(regions, nRules); | |||
} | |||
public CFHeaderRecord(RecordInputStream in) { | |||
read(in); | |||
} | |||
protected String getRecordName() { | |||
return "CFHEADER"; | |||
} | |||
public short getSid() { | |||
return sid; | |||
} | |||
public Object clone() { | |||
CFHeaderRecord result = new CFHeaderRecord(); | |||
super.copyTo(result); | |||
return result; | |||
} | |||
} |
@@ -0,0 +1,365 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.record; | |||
import java.util.Arrays; | |||
import org.apache.poi.hssf.record.cf.IconMultiStateFormatting; | |||
import org.apache.poi.hssf.record.cf.Threshold; | |||
import org.apache.poi.hssf.record.common.FtrHeader; | |||
import org.apache.poi.hssf.record.common.FutureRecord; | |||
import org.apache.poi.hssf.usermodel.HSSFSheet; | |||
import org.apache.poi.ss.formula.Formula; | |||
import org.apache.poi.ss.formula.ptg.Ptg; | |||
import org.apache.poi.ss.usermodel.IconMultiStateFormatting.IconSet; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
import org.apache.poi.util.POILogger; | |||
/** | |||
* Conditional Formatting v12 Rule Record (0x087A). | |||
* | |||
* <p>This is for newer-style Excel conditional formattings, | |||
* from Excel 2007 onwards. | |||
* | |||
* <p>{@link CFRuleRecord} is used where the condition type is | |||
* {@link #CONDITION_TYPE_CELL_VALUE_IS} or {@link #CONDITION_TYPE_FORMULA}, | |||
* this is only used for the other types | |||
*/ | |||
public final class CFRule12Record extends CFRuleBase implements FutureRecord { | |||
public static final short sid = 0x087A; | |||
private FtrHeader futureHeader; | |||
private int ext_formatting_length; | |||
private byte[] ext_formatting_data; | |||
private Formula formula_scale; | |||
private byte ext_opts; | |||
private int priority; | |||
private int template_type; | |||
private byte template_param_length; | |||
private byte[] template_params; | |||
private IconMultiStateFormatting multistate; | |||
// TODO Parse these | |||
private byte[] gradient_data; | |||
private byte[] databar_data; | |||
private byte[] filter_data; | |||
/** Creates new CFRuleRecord */ | |||
private CFRule12Record(byte conditionType, byte comparisonOperation) { | |||
super(conditionType, comparisonOperation); | |||
setDefaults(); | |||
} | |||
private CFRule12Record(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2, Ptg[] formulaScale) { | |||
super(conditionType, comparisonOperation, formula1, formula2); | |||
setDefaults(); | |||
this.formula_scale = Formula.create(formulaScale); | |||
} | |||
private void setDefaults() { | |||
futureHeader = new FtrHeader(); | |||
futureHeader.setRecordType(sid); | |||
ext_formatting_length = 0; | |||
ext_formatting_data = new byte[4]; | |||
formula_scale = Formula.create(Ptg.EMPTY_PTG_ARRAY); | |||
ext_opts = 0; | |||
priority = 0; | |||
template_type = getConditionType(); | |||
template_param_length = 16; | |||
template_params = new byte[template_param_length]; | |||
} | |||
/** | |||
* Creates a new comparison operation rule | |||
*/ | |||
public static CFRule12Record create(HSSFSheet sheet, String formulaText) { | |||
Ptg[] formula1 = parseFormula(formulaText, sheet); | |||
return new CFRule12Record(CONDITION_TYPE_FORMULA, ComparisonOperator.NO_COMPARISON, | |||
formula1, null, null); | |||
} | |||
/** | |||
* Creates a new comparison operation rule | |||
*/ | |||
public static CFRule12Record create(HSSFSheet sheet, byte comparisonOperation, | |||
String formulaText1, String formulaText2) { | |||
Ptg[] formula1 = parseFormula(formulaText1, sheet); | |||
Ptg[] formula2 = parseFormula(formulaText2, sheet); | |||
return new CFRule12Record(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation, | |||
formula1, formula2, null); | |||
} | |||
/** | |||
* Creates a new comparison operation rule | |||
*/ | |||
public static CFRule12Record create(HSSFSheet sheet, byte comparisonOperation, | |||
String formulaText1, String formulaText2, String formulaTextScale) { | |||
Ptg[] formula1 = parseFormula(formulaText1, sheet); | |||
Ptg[] formula2 = parseFormula(formulaText2, sheet); | |||
Ptg[] formula3 = parseFormula(formulaTextScale, sheet); | |||
return new CFRule12Record(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation, | |||
formula1, formula2, formula3); | |||
} | |||
/** | |||
* Creates a new Icon Set / Multi-State formatting | |||
*/ | |||
public static CFRule12Record create(HSSFSheet sheet, IconSet iconSet) { | |||
Threshold[] ts = new Threshold[iconSet.num]; | |||
for (int i=0; i<ts.length; i++) { | |||
ts[i] = new Threshold(); | |||
} | |||
CFRule12Record r = new CFRule12Record(CONDITION_TYPE_COLOR_SCALE, | |||
ComparisonOperator.NO_COMPARISON); | |||
IconMultiStateFormatting imf = r.createMultiStateFormatting(); | |||
imf.setIconSet(iconSet); | |||
imf.setThresholds(ts); | |||
return r; | |||
} | |||
// TODO Static creators for the other record types | |||
public CFRule12Record(RecordInputStream in) { | |||
futureHeader = new FtrHeader(in); | |||
setConditionType(in.readByte()); | |||
setComparisonOperation(in.readByte()); | |||
int field_3_formula1_len = in.readUShort(); | |||
int field_4_formula2_len = in.readUShort(); | |||
ext_formatting_length = in.readInt(); | |||
ext_formatting_data = new byte[0]; | |||
if (ext_formatting_length == 0) { | |||
// 2 bytes reserved | |||
in.readUShort(); | |||
} else { | |||
int len = readFormatOptions(in); | |||
if (len < ext_formatting_length) { | |||
ext_formatting_data = new byte[ext_formatting_length-len]; | |||
in.readFully(ext_formatting_data); | |||
} | |||
} | |||
setFormula1(Formula.read(field_3_formula1_len, in)); | |||
setFormula2(Formula.read(field_4_formula2_len, in)); | |||
int formula_scale_len = in.readUShort(); | |||
formula_scale = Formula.read(formula_scale_len, in); | |||
ext_opts = in.readByte(); | |||
priority = in.readUShort(); | |||
template_type = in.readUShort(); | |||
template_param_length = in.readByte(); | |||
if (template_param_length == 0 || template_param_length == 16) { | |||
template_params = new byte[template_param_length]; | |||
in.readFully(template_params); | |||
} else { | |||
logger.log(POILogger.WARN, "CF Rule v12 template params length should be 0 or 16, found " + template_param_length); | |||
in.readRemainder(); | |||
} | |||
byte type = getConditionType(); | |||
if (type == CONDITION_TYPE_COLOR_SCALE) { | |||
gradient_data = in.readRemainder(); | |||
} else if (type == CONDITION_TYPE_DATA_BAR) { | |||
databar_data = in.readRemainder(); | |||
} else if (type == CONDITION_TYPE_FILTER) { | |||
filter_data = in.readRemainder(); | |||
} else if (type == CONDITION_TYPE_ICON_SET) { | |||
multistate = new IconMultiStateFormatting(in); | |||
} | |||
} | |||
public boolean containsMultiStateBlock() { | |||
return (multistate != null); | |||
} | |||
public IconMultiStateFormatting getMultiStateFormatting() { | |||
return multistate; | |||
} | |||
public IconMultiStateFormatting createMultiStateFormatting() { | |||
if (multistate != null) return multistate; | |||
// Convert, setup and return | |||
setConditionType(CONDITION_TYPE_ICON_SET); | |||
multistate = new IconMultiStateFormatting(); | |||
return multistate; | |||
} | |||
/** | |||
* get the stack of the scale expression as a list | |||
* | |||
* @return list of tokens (casts stack to a list and returns it!) | |||
* this method can return null is we are unable to create Ptgs from | |||
* existing excel file | |||
* callers should check for null! | |||
*/ | |||
public Ptg[] getParsedExpressionScale() { | |||
return formula_scale.getTokens(); | |||
} | |||
public void setParsedExpressionScale(Ptg[] ptgs) { | |||
formula_scale = Formula.create(ptgs); | |||
} | |||
public short getSid() { | |||
return sid; | |||
} | |||
/** | |||
* called by the class that is responsible for writing this sucker. | |||
* Subclasses should implement this so that their data is passed back in a | |||
* byte array. | |||
* | |||
* @param out the stream to write to | |||
*/ | |||
public void serialize(LittleEndianOutput out) { | |||
futureHeader.serialize(out); | |||
int formula1Len=getFormulaSize(getFormula1()); | |||
int formula2Len=getFormulaSize(getFormula2()); | |||
out.writeByte(getConditionType()); | |||
out.writeByte(getComparisonOperation()); | |||
out.writeShort(formula1Len); | |||
out.writeShort(formula2Len); | |||
// TODO Update ext_formatting_length | |||
if (ext_formatting_length == 0) { | |||
out.writeInt(0); | |||
out.writeShort(0); | |||
} else { | |||
out.writeInt(ext_formatting_length); | |||
serializeFormattingBlock(out); | |||
out.write(ext_formatting_data); | |||
} | |||
getFormula1().serializeTokens(out); | |||
getFormula2().serializeTokens(out); | |||
out.writeShort(getFormulaSize(formula_scale)); | |||
formula_scale.serializeTokens(out); | |||
out.writeByte(ext_opts); | |||
out.writeShort(priority); | |||
out.writeShort(template_type); | |||
out.writeByte(template_param_length); | |||
out.write(template_params); | |||
byte type = getConditionType(); | |||
if (type == CONDITION_TYPE_COLOR_SCALE) { | |||
out.write(gradient_data); | |||
} else if (type == CONDITION_TYPE_DATA_BAR) { | |||
out.write(databar_data); | |||
} else if (type == CONDITION_TYPE_FILTER) { | |||
out.write(filter_data); | |||
} else if (type == CONDITION_TYPE_ICON_SET) { | |||
multistate.serialize(out); | |||
} | |||
} | |||
protected int getDataSize() { | |||
int len = FtrHeader.getDataSize() + 6; | |||
if (ext_formatting_length == 0) { | |||
len += 6; | |||
} else { | |||
len += 4 + getFormattingBlockSize() + ext_formatting_data.length; | |||
} | |||
len += getFormulaSize(getFormula1()); | |||
len += getFormulaSize(getFormula2()); | |||
len += 2 + getFormulaSize(formula_scale); | |||
len += 6 + template_params.length; | |||
byte type = getConditionType(); | |||
if (type == CONDITION_TYPE_COLOR_SCALE) { | |||
len += gradient_data.length; | |||
} else if (type == CONDITION_TYPE_DATA_BAR) { | |||
len += databar_data.length; | |||
} else if (type == CONDITION_TYPE_FILTER) { | |||
len += filter_data.length; | |||
} else if (type == CONDITION_TYPE_ICON_SET) { | |||
len += multistate.getDataLength(); | |||
} | |||
return len; | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[CFRULE12]\n"); | |||
buffer.append(" .condition_type=").append(getConditionType()).append("\n"); | |||
buffer.append(" .dxfn12_length =0x").append(Integer.toHexString(ext_formatting_length)).append("\n"); | |||
buffer.append(" .option_flags =0x").append(Integer.toHexString(getOptions())).append("\n"); | |||
if (containsFontFormattingBlock()) { | |||
buffer.append(_fontFormatting.toString()).append("\n"); | |||
} | |||
if (containsBorderFormattingBlock()) { | |||
buffer.append(_borderFormatting.toString()).append("\n"); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
buffer.append(_patternFormatting.toString()).append("\n"); | |||
} | |||
buffer.append(" .dxfn12_ext=").append(HexDump.toHex(ext_formatting_data)).append("\n"); | |||
buffer.append(" .formula_1 =").append(Arrays.toString(getFormula1().getTokens())).append("\n"); | |||
buffer.append(" .formula_2 =").append(Arrays.toString(getFormula2().getTokens())).append("\n"); | |||
buffer.append(" .formula_S =").append(Arrays.toString(formula_scale.getTokens())).append("\n"); | |||
buffer.append(" .ext_opts =").append(ext_opts).append("\n"); | |||
buffer.append(" .priority =").append(priority).append("\n"); | |||
buffer.append(" .template_type =").append(template_type).append("\n"); | |||
buffer.append(" .template_params=").append(HexDump.toHex(template_params)).append("\n"); | |||
buffer.append(" .gradient_data =").append(HexDump.toHex(gradient_data)).append("\n"); | |||
buffer.append(" .databar_data =").append(HexDump.toHex(databar_data)).append("\n"); | |||
buffer.append(" .filter_data =").append(HexDump.toHex(filter_data)).append("\n"); | |||
if (multistate != null) { | |||
buffer.append(multistate); | |||
} | |||
buffer.append("[/CFRULE12]\n"); | |||
return buffer.toString(); | |||
} | |||
public Object clone() { | |||
CFRule12Record rec = new CFRule12Record(getConditionType(), getComparisonOperation()); | |||
rec.futureHeader.setAssociatedRange(futureHeader.getAssociatedRange().copy()); | |||
super.copyTo(rec); | |||
rec.ext_formatting_length = ext_formatting_length; | |||
rec.ext_formatting_data = new byte[ext_formatting_length]; | |||
System.arraycopy(ext_formatting_data, 0, rec.ext_formatting_data, 0, ext_formatting_length); | |||
rec.formula_scale = formula_scale.copy(); | |||
rec.ext_opts = ext_opts; | |||
rec.priority = priority; | |||
rec.template_type = template_type; | |||
rec.template_param_length = template_param_length; | |||
rec.template_params = new byte[template_param_length]; | |||
System.arraycopy(template_params, 0, rec.template_params, 0, template_param_length); | |||
// TODO Clone the rgbCT data like Gradients, Databars etc | |||
return rec; | |||
} | |||
public short getFutureRecordType() { | |||
return futureHeader.getRecordType(); | |||
} | |||
public FtrHeader getFutureHeader() { | |||
return futureHeader; | |||
} | |||
public CellRangeAddress getAssociatedRange() { | |||
return futureHeader.getAssociatedRange(); | |||
} | |||
} |
@@ -0,0 +1,455 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.record; | |||
import org.apache.poi.hssf.model.HSSFFormulaParser; | |||
import org.apache.poi.hssf.record.cf.BorderFormatting; | |||
import org.apache.poi.hssf.record.cf.FontFormatting; | |||
import org.apache.poi.hssf.record.cf.PatternFormatting; | |||
import org.apache.poi.hssf.usermodel.HSSFSheet; | |||
import org.apache.poi.ss.formula.Formula; | |||
import org.apache.poi.ss.formula.FormulaType; | |||
import org.apache.poi.ss.formula.ptg.Ptg; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
/** | |||
* Conditional Formatting Rules. This can hold old-style rules | |||
* | |||
* | |||
* <p>This is for the older-style Excel conditional formattings, | |||
* new-style (Excel 2007+) also make use of {@link CFRule12Record} | |||
* and {@link CFExRuleRecord} for their rules. | |||
*/ | |||
public abstract class CFRuleBase extends StandardRecord { | |||
public static final class ComparisonOperator { | |||
public static final byte NO_COMPARISON = 0; | |||
public static final byte BETWEEN = 1; | |||
public static final byte NOT_BETWEEN = 2; | |||
public static final byte EQUAL = 3; | |||
public static final byte NOT_EQUAL = 4; | |||
public static final byte GT = 5; | |||
public static final byte LT = 6; | |||
public static final byte GE = 7; | |||
public static final byte LE = 8; | |||
private static final byte max_operator = 8; | |||
} | |||
protected static final POILogger logger = POILogFactory.getLogger(CFRuleBase.class); | |||
private byte condition_type; | |||
// The only kinds that CFRuleRecord handles | |||
public static final byte CONDITION_TYPE_CELL_VALUE_IS = 1; | |||
public static final byte CONDITION_TYPE_FORMULA = 2; | |||
// These are CFRule12Rule only | |||
public static final byte CONDITION_TYPE_COLOR_SCALE = 3; | |||
public static final byte CONDITION_TYPE_DATA_BAR = 4; | |||
public static final byte CONDITION_TYPE_FILTER = 5; | |||
public static final byte CONDITION_TYPE_ICON_SET = 6; | |||
private byte comparison_operator; | |||
public static final int TEMPLATE_CELL_VALUE = 0x0000; | |||
public static final int TEMPLATE_FORMULA = 0x0001; | |||
public static final int TEMPLATE_COLOR_SCALE_FORMATTING = 0x0002; | |||
public static final int TEMPLATE_DATA_BAR_FORMATTING = 0x0003; | |||
public static final int TEMPLATE_ICON_SET_FORMATTING = 0x0004; | |||
public static final int TEMPLATE_FILTER = 0x0005; | |||
public static final int TEMPLATE_UNIQUE_VALUES = 0x0007; | |||
public static final int TEMPLATE_CONTAINS_TEXT = 0x0008; | |||
public static final int TEMPLATE_CONTAINS_BLANKS = 0x0009; | |||
public static final int TEMPLATE_CONTAINS_NO_BLANKS = 0x000A; | |||
public static final int TEMPLATE_CONTAINS_ERRORS = 0x000B; | |||
public static final int TEMPLATE_CONTAINS_NO_ERRORS = 0x000C; | |||
public static final int TEMPLATE_TODAY = 0x000F; | |||
public static final int TEMPLATE_TOMORROW = 0x0010; | |||
public static final int TEMPLATE_YESTERDAY = 0x0011; | |||
public static final int TEMPLATE_LAST_7_DAYS = 0x0012; | |||
public static final int TEMPLATE_LAST_MONTH = 0x0013; | |||
public static final int TEMPLATE_NEXT_MONTH = 0x0014; | |||
public static final int TEMPLATE_THIS_WEEK = 0x0015; | |||
public static final int TEMPLATE_NEXT_WEEK = 0x0016; | |||
public static final int TEMPLATE_LAST_WEEK = 0x0017; | |||
public static final int TEMPLATE_THIS_MONTH = 0x0018; | |||
public static final int TEMPLATE_ABOVE_AVERAGE = 0x0019; | |||
public static final int TEMPLATE_BELOW_AVERAGE = 0x001A; | |||
public static final int TEMPLATE_DUPLICATE_VALUES = 0x001B; | |||
public static final int TEMPLATE_ABOVE_OR_EQUAL_TO_AVERAGE = 0x001D; | |||
public static final int TEMPLATE_BELOW_OR_EQUAL_TO_AVERAGE = 0x001E; | |||
static final BitField modificationBits = bf(0x003FFFFF); // Bits: font,align,bord,patt,prot | |||
static final BitField alignHor = bf(0x00000001); // 0 = Horizontal alignment modified | |||
static final BitField alignVer = bf(0x00000002); // 0 = Vertical alignment modified | |||
static final BitField alignWrap = bf(0x00000004); // 0 = Text wrapped flag modified | |||
static final BitField alignRot = bf(0x00000008); // 0 = Text rotation modified | |||
static final BitField alignJustLast = bf(0x00000010); // 0 = Justify last line flag modified | |||
static final BitField alignIndent = bf(0x00000020); // 0 = Indentation modified | |||
static final BitField alignShrin = bf(0x00000040); // 0 = Shrink to fit flag modified | |||
static final BitField mergeCell = bf(0x00000080); // Normally 1, 0 = Merge Cell flag modified | |||
static final BitField protLocked = bf(0x00000100); // 0 = Cell locked flag modified | |||
static final BitField protHidden = bf(0x00000200); // 0 = Cell hidden flag modified | |||
static final BitField bordLeft = bf(0x00000400); // 0 = Left border style and colour modified | |||
static final BitField bordRight = bf(0x00000800); // 0 = Right border style and colour modified | |||
static final BitField bordTop = bf(0x00001000); // 0 = Top border style and colour modified | |||
static final BitField bordBot = bf(0x00002000); // 0 = Bottom border style and colour modified | |||
static final BitField bordTlBr = bf(0x00004000); // 0 = Top-left to bottom-right border flag modified | |||
static final BitField bordBlTr = bf(0x00008000); // 0 = Bottom-left to top-right border flag modified | |||
static final BitField pattStyle = bf(0x00010000); // 0 = Pattern style modified | |||
static final BitField pattCol = bf(0x00020000); // 0 = Pattern colour modified | |||
static final BitField pattBgCol = bf(0x00040000); // 0 = Pattern background colour modified | |||
static final BitField notUsed2 = bf(0x00380000); // Always 111 (ifmt / ifnt / 1) | |||
static final BitField undocumented = bf(0x03C00000); // Undocumented bits | |||
static final BitField fmtBlockBits = bf(0x7C000000); // Bits: font,align,bord,patt,prot | |||
static final BitField font = bf(0x04000000); // 1 = Record contains font formatting block | |||
static final BitField align = bf(0x08000000); // 1 = Record contains alignment formatting block | |||
static final BitField bord = bf(0x10000000); // 1 = Record contains border formatting block | |||
static final BitField patt = bf(0x20000000); // 1 = Record contains pattern formatting block | |||
static final BitField prot = bf(0x40000000); // 1 = Record contains protection formatting block | |||
static final BitField alignTextDir = bf(0x80000000); // 0 = Text direction modified | |||
private static BitField bf(int i) { | |||
return BitFieldFactory.getInstance(i); | |||
} | |||
protected int formatting_options; | |||
protected short formatting_not_used; // TODO Decode this properly | |||
protected FontFormatting _fontFormatting; | |||
protected BorderFormatting _borderFormatting; | |||
protected PatternFormatting _patternFormatting; | |||
private Formula formula1; | |||
private Formula formula2; | |||
/** Creates new CFRuleRecord */ | |||
protected CFRuleBase(byte conditionType, byte comparisonOperation) { | |||
setConditionType(conditionType); | |||
setComparisonOperation(comparisonOperation); | |||
formula1 = Formula.create(Ptg.EMPTY_PTG_ARRAY); | |||
formula2 = Formula.create(Ptg.EMPTY_PTG_ARRAY); | |||
} | |||
protected CFRuleBase(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2) { | |||
this(conditionType, comparisonOperation); | |||
this.formula1 = Formula.create(formula1); | |||
this.formula2 = Formula.create(formula2); | |||
} | |||
protected CFRuleBase() {} | |||
protected int readFormatOptions(RecordInputStream in) { | |||
formatting_options = in.readInt(); | |||
formatting_not_used = in.readShort(); | |||
int len = 6; | |||
if (containsFontFormattingBlock()) { | |||
_fontFormatting = new FontFormatting(in); | |||
len += _fontFormatting.getDataLength(); | |||
} | |||
if (containsBorderFormattingBlock()) { | |||
_borderFormatting = new BorderFormatting(in); | |||
len += _borderFormatting.getDataLength(); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
_patternFormatting = new PatternFormatting(in); | |||
len += _patternFormatting.getDataLength(); | |||
} | |||
return len; | |||
} | |||
public byte getConditionType() { | |||
return condition_type; | |||
} | |||
protected void setConditionType(byte condition_type) { | |||
if ((this instanceof CFRuleRecord)) { | |||
if (condition_type == CONDITION_TYPE_CELL_VALUE_IS || | |||
condition_type == CONDITION_TYPE_FORMULA) { | |||
// Good, valid combination | |||
} else { | |||
throw new IllegalArgumentException("CFRuleRecord only accepts Value-Is and Formula types"); | |||
} | |||
} | |||
this.condition_type = condition_type; | |||
} | |||
public void setComparisonOperation(byte operation) { | |||
if (operation < 0 || operation > ComparisonOperator.max_operator) | |||
throw new IllegalArgumentException( | |||
"Valid operators are only in the range 0 to " +ComparisonOperator.max_operator); | |||
this.comparison_operator = operation; | |||
} | |||
public byte getComparisonOperation() { | |||
return comparison_operator; | |||
} | |||
public boolean containsFontFormattingBlock() { | |||
return getOptionFlag(font); | |||
} | |||
public void setFontFormatting(FontFormatting fontFormatting) { | |||
_fontFormatting = fontFormatting; | |||
setOptionFlag(fontFormatting != null, font); | |||
} | |||
public FontFormatting getFontFormatting() { | |||
if( containsFontFormattingBlock()) { | |||
return _fontFormatting; | |||
} | |||
return null; | |||
} | |||
public boolean containsAlignFormattingBlock() { | |||
return getOptionFlag(align); | |||
} | |||
public void setAlignFormattingUnchanged() { | |||
setOptionFlag(false,align); | |||
} | |||
public boolean containsBorderFormattingBlock() { | |||
return getOptionFlag(bord); | |||
} | |||
public void setBorderFormatting(BorderFormatting borderFormatting) { | |||
_borderFormatting = borderFormatting; | |||
setOptionFlag(borderFormatting != null, bord); | |||
} | |||
public BorderFormatting getBorderFormatting() { | |||
if( containsBorderFormattingBlock()) { | |||
return _borderFormatting; | |||
} | |||
return null; | |||
} | |||
public boolean containsPatternFormattingBlock() { | |||
return getOptionFlag(patt); | |||
} | |||
public void setPatternFormatting(PatternFormatting patternFormatting) { | |||
_patternFormatting = patternFormatting; | |||
setOptionFlag(patternFormatting!=null, patt); | |||
} | |||
public PatternFormatting getPatternFormatting() { | |||
if( containsPatternFormattingBlock()) | |||
{ | |||
return _patternFormatting; | |||
} | |||
return null; | |||
} | |||
public boolean containsProtectionFormattingBlock() { | |||
return getOptionFlag(prot); | |||
} | |||
public void setProtectionFormattingUnchanged() { | |||
setOptionFlag(false,prot); | |||
} | |||
/** | |||
* get the option flags | |||
* | |||
* @return bit mask | |||
*/ | |||
public int getOptions() { | |||
return formatting_options; | |||
} | |||
private boolean isModified(BitField field) { | |||
return !field.isSet(formatting_options); | |||
} | |||
private void setModified(boolean modified, BitField field) { | |||
formatting_options = field.setBoolean(formatting_options, !modified); | |||
} | |||
public boolean isLeftBorderModified() { | |||
return isModified(bordLeft); | |||
} | |||
public void setLeftBorderModified(boolean modified) { | |||
setModified(modified,bordLeft); | |||
} | |||
public boolean isRightBorderModified() { | |||
return isModified(bordRight); | |||
} | |||
public void setRightBorderModified(boolean modified) | |||
{ | |||
setModified(modified,bordRight); | |||
} | |||
public boolean isTopBorderModified() { | |||
return isModified(bordTop); | |||
} | |||
public void setTopBorderModified(boolean modified) { | |||
setModified(modified,bordTop); | |||
} | |||
public boolean isBottomBorderModified() { | |||
return isModified(bordBot); | |||
} | |||
public void setBottomBorderModified(boolean modified) { | |||
setModified(modified,bordBot); | |||
} | |||
public boolean isTopLeftBottomRightBorderModified() { | |||
return isModified(bordTlBr); | |||
} | |||
public void setTopLeftBottomRightBorderModified(boolean modified) { | |||
setModified(modified,bordTlBr); | |||
} | |||
public boolean isBottomLeftTopRightBorderModified() { | |||
return isModified(bordBlTr); | |||
} | |||
public void setBottomLeftTopRightBorderModified(boolean modified) { | |||
setModified(modified,bordBlTr); | |||
} | |||
public boolean isPatternStyleModified() { | |||
return isModified(pattStyle); | |||
} | |||
public void setPatternStyleModified(boolean modified) { | |||
setModified(modified,pattStyle); | |||
} | |||
public boolean isPatternColorModified() { | |||
return isModified(pattCol); | |||
} | |||
public void setPatternColorModified(boolean modified) { | |||
setModified(modified,pattCol); | |||
} | |||
public boolean isPatternBackgroundColorModified() { | |||
return isModified(pattBgCol); | |||
} | |||
public void setPatternBackgroundColorModified(boolean modified) { | |||
setModified(modified,pattBgCol); | |||
} | |||
private boolean getOptionFlag(BitField field) { | |||
return field.isSet(formatting_options); | |||
} | |||
private void setOptionFlag(boolean flag, BitField field) { | |||
formatting_options = field.setBoolean(formatting_options, flag); | |||
} | |||
protected int getFormattingBlockSize() { | |||
return 6 + | |||
(containsFontFormattingBlock()?_fontFormatting.getRawRecord().length:0)+ | |||
(containsBorderFormattingBlock()?8:0)+ | |||
(containsPatternFormattingBlock()?4:0); | |||
} | |||
protected void serializeFormattingBlock(LittleEndianOutput out) { | |||
out.writeInt(formatting_options); | |||
out.writeShort(formatting_not_used); | |||
if (containsFontFormattingBlock()) { | |||
byte[] fontFormattingRawRecord = _fontFormatting.getRawRecord(); | |||
out.write(fontFormattingRawRecord); | |||
} | |||
if (containsBorderFormattingBlock()) { | |||
_borderFormatting.serialize(out); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
_patternFormatting.serialize(out); | |||
} | |||
} | |||
/** | |||
* get the stack of the 1st expression as a list | |||
* | |||
* @return list of tokens (casts stack to a list and returns it!) | |||
* this method can return null is we are unable to create Ptgs from | |||
* existing excel file | |||
* callers should check for null! | |||
*/ | |||
public Ptg[] getParsedExpression1() { | |||
return formula1.getTokens(); | |||
} | |||
public void setParsedExpression1(Ptg[] ptgs) { | |||
formula1 = Formula.create(ptgs); | |||
} | |||
protected Formula getFormula1() { | |||
return formula1; | |||
} | |||
protected void setFormula1(Formula formula1) { | |||
this.formula1 = formula1; | |||
} | |||
/** | |||
* get the stack of the 2nd expression as a list | |||
* | |||
* @return array of {@link Ptg}s, possibly <code>null</code> | |||
*/ | |||
public Ptg[] getParsedExpression2() { | |||
return Formula.getTokens(formula2); | |||
} | |||
public void setParsedExpression2(Ptg[] ptgs) { | |||
formula2 = Formula.create(ptgs); | |||
} | |||
protected Formula getFormula2() { | |||
return formula2; | |||
} | |||
protected void setFormula2(Formula formula2) { | |||
this.formula2 = formula2; | |||
} | |||
/** | |||
* @param formula must not be <code>null</code> | |||
* @return encoded size of the formula tokens (does not include 2 bytes for ushort length) | |||
*/ | |||
protected static int getFormulaSize(Formula formula) { | |||
return formula.getEncodedTokenSize(); | |||
} | |||
/** | |||
* TODO - parse conditional format formulas properly i.e. produce tRefN and tAreaN instead of tRef and tArea | |||
* this call will produce the wrong results if the formula contains any cell references | |||
* One approach might be to apply the inverse of SharedFormulaRecord.convertSharedFormulas(Stack, int, int) | |||
* Note - two extra parameters (rowIx & colIx) will be required. They probably come from one of the Region objects. | |||
* | |||
* @return <code>null</code> if <tt>formula</tt> was null. | |||
*/ | |||
public static Ptg[] parseFormula(String formula, HSSFSheet sheet) { | |||
if(formula == null) { | |||
return null; | |||
} | |||
int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet); | |||
return HSSFFormulaParser.parse(formula, sheet.getWorkbook(), FormulaType.CELL, sheetIndex); | |||
} | |||
protected void copyTo(CFRuleBase rec) { | |||
rec.condition_type = condition_type; | |||
rec.comparison_operator = comparison_operator; | |||
rec.formatting_options = formatting_options; | |||
rec.formatting_not_used = formatting_not_used; | |||
if (containsFontFormattingBlock()) { | |||
rec._fontFormatting = (FontFormatting) _fontFormatting.clone(); | |||
} | |||
if (containsBorderFormattingBlock()) { | |||
rec._borderFormatting = (BorderFormatting) _borderFormatting.clone(); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
rec._patternFormatting = (PatternFormatting) _patternFormatting.clone(); | |||
} | |||
rec.setFormula1(getFormula1().copy()); | |||
rec.setFormula2(getFormula2().copy()); | |||
} | |||
} |
@@ -19,512 +19,129 @@ package org.apache.poi.hssf.record; | |||
import java.util.Arrays; | |||
import org.apache.poi.hssf.model.HSSFFormulaParser; | |||
import org.apache.poi.hssf.record.cf.BorderFormatting; | |||
import org.apache.poi.hssf.record.cf.FontFormatting; | |||
import org.apache.poi.hssf.record.cf.PatternFormatting; | |||
import org.apache.poi.hssf.usermodel.HSSFSheet; | |||
import org.apache.poi.ss.formula.Formula; | |||
import org.apache.poi.ss.formula.FormulaType; | |||
import org.apache.poi.ss.formula.ptg.Ptg; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Conditional Formatting Rule Record (0x01B1).<br/> | |||
* | |||
* @author Dmitriy Kumshayev | |||
* Conditional Formatting Rule Record (0x01B1). | |||
* | |||
* <p>This is for the older-style Excel conditional formattings, | |||
* new-style (Excel 2007+) also make use of {@link CFRule12Record} | |||
* and {@link CFExRuleRecord} for their rules. | |||
*/ | |||
public final class CFRuleRecord extends StandardRecord { | |||
public final class CFRuleRecord extends CFRuleBase { | |||
public static final short sid = 0x01B1; | |||
public static final short sid = 0x01B1; | |||
public static final class ComparisonOperator { | |||
public static final byte NO_COMPARISON = 0; | |||
public static final byte BETWEEN = 1; | |||
public static final byte NOT_BETWEEN = 2; | |||
public static final byte EQUAL = 3; | |||
public static final byte NOT_EQUAL = 4; | |||
public static final byte GT = 5; | |||
public static final byte LT = 6; | |||
public static final byte GE = 7; | |||
public static final byte LE = 8; | |||
} | |||
private byte field_1_condition_type; | |||
public static final byte CONDITION_TYPE_CELL_VALUE_IS = 1; | |||
public static final byte CONDITION_TYPE_FORMULA = 2; | |||
private byte field_2_comparison_operator; | |||
private int field_5_options; | |||
private static final BitField modificationBits = bf(0x003FFFFF); // Bits: font,align,bord,patt,prot | |||
private static final BitField alignHor = bf(0x00000001); // 0 = Horizontal alignment modified | |||
private static final BitField alignVer = bf(0x00000002); // 0 = Vertical alignment modified | |||
private static final BitField alignWrap = bf(0x00000004); // 0 = Text wrapped flag modified | |||
private static final BitField alignRot = bf(0x00000008); // 0 = Text rotation modified | |||
private static final BitField alignJustLast = bf(0x00000010); // 0 = Justify last line flag modified | |||
private static final BitField alignIndent = bf(0x00000020); // 0 = Indentation modified | |||
private static final BitField alignShrin = bf(0x00000040); // 0 = Shrink to fit flag modified | |||
private static final BitField notUsed1 = bf(0x00000080); // Always 1 | |||
private static final BitField protLocked = bf(0x00000100); // 0 = Cell locked flag modified | |||
private static final BitField protHidden = bf(0x00000200); // 0 = Cell hidden flag modified | |||
private static final BitField bordLeft = bf(0x00000400); // 0 = Left border style and colour modified | |||
private static final BitField bordRight = bf(0x00000800); // 0 = Right border style and colour modified | |||
private static final BitField bordTop = bf(0x00001000); // 0 = Top border style and colour modified | |||
private static final BitField bordBot = bf(0x00002000); // 0 = Bottom border style and colour modified | |||
private static final BitField bordTlBr = bf(0x00004000); // 0 = Top-left to bottom-right border flag modified | |||
private static final BitField bordBlTr = bf(0x00008000); // 0 = Bottom-left to top-right border flag modified | |||
private static final BitField pattStyle = bf(0x00010000); // 0 = Pattern style modified | |||
private static final BitField pattCol = bf(0x00020000); // 0 = Pattern colour modified | |||
private static final BitField pattBgCol = bf(0x00040000); // 0 = Pattern background colour modified | |||
private static final BitField notUsed2 = bf(0x00380000); // Always 111 | |||
private static final BitField undocumented = bf(0x03C00000); // Undocumented bits | |||
private static final BitField fmtBlockBits = bf(0x7C000000); // Bits: font,align,bord,patt,prot | |||
private static final BitField font = bf(0x04000000); // 1 = Record contains font formatting block | |||
private static final BitField align = bf(0x08000000); // 1 = Record contains alignment formatting block | |||
private static final BitField bord = bf(0x10000000); // 1 = Record contains border formatting block | |||
private static final BitField patt = bf(0x20000000); // 1 = Record contains pattern formatting block | |||
private static final BitField prot = bf(0x40000000); // 1 = Record contains protection formatting block | |||
private static final BitField alignTextDir = bf(0x80000000); // 0 = Text direction modified | |||
private static BitField bf(int i) { | |||
return BitFieldFactory.getInstance(i); | |||
} | |||
private short field_6_not_used; | |||
private FontFormatting _fontFormatting; | |||
private BorderFormatting _borderFormatting; | |||
private PatternFormatting _patternFormatting; | |||
private Formula field_17_formula1; | |||
private Formula field_18_formula2; | |||
/** Creates new CFRuleRecord */ | |||
private CFRuleRecord(byte conditionType, byte comparisonOperation) | |||
{ | |||
field_1_condition_type=conditionType; | |||
field_2_comparison_operator=comparisonOperation; | |||
// Set modification flags to 1: by default options are not modified | |||
field_5_options = modificationBits.setValue(field_5_options, -1); | |||
// Set formatting block flags to 0 (no formatting blocks) | |||
field_5_options = fmtBlockBits.setValue(field_5_options, 0); | |||
field_5_options = undocumented.clear(field_5_options); | |||
field_6_not_used = (short)0x8002; // Excel seems to write this value, but it doesn't seem to care what it reads | |||
_fontFormatting=null; | |||
_borderFormatting=null; | |||
_patternFormatting=null; | |||
field_17_formula1=Formula.create(Ptg.EMPTY_PTG_ARRAY); | |||
field_18_formula2=Formula.create(Ptg.EMPTY_PTG_ARRAY); | |||
} | |||
/** Creates new CFRuleRecord */ | |||
private CFRuleRecord(byte conditionType, byte comparisonOperation) { | |||
super(conditionType, comparisonOperation); | |||
setDefaults(); | |||
} | |||
private CFRuleRecord(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2) { | |||
this(conditionType, comparisonOperation); | |||
field_17_formula1 = Formula.create(formula1); | |||
field_18_formula2 = Formula.create(formula2); | |||
} | |||
private CFRuleRecord(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2) { | |||
super(conditionType, comparisonOperation, formula1, formula2); | |||
setDefaults(); | |||
} | |||
private void setDefaults() { | |||
// Set modification flags to 1: by default options are not modified | |||
formatting_options = modificationBits.setValue(formatting_options, -1); | |||
// Set formatting block flags to 0 (no formatting blocks) | |||
formatting_options = fmtBlockBits.setValue(formatting_options, 0); | |||
formatting_options = undocumented.clear(formatting_options); | |||
formatting_not_used = (short)0x8002; // Excel seems to write this value, but it doesn't seem to care what it reads | |||
_fontFormatting = null; | |||
_borderFormatting = null; | |||
_patternFormatting = null; | |||
} | |||
/** | |||
* Creates a new comparison operation rule | |||
*/ | |||
/** | |||
* Creates a new comparison operation rule | |||
*/ | |||
public static CFRuleRecord create(HSSFSheet sheet, String formulaText) { | |||
Ptg[] formula1 = parseFormula(formulaText, sheet); | |||
return new CFRuleRecord(CONDITION_TYPE_FORMULA, ComparisonOperator.NO_COMPARISON, | |||
formula1, null); | |||
} | |||
/** | |||
* Creates a new comparison operation rule | |||
*/ | |||
public static CFRuleRecord create(HSSFSheet sheet, byte comparisonOperation, | |||
String formulaText1, String formulaText2) { | |||
Ptg[] formula1 = parseFormula(formulaText1, sheet); | |||
Ptg[] formula2 = parseFormula(formulaText2, sheet); | |||
return new CFRuleRecord(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation, formula1, formula2); | |||
} | |||
public CFRuleRecord(RecordInputStream in) { | |||
field_1_condition_type = in.readByte(); | |||
field_2_comparison_operator = in.readByte(); | |||
int field_3_formula1_len = in.readUShort(); | |||
int field_4_formula2_len = in.readUShort(); | |||
field_5_options = in.readInt(); | |||
field_6_not_used = in.readShort(); | |||
if (containsFontFormattingBlock()) { | |||
_fontFormatting = new FontFormatting(in); | |||
} | |||
if (containsBorderFormattingBlock()) { | |||
_borderFormatting = new BorderFormatting(in); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
_patternFormatting = new PatternFormatting(in); | |||
} | |||
// "You may not use unions, intersections or array constants in Conditional Formatting criteria" | |||
field_17_formula1 = Formula.read(field_3_formula1_len, in); | |||
field_18_formula2 = Formula.read(field_4_formula2_len, in); | |||
} | |||
public byte getConditionType() | |||
{ | |||
return field_1_condition_type; | |||
} | |||
public boolean containsFontFormattingBlock() | |||
{ | |||
return getOptionFlag(font); | |||
} | |||
public void setFontFormatting(FontFormatting fontFormatting) | |||
{ | |||
_fontFormatting = fontFormatting; | |||
setOptionFlag(fontFormatting != null, font); | |||
} | |||
public FontFormatting getFontFormatting() | |||
{ | |||
if( containsFontFormattingBlock()) | |||
{ | |||
return _fontFormatting; | |||
} | |||
return null; | |||
} | |||
public boolean containsAlignFormattingBlock() | |||
{ | |||
return getOptionFlag(align); | |||
} | |||
public void setAlignFormattingUnchanged() | |||
{ | |||
setOptionFlag(false,align); | |||
} | |||
public boolean containsBorderFormattingBlock() | |||
{ | |||
return getOptionFlag(bord); | |||
} | |||
public void setBorderFormatting(BorderFormatting borderFormatting) | |||
{ | |||
_borderFormatting = borderFormatting; | |||
setOptionFlag(borderFormatting != null, bord); | |||
} | |||
public BorderFormatting getBorderFormatting() | |||
{ | |||
if( containsBorderFormattingBlock()) | |||
{ | |||
return _borderFormatting; | |||
} | |||
return null; | |||
} | |||
public boolean containsPatternFormattingBlock() | |||
{ | |||
return getOptionFlag(patt); | |||
} | |||
public void setPatternFormatting(PatternFormatting patternFormatting) | |||
{ | |||
_patternFormatting = patternFormatting; | |||
setOptionFlag(patternFormatting!=null, patt); | |||
} | |||
public PatternFormatting getPatternFormatting() | |||
{ | |||
if( containsPatternFormattingBlock()) | |||
{ | |||
return _patternFormatting; | |||
} | |||
return null; | |||
} | |||
public boolean containsProtectionFormattingBlock() | |||
{ | |||
return getOptionFlag(prot); | |||
} | |||
public void setProtectionFormattingUnchanged() | |||
{ | |||
setOptionFlag(false,prot); | |||
} | |||
public void setComparisonOperation(byte operation) | |||
{ | |||
field_2_comparison_operator = operation; | |||
} | |||
public byte getComparisonOperation() | |||
{ | |||
return field_2_comparison_operator; | |||
} | |||
/** | |||
* get the option flags | |||
* | |||
* @return bit mask | |||
*/ | |||
public int getOptions() | |||
{ | |||
return field_5_options; | |||
} | |||
private boolean isModified(BitField field) | |||
{ | |||
return !field.isSet(field_5_options); | |||
} | |||
private void setModified(boolean modified, BitField field) | |||
{ | |||
field_5_options = field.setBoolean(field_5_options, !modified); | |||
} | |||
public boolean isLeftBorderModified() | |||
{ | |||
return isModified(bordLeft); | |||
} | |||
public void setLeftBorderModified(boolean modified) | |||
{ | |||
setModified(modified,bordLeft); | |||
} | |||
public boolean isRightBorderModified() | |||
{ | |||
return isModified(bordRight); | |||
} | |||
public void setRightBorderModified(boolean modified) | |||
{ | |||
setModified(modified,bordRight); | |||
} | |||
public boolean isTopBorderModified() | |||
{ | |||
return isModified(bordTop); | |||
} | |||
public void setTopBorderModified(boolean modified) | |||
{ | |||
setModified(modified,bordTop); | |||
} | |||
public boolean isBottomBorderModified() | |||
{ | |||
return isModified(bordBot); | |||
} | |||
public void setBottomBorderModified(boolean modified) | |||
{ | |||
setModified(modified,bordBot); | |||
} | |||
public boolean isTopLeftBottomRightBorderModified() | |||
{ | |||
return isModified(bordTlBr); | |||
} | |||
public void setTopLeftBottomRightBorderModified(boolean modified) | |||
{ | |||
setModified(modified,bordTlBr); | |||
} | |||
public boolean isBottomLeftTopRightBorderModified() | |||
{ | |||
return isModified(bordBlTr); | |||
} | |||
public void setBottomLeftTopRightBorderModified(boolean modified) | |||
{ | |||
setModified(modified,bordBlTr); | |||
} | |||
public boolean isPatternStyleModified() | |||
{ | |||
return isModified(pattStyle); | |||
} | |||
public void setPatternStyleModified(boolean modified) | |||
{ | |||
setModified(modified,pattStyle); | |||
} | |||
public boolean isPatternColorModified() | |||
{ | |||
return isModified(pattCol); | |||
} | |||
public void setPatternColorModified(boolean modified) | |||
{ | |||
setModified(modified,pattCol); | |||
} | |||
public boolean isPatternBackgroundColorModified() | |||
{ | |||
return isModified(pattBgCol); | |||
} | |||
public void setPatternBackgroundColorModified(boolean modified) | |||
{ | |||
setModified(modified,pattBgCol); | |||
} | |||
private boolean getOptionFlag(BitField field) | |||
{ | |||
return field.isSet(field_5_options); | |||
} | |||
private void setOptionFlag(boolean flag, BitField field) | |||
{ | |||
field_5_options = field.setBoolean(field_5_options, flag); | |||
} | |||
/** | |||
* get the stack of the 1st expression as a list | |||
* | |||
* @return list of tokens (casts stack to a list and returns it!) | |||
* this method can return null is we are unable to create Ptgs from | |||
* existing excel file | |||
* callers should check for null! | |||
*/ | |||
public Ptg[] getParsedExpression1() | |||
{ | |||
return field_17_formula1.getTokens(); | |||
} | |||
public void setParsedExpression1(Ptg[] ptgs) { | |||
field_17_formula1 = Formula.create(ptgs); | |||
} | |||
/** | |||
* get the stack of the 2nd expression as a list | |||
* | |||
* @return array of {@link Ptg}s, possibly <code>null</code> | |||
*/ | |||
public Ptg[] getParsedExpression2() { | |||
return Formula.getTokens(field_18_formula2); | |||
} | |||
public void setParsedExpression2(Ptg[] ptgs) { | |||
field_18_formula2 = Formula.create(ptgs); | |||
} | |||
public short getSid() | |||
{ | |||
return sid; | |||
} | |||
/** | |||
* @param ptgs must not be <code>null</code> | |||
* @return encoded size of the formula tokens (does not include 2 bytes for ushort length) | |||
*/ | |||
private static int getFormulaSize(Formula formula) { | |||
return formula.getEncodedTokenSize(); | |||
} | |||
/** | |||
* called by the class that is responsible for writing this sucker. | |||
* Subclasses should implement this so that their data is passed back in a | |||
* byte array. | |||
* | |||
* @param out the stream to write to | |||
*/ | |||
public void serialize(LittleEndianOutput out) { | |||
int formula1Len=getFormulaSize(field_17_formula1); | |||
int formula2Len=getFormulaSize(field_18_formula2); | |||
out.writeByte(field_1_condition_type); | |||
out.writeByte(field_2_comparison_operator); | |||
out.writeShort(formula1Len); | |||
out.writeShort(formula2Len); | |||
out.writeInt(field_5_options); | |||
out.writeShort(field_6_not_used); | |||
if (containsFontFormattingBlock()) { | |||
byte[] fontFormattingRawRecord = _fontFormatting.getRawRecord(); | |||
out.write(fontFormattingRawRecord); | |||
} | |||
if (containsBorderFormattingBlock()) { | |||
_borderFormatting.serialize(out); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
_patternFormatting.serialize(out); | |||
} | |||
field_17_formula1.serializeTokens(out); | |||
field_18_formula2.serializeTokens(out); | |||
} | |||
/** | |||
* Creates a new comparison operation rule | |||
*/ | |||
public static CFRuleRecord create(HSSFSheet sheet, byte comparisonOperation, | |||
String formulaText1, String formulaText2) { | |||
Ptg[] formula1 = parseFormula(formulaText1, sheet); | |||
Ptg[] formula2 = parseFormula(formulaText2, sheet); | |||
return new CFRuleRecord(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation, formula1, formula2); | |||
} | |||
protected int getDataSize() { | |||
int i = 12 + | |||
(containsFontFormattingBlock()?_fontFormatting.getRawRecord().length:0)+ | |||
(containsBorderFormattingBlock()?8:0)+ | |||
(containsPatternFormattingBlock()?4:0)+ | |||
getFormulaSize(field_17_formula1)+ | |||
getFormulaSize(field_18_formula2); | |||
return i | |||
; | |||
} | |||
public CFRuleRecord(RecordInputStream in) { | |||
setConditionType(in.readByte()); | |||
setComparisonOperation(in.readByte()); | |||
int field_3_formula1_len = in.readUShort(); | |||
int field_4_formula2_len = in.readUShort(); | |||
readFormatOptions(in); | |||
// "You may not use unions, intersections or array constants in Conditional Formatting criteria" | |||
setFormula1(Formula.read(field_3_formula1_len, in)); | |||
setFormula2(Formula.read(field_4_formula2_len, in)); | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[CFRULE]\n"); | |||
buffer.append(" .condition_type =").append(field_1_condition_type).append("\n"); | |||
buffer.append(" OPTION FLAGS=0x").append(Integer.toHexString(getOptions())).append("\n"); | |||
if (containsFontFormattingBlock()) { | |||
buffer.append(_fontFormatting.toString()).append("\n"); | |||
} | |||
if (containsBorderFormattingBlock()) { | |||
buffer.append(_borderFormatting.toString()).append("\n"); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
buffer.append(_patternFormatting.toString()).append("\n"); | |||
} | |||
buffer.append(" Formula 1 =").append(Arrays.toString(field_17_formula1.getTokens())).append("\n"); | |||
buffer.append(" Formula 2 =").append(Arrays.toString(field_18_formula2.getTokens())).append("\n"); | |||
buffer.append("[/CFRULE]\n"); | |||
return buffer.toString(); | |||
} | |||
public short getSid() { | |||
return sid; | |||
} | |||
public Object clone() { | |||
CFRuleRecord rec = new CFRuleRecord(field_1_condition_type, field_2_comparison_operator); | |||
rec.field_5_options = field_5_options; | |||
rec.field_6_not_used = field_6_not_used; | |||
if (containsFontFormattingBlock()) { | |||
rec._fontFormatting = (FontFormatting) _fontFormatting.clone(); | |||
} | |||
if (containsBorderFormattingBlock()) { | |||
rec._borderFormatting = (BorderFormatting) _borderFormatting.clone(); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
rec._patternFormatting = (PatternFormatting) _patternFormatting.clone(); | |||
} | |||
rec.field_17_formula1 = field_17_formula1.copy(); | |||
rec.field_18_formula2 = field_18_formula2.copy(); | |||
/** | |||
* called by the class that is responsible for writing this sucker. | |||
* Subclasses should implement this so that their data is passed back in a | |||
* byte array. | |||
* | |||
* @param out the stream to write to | |||
*/ | |||
public void serialize(LittleEndianOutput out) { | |||
int formula1Len=getFormulaSize(getFormula1()); | |||
int formula2Len=getFormulaSize(getFormula2()); | |||
out.writeByte(getConditionType()); | |||
out.writeByte(getComparisonOperation()); | |||
out.writeShort(formula1Len); | |||
out.writeShort(formula2Len); | |||
serializeFormattingBlock(out); | |||
getFormula1().serializeTokens(out); | |||
getFormula2().serializeTokens(out); | |||
} | |||
return rec; | |||
} | |||
protected int getDataSize() { | |||
return 6 + getFormattingBlockSize() + | |||
getFormulaSize(getFormula1())+ | |||
getFormulaSize(getFormula2()); | |||
} | |||
/** | |||
* TODO - parse conditional format formulas properly i.e. produce tRefN and tAreaN instead of tRef and tArea | |||
* this call will produce the wrong results if the formula contains any cell references | |||
* One approach might be to apply the inverse of SharedFormulaRecord.convertSharedFormulas(Stack, int, int) | |||
* Note - two extra parameters (rowIx & colIx) will be required. They probably come from one of the Region objects. | |||
* | |||
* @return <code>null</code> if <tt>formula</tt> was null. | |||
*/ | |||
private static Ptg[] parseFormula(String formula, HSSFSheet sheet) { | |||
if(formula == null) { | |||
return null; | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[CFRULE]\n"); | |||
buffer.append(" .condition_type =").append(getConditionType()).append("\n"); | |||
buffer.append(" OPTION FLAGS=0x").append(Integer.toHexString(getOptions())).append("\n"); | |||
if (containsFontFormattingBlock()) { | |||
buffer.append(_fontFormatting.toString()).append("\n"); | |||
} | |||
int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet); | |||
return HSSFFormulaParser.parse(formula, sheet.getWorkbook(), FormulaType.CELL, sheetIndex); | |||
if (containsBorderFormattingBlock()) { | |||
buffer.append(_borderFormatting.toString()).append("\n"); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
buffer.append(_patternFormatting.toString()).append("\n"); | |||
} | |||
buffer.append(" Formula 1 =").append(Arrays.toString(getFormula1().getTokens())).append("\n"); | |||
buffer.append(" Formula 2 =").append(Arrays.toString(getFormula2().getTokens())).append("\n"); | |||
buffer.append("[/CFRULE]\n"); | |||
return buffer.toString(); | |||
} | |||
public Object clone() { | |||
CFRuleRecord rec = new CFRuleRecord(getConditionType(), getComparisonOperation()); | |||
super.copyTo(rec); | |||
return rec; | |||
} | |||
} |
@@ -34,8 +34,11 @@ import org.apache.poi.util.POILogger; | |||
* up with a {@link FeatHdrRecord}. | |||
*/ | |||
public final class FeatRecord extends StandardRecord { | |||
private static POILogger logger = POILogFactory.getLogger(FeatRecord.class); | |||
public final static short sid = 0x0868; | |||
private static POILogger logger = POILogFactory.getLogger(FeatRecord.class); | |||
public final static short sid = 0x0868; | |||
// SIDs from newer versions | |||
public final static short v11_sid = 0x0872; | |||
public final static short v12_sid = 0x0878; | |||
private FtrHeader futureHeader; | |||
@@ -62,17 +62,27 @@ public final class NameCommentRecord extends StandardRecord { | |||
out.writeShort(field_4_name_length); | |||
out.writeShort(field_5_comment_length); | |||
out.writeByte(0); | |||
StringUtil.putCompressedUnicode(field_6_name_text, out); | |||
out.writeByte(0); | |||
StringUtil.putCompressedUnicode(field_7_comment_text, out); | |||
boolean isNameMultiByte = StringUtil.hasMultibyte(field_6_name_text); | |||
out.writeByte(isNameMultiByte ? 1 : 0); | |||
if (isNameMultiByte) { | |||
StringUtil.putUnicodeLE(field_6_name_text, out); | |||
} else { | |||
StringUtil.putCompressedUnicode(field_6_name_text, out); | |||
} | |||
boolean isCommentMultiByte = StringUtil.hasMultibyte(field_7_comment_text); | |||
out.writeByte(isCommentMultiByte ? 1 : 0); | |||
if (isCommentMultiByte) { | |||
StringUtil.putUnicodeLE(field_7_comment_text, out); | |||
} else { | |||
StringUtil.putCompressedUnicode(field_7_comment_text, out); | |||
} | |||
} | |||
@Override | |||
protected int getDataSize() { | |||
return 18 // 4 shorts + 1 long + 2 spurious 'nul's | |||
+ field_6_name_text.length() | |||
+ field_7_comment_text.length(); | |||
+ (StringUtil.hasMultibyte(field_6_name_text) ? field_6_name_text.length()*2 : field_6_name_text.length()) | |||
+ (StringUtil.hasMultibyte(field_7_comment_text) ? field_7_comment_text.length()*2 : field_7_comment_text.length()); | |||
} | |||
/** | |||
@@ -86,10 +96,16 @@ public final class NameCommentRecord extends StandardRecord { | |||
final int field_4_name_length = in.readShort(); | |||
final int field_5_comment_length = in.readShort(); | |||
in.readByte(); //spurious NUL | |||
field_6_name_text = StringUtil.readCompressedUnicode(in, field_4_name_length); | |||
in.readByte(); //spurious NUL | |||
field_7_comment_text = StringUtil.readCompressedUnicode(in, field_5_comment_length); | |||
if (in.readByte() == 0) { | |||
field_6_name_text = StringUtil.readCompressedUnicode(in, field_4_name_length); | |||
} else { | |||
field_6_name_text = StringUtil.readUnicodeLE(in, field_4_name_length); | |||
} | |||
if (in.readByte() == 0) { | |||
field_7_comment_text = StringUtil.readCompressedUnicode(in, field_5_comment_length); | |||
} else { | |||
field_7_comment_text = StringUtil.readUnicodeLE(in, field_5_comment_length); | |||
} | |||
} | |||
/** |
@@ -411,11 +411,12 @@ public final class NameRecord extends ContinuableRecord { | |||
* @return extern sheet index | |||
*/ | |||
public int getExternSheetNumber(){ | |||
if (field_13_name_definition.getEncodedSize() < 1) { | |||
Ptg[] tokens = field_13_name_definition.getTokens(); | |||
if (tokens.length == 0) { | |||
return 0; | |||
} | |||
Ptg ptg = field_13_name_definition.getTokens()[0]; | |||
Ptg ptg = tokens[0]; | |||
if (ptg.getClass() == Area3DPtg.class){ | |||
return ((Area3DPtg) ptg).getExternSheetIndex(); | |||
@@ -62,425 +62,420 @@ import org.apache.poi.hssf.record.pivottable.ViewSourceRecord; | |||
* Description: Takes a stream and outputs an array of Record objects.<P> | |||
* | |||
* @see org.apache.poi.hssf.eventmodel.EventRecordFactory | |||
* @author Andrew C. Oliver (acoliver at apache dot org) | |||
* @author Marc Johnson (mjohnson at apache dot org) | |||
* @author Glen Stampoultzis (glens at apache.org) | |||
* @author Csaba Nagy (ncsaba at yahoo dot com) | |||
*/ | |||
public final class RecordFactory { | |||
private static final int NUM_RECORDS = 512; | |||
private interface I_RecordCreator { | |||
Record create(RecordInputStream in); | |||
Class<? extends Record> getRecordClass(); | |||
} | |||
private static final class ReflectionConstructorRecordCreator implements I_RecordCreator { | |||
private final Constructor<? extends Record> _c; | |||
public ReflectionConstructorRecordCreator(Constructor<? extends Record> c) { | |||
_c = c; | |||
} | |||
public Record create(RecordInputStream in) { | |||
Object[] args = { in, }; | |||
try { | |||
return _c.newInstance(args); | |||
} catch (IllegalArgumentException e) { | |||
throw new RuntimeException(e); | |||
} catch (InstantiationException e) { | |||
throw new RuntimeException(e); | |||
} catch (IllegalAccessException e) { | |||
throw new RuntimeException(e); | |||
} catch (InvocationTargetException e) { | |||
Throwable t = e.getTargetException(); | |||
if (t instanceof RecordFormatException) { | |||
throw (RecordFormatException)t; | |||
} else if (t instanceof EncryptedDocumentException) { | |||
throw (EncryptedDocumentException)t; | |||
} else { | |||
throw new RecordFormatException("Unable to construct record instance" , t); | |||
} | |||
} | |||
} | |||
public Class<? extends Record> getRecordClass() { | |||
return _c.getDeclaringClass(); | |||
} | |||
} | |||
/** | |||
* A "create" method is used instead of the usual constructor if the created record might | |||
* be of a different class to the declaring class. | |||
*/ | |||
private static final class ReflectionMethodRecordCreator implements I_RecordCreator { | |||
private final Method _m; | |||
public ReflectionMethodRecordCreator(Method m) { | |||
_m = m; | |||
} | |||
public Record create(RecordInputStream in) { | |||
Object[] args = { in, }; | |||
try { | |||
return (Record) _m.invoke(null, args); | |||
} catch (IllegalArgumentException e) { | |||
throw new RuntimeException(e); | |||
} catch (IllegalAccessException e) { | |||
throw new RuntimeException(e); | |||
} catch (InvocationTargetException e) { | |||
throw new RecordFormatException("Unable to construct record instance" , e.getTargetException()); | |||
} | |||
} | |||
@SuppressWarnings("unchecked") | |||
public Class<? extends Record> getRecordClass() { | |||
return (Class<? extends Record>) _m.getDeclaringClass(); | |||
} | |||
} | |||
private static final Class<?>[] CONSTRUCTOR_ARGS = { RecordInputStream.class, }; | |||
/** | |||
* contains the classes for all the records we want to parse.<br/> | |||
* Note - this most but not *every* subclass of Record. | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
private static final Class<? extends Record>[] recordClasses = new Class[] { | |||
ArrayRecord.class, | |||
private static final int NUM_RECORDS = 512; | |||
private interface I_RecordCreator { | |||
Record create(RecordInputStream in); | |||
Class<? extends Record> getRecordClass(); | |||
} | |||
private static final class ReflectionConstructorRecordCreator implements I_RecordCreator { | |||
private final Constructor<? extends Record> _c; | |||
public ReflectionConstructorRecordCreator(Constructor<? extends Record> c) { | |||
_c = c; | |||
} | |||
public Record create(RecordInputStream in) { | |||
Object[] args = { in, }; | |||
try { | |||
return _c.newInstance(args); | |||
} catch (IllegalArgumentException e) { | |||
throw new RuntimeException(e); | |||
} catch (InstantiationException e) { | |||
throw new RuntimeException(e); | |||
} catch (IllegalAccessException e) { | |||
throw new RuntimeException(e); | |||
} catch (InvocationTargetException e) { | |||
Throwable t = e.getTargetException(); | |||
if (t instanceof RecordFormatException) { | |||
throw (RecordFormatException)t; | |||
} else if (t instanceof EncryptedDocumentException) { | |||
throw (EncryptedDocumentException)t; | |||
} else { | |||
throw new RecordFormatException("Unable to construct record instance" , t); | |||
} | |||
} | |||
} | |||
public Class<? extends Record> getRecordClass() { | |||
return _c.getDeclaringClass(); | |||
} | |||
} | |||
/** | |||
* A "create" method is used instead of the usual constructor if the created record might | |||
* be of a different class to the declaring class. | |||
*/ | |||
private static final class ReflectionMethodRecordCreator implements I_RecordCreator { | |||
private final Method _m; | |||
public ReflectionMethodRecordCreator(Method m) { | |||
_m = m; | |||
} | |||
public Record create(RecordInputStream in) { | |||
Object[] args = { in, }; | |||
try { | |||
return (Record) _m.invoke(null, args); | |||
} catch (IllegalArgumentException e) { | |||
throw new RuntimeException(e); | |||
} catch (IllegalAccessException e) { | |||
throw new RuntimeException(e); | |||
} catch (InvocationTargetException e) { | |||
throw new RecordFormatException("Unable to construct record instance" , e.getTargetException()); | |||
} | |||
} | |||
@SuppressWarnings("unchecked") | |||
public Class<? extends Record> getRecordClass() { | |||
return (Class<? extends Record>) _m.getDeclaringClass(); | |||
} | |||
} | |||
private static final Class<?>[] CONSTRUCTOR_ARGS = { RecordInputStream.class, }; | |||
/** | |||
* contains the classes for all the records we want to parse.<br/> | |||
* Note - this most but not *every* subclass of Record. | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
private static final Class<? extends Record>[] recordClasses = new Class[] { | |||
ArrayRecord.class, | |||
AutoFilterInfoRecord.class, | |||
BackupRecord.class, | |||
BlankRecord.class, | |||
BOFRecord.class, | |||
BookBoolRecord.class, | |||
BoolErrRecord.class, | |||
BottomMarginRecord.class, | |||
BoundSheetRecord.class, | |||
CalcCountRecord.class, | |||
CalcModeRecord.class, | |||
CFHeaderRecord.class, | |||
CFRuleRecord.class, | |||
ChartRecord.class, | |||
ChartTitleFormatRecord.class, | |||
CodepageRecord.class, | |||
ColumnInfoRecord.class, | |||
ContinueRecord.class, | |||
CountryRecord.class, | |||
CRNCountRecord.class, | |||
CRNRecord.class, | |||
DateWindow1904Record.class, | |||
DBCellRecord.class, | |||
DConRefRecord.class, | |||
DefaultColWidthRecord.class, | |||
DefaultRowHeightRecord.class, | |||
DeltaRecord.class, | |||
DimensionsRecord.class, | |||
DrawingGroupRecord.class, | |||
DrawingRecord.class, | |||
DrawingSelectionRecord.class, | |||
DSFRecord.class, | |||
DVALRecord.class, | |||
DVRecord.class, | |||
EOFRecord.class, | |||
ExtendedFormatRecord.class, | |||
ExternalNameRecord.class, | |||
ExternSheetRecord.class, | |||
ExtSSTRecord.class, | |||
FeatRecord.class, | |||
FeatHdrRecord.class, | |||
FilePassRecord.class, | |||
FileSharingRecord.class, | |||
FnGroupCountRecord.class, | |||
FontRecord.class, | |||
FooterRecord.class, | |||
FormatRecord.class, | |||
FormulaRecord.class, | |||
GridsetRecord.class, | |||
GutsRecord.class, | |||
HCenterRecord.class, | |||
HeaderRecord.class, | |||
BlankRecord.class, | |||
BOFRecord.class, | |||
BookBoolRecord.class, | |||
BoolErrRecord.class, | |||
BottomMarginRecord.class, | |||
BoundSheetRecord.class, | |||
CalcCountRecord.class, | |||
CalcModeRecord.class, | |||
CFHeaderRecord.class, | |||
CFHeader12Record.class, | |||
CFRuleRecord.class, | |||
CFRule12Record.class, | |||
ChartRecord.class, | |||
ChartTitleFormatRecord.class, | |||
CodepageRecord.class, | |||
ColumnInfoRecord.class, | |||
ContinueRecord.class, | |||
CountryRecord.class, | |||
CRNCountRecord.class, | |||
CRNRecord.class, | |||
DateWindow1904Record.class, | |||
DBCellRecord.class, | |||
DConRefRecord.class, | |||
DefaultColWidthRecord.class, | |||
DefaultRowHeightRecord.class, | |||
DeltaRecord.class, | |||
DimensionsRecord.class, | |||
DrawingGroupRecord.class, | |||
DrawingRecord.class, | |||
DrawingSelectionRecord.class, | |||
DSFRecord.class, | |||
DVALRecord.class, | |||
DVRecord.class, | |||
EOFRecord.class, | |||
ExtendedFormatRecord.class, | |||
ExternalNameRecord.class, | |||
ExternSheetRecord.class, | |||
ExtSSTRecord.class, | |||
FeatRecord.class, | |||
FeatHdrRecord.class, | |||
FilePassRecord.class, | |||
FileSharingRecord.class, | |||
FnGroupCountRecord.class, | |||
FontRecord.class, | |||
FooterRecord.class, | |||
FormatRecord.class, | |||
FormulaRecord.class, | |||
GridsetRecord.class, | |||
GutsRecord.class, | |||
HCenterRecord.class, | |||
HeaderRecord.class, | |||
HeaderFooterRecord.class, | |||
HideObjRecord.class, | |||
HorizontalPageBreakRecord.class, | |||
HyperlinkRecord.class, | |||
IndexRecord.class, | |||
InterfaceEndRecord.class, | |||
InterfaceHdrRecord.class, | |||
IterationRecord.class, | |||
LabelRecord.class, | |||
LabelSSTRecord.class, | |||
LeftMarginRecord.class, | |||
LegendRecord.class, | |||
MergeCellsRecord.class, | |||
MMSRecord.class, | |||
MulBlankRecord.class, | |||
MulRKRecord.class, | |||
NameRecord.class, | |||
NameCommentRecord.class, | |||
NoteRecord.class, | |||
NumberRecord.class, | |||
ObjectProtectRecord.class, | |||
ObjRecord.class, | |||
PaletteRecord.class, | |||
PaneRecord.class, | |||
PasswordRecord.class, | |||
PasswordRev4Record.class, | |||
PrecisionRecord.class, | |||
PrintGridlinesRecord.class, | |||
PrintHeadersRecord.class, | |||
PrintSetupRecord.class, | |||
ProtectionRev4Record.class, | |||
ProtectRecord.class, | |||
RecalcIdRecord.class, | |||
RefModeRecord.class, | |||
RefreshAllRecord.class, | |||
RightMarginRecord.class, | |||
RKRecord.class, | |||
RowRecord.class, | |||
SaveRecalcRecord.class, | |||
ScenarioProtectRecord.class, | |||
SelectionRecord.class, | |||
SeriesRecord.class, | |||
SeriesTextRecord.class, | |||
SharedFormulaRecord.class, | |||
SSTRecord.class, | |||
StringRecord.class, | |||
StyleRecord.class, | |||
SupBookRecord.class, | |||
TabIdRecord.class, | |||
TableRecord.class, | |||
TableStylesRecord.class, | |||
TextObjectRecord.class, | |||
TopMarginRecord.class, | |||
UncalcedRecord.class, | |||
UseSelFSRecord.class, | |||
UserSViewBegin.class, | |||
UserSViewEnd.class, | |||
ValueRangeRecord.class, | |||
VCenterRecord.class, | |||
VerticalPageBreakRecord.class, | |||
WindowOneRecord.class, | |||
WindowProtectRecord.class, | |||
WindowTwoRecord.class, | |||
WriteAccessRecord.class, | |||
WriteProtectRecord.class, | |||
WSBoolRecord.class, | |||
// chart records | |||
BeginRecord.class, | |||
ChartFRTInfoRecord.class, | |||
ChartStartBlockRecord.class, | |||
ChartEndBlockRecord.class, | |||
// TODO ChartFormatRecord.class, | |||
ChartStartObjectRecord.class, | |||
ChartEndObjectRecord.class, | |||
CatLabRecord.class, | |||
DataFormatRecord.class, | |||
EndRecord.class, | |||
LinkedDataRecord.class, | |||
SeriesToChartGroupRecord.class, | |||
// pivot table records | |||
DataItemRecord.class, | |||
ExtendedPivotTableViewFieldsRecord.class, | |||
PageItemRecord.class, | |||
StreamIDRecord.class, | |||
ViewDefinitionRecord.class, | |||
ViewFieldsRecord.class, | |||
ViewSourceRecord.class, | |||
}; | |||
/** | |||
* cache of the recordsToMap(); | |||
*/ | |||
private static final Map<Integer, I_RecordCreator> _recordCreatorsById = recordsToMap(recordClasses); | |||
private static short[] _allKnownRecordSIDs; | |||
/** | |||
* Debug / diagnosis method<br/> | |||
* Gets the POI implementation class for a given <tt>sid</tt>. Only a subset of the any BIFF | |||
* records are actually interpreted by POI. A few others are known but not interpreted | |||
* (see {@link UnknownRecord#getBiffName(int)}). | |||
* @return the POI implementation class for the specified record <tt>sid</tt>. | |||
* <code>null</code> if the specified record is not interpreted by POI. | |||
*/ | |||
public static Class<? extends Record> getRecordClass(int sid) { | |||
I_RecordCreator rc = _recordCreatorsById.get(Integer.valueOf(sid)); | |||
if (rc == null) { | |||
return null; | |||
} | |||
return rc.getRecordClass(); | |||
} | |||
/** | |||
* create a record, if there are MUL records than multiple records | |||
* are returned digested into the non-mul form. | |||
*/ | |||
public static Record [] createRecord(RecordInputStream in) { | |||
Record record = createSingleRecord(in); | |||
if (record instanceof DBCellRecord) { | |||
// Not needed by POI. Regenerated from scratch by POI when spreadsheet is written | |||
return new Record[] { null, }; | |||
} | |||
if (record instanceof RKRecord) { | |||
return new Record[] { convertToNumberRecord((RKRecord) record), }; | |||
} | |||
if (record instanceof MulRKRecord) { | |||
return convertRKRecords((MulRKRecord)record); | |||
} | |||
return new Record[] { record, }; | |||
} | |||
public static Record createSingleRecord(RecordInputStream in) { | |||
I_RecordCreator constructor = _recordCreatorsById.get(Integer.valueOf(in.getSid())); | |||
if (constructor == null) { | |||
return new UnknownRecord(in); | |||
} | |||
return constructor.create(in); | |||
} | |||
/** | |||
* RK record is a slightly smaller alternative to NumberRecord | |||
* POI likes NumberRecord better | |||
*/ | |||
public static NumberRecord convertToNumberRecord(RKRecord rk) { | |||
NumberRecord num = new NumberRecord(); | |||
num.setColumn(rk.getColumn()); | |||
num.setRow(rk.getRow()); | |||
num.setXFIndex(rk.getXFIndex()); | |||
num.setValue(rk.getRKNumber()); | |||
return num; | |||
} | |||
/** | |||
* Converts a {@link MulRKRecord} into an equivalent array of {@link NumberRecord}s | |||
*/ | |||
public static NumberRecord[] convertRKRecords(MulRKRecord mrk) { | |||
NumberRecord[] mulRecs = new NumberRecord[mrk.getNumColumns()]; | |||
for (int k = 0; k < mrk.getNumColumns(); k++) { | |||
NumberRecord nr = new NumberRecord(); | |||
nr.setColumn((short) (k + mrk.getFirstColumn())); | |||
nr.setRow(mrk.getRow()); | |||
nr.setXFIndex(mrk.getXFAt(k)); | |||
nr.setValue(mrk.getRKNumberAt(k)); | |||
mulRecs[k] = nr; | |||
} | |||
return mulRecs; | |||
} | |||
/** | |||
* Converts a {@link MulBlankRecord} into an equivalent array of {@link BlankRecord}s | |||
*/ | |||
public static BlankRecord[] convertBlankRecords(MulBlankRecord mbk) { | |||
BlankRecord[] mulRecs = new BlankRecord[mbk.getNumColumns()]; | |||
for (int k = 0; k < mbk.getNumColumns(); k++) { | |||
BlankRecord br = new BlankRecord(); | |||
br.setColumn((short) (k + mbk.getFirstColumn())); | |||
br.setRow(mbk.getRow()); | |||
br.setXFIndex(mbk.getXFAt(k)); | |||
mulRecs[k] = br; | |||
} | |||
return mulRecs; | |||
} | |||
/** | |||
* @return an array of all the SIDS for all known records | |||
*/ | |||
public static short[] getAllKnownRecordSIDs() { | |||
if (_allKnownRecordSIDs == null) { | |||
short[] results = new short[ _recordCreatorsById.size() ]; | |||
int i = 0; | |||
for (Iterator<Integer> iterator = _recordCreatorsById.keySet().iterator(); iterator.hasNext(); ) { | |||
Integer sid = iterator.next(); | |||
results[i++] = sid.shortValue(); | |||
} | |||
Arrays.sort(results); | |||
_allKnownRecordSIDs = results; | |||
} | |||
return _allKnownRecordSIDs.clone(); | |||
} | |||
/** | |||
* gets the record constructors and sticks them in the map by SID | |||
* @return map of SIDs to short,short,byte[] constructors for Record classes | |||
* most of org.apache.poi.hssf.record.* | |||
*/ | |||
private static Map<Integer, I_RecordCreator> recordsToMap(Class<? extends Record> [] records) { | |||
Map<Integer, I_RecordCreator> result = new HashMap<Integer, I_RecordCreator>(); | |||
Set<Class<?>> uniqueRecClasses = new HashSet<Class<?>>(records.length * 3 / 2); | |||
for (int i = 0; i < records.length; i++) { | |||
Class<? extends Record> recClass = records[ i ]; | |||
if(!Record.class.isAssignableFrom(recClass)) { | |||
throw new RuntimeException("Invalid record sub-class (" + recClass.getName() + ")"); | |||
} | |||
if(Modifier.isAbstract(recClass.getModifiers())) { | |||
throw new RuntimeException("Invalid record class (" + recClass.getName() + ") - must not be abstract"); | |||
} | |||
if(!uniqueRecClasses.add(recClass)) { | |||
throw new RuntimeException("duplicate record class (" + recClass.getName() + ")"); | |||
} | |||
int sid; | |||
try { | |||
sid = recClass.getField("sid").getShort(null); | |||
} catch (Exception illegalArgumentException) { | |||
throw new RecordFormatException( | |||
"Unable to determine record types"); | |||
} | |||
Integer key = Integer.valueOf(sid); | |||
if (result.containsKey(key)) { | |||
Class<?> prevClass = result.get(key).getRecordClass(); | |||
throw new RuntimeException("duplicate record sid 0x" + Integer.toHexString(sid).toUpperCase() | |||
+ " for classes (" + recClass.getName() + ") and (" + prevClass.getName() + ")"); | |||
} | |||
result.put(key, getRecordCreator(recClass)); | |||
} | |||
// result.put(Integer.valueOf(0x0406), result.get(Integer.valueOf(0x06))); | |||
return result; | |||
} | |||
private static I_RecordCreator getRecordCreator(Class<? extends Record> recClass) { | |||
try { | |||
Constructor<? extends Record> constructor; | |||
constructor = recClass.getConstructor(CONSTRUCTOR_ARGS); | |||
return new ReflectionConstructorRecordCreator(constructor); | |||
} catch (NoSuchMethodException e) { | |||
// fall through and look for other construction methods | |||
} | |||
try { | |||
Method m = recClass.getDeclaredMethod("create", CONSTRUCTOR_ARGS); | |||
return new ReflectionMethodRecordCreator(m); | |||
} catch (NoSuchMethodException e) { | |||
throw new RuntimeException("Failed to find constructor or create method for (" + recClass.getName() + ")."); | |||
} | |||
} | |||
/** | |||
* Create an array of records from an input stream | |||
* | |||
* @param in the InputStream from which the records will be obtained | |||
* | |||
* @return an array of Records created from the InputStream | |||
* | |||
* @exception RecordFormatException on error processing the InputStream | |||
*/ | |||
public static List<Record> createRecords(InputStream in) throws RecordFormatException { | |||
List<Record> records = new ArrayList<Record>(NUM_RECORDS); | |||
RecordFactoryInputStream recStream = new RecordFactoryInputStream(in, true); | |||
Record record; | |||
while ((record = recStream.nextRecord())!=null) { | |||
records.add(record); | |||
} | |||
return records; | |||
} | |||
HideObjRecord.class, | |||
HorizontalPageBreakRecord.class, | |||
HyperlinkRecord.class, | |||
IndexRecord.class, | |||
InterfaceEndRecord.class, | |||
InterfaceHdrRecord.class, | |||
IterationRecord.class, | |||
LabelRecord.class, | |||
LabelSSTRecord.class, | |||
LeftMarginRecord.class, | |||
LegendRecord.class, | |||
MergeCellsRecord.class, | |||
MMSRecord.class, | |||
MulBlankRecord.class, | |||
MulRKRecord.class, | |||
NameRecord.class, | |||
NameCommentRecord.class, | |||
NoteRecord.class, | |||
NumberRecord.class, | |||
ObjectProtectRecord.class, | |||
ObjRecord.class, | |||
PaletteRecord.class, | |||
PaneRecord.class, | |||
PasswordRecord.class, | |||
PasswordRev4Record.class, | |||
PrecisionRecord.class, | |||
PrintGridlinesRecord.class, | |||
PrintHeadersRecord.class, | |||
PrintSetupRecord.class, | |||
ProtectionRev4Record.class, | |||
ProtectRecord.class, | |||
RecalcIdRecord.class, | |||
RefModeRecord.class, | |||
RefreshAllRecord.class, | |||
RightMarginRecord.class, | |||
RKRecord.class, | |||
RowRecord.class, | |||
SaveRecalcRecord.class, | |||
ScenarioProtectRecord.class, | |||
SelectionRecord.class, | |||
SeriesRecord.class, | |||
SeriesTextRecord.class, | |||
SharedFormulaRecord.class, | |||
SSTRecord.class, | |||
StringRecord.class, | |||
StyleRecord.class, | |||
SupBookRecord.class, | |||
TabIdRecord.class, | |||
TableRecord.class, | |||
TableStylesRecord.class, | |||
TextObjectRecord.class, | |||
TopMarginRecord.class, | |||
UncalcedRecord.class, | |||
UseSelFSRecord.class, | |||
UserSViewBegin.class, | |||
UserSViewEnd.class, | |||
ValueRangeRecord.class, | |||
VCenterRecord.class, | |||
VerticalPageBreakRecord.class, | |||
WindowOneRecord.class, | |||
WindowProtectRecord.class, | |||
WindowTwoRecord.class, | |||
WriteAccessRecord.class, | |||
WriteProtectRecord.class, | |||
WSBoolRecord.class, | |||
// chart records | |||
BeginRecord.class, | |||
ChartFRTInfoRecord.class, | |||
ChartStartBlockRecord.class, | |||
ChartEndBlockRecord.class, | |||
// TODO ChartFormatRecord.class, | |||
ChartStartObjectRecord.class, | |||
ChartEndObjectRecord.class, | |||
CatLabRecord.class, | |||
DataFormatRecord.class, | |||
EndRecord.class, | |||
LinkedDataRecord.class, | |||
SeriesToChartGroupRecord.class, | |||
// pivot table records | |||
DataItemRecord.class, | |||
ExtendedPivotTableViewFieldsRecord.class, | |||
PageItemRecord.class, | |||
StreamIDRecord.class, | |||
ViewDefinitionRecord.class, | |||
ViewFieldsRecord.class, | |||
ViewSourceRecord.class, | |||
}; | |||
/** | |||
* cache of the recordsToMap(); | |||
*/ | |||
private static final Map<Integer, I_RecordCreator> _recordCreatorsById = recordsToMap(recordClasses); | |||
private static short[] _allKnownRecordSIDs; | |||
/** | |||
* Debug / diagnosis method<br/> | |||
* Gets the POI implementation class for a given <tt>sid</tt>. Only a subset of the any BIFF | |||
* records are actually interpreted by POI. A few others are known but not interpreted | |||
* (see {@link UnknownRecord#getBiffName(int)}). | |||
* @return the POI implementation class for the specified record <tt>sid</tt>. | |||
* <code>null</code> if the specified record is not interpreted by POI. | |||
*/ | |||
public static Class<? extends Record> getRecordClass(int sid) { | |||
I_RecordCreator rc = _recordCreatorsById.get(Integer.valueOf(sid)); | |||
if (rc == null) { | |||
return null; | |||
} | |||
return rc.getRecordClass(); | |||
} | |||
/** | |||
* create a record, if there are MUL records than multiple records | |||
* are returned digested into the non-mul form. | |||
*/ | |||
public static Record [] createRecord(RecordInputStream in) { | |||
Record record = createSingleRecord(in); | |||
if (record instanceof DBCellRecord) { | |||
// Not needed by POI. Regenerated from scratch by POI when spreadsheet is written | |||
return new Record[] { null, }; | |||
} | |||
if (record instanceof RKRecord) { | |||
return new Record[] { convertToNumberRecord((RKRecord) record), }; | |||
} | |||
if (record instanceof MulRKRecord) { | |||
return convertRKRecords((MulRKRecord)record); | |||
} | |||
return new Record[] { record, }; | |||
} | |||
public static Record createSingleRecord(RecordInputStream in) { | |||
I_RecordCreator constructor = _recordCreatorsById.get(Integer.valueOf(in.getSid())); | |||
if (constructor == null) { | |||
return new UnknownRecord(in); | |||
} | |||
return constructor.create(in); | |||
} | |||
/** | |||
* RK record is a slightly smaller alternative to NumberRecord | |||
* POI likes NumberRecord better | |||
*/ | |||
public static NumberRecord convertToNumberRecord(RKRecord rk) { | |||
NumberRecord num = new NumberRecord(); | |||
num.setColumn(rk.getColumn()); | |||
num.setRow(rk.getRow()); | |||
num.setXFIndex(rk.getXFIndex()); | |||
num.setValue(rk.getRKNumber()); | |||
return num; | |||
} | |||
/** | |||
* Converts a {@link MulRKRecord} into an equivalent array of {@link NumberRecord}s | |||
*/ | |||
public static NumberRecord[] convertRKRecords(MulRKRecord mrk) { | |||
NumberRecord[] mulRecs = new NumberRecord[mrk.getNumColumns()]; | |||
for (int k = 0; k < mrk.getNumColumns(); k++) { | |||
NumberRecord nr = new NumberRecord(); | |||
nr.setColumn((short) (k + mrk.getFirstColumn())); | |||
nr.setRow(mrk.getRow()); | |||
nr.setXFIndex(mrk.getXFAt(k)); | |||
nr.setValue(mrk.getRKNumberAt(k)); | |||
mulRecs[k] = nr; | |||
} | |||
return mulRecs; | |||
} | |||
/** | |||
* Converts a {@link MulBlankRecord} into an equivalent array of {@link BlankRecord}s | |||
*/ | |||
public static BlankRecord[] convertBlankRecords(MulBlankRecord mbk) { | |||
BlankRecord[] mulRecs = new BlankRecord[mbk.getNumColumns()]; | |||
for (int k = 0; k < mbk.getNumColumns(); k++) { | |||
BlankRecord br = new BlankRecord(); | |||
br.setColumn((short) (k + mbk.getFirstColumn())); | |||
br.setRow(mbk.getRow()); | |||
br.setXFIndex(mbk.getXFAt(k)); | |||
mulRecs[k] = br; | |||
} | |||
return mulRecs; | |||
} | |||
/** | |||
* @return an array of all the SIDS for all known records | |||
*/ | |||
public static short[] getAllKnownRecordSIDs() { | |||
if (_allKnownRecordSIDs == null) { | |||
short[] results = new short[ _recordCreatorsById.size() ]; | |||
int i = 0; | |||
for (Iterator<Integer> iterator = _recordCreatorsById.keySet().iterator(); iterator.hasNext(); ) { | |||
Integer sid = iterator.next(); | |||
results[i++] = sid.shortValue(); | |||
} | |||
Arrays.sort(results); | |||
_allKnownRecordSIDs = results; | |||
} | |||
return _allKnownRecordSIDs.clone(); | |||
} | |||
/** | |||
* gets the record constructors and sticks them in the map by SID | |||
* @return map of SIDs to short,short,byte[] constructors for Record classes | |||
* most of org.apache.poi.hssf.record.* | |||
*/ | |||
private static Map<Integer, I_RecordCreator> recordsToMap(Class<? extends Record> [] records) { | |||
Map<Integer, I_RecordCreator> result = new HashMap<Integer, I_RecordCreator>(); | |||
Set<Class<?>> uniqueRecClasses = new HashSet<Class<?>>(records.length * 3 / 2); | |||
for (int i = 0; i < records.length; i++) { | |||
Class<? extends Record> recClass = records[ i ]; | |||
if(!Record.class.isAssignableFrom(recClass)) { | |||
throw new RuntimeException("Invalid record sub-class (" + recClass.getName() + ")"); | |||
} | |||
if(Modifier.isAbstract(recClass.getModifiers())) { | |||
throw new RuntimeException("Invalid record class (" + recClass.getName() + ") - must not be abstract"); | |||
} | |||
if(!uniqueRecClasses.add(recClass)) { | |||
throw new RuntimeException("duplicate record class (" + recClass.getName() + ")"); | |||
} | |||
int sid; | |||
try { | |||
sid = recClass.getField("sid").getShort(null); | |||
} catch (Exception illegalArgumentException) { | |||
throw new RecordFormatException( | |||
"Unable to determine record types"); | |||
} | |||
Integer key = Integer.valueOf(sid); | |||
if (result.containsKey(key)) { | |||
Class<?> prevClass = result.get(key).getRecordClass(); | |||
throw new RuntimeException("duplicate record sid 0x" + Integer.toHexString(sid).toUpperCase() | |||
+ " for classes (" + recClass.getName() + ") and (" + prevClass.getName() + ")"); | |||
} | |||
result.put(key, getRecordCreator(recClass)); | |||
} | |||
// result.put(Integer.valueOf(0x0406), result.get(Integer.valueOf(0x06))); | |||
return result; | |||
} | |||
private static I_RecordCreator getRecordCreator(Class<? extends Record> recClass) { | |||
try { | |||
Constructor<? extends Record> constructor; | |||
constructor = recClass.getConstructor(CONSTRUCTOR_ARGS); | |||
return new ReflectionConstructorRecordCreator(constructor); | |||
} catch (NoSuchMethodException e) { | |||
// fall through and look for other construction methods | |||
} | |||
try { | |||
Method m = recClass.getDeclaredMethod("create", CONSTRUCTOR_ARGS); | |||
return new ReflectionMethodRecordCreator(m); | |||
} catch (NoSuchMethodException e) { | |||
throw new RuntimeException("Failed to find constructor or create method for (" + recClass.getName() + ")."); | |||
} | |||
} | |||
/** | |||
* Create an array of records from an input stream | |||
* | |||
* @param in the InputStream from which the records will be obtained | |||
* | |||
* @return an array of Records created from the InputStream | |||
* | |||
* @exception RecordFormatException on error processing the InputStream | |||
*/ | |||
public static List<Record> createRecords(InputStream in) throws RecordFormatException { | |||
List<Record> records = new ArrayList<Record>(NUM_RECORDS); | |||
RecordFactoryInputStream recStream = new RecordFactoryInputStream(in, true); | |||
Record record; | |||
while ((record = recStream.nextRecord())!=null) { | |||
records.add(record); | |||
} | |||
return records; | |||
} | |||
} |
@@ -52,8 +52,17 @@ public final class RecordInputStream implements LittleEndianInput { | |||
public static final class LeftoverDataException extends RuntimeException { | |||
public LeftoverDataException(int sid, int remainingByteCount) { | |||
super("Initialisation of record 0x" + Integer.toHexString(sid).toUpperCase() | |||
+ " left " + remainingByteCount + " bytes remaining still to be read."); | |||
+ "(" + getRecordName(sid) + ") left " + remainingByteCount | |||
+ " bytes remaining still to be read."); | |||
} | |||
private static String getRecordName(int sid) { | |||
Class<? extends Record> recordClass = RecordFactory.getRecordClass(sid); | |||
if(recordClass == null) { | |||
return null; | |||
} | |||
return recordClass.getSimpleName(); | |||
} | |||
} | |||
/** Header {@link LittleEndianInput} facet of the wrapped {@link InputStream} */ |
@@ -64,6 +64,9 @@ public final class RowRecord extends StandardRecord { | |||
// bit 15 is unused | |||
public RowRecord(int rowNumber) { | |||
if(rowNumber < 0) { | |||
throw new IllegalArgumentException("Invalid row number (" + rowNumber + ")"); | |||
} | |||
field_1_row_number = rowNumber; | |||
field_4_height = (short)0xFF; | |||
field_5_optimize = ( short ) 0; | |||
@@ -76,6 +79,9 @@ public final class RowRecord extends StandardRecord { | |||
public RowRecord(RecordInputStream in) { | |||
field_1_row_number = in.readUShort(); | |||
if(field_1_row_number < 0) { | |||
throw new IllegalArgumentException("Invalid row number " + field_1_row_number + " found in InputStream"); | |||
} | |||
field_2_first_col = in.readShort(); | |||
field_3_last_col = in.readShort(); | |||
field_4_height = in.readShort(); |
@@ -179,8 +179,10 @@ public final class UnknownRecord extends StandardRecord { | |||
case SHEETPROTECTION_0867: return "SHEETPROTECTION"; | |||
case 0x086B: return "DATALABEXTCONTENTS"; | |||
case 0x086C: return "CELLWATCH"; | |||
case FeatRecord.v11_sid: return "SHARED FEATURE v11"; | |||
case 0x0874: return "DROPDOWNOBJIDS"; | |||
case 0x0876: return "DCONN"; | |||
case FeatRecord.v12_sid: return "SHARED FEATURE v12"; | |||
case 0x087B: return "CFEX"; | |||
case 0x087C: return "XFCRC"; | |||
case 0x087D: return "XFEXT"; | |||
@@ -194,15 +196,21 @@ public final class UnknownRecord extends StandardRecord { | |||
case 0x089A: return "MTRSETTINGS"; | |||
case 0x089B: return "COMPRESSPICTURES"; | |||
case HEADER_FOOTER_089C: return "HEADERFOOTER"; | |||
case 0x089D: return "CRTLAYOUT12"; | |||
case 0x089E: return "CRTMLFRT"; | |||
case 0x089F: return "CRTMLFRTCONTINUE"; | |||
case 0x08A1: return "SHAPEPROPSSTREAM"; | |||
case 0x08A3: return "FORCEFULLCALCULATION"; | |||
case 0x08A4: return "SHAPEPROPSSTREAM"; | |||
case 0x08A5: return "TEXTPROPSSTREAM"; | |||
case 0x08A6: return "RICHTEXTSTREAM"; | |||
case 0x08A7: return "CRTLAYOUT12A"; | |||
case 0x08C8: return "PLV{Mac Excel}"; | |||
case 0x1001: return "UNITS"; | |||
case 0x1006: return "CHARTDATAFORMAT"; | |||
case 0x1007: return "CHARTLINEFORMAT"; | |||
} | |||
if (isObservedButUnknown(sid)) { | |||
return "UNKNOWN-" + Integer.toHexString(sid).toUpperCase(); | |||
@@ -215,6 +223,7 @@ public final class UnknownRecord extends StandardRecord { | |||
* @return <code>true</code> if the unknown record id has been observed in POI unit tests | |||
*/ | |||
private static boolean isObservedButUnknown(int sid) { | |||
// TODO Look up more of these in the latest [MS-XLS] doc and move to getBiffName | |||
switch (sid) { | |||
case 0x0033: | |||
// contains 2 bytes of data: 0x0001 or 0x0003 | |||
@@ -227,13 +236,7 @@ public final class UnknownRecord extends StandardRecord { | |||
// Written by Excel 2007 | |||
// rawData is multiple of 12 bytes long | |||
// appears after last cell value record and before WINDOW2 or drawing records | |||
case 0x089D: | |||
case 0x089E: | |||
case 0x08A7: | |||
case 0x1001: | |||
case 0x1006: | |||
case 0x1007: | |||
case 0x1009: | |||
case 0x100A: | |||
case 0x100B: |
@@ -21,7 +21,11 @@ import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.hssf.model.RecordStream; | |||
import org.apache.poi.hssf.record.CFHeader12Record; | |||
import org.apache.poi.hssf.record.CFHeaderBase; | |||
import org.apache.poi.hssf.record.CFHeaderRecord; | |||
import org.apache.poi.hssf.record.CFRule12Record; | |||
import org.apache.poi.hssf.record.CFRuleBase; | |||
import org.apache.poi.hssf.record.CFRuleRecord; | |||
import org.apache.poi.hssf.record.Record; | |||
import org.apache.poi.ss.formula.FormulaShifter; | |||
@@ -38,212 +42,240 @@ import org.apache.poi.util.POILogger; | |||
* <p>Note that Excel versions before 2007 can only cope with a maximum of 3 | |||
* Conditional Formatting rules per sheet. Excel 2007 or newer can cope with | |||
* unlimited numbers, as can Apache OpenOffice. This is an Excel limitation, | |||
* not a file format one.</p> | |||
* not a file format one.</p> | |||
*/ | |||
public final class CFRecordsAggregate extends RecordAggregate { | |||
/** Excel 97-2003 allows up to 3 conditional formating rules */ | |||
private static final int MAX_97_2003_CONDTIONAL_FORMAT_RULES = 3; | |||
private static final POILogger logger = POILogFactory.getLogger(CFRecordsAggregate.class); | |||
private final CFHeaderRecord header; | |||
/** List of CFRuleRecord objects */ | |||
private final List<CFRuleRecord> rules; | |||
private CFRecordsAggregate(CFHeaderRecord pHeader, CFRuleRecord[] pRules) { | |||
if(pHeader == null) { | |||
throw new IllegalArgumentException("header must not be null"); | |||
} | |||
if(pRules == null) { | |||
throw new IllegalArgumentException("rules must not be null"); | |||
} | |||
if(pRules.length > MAX_97_2003_CONDTIONAL_FORMAT_RULES) { | |||
logger.log(POILogger.WARN, "Excel versions before 2007 require that " | |||
+ "No more than " + MAX_97_2003_CONDTIONAL_FORMAT_RULES | |||
+ " rules may be specified, " + pRules.length + " were found," | |||
+ " this file will cause problems with old Excel versions"); | |||
} | |||
if (pRules.length != pHeader.getNumberOfConditionalFormats()) { | |||
throw new RuntimeException("Mismatch number of rules"); | |||
} | |||
header = pHeader; | |||
rules = new ArrayList<CFRuleRecord>(3); | |||
for (int i = 0; i < pRules.length; i++) { | |||
rules.add(pRules[i]); | |||
} | |||
} | |||
public CFRecordsAggregate(CellRangeAddress[] regions, CFRuleRecord[] rules) { | |||
this(new CFHeaderRecord(regions, rules.length), rules); | |||
} | |||
/** | |||
* Create CFRecordsAggregate from a list of CF Records | |||
* @param rs - the stream to read from | |||
* @return CFRecordsAggregate object | |||
*/ | |||
public static CFRecordsAggregate createCFAggregate(RecordStream rs) { | |||
Record rec = rs.getNext(); | |||
if (rec.getSid() != CFHeaderRecord.sid) { | |||
throw new IllegalStateException("next record sid was " + rec.getSid() | |||
+ " instead of " + CFHeaderRecord.sid + " as expected"); | |||
} | |||
CFHeaderRecord header = (CFHeaderRecord)rec; | |||
int nRules = header.getNumberOfConditionalFormats(); | |||
CFRuleRecord[] rules = new CFRuleRecord[nRules]; | |||
for (int i = 0; i < rules.length; i++) { | |||
rules[i] = (CFRuleRecord) rs.getNext(); | |||
} | |||
return new CFRecordsAggregate(header, rules); | |||
} | |||
/** | |||
* Create a deep clone of the record | |||
*/ | |||
public CFRecordsAggregate cloneCFAggregate() | |||
{ | |||
CFRuleRecord[] newRecs = new CFRuleRecord[rules.size()]; | |||
for (int i = 0; i < newRecs.length; i++) { | |||
newRecs[i] = (CFRuleRecord) getRule(i).clone(); | |||
} | |||
return new CFRecordsAggregate((CFHeaderRecord) header.clone(), newRecs); | |||
} | |||
/** | |||
* @return the header. Never <code>null</code>. | |||
*/ | |||
public CFHeaderRecord getHeader() | |||
{ | |||
return header; | |||
} | |||
private void checkRuleIndex(int idx) { | |||
if(idx < 0 || idx >= rules.size()) { | |||
throw new IllegalArgumentException("Bad rule record index (" + idx | |||
+ ") nRules=" + rules.size()); | |||
} | |||
} | |||
public CFRuleRecord getRule(int idx) { | |||
checkRuleIndex(idx); | |||
return rules.get(idx); | |||
} | |||
public void setRule(int idx, CFRuleRecord r) { | |||
if (r == null) { | |||
throw new IllegalArgumentException("r must not be null"); | |||
} | |||
checkRuleIndex(idx); | |||
rules.set(idx, r); | |||
} | |||
public void addRule(CFRuleRecord r) { | |||
if (r == null) { | |||
throw new IllegalArgumentException("r must not be null"); | |||
} | |||
if(rules.size() >= MAX_97_2003_CONDTIONAL_FORMAT_RULES) { | |||
/** Excel 97-2003 allows up to 3 conditional formating rules */ | |||
private static final int MAX_97_2003_CONDTIONAL_FORMAT_RULES = 3; | |||
private static final POILogger logger = POILogFactory.getLogger(CFRecordsAggregate.class); | |||
private final CFHeaderBase header; | |||
/** List of CFRuleRecord objects */ | |||
private final List<CFRuleBase> rules; | |||
private CFRecordsAggregate(CFHeaderBase pHeader, CFRuleBase[] pRules) { | |||
if(pHeader == null) { | |||
throw new IllegalArgumentException("header must not be null"); | |||
} | |||
if(pRules == null) { | |||
throw new IllegalArgumentException("rules must not be null"); | |||
} | |||
if(pRules.length > MAX_97_2003_CONDTIONAL_FORMAT_RULES) { | |||
logger.log(POILogger.WARN, "Excel versions before 2007 require that " | |||
+ "No more than " + MAX_97_2003_CONDTIONAL_FORMAT_RULES | |||
+ " rules may be specified, " + pRules.length + " were found," | |||
+ " this file will cause problems with old Excel versions"); | |||
} | |||
if (pRules.length != pHeader.getNumberOfConditionalFormats()) { | |||
throw new RuntimeException("Mismatch number of rules"); | |||
} | |||
header = pHeader; | |||
rules = new ArrayList<CFRuleBase>(pRules.length); | |||
for (int i = 0; i < pRules.length; i++) { | |||
checkRuleType(pRules[i]); | |||
rules.add(pRules[i]); | |||
} | |||
} | |||
public CFRecordsAggregate(CellRangeAddress[] regions, CFRuleBase[] rules) { | |||
this(createHeader(regions, rules), rules); | |||
} | |||
private static CFHeaderBase createHeader(CellRangeAddress[] regions, CFRuleBase[] rules) { | |||
if (rules.length == 0 || rules[0] instanceof CFRuleRecord) { | |||
return new CFHeaderRecord(regions, rules.length); | |||
} | |||
return new CFHeader12Record(regions, rules.length); | |||
} | |||
/** | |||
* Create CFRecordsAggregate from a list of CF Records | |||
* @param rs - the stream to read from | |||
* @return CFRecordsAggregate object | |||
*/ | |||
public static CFRecordsAggregate createCFAggregate(RecordStream rs) { | |||
Record rec = rs.getNext(); | |||
if (rec.getSid() != CFHeaderRecord.sid && | |||
rec.getSid() != CFHeader12Record.sid) { | |||
throw new IllegalStateException("next record sid was " + rec.getSid() | |||
+ " instead of " + CFHeaderRecord.sid + " or " + | |||
CFHeader12Record.sid + " as expected"); | |||
} | |||
CFHeaderBase header = (CFHeaderBase)rec; | |||
int nRules = header.getNumberOfConditionalFormats(); | |||
CFRuleBase[] rules = new CFRuleBase[nRules]; | |||
for (int i = 0; i < rules.length; i++) { | |||
rules[i] = (CFRuleBase) rs.getNext(); | |||
} | |||
return new CFRecordsAggregate(header, rules); | |||
} | |||
/** | |||
* Create a deep clone of the record | |||
*/ | |||
public CFRecordsAggregate cloneCFAggregate() { | |||
CFRuleBase[] newRecs = new CFRuleBase[rules.size()]; | |||
for (int i = 0; i < newRecs.length; i++) { | |||
newRecs[i] = (CFRuleRecord) getRule(i).clone(); | |||
} | |||
return new CFRecordsAggregate((CFHeaderBase)header.clone(), newRecs); | |||
} | |||
/** | |||
* @return the header. Never <code>null</code>. | |||
*/ | |||
public CFHeaderBase getHeader() { | |||
return header; | |||
} | |||
private void checkRuleIndex(int idx) { | |||
if(idx < 0 || idx >= rules.size()) { | |||
throw new IllegalArgumentException("Bad rule record index (" + idx | |||
+ ") nRules=" + rules.size()); | |||
} | |||
} | |||
private void checkRuleType(CFRuleBase r) { | |||
if (header instanceof CFHeaderRecord && | |||
r instanceof CFRuleRecord) { | |||
return; | |||
} | |||
if (header instanceof CFHeader12Record && | |||
r instanceof CFRule12Record) { | |||
return; | |||
} | |||
throw new IllegalArgumentException("Header and Rule must both be CF or both be CF12, can't mix"); | |||
} | |||
public CFRuleBase getRule(int idx) { | |||
checkRuleIndex(idx); | |||
return rules.get(idx); | |||
} | |||
public void setRule(int idx, CFRuleBase r) { | |||
if (r == null) { | |||
throw new IllegalArgumentException("r must not be null"); | |||
} | |||
checkRuleIndex(idx); | |||
checkRuleType(r); | |||
rules.set(idx, r); | |||
} | |||
public void addRule(CFRuleBase r) { | |||
if (r == null) { | |||
throw new IllegalArgumentException("r must not be null"); | |||
} | |||
if(rules.size() >= MAX_97_2003_CONDTIONAL_FORMAT_RULES) { | |||
logger.log(POILogger.WARN, "Excel versions before 2007 cannot cope with" | |||
+ " any more than " + MAX_97_2003_CONDTIONAL_FORMAT_RULES | |||
+ " any more than " + MAX_97_2003_CONDTIONAL_FORMAT_RULES | |||
+ " - this file will cause problems with old Excel versions"); | |||
} | |||
rules.add(r); | |||
header.setNumberOfConditionalFormats(rules.size()); | |||
} | |||
public int getNumberOfRules() { | |||
return rules.size(); | |||
} | |||
/** | |||
* String representation of CFRecordsAggregate | |||
*/ | |||
public String toString() | |||
{ | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[CF]\n"); | |||
if( header != null ) | |||
{ | |||
buffer.append(header.toString()); | |||
} | |||
for(int i=0; i<rules.size(); i++) | |||
{ | |||
CFRuleRecord cfRule = rules.get(i); | |||
buffer.append(cfRule.toString()); | |||
} | |||
buffer.append("[/CF]\n"); | |||
return buffer.toString(); | |||
} | |||
public void visitContainedRecords(RecordVisitor rv) { | |||
rv.visitRecord(header); | |||
for(int i=0; i<rules.size(); i++) { | |||
CFRuleRecord rule = rules.get(i); | |||
rv.visitRecord(rule); | |||
} | |||
} | |||
/** | |||
* @return <code>false</code> if this whole {@link CFHeaderRecord} / {@link CFRuleRecord}s should be deleted | |||
*/ | |||
public boolean updateFormulasAfterCellShift(FormulaShifter shifter, int currentExternSheetIx) { | |||
CellRangeAddress[] cellRanges = header.getCellRanges(); | |||
boolean changed = false; | |||
List<CellRangeAddress> temp = new ArrayList<CellRangeAddress>(); | |||
for (int i = 0; i < cellRanges.length; i++) { | |||
CellRangeAddress craOld = cellRanges[i]; | |||
CellRangeAddress craNew = shiftRange(shifter, craOld, currentExternSheetIx); | |||
if (craNew == null) { | |||
changed = true; | |||
continue; | |||
} | |||
temp.add(craNew); | |||
if (craNew != craOld) { | |||
changed = true; | |||
} | |||
} | |||
if (changed) { | |||
int nRanges = temp.size(); | |||
if (nRanges == 0) { | |||
return false; | |||
} | |||
CellRangeAddress[] newRanges = new CellRangeAddress[nRanges]; | |||
temp.toArray(newRanges); | |||
header.setCellRanges(newRanges); | |||
} | |||
for(int i=0; i<rules.size(); i++) { | |||
CFRuleRecord rule = rules.get(i); | |||
Ptg[] ptgs; | |||
ptgs = rule.getParsedExpression1(); | |||
if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) { | |||
rule.setParsedExpression1(ptgs); | |||
} | |||
ptgs = rule.getParsedExpression2(); | |||
if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) { | |||
rule.setParsedExpression2(ptgs); | |||
} | |||
} | |||
return true; | |||
} | |||
private static CellRangeAddress shiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx) { | |||
// FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here | |||
AreaPtg aptg = new AreaPtg(cra.getFirstRow(), cra.getLastRow(), cra.getFirstColumn(), cra.getLastColumn(), false, false, false, false); | |||
Ptg[] ptgs = { aptg, }; | |||
if (!shifter.adjustFormula(ptgs, currentExternSheetIx)) { | |||
return cra; | |||
} | |||
Ptg ptg0 = ptgs[0]; | |||
if (ptg0 instanceof AreaPtg) { | |||
AreaPtg bptg = (AreaPtg) ptg0; | |||
return new CellRangeAddress(bptg.getFirstRow(), bptg.getLastRow(), bptg.getFirstColumn(), bptg.getLastColumn()); | |||
} | |||
if (ptg0 instanceof AreaErrPtg) { | |||
return null; | |||
} | |||
throw new IllegalStateException("Unexpected shifted ptg class (" + ptg0.getClass().getName() + ")"); | |||
} | |||
} | |||
checkRuleType(r); | |||
rules.add(r); | |||
header.setNumberOfConditionalFormats(rules.size()); | |||
} | |||
public int getNumberOfRules() { | |||
return rules.size(); | |||
} | |||
/** | |||
* String representation of CFRecordsAggregate | |||
*/ | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
String type = "CF"; | |||
if (header instanceof CFHeader12Record) { | |||
type = "CF12"; | |||
} | |||
buffer.append("[").append(type).append("]\n"); | |||
if( header != null ) { | |||
buffer.append(header.toString()); | |||
} | |||
for(int i=0; i<rules.size(); i++) { | |||
CFRuleBase cfRule = rules.get(i); | |||
buffer.append(cfRule.toString()); | |||
} | |||
buffer.append("[/").append(type).append("]\n"); | |||
return buffer.toString(); | |||
} | |||
public void visitContainedRecords(RecordVisitor rv) { | |||
rv.visitRecord(header); | |||
for(int i=0; i<rules.size(); i++) { | |||
CFRuleBase rule = rules.get(i); | |||
rv.visitRecord(rule); | |||
} | |||
} | |||
/** | |||
* @return <code>false</code> if this whole {@link CFHeaderRecord} / {@link CFRuleRecord}s should be deleted | |||
*/ | |||
public boolean updateFormulasAfterCellShift(FormulaShifter shifter, int currentExternSheetIx) { | |||
CellRangeAddress[] cellRanges = header.getCellRanges(); | |||
boolean changed = false; | |||
List<CellRangeAddress> temp = new ArrayList<CellRangeAddress>(); | |||
for (int i = 0; i < cellRanges.length; i++) { | |||
CellRangeAddress craOld = cellRanges[i]; | |||
CellRangeAddress craNew = shiftRange(shifter, craOld, currentExternSheetIx); | |||
if (craNew == null) { | |||
changed = true; | |||
continue; | |||
} | |||
temp.add(craNew); | |||
if (craNew != craOld) { | |||
changed = true; | |||
} | |||
} | |||
if (changed) { | |||
int nRanges = temp.size(); | |||
if (nRanges == 0) { | |||
return false; | |||
} | |||
CellRangeAddress[] newRanges = new CellRangeAddress[nRanges]; | |||
temp.toArray(newRanges); | |||
header.setCellRanges(newRanges); | |||
} | |||
for(int i=0; i<rules.size(); i++) { | |||
CFRuleBase rule = rules.get(i); | |||
Ptg[] ptgs; | |||
ptgs = rule.getParsedExpression1(); | |||
if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) { | |||
rule.setParsedExpression1(ptgs); | |||
} | |||
ptgs = rule.getParsedExpression2(); | |||
if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) { | |||
rule.setParsedExpression2(ptgs); | |||
} | |||
if (rule instanceof CFRule12Record) { | |||
CFRule12Record rule12 = (CFRule12Record)rule; | |||
ptgs = rule12.getParsedExpressionScale(); | |||
if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) { | |||
rule12.setParsedExpressionScale(ptgs); | |||
} | |||
} | |||
} | |||
return true; | |||
} | |||
private static CellRangeAddress shiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx) { | |||
// FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here | |||
AreaPtg aptg = new AreaPtg(cra.getFirstRow(), cra.getLastRow(), cra.getFirstColumn(), cra.getLastColumn(), false, false, false, false); | |||
Ptg[] ptgs = { aptg, }; | |||
if (!shifter.adjustFormula(ptgs, currentExternSheetIx)) { | |||
return cra; | |||
} | |||
Ptg ptg0 = ptgs[0]; | |||
if (ptg0 instanceof AreaPtg) { | |||
AreaPtg bptg = (AreaPtg) ptg0; | |||
return new CellRangeAddress(bptg.getFirstRow(), bptg.getLastRow(), bptg.getFirstColumn(), bptg.getLastColumn()); | |||
} | |||
if (ptg0 instanceof AreaErrPtg) { | |||
return null; | |||
} | |||
throw new IllegalStateException("Unexpected shifted ptg class (" + ptg0.getClass().getName() + ")"); | |||
} | |||
} |
@@ -21,6 +21,7 @@ import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.hssf.model.RecordStream; | |||
import org.apache.poi.hssf.record.CFHeader12Record; | |||
import org.apache.poi.hssf.record.CFHeaderRecord; | |||
import org.apache.poi.ss.formula.FormulaShifter; | |||
@@ -28,11 +29,8 @@ import org.apache.poi.ss.formula.FormulaShifter; | |||
* Holds all the conditional formatting for a workbook sheet.<p/> | |||
* | |||
* See OOO exelfileformat.pdf sec 4.12 'Conditional Formatting Table' | |||
* | |||
* @author Josh Micich | |||
*/ | |||
public final class ConditionalFormattingTable extends RecordAggregate { | |||
private final List<CFRecordsAggregate> _cfHeaders; | |||
/** | |||
@@ -45,7 +43,8 @@ public final class ConditionalFormattingTable extends RecordAggregate { | |||
public ConditionalFormattingTable(RecordStream rs) { | |||
List<CFRecordsAggregate> temp = new ArrayList<CFRecordsAggregate>(); | |||
while (rs.peekNextClass() == CFHeaderRecord.class) { | |||
while (rs.peekNextClass() == CFHeaderRecord.class || | |||
rs.peekNextClass() == CFHeader12Record.class) { | |||
temp.add(CFRecordsAggregate.createCFAggregate(rs)); | |||
} | |||
_cfHeaders = temp; | |||
@@ -62,6 +61,7 @@ public final class ConditionalFormattingTable extends RecordAggregate { | |||
* @return index of the newly added CF header aggregate | |||
*/ | |||
public int add(CFRecordsAggregate cfAggregate) { | |||
cfAggregate.getHeader().setID(_cfHeaders.size()); | |||
_cfHeaders.add(cfAggregate); | |||
return _cfHeaders.size() - 1; | |||
} |
@@ -25,11 +25,8 @@ import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Border Formatting Block of the Conditional Formatting Rule Record. | |||
* | |||
* @author Dmitriy Kumshayev | |||
*/ | |||
public final class BorderFormatting { | |||
/** No border */ | |||
public final static short BORDER_NONE = 0x0; | |||
/** Thin border */ | |||
@@ -89,6 +86,9 @@ public final class BorderFormatting { | |||
field_14_border_styles2 = in.readInt(); | |||
} | |||
public int getDataLength() { | |||
return 8; | |||
} | |||
/** | |||
* set the type of border to use for the left border of the cell |
@@ -23,97 +23,93 @@ import java.util.List; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
/** | |||
* | |||
* @author Dmitriy Kumshayev | |||
* TODO Should this move to org.apache.poi.ss.util ? | |||
*/ | |||
public final class CellRangeUtil | |||
{ | |||
private CellRangeUtil() { | |||
// no instance of this class | |||
} | |||
public static final int NO_INTERSECTION = 1; | |||
public static final int OVERLAP = 2; | |||
/** first range is within the second range */ | |||
public static final int INSIDE = 3; | |||
/** first range encloses or is equal to the second */ | |||
public static final int ENCLOSES = 4; | |||
/** | |||
* Intersect this range with the specified range. | |||
* | |||
* @param crB - the specified range | |||
* @return code which reflects how the specified range is related to this range.<br/> | |||
* Possible return codes are: | |||
* NO_INTERSECTION - the specified range is outside of this range;<br/> | |||
* OVERLAP - both ranges partially overlap;<br/> | |||
* INSIDE - the specified range is inside of this one<br/> | |||
* ENCLOSES - the specified range encloses (possibly exactly the same as) this range<br/> | |||
*/ | |||
public static int intersect(CellRangeAddress crA, CellRangeAddress crB ) | |||
{ | |||
int firstRow = crB.getFirstRow(); | |||
int lastRow = crB.getLastRow(); | |||
int firstCol = crB.getFirstColumn(); | |||
int lastCol = crB.getLastColumn(); | |||
if | |||
( | |||
public final class CellRangeUtil { | |||
private CellRangeUtil() { | |||
// no instance of this class | |||
} | |||
public static final int NO_INTERSECTION = 1; | |||
public static final int OVERLAP = 2; | |||
/** first range is within the second range */ | |||
public static final int INSIDE = 3; | |||
/** first range encloses or is equal to the second */ | |||
public static final int ENCLOSES = 4; | |||
/** | |||
* Intersect this range with the specified range. | |||
* | |||
* @param crB - the specified range | |||
* @return code which reflects how the specified range is related to this range.<br/> | |||
* Possible return codes are: | |||
* NO_INTERSECTION - the specified range is outside of this range;<br/> | |||
* OVERLAP - both ranges partially overlap;<br/> | |||
* INSIDE - the specified range is inside of this one<br/> | |||
* ENCLOSES - the specified range encloses (possibly exactly the same as) this range<br/> | |||
*/ | |||
public static int intersect(CellRangeAddress crA, CellRangeAddress crB ) | |||
{ | |||
int firstRow = crB.getFirstRow(); | |||
int lastRow = crB.getLastRow(); | |||
int firstCol = crB.getFirstColumn(); | |||
int lastCol = crB.getLastColumn(); | |||
if | |||
( | |||
gt(crA.getFirstRow(),lastRow) || | |||
lt(crA.getLastRow(),firstRow) || | |||
gt(crA.getFirstColumn(),lastCol) || | |||
lt(crA.getLastColumn(),firstCol) | |||
) | |||
{ | |||
return NO_INTERSECTION; | |||
} | |||
else if( contains(crA, crB) ) | |||
{ | |||
return INSIDE; | |||
} | |||
else if( contains(crB, crA)) | |||
{ | |||
return ENCLOSES; | |||
} | |||
else | |||
{ | |||
return OVERLAP; | |||
} | |||
} | |||
/** | |||
* Do all possible cell merges between cells of the list so that:<br> | |||
* <li>if a cell range is completely inside of another cell range, it gets removed from the list | |||
* <li>if two cells have a shared border, merge them into one bigger cell range | |||
* @param cellRanges | |||
* @return updated List of cell ranges | |||
*/ | |||
public static CellRangeAddress[] mergeCellRanges(CellRangeAddress[] cellRanges) { | |||
if(cellRanges.length < 1) { | |||
return cellRanges; | |||
} | |||
) | |||
{ | |||
return NO_INTERSECTION; | |||
} | |||
else if( contains(crA, crB) ) | |||
{ | |||
return INSIDE; | |||
} | |||
else if( contains(crB, crA)) | |||
{ | |||
return ENCLOSES; | |||
} | |||
else | |||
{ | |||
return OVERLAP; | |||
} | |||
} | |||
/** | |||
* Do all possible cell merges between cells of the list so that:<br> | |||
* <li>if a cell range is completely inside of another cell range, it gets removed from the list | |||
* <li>if two cells have a shared border, merge them into one bigger cell range | |||
* @param cellRanges | |||
* @return updated List of cell ranges | |||
*/ | |||
public static CellRangeAddress[] mergeCellRanges(CellRangeAddress[] cellRanges) { | |||
if(cellRanges.length < 1) { | |||
return cellRanges; | |||
} | |||
List<CellRangeAddress> lst = new ArrayList<CellRangeAddress>(); | |||
for(CellRangeAddress cr : cellRanges) { | |||
lst.add(cr); | |||
} | |||
List<CellRangeAddress> temp = mergeCellRanges(lst); | |||
return toArray(temp); | |||
} | |||
return toArray(temp); | |||
} | |||
private static List<CellRangeAddress> mergeCellRanges(List<CellRangeAddress> cellRangeList) | |||
{ | |||
// loop until either only one item is left or we did not merge anything any more | |||
private static List<CellRangeAddress> mergeCellRanges(List<CellRangeAddress> cellRangeList) | |||
{ | |||
// loop until either only one item is left or we did not merge anything any more | |||
while (cellRangeList.size() > 1) { | |||
boolean somethingGotMerged = false; | |||
// look at all cell-ranges | |||
for (int i = 0; i < cellRangeList.size(); i++) { | |||
CellRangeAddress range1 = cellRangeList.get(i); | |||
// compare each cell range to all other cell-ranges | |||
for (int j = i + 1; j < cellRangeList.size(); j++) { | |||
CellRangeAddress range2 = cellRangeList.get(j); | |||
@@ -139,16 +135,16 @@ public final class CellRangeUtil | |||
} | |||
} | |||
return cellRangeList; | |||
} | |||
/** | |||
* @return the new range(s) to replace the supplied ones. <code>null</code> if no merge is possible | |||
*/ | |||
private static CellRangeAddress[] mergeRanges(CellRangeAddress range1, CellRangeAddress range2) { | |||
int x = intersect(range1, range2); | |||
switch(x) | |||
{ | |||
return cellRangeList; | |||
} | |||
/** | |||
* @return the new range(s) to replace the supplied ones. <code>null</code> if no merge is possible | |||
*/ | |||
private static CellRangeAddress[] mergeRanges(CellRangeAddress range1, CellRangeAddress range2) { | |||
int x = intersect(range1, range2); | |||
switch(x) | |||
{ | |||
case CellRangeUtil.NO_INTERSECTION: | |||
// nothing in common: at most they could be adjacent to each other and thus form a single bigger area | |||
if(hasExactSharedBorder(range1, range2)) { | |||
@@ -171,108 +167,103 @@ public final class CellRangeUtil | |||
throw new RuntimeException("unexpected intersection result (" + x + ")"); | |||
} | |||
private static CellRangeAddress[] toArray(List<CellRangeAddress> temp) { | |||
CellRangeAddress[] result = new CellRangeAddress[temp.size()]; | |||
temp.toArray(result); | |||
return result; | |||
} | |||
private static CellRangeAddress[] toArray(List<CellRangeAddress> temp) { | |||
CellRangeAddress[] result = new CellRangeAddress[temp.size()]; | |||
temp.toArray(result); | |||
return result; | |||
} | |||
/** | |||
* Check if the specified range is located inside of this cell range. | |||
* | |||
* @param crB | |||
* @return true if this cell range contains the argument range inside if it's area | |||
*/ | |||
public static boolean contains(CellRangeAddress crA, CellRangeAddress crB) | |||
{ | |||
int firstRow = crB.getFirstRow(); | |||
int lastRow = crB.getLastRow(); | |||
int firstCol = crB.getFirstColumn(); | |||
int lastCol = crB.getLastColumn(); | |||
return le(crA.getFirstRow(), firstRow) && ge(crA.getLastRow(), lastRow) | |||
&& le(crA.getFirstColumn(), firstCol) && ge(crA.getLastColumn(), lastCol); | |||
} | |||
/** | |||
* Check if the two cell ranges have a shared border. | |||
* | |||
* @return <code>true</code> if the ranges have a complete shared border (i.e. | |||
* the two ranges together make a simple rectangular region. | |||
*/ | |||
public static boolean hasExactSharedBorder(CellRangeAddress crA, CellRangeAddress crB) { | |||
int oFirstRow = crB.getFirstRow(); | |||
int oLastRow = crB.getLastRow(); | |||
int oFirstCol = crB.getFirstColumn(); | |||
int oLastCol = crB.getLastColumn(); | |||
/** | |||
* Check if the specified range is located inside of this cell range. | |||
* | |||
* @param crB | |||
* @return true if this cell range contains the argument range inside if it's area | |||
*/ | |||
public static boolean contains(CellRangeAddress crA, CellRangeAddress crB) | |||
{ | |||
int firstRow = crB.getFirstRow(); | |||
int lastRow = crB.getLastRow(); | |||
int firstCol = crB.getFirstColumn(); | |||
int lastCol = crB.getLastColumn(); | |||
return le(crA.getFirstRow(), firstRow) && ge(crA.getLastRow(), lastRow) | |||
&& le(crA.getFirstColumn(), firstCol) && ge(crA.getLastColumn(), lastCol); | |||
} | |||
/** | |||
* Check if the two cell ranges have a shared border. | |||
* | |||
* @return <code>true</code> if the ranges have a complete shared border (i.e. | |||
* the two ranges together make a simple rectangular region. | |||
*/ | |||
public static boolean hasExactSharedBorder(CellRangeAddress crA, CellRangeAddress crB) { | |||
int oFirstRow = crB.getFirstRow(); | |||
int oLastRow = crB.getLastRow(); | |||
int oFirstCol = crB.getFirstColumn(); | |||
int oLastCol = crB.getLastColumn(); | |||
if (crA.getFirstRow() > 0 && crA.getFirstRow()-1 == oLastRow || | |||
oFirstRow > 0 && oFirstRow-1 == crA.getLastRow()) { | |||
// ranges have a horizontal border in common | |||
// make sure columns are identical: | |||
return crA.getFirstColumn() == oFirstCol && crA.getLastColumn() == oLastCol; | |||
} | |||
if (crA.getFirstRow() > 0 && crA.getFirstRow()-1 == oLastRow || | |||
oFirstRow > 0 && oFirstRow-1 == crA.getLastRow()) { | |||
// ranges have a horizontal border in common | |||
// make sure columns are identical: | |||
return crA.getFirstColumn() == oFirstCol && crA.getLastColumn() == oLastCol; | |||
} | |||
if (crA.getFirstColumn()>0 && crA.getFirstColumn() - 1 == oLastCol || | |||
oFirstCol>0 && crA.getLastColumn() == oFirstCol -1) { | |||
// ranges have a vertical border in common | |||
// make sure rows are identical: | |||
return crA.getFirstRow() == oFirstRow && crA.getLastRow() == oLastRow; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Create an enclosing CellRange for the two cell ranges. | |||
* | |||
* @return enclosing CellRange | |||
*/ | |||
public static CellRangeAddress createEnclosingCellRange(CellRangeAddress crA, CellRangeAddress crB) { | |||
if( crB == null) { | |||
return crA.copy(); | |||
} | |||
return | |||
new CellRangeAddress( | |||
lt(crB.getFirstRow(), crA.getFirstRow()) ?crB.getFirstRow() :crA.getFirstRow(), | |||
gt(crB.getLastRow(), crA.getLastRow()) ?crB.getLastRow() :crA.getLastRow(), | |||
lt(crB.getFirstColumn(),crA.getFirstColumn())?crB.getFirstColumn():crA.getFirstColumn(), | |||
gt(crB.getLastColumn(), crA.getLastColumn()) ?crB.getLastColumn() :crA.getLastColumn() | |||
); | |||
} | |||
/** | |||
* @return true if a < b | |||
*/ | |||
private static boolean lt(int a, int b) | |||
{ | |||
return a == -1 ? false : (b == -1 ? true : a < b); | |||
} | |||
/** | |||
* @return true if a <= b | |||
*/ | |||
private static boolean le(int a, int b) | |||
{ | |||
return a == b || lt(a,b); | |||
} | |||
/** | |||
* @return true if a > b | |||
*/ | |||
private static boolean gt(int a, int b) | |||
{ | |||
return lt(b,a); | |||
} | |||
if (crA.getFirstColumn()>0 && crA.getFirstColumn() - 1 == oLastCol || | |||
oFirstCol>0 && crA.getLastColumn() == oFirstCol -1) { | |||
// ranges have a vertical border in common | |||
// make sure rows are identical: | |||
return crA.getFirstRow() == oFirstRow && crA.getLastRow() == oLastRow; | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return true if a >= b | |||
*/ | |||
private static boolean ge(int a, int b) | |||
{ | |||
return !lt(a,b); | |||
} | |||
/** | |||
* Create an enclosing CellRange for the two cell ranges. | |||
* | |||
* @return enclosing CellRange | |||
*/ | |||
public static CellRangeAddress createEnclosingCellRange(CellRangeAddress crA, CellRangeAddress crB) { | |||
if( crB == null) { | |||
return crA.copy(); | |||
} | |||
return new CellRangeAddress( | |||
lt(crB.getFirstRow(), crA.getFirstRow()) ?crB.getFirstRow() :crA.getFirstRow(), | |||
gt(crB.getLastRow(), crA.getLastRow()) ?crB.getLastRow() :crA.getLastRow(), | |||
lt(crB.getFirstColumn(),crA.getFirstColumn())?crB.getFirstColumn():crA.getFirstColumn(), | |||
gt(crB.getLastColumn(), crA.getLastColumn()) ?crB.getLastColumn() :crA.getLastColumn() | |||
); | |||
} | |||
/** | |||
* @return true if a < b | |||
*/ | |||
private static boolean lt(int a, int b) | |||
{ | |||
return a == -1 ? false : (b == -1 ? true : a < b); | |||
} | |||
/** | |||
* @return true if a <= b | |||
*/ | |||
private static boolean le(int a, int b) | |||
{ | |||
return a == b || lt(a,b); | |||
} | |||
/** | |||
* @return true if a > b | |||
*/ | |||
private static boolean gt(int a, int b) | |||
{ | |||
return lt(b,a); | |||
} | |||
/** | |||
* @return true if a >= b | |||
*/ | |||
private static boolean ge(int a, int b) | |||
{ | |||
return !lt(a,b); | |||
} | |||
} |
@@ -0,0 +1,139 @@ | |||
/* ==================================================================== | |||
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.cf; | |||
import org.apache.poi.ss.usermodel.IconMultiStateFormatting.IconSet; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
/** | |||
* Icon / Multi-State Conditional Formatting Rule Record. | |||
*/ | |||
public final class IconMultiStateFormatting implements Cloneable { | |||
private static POILogger log = POILogFactory.getLogger(IconMultiStateFormatting.class); | |||
private IconSet iconSet; | |||
private byte options; | |||
private Threshold[] thresholds; | |||
private static BitField iconOnly = BitFieldFactory.getInstance(0x01); | |||
private static BitField reversed = BitFieldFactory.getInstance(0x04); | |||
public IconMultiStateFormatting() { | |||
iconSet = IconSet.GYR_3_TRAFFIC_LIGHTS; | |||
options = 0; | |||
thresholds = new Threshold[iconSet.num]; | |||
} | |||
public IconMultiStateFormatting(LittleEndianInput in) { | |||
in.readShort(); // Ignored | |||
in.readByte(); // Reserved | |||
int num = in.readByte(); | |||
int set = in.readByte(); | |||
iconSet = IconSet.byId(set); | |||
if (iconSet.num != num) { | |||
log.log(POILogger.WARN, "Inconsistent Icon Set defintion, found " + iconSet + " but defined as " + num + " entries"); | |||
} | |||
options = in.readByte(); | |||
thresholds = new Threshold[iconSet.num]; | |||
for (int i=0; i<thresholds.length; i++) { | |||
thresholds[i] = new Threshold(in); | |||
} | |||
} | |||
public IconSet getIconSet() { | |||
return iconSet; | |||
} | |||
public void setIconSet(IconSet set) { | |||
this.iconSet = set; | |||
} | |||
public Threshold[] getThresholds() { | |||
return thresholds; | |||
} | |||
public void setThresholds(Threshold[] thresholds) { | |||
this.thresholds = thresholds; | |||
} | |||
public boolean isIconOnly() { | |||
return getOptionFlag(iconOnly); | |||
} | |||
public void setIconOnly(boolean only) { | |||
setOptionFlag(only, iconOnly); | |||
} | |||
public boolean isReversed() { | |||
return getOptionFlag(reversed); | |||
} | |||
public void setReversed(boolean rev) { | |||
setOptionFlag(rev, reversed); | |||
} | |||
private boolean getOptionFlag(BitField field) { | |||
int value = field.getValue(options); | |||
return value==0 ? false : true; | |||
} | |||
private void setOptionFlag(boolean option, BitField field) { | |||
options = field.setByteBoolean(options, option); | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append(" [Icon Formatting]\n"); | |||
buffer.append(" .icon_set = ").append(iconSet).append("\n"); | |||
buffer.append(" .icon_only= ").append(isIconOnly()).append("\n"); | |||
buffer.append(" .reversed = ").append(isReversed()).append("\n"); | |||
for (Threshold t : thresholds) { | |||
buffer.append(t.toString()); | |||
} | |||
buffer.append(" [/Icon Formatting]\n"); | |||
return buffer.toString(); | |||
} | |||
public Object clone() { | |||
IconMultiStateFormatting rec = new IconMultiStateFormatting(); | |||
rec.iconSet = iconSet; | |||
rec.options = options; | |||
rec.thresholds = new Threshold[thresholds.length]; | |||
System.arraycopy(thresholds, 0, rec.thresholds, 0, thresholds.length); | |||
return rec; | |||
} | |||
public int getDataLength() { | |||
int len = 6; | |||
for (Threshold t : thresholds) { | |||
len += t.getDataLength(); | |||
} | |||
return len; | |||
} | |||
public void serialize(LittleEndianOutput out) { | |||
out.writeShort(0); | |||
out.writeByte(0); | |||
out.writeByte(iconSet.num); | |||
out.writeByte(iconSet.id); | |||
out.writeByte(options); | |||
for (Threshold t : thresholds) { | |||
t.serialize(out); | |||
} | |||
} | |||
} |
@@ -24,8 +24,6 @@ import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Pattern Formatting Block of the Conditional Formatting Rule Record. | |||
* | |||
* @author Dmitriy Kumshayev | |||
*/ | |||
public final class PatternFormatting implements Cloneable { | |||
/** No background */ | |||
@@ -89,6 +87,10 @@ public final class PatternFormatting implements Cloneable { | |||
field_16_pattern_color_indexes = in.readUShort(); | |||
} | |||
public int getDataLength() { | |||
return 4; | |||
} | |||
/** | |||
* setting fill pattern | |||
* |
@@ -0,0 +1,146 @@ | |||
/* ==================================================================== | |||
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.cf; | |||
import java.util.Arrays; | |||
import org.apache.poi.ss.formula.Formula; | |||
import org.apache.poi.ss.formula.ptg.Ptg; | |||
import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold.RangeType; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Threshold / value for changes in Conditional Formatting | |||
*/ | |||
public final class Threshold { | |||
/** | |||
* Cell values that are equal to the threshold value do not pass the threshold | |||
*/ | |||
public static final byte EQUALS_EXCLUDE = 0; | |||
/** | |||
* Cell values that are equal to the threshold value pass the threshold. | |||
*/ | |||
public static final byte EQUALS_INCLUDE = 1; | |||
private byte type; | |||
private Formula formula; | |||
private Double value; | |||
private byte equals; | |||
public Threshold() { | |||
type = (byte)RangeType.NUMBER.id; | |||
formula = Formula.create(null); | |||
value = 0d; | |||
} | |||
/** Creates new Threshold */ | |||
public Threshold(LittleEndianInput in) { | |||
type = in.readByte(); | |||
short formulaLen = in.readShort(); | |||
if (formulaLen > 0) { | |||
formula = Formula.read(formulaLen, in); | |||
} else { | |||
formula = Formula.create(null); | |||
} | |||
// Value is only there for non-formula, non min/max thresholds | |||
if (formulaLen == 0 && type != RangeType.MIN.id && | |||
type != RangeType.MAX.id) { | |||
value = in.readDouble(); | |||
} | |||
equals = in.readByte(); | |||
// Reserved, 4 bytes, all 0 | |||
in.readInt(); | |||
} | |||
public byte getType() { | |||
return type; | |||
} | |||
public void setType(byte type) { | |||
this.type = type; | |||
} | |||
public void setType(int type) { | |||
this.type = (byte)type; | |||
} | |||
protected Formula getFormula() { | |||
return formula; | |||
} | |||
public Ptg[] getParsedExpression() { | |||
return formula.getTokens(); | |||
} | |||
public void setParsedExpression(Ptg[] ptgs) { | |||
formula = Formula.create(ptgs); | |||
} | |||
public Double getValue() { | |||
return value; | |||
} | |||
public void setValue(Double value) { | |||
this.value = value; | |||
} | |||
public byte getEquals() { | |||
return equals; | |||
} | |||
public void setEquals(byte equals) { | |||
this.equals = equals; | |||
} | |||
public int getDataLength() { | |||
int len = 1 + formula.getEncodedSize(); | |||
if (value != null) { | |||
len += 8; | |||
} | |||
len += 5; | |||
return len; | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append(" [CF Threshold]\n"); | |||
buffer.append(" .type = ").append(Integer.toHexString(type)).append("\n"); | |||
buffer.append(" .formula = ").append(Arrays.toString(formula.getTokens())).append("\n"); | |||
buffer.append(" .value = ").append(value).append("\n"); | |||
buffer.append(" [/CF Threshold]\n"); | |||
return buffer.toString(); | |||
} | |||
public Object clone() { | |||
Threshold rec = new Threshold(); | |||
rec.type = type; | |||
rec.formula = formula; | |||
rec.value = value; | |||
rec.equals = equals; | |||
return rec; | |||
} | |||
public void serialize(LittleEndianOutput out) { | |||
out.writeByte(type); | |||
if (formula.getTokens().length == 0) { | |||
out.writeShort(0); | |||
} else { | |||
formula.serialize(out); | |||
} | |||
if (value != null) { | |||
out.writeDouble(value); | |||
} | |||
out.writeByte(equals); | |||
out.writeInt(0); // Reserved | |||
} | |||
} |
@@ -18,6 +18,7 @@ | |||
package org.apache.poi.hssf.record.common; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
@@ -28,62 +29,69 @@ import org.apache.poi.util.LittleEndianOutput; | |||
* beyond those of a traditional record. | |||
*/ | |||
public final class FtrHeader { | |||
/** This MUST match the type on the containing record */ | |||
private short recordType; | |||
/** This is a FrtFlags */ | |||
private short grbitFrt; | |||
/** MUST be 8 bytes and all zero */ | |||
private byte[] reserved; | |||
/** This MUST match the type on the containing record */ | |||
private short recordType; | |||
/** This is a FrtFlags */ | |||
private short grbitFrt; | |||
/** The range of cells the parent record applies to, or 0 if N/A */ | |||
private CellRangeAddress associatedRange; | |||
public FtrHeader() { | |||
reserved = new byte[8]; | |||
} | |||
public FtrHeader() { | |||
associatedRange = new CellRangeAddress(0, 0, 0, 0); | |||
} | |||
public FtrHeader(RecordInputStream in) { | |||
recordType = in.readShort(); | |||
grbitFrt = in.readShort(); | |||
reserved = new byte[8]; | |||
in.read(reserved, 0, 8); | |||
} | |||
public FtrHeader(RecordInputStream in) { | |||
recordType = in.readShort(); | |||
grbitFrt = in.readShort(); | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append(" [FUTURE HEADER]\n"); | |||
buffer.append(" Type " + recordType); | |||
buffer.append(" Flags " + grbitFrt); | |||
buffer.append(" [/FUTURE HEADER]\n"); | |||
return buffer.toString(); | |||
} | |||
associatedRange = new CellRangeAddress(in); | |||
} | |||
public void serialize(LittleEndianOutput out) { | |||
out.writeShort(recordType); | |||
out.writeShort(grbitFrt); | |||
out.write(reserved); | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append(" [FUTURE HEADER]\n"); | |||
buffer.append(" Type " + recordType); | |||
buffer.append(" Flags " + grbitFrt); | |||
buffer.append(" [/FUTURE HEADER]\n"); | |||
return buffer.toString(); | |||
} | |||
public static int getDataSize() { | |||
return 12; | |||
} | |||
public void serialize(LittleEndianOutput out) { | |||
out.writeShort(recordType); | |||
out.writeShort(grbitFrt); | |||
associatedRange.serialize(out); | |||
} | |||
public short getRecordType() { | |||
return recordType; | |||
} | |||
public void setRecordType(short recordType) { | |||
this.recordType = recordType; | |||
} | |||
public static int getDataSize() { | |||
return 12; | |||
} | |||
public short getGrbitFrt() { | |||
return grbitFrt; | |||
} | |||
public void setGrbitFrt(short grbitFrt) { | |||
this.grbitFrt = grbitFrt; | |||
} | |||
public short getRecordType() { | |||
return recordType; | |||
} | |||
public void setRecordType(short recordType) { | |||
this.recordType = recordType; | |||
} | |||
public byte[] getReserved() { | |||
return reserved; | |||
} | |||
public void setReserved(byte[] reserved) { | |||
this.reserved = reserved; | |||
} | |||
public short getGrbitFrt() { | |||
return grbitFrt; | |||
} | |||
public void setGrbitFrt(short grbitFrt) { | |||
this.grbitFrt = grbitFrt; | |||
} | |||
public CellRangeAddress getAssociatedRange() { | |||
return associatedRange; | |||
} | |||
public void setAssociatedRange(CellRangeAddress associatedRange) { | |||
this.associatedRange = associatedRange; | |||
} | |||
public Object clone() { | |||
FtrHeader result = new FtrHeader(); | |||
result.recordType = recordType; | |||
result.grbitFrt = grbitFrt; | |||
result.associatedRange = associatedRange.copy(); | |||
return result; | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
/* ==================================================================== | |||
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.ss.util.CellRangeAddress; | |||
/** | |||
* Title: Future Record, a newer (largely Excel 2007+) record | |||
* which contains a Future Record Header ({@link FtrHeader}) | |||
*/ | |||
public interface FutureRecord { | |||
public short getFutureRecordType(); | |||
public FtrHeader getFutureHeader(); | |||
public CellRangeAddress getAssociatedRange(); | |||
} |
@@ -95,7 +95,7 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia | |||
public int readUByte() { | |||
return _cipher.xorByte(_le.readUByte()); | |||
return readByte() & 0xFF; | |||
} | |||
public byte readByte() { | |||
return (byte) _cipher.xorByte(_le.readUByte()); | |||
@@ -103,7 +103,7 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia | |||
public int readUShort() { | |||
return _cipher.xorShort(_le.readUShort()); | |||
return readShort() & 0xFFFF; | |||
} | |||
public short readShort() { | |||
return (short) _cipher.xorShort(_le.readUShort()); |
@@ -17,196 +17,246 @@ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.hssf.record.CFRuleRecord; | |||
import org.apache.poi.hssf.record.CFRuleBase; | |||
import org.apache.poi.hssf.record.cf.BorderFormatting; | |||
import org.apache.poi.hssf.util.HSSFColor; | |||
import org.apache.poi.ss.usermodel.Color; | |||
/** | |||
* High level representation for Border Formatting component | |||
* of Conditional Formatting settings | |||
*/ | |||
public final class HSSFBorderFormatting implements org.apache.poi.ss.usermodel.BorderFormatting | |||
{ | |||
private final CFRuleRecord cfRuleRecord; | |||
private final BorderFormatting borderFormatting; | |||
protected HSSFBorderFormatting(CFRuleRecord cfRuleRecord) | |||
{ | |||
this.cfRuleRecord = cfRuleRecord; | |||
this.borderFormatting = cfRuleRecord.getBorderFormatting(); | |||
} | |||
protected BorderFormatting getBorderFormattingBlock() | |||
{ | |||
return borderFormatting; | |||
} | |||
public short getBorderBottom() | |||
{ | |||
return (short)borderFormatting.getBorderBottom(); | |||
} | |||
public short getBorderDiagonal() | |||
{ | |||
return (short)borderFormatting.getBorderDiagonal(); | |||
} | |||
public short getBorderLeft() | |||
{ | |||
return (short)borderFormatting.getBorderLeft(); | |||
} | |||
public short getBorderRight() | |||
{ | |||
return (short)borderFormatting.getBorderRight(); | |||
} | |||
public short getBorderTop() | |||
{ | |||
return (short)borderFormatting.getBorderTop(); | |||
} | |||
public short getBottomBorderColor() | |||
{ | |||
return (short)borderFormatting.getBottomBorderColor(); | |||
} | |||
public short getDiagonalBorderColor() | |||
{ | |||
return (short)borderFormatting.getDiagonalBorderColor(); | |||
} | |||
public short getLeftBorderColor() | |||
{ | |||
return (short)borderFormatting.getLeftBorderColor(); | |||
} | |||
public short getRightBorderColor() | |||
{ | |||
return (short)borderFormatting.getRightBorderColor(); | |||
} | |||
public short getTopBorderColor() | |||
{ | |||
return (short)borderFormatting.getTopBorderColor(); | |||
} | |||
public boolean isBackwardDiagonalOn() | |||
{ | |||
return borderFormatting.isBackwardDiagonalOn(); | |||
} | |||
public boolean isForwardDiagonalOn() | |||
{ | |||
return borderFormatting.isForwardDiagonalOn(); | |||
} | |||
public void setBackwardDiagonalOn(boolean on) | |||
{ | |||
borderFormatting.setBackwardDiagonalOn(on); | |||
if( on ) | |||
{ | |||
cfRuleRecord.setTopLeftBottomRightBorderModified(on); | |||
} | |||
} | |||
public void setBorderBottom(short border) | |||
{ | |||
borderFormatting.setBorderBottom(border); | |||
if( border != 0) | |||
{ | |||
cfRuleRecord.setBottomBorderModified(true); | |||
} | |||
} | |||
public void setBorderDiagonal(short border) | |||
{ | |||
borderFormatting.setBorderDiagonal(border); | |||
if( border != 0) | |||
{ | |||
cfRuleRecord.setBottomLeftTopRightBorderModified(true); | |||
cfRuleRecord.setTopLeftBottomRightBorderModified(true); | |||
} | |||
} | |||
public void setBorderLeft(short border) | |||
{ | |||
borderFormatting.setBorderLeft(border); | |||
if( border != 0) | |||
{ | |||
cfRuleRecord.setLeftBorderModified(true); | |||
} | |||
} | |||
public void setBorderRight(short border) | |||
{ | |||
borderFormatting.setBorderRight(border); | |||
if( border != 0) | |||
{ | |||
cfRuleRecord.setRightBorderModified(true); | |||
} | |||
} | |||
public void setBorderTop(short border) | |||
{ | |||
borderFormatting.setBorderTop(border); | |||
if( border != 0) | |||
{ | |||
cfRuleRecord.setTopBorderModified(true); | |||
} | |||
} | |||
public void setBottomBorderColor(short color) | |||
{ | |||
borderFormatting.setBottomBorderColor(color); | |||
if( color != 0) | |||
{ | |||
cfRuleRecord.setBottomBorderModified(true); | |||
} | |||
} | |||
public void setDiagonalBorderColor(short color) | |||
{ | |||
borderFormatting.setDiagonalBorderColor(color); | |||
if( color != 0) | |||
{ | |||
cfRuleRecord.setBottomLeftTopRightBorderModified(true); | |||
cfRuleRecord.setTopLeftBottomRightBorderModified(true); | |||
} | |||
} | |||
public void setForwardDiagonalOn(boolean on) | |||
{ | |||
borderFormatting.setForwardDiagonalOn(on); | |||
if( on ) | |||
{ | |||
cfRuleRecord.setBottomLeftTopRightBorderModified(on); | |||
} | |||
} | |||
public void setLeftBorderColor(short color) | |||
{ | |||
borderFormatting.setLeftBorderColor(color); | |||
if( color != 0) | |||
{ | |||
cfRuleRecord.setLeftBorderModified(true); | |||
} | |||
} | |||
public void setRightBorderColor(short color) | |||
{ | |||
borderFormatting.setRightBorderColor(color); | |||
if( color != 0) | |||
{ | |||
cfRuleRecord.setRightBorderModified(true); | |||
} | |||
} | |||
public void setTopBorderColor(short color) | |||
{ | |||
borderFormatting.setTopBorderColor(color); | |||
if( color != 0) | |||
{ | |||
cfRuleRecord.setTopBorderModified(true); | |||
} | |||
} | |||
public final class HSSFBorderFormatting implements org.apache.poi.ss.usermodel.BorderFormatting { | |||
private final HSSFWorkbook workbook; | |||
private final CFRuleBase cfRuleRecord; | |||
private final BorderFormatting borderFormatting; | |||
protected HSSFBorderFormatting(CFRuleBase cfRuleRecord, HSSFWorkbook workbook) { | |||
this.workbook = workbook; | |||
this.cfRuleRecord = cfRuleRecord; | |||
this.borderFormatting = cfRuleRecord.getBorderFormatting(); | |||
} | |||
protected BorderFormatting getBorderFormattingBlock() { | |||
return borderFormatting; | |||
} | |||
public short getBorderBottom() { | |||
return (short)borderFormatting.getBorderBottom(); | |||
} | |||
public short getBorderDiagonal() { | |||
return (short)borderFormatting.getBorderDiagonal(); | |||
} | |||
public short getBorderLeft() { | |||
return (short)borderFormatting.getBorderLeft(); | |||
} | |||
public short getBorderRight() { | |||
return (short)borderFormatting.getBorderRight(); | |||
} | |||
public short getBorderTop() { | |||
return (short)borderFormatting.getBorderTop(); | |||
} | |||
public short getBottomBorderColor() { | |||
return (short)borderFormatting.getBottomBorderColor(); | |||
} | |||
public HSSFColor getBottomBorderColorColor() { | |||
return workbook.getCustomPalette().getColor( | |||
borderFormatting.getBottomBorderColor() | |||
); | |||
} | |||
public short getDiagonalBorderColor() { | |||
return (short)borderFormatting.getDiagonalBorderColor(); | |||
} | |||
public HSSFColor getDiagonalBorderColorColor() { | |||
return workbook.getCustomPalette().getColor( | |||
borderFormatting.getDiagonalBorderColor() | |||
); | |||
} | |||
public short getLeftBorderColor() { | |||
return (short)borderFormatting.getLeftBorderColor(); | |||
} | |||
public HSSFColor getLeftBorderColorColor() { | |||
return workbook.getCustomPalette().getColor( | |||
borderFormatting.getLeftBorderColor() | |||
); | |||
} | |||
public short getRightBorderColor() { | |||
return (short)borderFormatting.getRightBorderColor(); | |||
} | |||
public HSSFColor getRightBorderColorColor() { | |||
return workbook.getCustomPalette().getColor( | |||
borderFormatting.getRightBorderColor() | |||
); | |||
} | |||
public short getTopBorderColor() { | |||
return (short)borderFormatting.getTopBorderColor(); | |||
} | |||
public HSSFColor getTopBorderColorColor() { | |||
return workbook.getCustomPalette().getColor( | |||
borderFormatting.getTopBorderColor() | |||
); | |||
} | |||
public boolean isBackwardDiagonalOn() { | |||
return borderFormatting.isBackwardDiagonalOn(); | |||
} | |||
public boolean isForwardDiagonalOn() { | |||
return borderFormatting.isForwardDiagonalOn(); | |||
} | |||
public void setBackwardDiagonalOn(boolean on) { | |||
borderFormatting.setBackwardDiagonalOn(on); | |||
if (on) { | |||
cfRuleRecord.setTopLeftBottomRightBorderModified(on); | |||
} | |||
} | |||
public void setForwardDiagonalOn(boolean on) { | |||
borderFormatting.setForwardDiagonalOn(on); | |||
if (on) { | |||
cfRuleRecord.setBottomLeftTopRightBorderModified(on); | |||
} | |||
} | |||
public void setBorderBottom(short border) { | |||
borderFormatting.setBorderBottom(border); | |||
if (border != 0) { | |||
cfRuleRecord.setBottomBorderModified(true); | |||
} else { | |||
cfRuleRecord.setBottomBorderModified(false); | |||
} | |||
} | |||
public void setBorderDiagonal(short border) { | |||
borderFormatting.setBorderDiagonal(border); | |||
if (border != 0) { | |||
cfRuleRecord.setBottomLeftTopRightBorderModified(true); | |||
cfRuleRecord.setTopLeftBottomRightBorderModified(true); | |||
} else { | |||
cfRuleRecord.setBottomLeftTopRightBorderModified(false); | |||
cfRuleRecord.setTopLeftBottomRightBorderModified(false); | |||
} | |||
} | |||
public void setBorderLeft(short border) { | |||
borderFormatting.setBorderLeft(border); | |||
if (border != 0) { | |||
cfRuleRecord.setLeftBorderModified(true); | |||
} else { | |||
cfRuleRecord.setLeftBorderModified(false); | |||
} | |||
} | |||
public void setBorderRight(short border) { | |||
borderFormatting.setBorderRight(border); | |||
if (border != 0) { | |||
cfRuleRecord.setRightBorderModified(true); | |||
} else { | |||
cfRuleRecord.setRightBorderModified(false); | |||
} | |||
} | |||
public void setBorderTop(short border) { | |||
borderFormatting.setBorderTop(border); | |||
if (border != 0) { | |||
cfRuleRecord.setTopBorderModified(true); | |||
} else { | |||
cfRuleRecord.setTopBorderModified(false); | |||
} | |||
} | |||
public void setBottomBorderColor(short color) { | |||
borderFormatting.setBottomBorderColor(color); | |||
if (color != 0) { | |||
cfRuleRecord.setBottomBorderModified(true); | |||
} else { | |||
cfRuleRecord.setBottomBorderModified(false); | |||
} | |||
} | |||
public void setBottomBorderColor(Color color) { | |||
HSSFColor hcolor = HSSFColor.toHSSFColor(color); | |||
if (hcolor == null) { | |||
setBottomBorderColor((short)0); | |||
} else { | |||
setBottomBorderColor(hcolor.getIndex()); | |||
} | |||
} | |||
public void setDiagonalBorderColor(short color) { | |||
borderFormatting.setDiagonalBorderColor(color); | |||
if (color != 0) { | |||
cfRuleRecord.setBottomLeftTopRightBorderModified(true); | |||
cfRuleRecord.setTopLeftBottomRightBorderModified(true); | |||
} else { | |||
cfRuleRecord.setBottomLeftTopRightBorderModified(false); | |||
cfRuleRecord.setTopLeftBottomRightBorderModified(false); | |||
} | |||
} | |||
public void setDiagonalBorderColor(Color color) { | |||
HSSFColor hcolor = HSSFColor.toHSSFColor(color); | |||
if (hcolor == null) { | |||
setDiagonalBorderColor((short)0); | |||
} else { | |||
setDiagonalBorderColor(hcolor.getIndex()); | |||
} | |||
} | |||
public void setLeftBorderColor(short color) { | |||
borderFormatting.setLeftBorderColor(color); | |||
if (color != 0) { | |||
cfRuleRecord.setLeftBorderModified(true); | |||
} else { | |||
cfRuleRecord.setLeftBorderModified(false); | |||
} | |||
} | |||
public void setLeftBorderColor(Color color) { | |||
HSSFColor hcolor = HSSFColor.toHSSFColor(color); | |||
if (hcolor == null) { | |||
setLeftBorderColor((short)0); | |||
} else { | |||
setLeftBorderColor(hcolor.getIndex()); | |||
} | |||
} | |||
public void setRightBorderColor(short color) { | |||
borderFormatting.setRightBorderColor(color); | |||
if (color != 0) { | |||
cfRuleRecord.setRightBorderModified(true); | |||
} else { | |||
cfRuleRecord.setRightBorderModified(false); | |||
} | |||
} | |||
public void setRightBorderColor(Color color) { | |||
HSSFColor hcolor = HSSFColor.toHSSFColor(color); | |||
if (hcolor == null) { | |||
setRightBorderColor((short)0); | |||
} else { | |||
setRightBorderColor(hcolor.getIndex()); | |||
} | |||
} | |||
public void setTopBorderColor(short color) { | |||
borderFormatting.setTopBorderColor(color); | |||
if (color != 0) { | |||
cfRuleRecord.setTopBorderModified(true); | |||
} else { | |||
cfRuleRecord.setTopBorderModified(false); | |||
} | |||
} | |||
public void setTopBorderColor(Color color) { | |||
HSSFColor hcolor = HSSFColor.toHSSFColor(color); | |||
if (hcolor == null) { | |||
setTopBorderColor((short)0); | |||
} else { | |||
setTopBorderColor(hcolor.getIndex()); | |||
} | |||
} | |||
} |
@@ -538,18 +538,17 @@ public class HSSFCell implements Cell { | |||
public void setCellValue(RichTextString value) | |||
{ | |||
HSSFRichTextString hvalue = (HSSFRichTextString) value; | |||
int row=_record.getRow(); | |||
short col=_record.getColumn(); | |||
short styleIndex=_record.getXFIndex(); | |||
if (hvalue == null) | |||
if (value == null) | |||
{ | |||
notifyFormulaChanging(); | |||
setCellType(CELL_TYPE_BLANK, false, row, col, styleIndex); | |||
return; | |||
} | |||
if(hvalue.length() > SpreadsheetVersion.EXCEL97.getMaxTextLength()){ | |||
if(value.length() > SpreadsheetVersion.EXCEL97.getMaxTextLength()){ | |||
throw new IllegalArgumentException("The maximum length of cell contents (text) is 32,767 characters"); | |||
} | |||
@@ -557,7 +556,7 @@ public class HSSFCell implements Cell { | |||
// Set the 'pre-evaluated result' for the formula | |||
// note - formulas do not preserve text formatting. | |||
FormulaRecordAggregate fr = (FormulaRecordAggregate) _record; | |||
fr.setCachedStringResult(hvalue.getString()); | |||
fr.setCachedStringResult(value.getString()); | |||
// Update our local cache to the un-formatted version | |||
_stringValue = new HSSFRichTextString(value.getString()); | |||
@@ -573,6 +572,7 @@ public class HSSFCell implements Cell { | |||
} | |||
int index = 0; | |||
HSSFRichTextString hvalue = (HSSFRichTextString) value; | |||
UnicodeString str = hvalue.getUnicodeString(); | |||
index = _book.getWorkbook().addSSTString(str); | |||
(( LabelSSTRecord ) _record).setSSTIndex(index); |
@@ -129,10 +129,13 @@ public class HSSFComment extends HSSFTextbox implements Comment { | |||
@Override | |||
void setShapeId(int shapeId) { | |||
if(shapeId > 65535) { | |||
throw new IllegalArgumentException("Cannot add more than " + 65535 + " shapes"); | |||
} | |||
super.setShapeId(shapeId); | |||
CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) getObjRecord().getSubRecords().get(0); | |||
cod.setObjectId((short) (shapeId % 1024)); | |||
_note.setShapeId(shapeId % 1024); | |||
cod.setObjectId(shapeId); | |||
_note.setShapeId(shapeId); | |||
} | |||
/** |
@@ -16,7 +16,7 @@ | |||
==================================================================== */ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.hssf.record.CFRuleRecord; | |||
import org.apache.poi.hssf.record.CFRuleBase; | |||
import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate; | |||
import org.apache.poi.ss.usermodel.ConditionalFormatting; | |||
import org.apache.poi.ss.usermodel.ConditionalFormattingRule; | |||
@@ -73,91 +73,84 @@ import org.apache.poi.ss.util.CellRangeAddress; | |||
* sheet.addConditionalFormatting(regions, rule); | |||
* </PRE> | |||
*/ | |||
public final class HSSFConditionalFormatting implements ConditionalFormatting | |||
{ | |||
private final HSSFWorkbook _workbook; | |||
private final CFRecordsAggregate cfAggregate; | |||
public final class HSSFConditionalFormatting implements ConditionalFormatting { | |||
private final HSSFSheet sheet; | |||
private final CFRecordsAggregate cfAggregate; | |||
HSSFConditionalFormatting(HSSFWorkbook workbook, CFRecordsAggregate cfAggregate) | |||
{ | |||
if(workbook == null) { | |||
throw new IllegalArgumentException("workbook must not be null"); | |||
} | |||
if(cfAggregate == null) { | |||
throw new IllegalArgumentException("cfAggregate must not be null"); | |||
} | |||
_workbook = workbook; | |||
this.cfAggregate = cfAggregate; | |||
} | |||
CFRecordsAggregate getCFRecordsAggregate() { | |||
return cfAggregate; | |||
} | |||
HSSFConditionalFormatting(HSSFSheet sheet, CFRecordsAggregate cfAggregate) { | |||
if(sheet == null) { | |||
throw new IllegalArgumentException("sheet must not be null"); | |||
} | |||
if(cfAggregate == null) { | |||
throw new IllegalArgumentException("cfAggregate must not be null"); | |||
} | |||
this.sheet = sheet; | |||
this.cfAggregate = cfAggregate; | |||
} | |||
CFRecordsAggregate getCFRecordsAggregate() { | |||
return cfAggregate; | |||
} | |||
/** | |||
* @deprecated (Aug-2008) use {@link HSSFConditionalFormatting#getFormattingRanges()} | |||
*/ | |||
public org.apache.poi.ss.util.Region[] getFormattingRegions() | |||
{ | |||
CellRangeAddress[] cellRanges = getFormattingRanges(); | |||
return org.apache.poi.ss.util.Region.convertCellRangesToRegions(cellRanges); | |||
} | |||
/** | |||
* @return array of <tt>CellRangeAddress</tt>s. never <code>null</code> | |||
*/ | |||
public CellRangeAddress[] getFormattingRanges() { | |||
return cfAggregate.getHeader().getCellRanges(); | |||
} | |||
/** | |||
* @deprecated (Aug-2008) use {@link HSSFConditionalFormatting#getFormattingRanges()} | |||
*/ | |||
public org.apache.poi.ss.util.Region[] getFormattingRegions() { | |||
CellRangeAddress[] cellRanges = getFormattingRanges(); | |||
return org.apache.poi.ss.util.Region.convertCellRangesToRegions(cellRanges); | |||
} | |||
/** | |||
* @return array of <tt>CellRangeAddress</tt>s. never <code>null</code> | |||
*/ | |||
public CellRangeAddress[] getFormattingRanges() { | |||
return cfAggregate.getHeader().getCellRanges(); | |||
} | |||
/** | |||
* Replaces an existing Conditional Formatting rule at position idx. | |||
* Excel allows to create up to 3 Conditional Formatting rules. | |||
* This method can be useful to modify existing Conditional Formatting rules. | |||
* | |||
* @param idx position of the rule. Should be between 0 and 2. | |||
* @param cfRule - Conditional Formatting rule | |||
*/ | |||
public void setRule(int idx, HSSFConditionalFormattingRule cfRule) | |||
{ | |||
cfAggregate.setRule(idx, cfRule.getCfRuleRecord()); | |||
} | |||
/** | |||
* Replaces an existing Conditional Formatting rule at position idx. | |||
* Older versions of Excel only allow up to 3 Conditional Formatting rules, | |||
* and will ignore rules beyond that, while newer versions are fine. | |||
* This method can be useful to modify existing Conditional Formatting rules. | |||
* | |||
* @param idx position of the rule. Should be between 0 and 2 for older Excel versions | |||
* @param cfRule - Conditional Formatting rule | |||
*/ | |||
public void setRule(int idx, HSSFConditionalFormattingRule cfRule) { | |||
cfAggregate.setRule(idx, cfRule.getCfRuleRecord()); | |||
} | |||
public void setRule(int idx, ConditionalFormattingRule cfRule){ | |||
setRule(idx, (HSSFConditionalFormattingRule)cfRule); | |||
} | |||
/** | |||
* add a Conditional Formatting rule. | |||
* Excel allows to create up to 3 Conditional Formatting rules. | |||
* @param cfRule - Conditional Formatting rule | |||
*/ | |||
public void addRule(HSSFConditionalFormattingRule cfRule) | |||
{ | |||
cfAggregate.addRule(cfRule.getCfRuleRecord()); | |||
} | |||
/** | |||
* add a Conditional Formatting rule. | |||
* Excel allows to create up to 3 Conditional Formatting rules. | |||
* @param cfRule - Conditional Formatting rule | |||
*/ | |||
public void addRule(HSSFConditionalFormattingRule cfRule) { | |||
cfAggregate.addRule(cfRule.getCfRuleRecord()); | |||
} | |||
public void addRule(ConditionalFormattingRule cfRule){ | |||
addRule((HSSFConditionalFormattingRule)cfRule); | |||
} | |||
/** | |||
* @return the Conditional Formatting rule at position idx. | |||
*/ | |||
public HSSFConditionalFormattingRule getRule(int idx) | |||
{ | |||
CFRuleRecord ruleRecord = cfAggregate.getRule(idx); | |||
return new HSSFConditionalFormattingRule(_workbook, ruleRecord); | |||
} | |||
/** | |||
* @return the Conditional Formatting rule at position idx. | |||
*/ | |||
public HSSFConditionalFormattingRule getRule(int idx) { | |||
CFRuleBase ruleRecord = cfAggregate.getRule(idx); | |||
return new HSSFConditionalFormattingRule(sheet, ruleRecord); | |||
} | |||
/** | |||
* @return number of Conditional Formatting rules. | |||
*/ | |||
public int getNumberOfRules() | |||
{ | |||
return cfAggregate.getNumberOfRules(); | |||
} | |||
/** | |||
* @return number of Conditional Formatting rules. | |||
*/ | |||
public int getNumberOfRules() { | |||
return cfAggregate.getNumberOfRules(); | |||
} | |||
public String toString() | |||
{ | |||
return cfAggregate.toString(); | |||
} | |||
public String toString() { | |||
return cfAggregate.toString(); | |||
} | |||
} |
@@ -18,12 +18,16 @@ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.hssf.model.HSSFFormulaParser; | |||
import org.apache.poi.hssf.record.CFRule12Record; | |||
import org.apache.poi.hssf.record.CFRuleBase; | |||
import org.apache.poi.hssf.record.CFRuleBase.ComparisonOperator; | |||
import org.apache.poi.hssf.record.CFRuleRecord; | |||
import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator; | |||
import org.apache.poi.hssf.record.cf.BorderFormatting; | |||
import org.apache.poi.hssf.record.cf.FontFormatting; | |||
import org.apache.poi.hssf.record.cf.IconMultiStateFormatting; | |||
import org.apache.poi.hssf.record.cf.PatternFormatting; | |||
import org.apache.poi.ss.formula.ptg.Ptg; | |||
import org.apache.poi.ss.usermodel.ConditionType; | |||
import org.apache.poi.ss.usermodel.ConditionalFormattingRule; | |||
/** | |||
@@ -32,178 +36,224 @@ import org.apache.poi.ss.usermodel.ConditionalFormattingRule; | |||
* It allows to specify formula based conditions for the Conditional Formatting | |||
* and the formatting settings such as font, border and pattern. | |||
*/ | |||
public final class HSSFConditionalFormattingRule implements ConditionalFormattingRule | |||
{ | |||
private static final byte CELL_COMPARISON = CFRuleRecord.CONDITION_TYPE_CELL_VALUE_IS; | |||
private final CFRuleRecord cfRuleRecord; | |||
private final HSSFWorkbook workbook; | |||
HSSFConditionalFormattingRule(HSSFWorkbook pWorkbook, CFRuleRecord pRuleRecord) { | |||
if (pWorkbook == null) { | |||
throw new IllegalArgumentException("pWorkbook must not be null"); | |||
} | |||
if (pRuleRecord == null) { | |||
throw new IllegalArgumentException("pRuleRecord must not be null"); | |||
} | |||
workbook = pWorkbook; | |||
cfRuleRecord = pRuleRecord; | |||
} | |||
CFRuleRecord getCfRuleRecord() | |||
{ | |||
return cfRuleRecord; | |||
} | |||
private HSSFFontFormatting getFontFormatting(boolean create) | |||
{ | |||
FontFormatting fontFormatting = cfRuleRecord.getFontFormatting(); | |||
if ( fontFormatting != null) | |||
{ | |||
cfRuleRecord.setFontFormatting(fontFormatting); | |||
return new HSSFFontFormatting(cfRuleRecord); | |||
} | |||
else if( create ) | |||
{ | |||
fontFormatting = new FontFormatting(); | |||
cfRuleRecord.setFontFormatting(fontFormatting); | |||
return new HSSFFontFormatting(cfRuleRecord); | |||
} | |||
else | |||
{ | |||
return null; | |||
} | |||
} | |||
/** | |||
* @return - font formatting object if defined, <code>null</code> otherwise | |||
*/ | |||
public HSSFFontFormatting getFontFormatting() | |||
{ | |||
return getFontFormatting(false); | |||
} | |||
/** | |||
* create a new font formatting structure if it does not exist, | |||
* otherwise just return existing object. | |||
* @return - font formatting object, never returns <code>null</code>. | |||
*/ | |||
public HSSFFontFormatting createFontFormatting() | |||
{ | |||
return getFontFormatting(true); | |||
} | |||
private HSSFBorderFormatting getBorderFormatting(boolean create) | |||
{ | |||
BorderFormatting borderFormatting = cfRuleRecord.getBorderFormatting(); | |||
if ( borderFormatting != null) | |||
{ | |||
cfRuleRecord.setBorderFormatting(borderFormatting); | |||
return new HSSFBorderFormatting(cfRuleRecord); | |||
} | |||
else if( create ) | |||
{ | |||
borderFormatting = new BorderFormatting(); | |||
cfRuleRecord.setBorderFormatting(borderFormatting); | |||
return new HSSFBorderFormatting(cfRuleRecord); | |||
} | |||
else | |||
{ | |||
return null; | |||
} | |||
} | |||
/** | |||
* @return - border formatting object if defined, <code>null</code> otherwise | |||
*/ | |||
public HSSFBorderFormatting getBorderFormatting() | |||
{ | |||
return getBorderFormatting(false); | |||
} | |||
/** | |||
* create a new border formatting structure if it does not exist, | |||
* otherwise just return existing object. | |||
* @return - border formatting object, never returns <code>null</code>. | |||
*/ | |||
public HSSFBorderFormatting createBorderFormatting() | |||
{ | |||
return getBorderFormatting(true); | |||
} | |||
private HSSFPatternFormatting getPatternFormatting(boolean create) | |||
{ | |||
PatternFormatting patternFormatting = cfRuleRecord.getPatternFormatting(); | |||
if ( patternFormatting != null) | |||
{ | |||
cfRuleRecord.setPatternFormatting(patternFormatting); | |||
return new HSSFPatternFormatting(cfRuleRecord); | |||
} | |||
else if( create ) | |||
{ | |||
patternFormatting = new PatternFormatting(); | |||
cfRuleRecord.setPatternFormatting(patternFormatting); | |||
return new HSSFPatternFormatting(cfRuleRecord); | |||
} | |||
else | |||
{ | |||
return null; | |||
} | |||
} | |||
/** | |||
* @return - pattern formatting object if defined, <code>null</code> otherwise | |||
*/ | |||
public HSSFPatternFormatting getPatternFormatting() | |||
{ | |||
return getPatternFormatting(false); | |||
} | |||
/** | |||
* create a new pattern formatting structure if it does not exist, | |||
* otherwise just return existing object. | |||
* @return - pattern formatting object, never returns <code>null</code>. | |||
*/ | |||
public HSSFPatternFormatting createPatternFormatting() | |||
{ | |||
return getPatternFormatting(true); | |||
} | |||
/** | |||
* @return - the conditiontype for the cfrule | |||
*/ | |||
public byte getConditionType() { | |||
return cfRuleRecord.getConditionType(); | |||
} | |||
/** | |||
* @return - the comparisionoperatation for the cfrule | |||
*/ | |||
public byte getComparisonOperation() { | |||
return cfRuleRecord.getComparisonOperation(); | |||
} | |||
public String getFormula1() | |||
{ | |||
return toFormulaString(cfRuleRecord.getParsedExpression1()); | |||
} | |||
public String getFormula2() | |||
{ | |||
byte conditionType = cfRuleRecord.getConditionType(); | |||
if (conditionType == CELL_COMPARISON) { | |||
byte comparisonOperation = cfRuleRecord.getComparisonOperation(); | |||
switch(comparisonOperation) | |||
{ | |||
case ComparisonOperator.BETWEEN: | |||
case ComparisonOperator.NOT_BETWEEN: | |||
return toFormulaString(cfRuleRecord.getParsedExpression2()); | |||
} | |||
} | |||
return null; | |||
} | |||
private String toFormulaString(Ptg[] parsedExpression) | |||
{ | |||
if(parsedExpression ==null) { | |||
return null; | |||
} | |||
return HSSFFormulaParser.toFormulaString(workbook, parsedExpression); | |||
} | |||
public final class HSSFConditionalFormattingRule implements ConditionalFormattingRule { | |||
private static final byte CELL_COMPARISON = CFRuleRecord.CONDITION_TYPE_CELL_VALUE_IS; | |||
private final CFRuleBase cfRuleRecord; | |||
private final HSSFWorkbook workbook; | |||
private final HSSFSheet sheet; | |||
HSSFConditionalFormattingRule(HSSFSheet pSheet, CFRuleBase pRuleRecord) { | |||
if (pSheet == null) { | |||
throw new IllegalArgumentException("pSheet must not be null"); | |||
} | |||
if (pRuleRecord == null) { | |||
throw new IllegalArgumentException("pRuleRecord must not be null"); | |||
} | |||
sheet = pSheet; | |||
workbook = pSheet.getWorkbook(); | |||
cfRuleRecord = pRuleRecord; | |||
} | |||
CFRuleBase getCfRuleRecord() | |||
{ | |||
return cfRuleRecord; | |||
} | |||
private HSSFFontFormatting getFontFormatting(boolean create) | |||
{ | |||
FontFormatting fontFormatting = cfRuleRecord.getFontFormatting(); | |||
if ( fontFormatting != null) | |||
{ | |||
cfRuleRecord.setFontFormatting(fontFormatting); | |||
return new HSSFFontFormatting(cfRuleRecord, workbook); | |||
} | |||
else if( create ) | |||
{ | |||
fontFormatting = new FontFormatting(); | |||
cfRuleRecord.setFontFormatting(fontFormatting); | |||
return new HSSFFontFormatting(cfRuleRecord, workbook); | |||
} | |||
else | |||
{ | |||
return null; | |||
} | |||
} | |||
/** | |||
* @return - font formatting object if defined, <code>null</code> otherwise | |||
*/ | |||
public HSSFFontFormatting getFontFormatting() | |||
{ | |||
return getFontFormatting(false); | |||
} | |||
/** | |||
* create a new font formatting structure if it does not exist, | |||
* otherwise just return existing object. | |||
* @return - font formatting object, never returns <code>null</code>. | |||
*/ | |||
public HSSFFontFormatting createFontFormatting() | |||
{ | |||
return getFontFormatting(true); | |||
} | |||
private HSSFBorderFormatting getBorderFormatting(boolean create) | |||
{ | |||
BorderFormatting borderFormatting = cfRuleRecord.getBorderFormatting(); | |||
if ( borderFormatting != null) | |||
{ | |||
cfRuleRecord.setBorderFormatting(borderFormatting); | |||
return new HSSFBorderFormatting(cfRuleRecord, workbook); | |||
} | |||
else if( create ) | |||
{ | |||
borderFormatting = new BorderFormatting(); | |||
cfRuleRecord.setBorderFormatting(borderFormatting); | |||
return new HSSFBorderFormatting(cfRuleRecord, workbook); | |||
} | |||
else | |||
{ | |||
return null; | |||
} | |||
} | |||
/** | |||
* @return - border formatting object if defined, <code>null</code> otherwise | |||
*/ | |||
public HSSFBorderFormatting getBorderFormatting() | |||
{ | |||
return getBorderFormatting(false); | |||
} | |||
/** | |||
* create a new border formatting structure if it does not exist, | |||
* otherwise just return existing object. | |||
* @return - border formatting object, never returns <code>null</code>. | |||
*/ | |||
public HSSFBorderFormatting createBorderFormatting() | |||
{ | |||
return getBorderFormatting(true); | |||
} | |||
private HSSFPatternFormatting getPatternFormatting(boolean create) | |||
{ | |||
PatternFormatting patternFormatting = cfRuleRecord.getPatternFormatting(); | |||
if ( patternFormatting != null) | |||
{ | |||
cfRuleRecord.setPatternFormatting(patternFormatting); | |||
return new HSSFPatternFormatting(cfRuleRecord, workbook); | |||
} | |||
else if( create ) | |||
{ | |||
patternFormatting = new PatternFormatting(); | |||
cfRuleRecord.setPatternFormatting(patternFormatting); | |||
return new HSSFPatternFormatting(cfRuleRecord, workbook); | |||
} | |||
else | |||
{ | |||
return null; | |||
} | |||
} | |||
/** | |||
* @return - pattern formatting object if defined, <code>null</code> otherwise | |||
*/ | |||
public HSSFPatternFormatting getPatternFormatting() | |||
{ | |||
return getPatternFormatting(false); | |||
} | |||
/** | |||
* create a new pattern formatting structure if it does not exist, | |||
* otherwise just return existing object. | |||
* @return - pattern formatting object, never returns <code>null</code>. | |||
*/ | |||
public HSSFPatternFormatting createPatternFormatting() | |||
{ | |||
return getPatternFormatting(true); | |||
} | |||
private HSSFIconMultiStateFormatting getMultiStateFormatting(boolean create) { | |||
if (cfRuleRecord instanceof CFRule12Record) { | |||
// Good | |||
} else { | |||
if (create) throw new IllegalArgumentException("Can't convert a CF into a CF12 record"); | |||
return null; | |||
} | |||
CFRule12Record cfRule12Record = (CFRule12Record)cfRuleRecord; | |||
IconMultiStateFormatting iconFormatting = cfRule12Record.getMultiStateFormatting(); | |||
if (iconFormatting != null) | |||
{ | |||
return new HSSFIconMultiStateFormatting(cfRule12Record, sheet); | |||
} | |||
else if( create ) | |||
{ | |||
iconFormatting = cfRule12Record.createMultiStateFormatting(); | |||
return new HSSFIconMultiStateFormatting(cfRule12Record, sheet); | |||
} | |||
else | |||
{ | |||
return null; | |||
} | |||
} | |||
/** | |||
* @return icon / multi-state formatting object if defined, <code>null</code> otherwise | |||
*/ | |||
public HSSFIconMultiStateFormatting getMultiStateFormatting() { | |||
return getMultiStateFormatting(false); | |||
} | |||
/** | |||
* create a new icon / multi-state formatting object if it does not exist, | |||
* otherwise just return the existing object. | |||
*/ | |||
public HSSFIconMultiStateFormatting createMultiStateFormatting() { | |||
return getMultiStateFormatting(true); | |||
} | |||
/** | |||
* @return - the conditiontype for the cfrule | |||
*/ | |||
public byte getConditionType() { | |||
return cfRuleRecord.getConditionType(); | |||
} | |||
/** | |||
* @return - the conditiontype for the cfrule | |||
*/ | |||
public ConditionType getConditionTypeType() { | |||
return ConditionType.forId(getConditionType()); | |||
} | |||
/** | |||
* @return - the comparisionoperatation for the cfrule | |||
*/ | |||
public byte getComparisonOperation() { | |||
return cfRuleRecord.getComparisonOperation(); | |||
} | |||
public String getFormula1() | |||
{ | |||
return toFormulaString(cfRuleRecord.getParsedExpression1()); | |||
} | |||
public String getFormula2() { | |||
byte conditionType = cfRuleRecord.getConditionType(); | |||
if (conditionType == CELL_COMPARISON) { | |||
byte comparisonOperation = cfRuleRecord.getComparisonOperation(); | |||
switch(comparisonOperation) { | |||
case ComparisonOperator.BETWEEN: | |||
case ComparisonOperator.NOT_BETWEEN: | |||
return toFormulaString(cfRuleRecord.getParsedExpression2()); | |||
} | |||
} | |||
return null; | |||
} | |||
protected String toFormulaString(Ptg[] parsedExpression) { | |||
return toFormulaString(parsedExpression, workbook); | |||
} | |||
protected static String toFormulaString(Ptg[] parsedExpression, HSSFWorkbook workbook) { | |||
if(parsedExpression == null || parsedExpression.length == 0) { | |||
return null; | |||
} | |||
return HSSFFormulaParser.toFormulaString(workbook, parsedExpression); | |||
} | |||
} |
@@ -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.usermodel; | |||
import static org.apache.poi.hssf.record.CFRuleBase.parseFormula; | |||
import static org.apache.poi.hssf.usermodel.HSSFConditionalFormattingRule.toFormulaString; | |||
import org.apache.poi.hssf.record.cf.Threshold; | |||
/** | |||
* High level representation for Icon / Multi-State / Databar / | |||
* Colour Scale change thresholds | |||
*/ | |||
public final class HSSFConditionalFormattingThreshold implements org.apache.poi.ss.usermodel.ConditionalFormattingThreshold { | |||
private final Threshold threshold; | |||
private final HSSFSheet sheet; | |||
private final HSSFWorkbook workbook; | |||
protected HSSFConditionalFormattingThreshold(Threshold threshold, HSSFSheet sheet) { | |||
this.threshold = threshold; | |||
this.sheet = sheet; | |||
this.workbook = sheet.getWorkbook(); | |||
} | |||
protected Threshold getThreshold() { | |||
return threshold; | |||
} | |||
public RangeType getRangeType() { | |||
return RangeType.byId(threshold.getType()); | |||
} | |||
public void setRangeType(RangeType type) { | |||
threshold.setType((byte)type.id); | |||
} | |||
public String getFormula() { | |||
return toFormulaString(threshold.getParsedExpression(), workbook); | |||
} | |||
public void setFormula(String formula) { | |||
threshold.setParsedExpression(parseFormula(formula, sheet)); | |||
} | |||
public Double getValue() { | |||
return threshold.getValue(); | |||
} | |||
public void setValue(Double value) { | |||
threshold.setValue(value); | |||
} | |||
} |
@@ -17,382 +17,388 @@ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.hssf.record.CFRuleRecord; | |||
import org.apache.poi.hssf.record.CFRuleBase; | |||
import org.apache.poi.hssf.record.cf.FontFormatting; | |||
import org.apache.poi.hssf.util.HSSFColor; | |||
import org.apache.poi.ss.usermodel.Color; | |||
/** | |||
* High level representation for Font Formatting component | |||
* of Conditional Formatting settings | |||
*/ | |||
public final class HSSFFontFormatting implements org.apache.poi.ss.usermodel.FontFormatting | |||
{ | |||
/** Underline type - None */ | |||
public final static byte U_NONE = FontFormatting.U_NONE; | |||
/** Underline type - Single */ | |||
public final static byte U_SINGLE = FontFormatting.U_SINGLE; | |||
/** Underline type - Double */ | |||
public final static byte U_DOUBLE = FontFormatting.U_DOUBLE; | |||
/** Underline type - Single Accounting */ | |||
public final static byte U_SINGLE_ACCOUNTING = FontFormatting.U_SINGLE_ACCOUNTING; | |||
/** Underline type - Double Accounting */ | |||
public final static byte U_DOUBLE_ACCOUNTING = FontFormatting.U_DOUBLE_ACCOUNTING; | |||
private final FontFormatting fontFormatting; | |||
protected HSSFFontFormatting(CFRuleRecord cfRuleRecord) | |||
{ | |||
this.fontFormatting = cfRuleRecord.getFontFormatting(); | |||
} | |||
protected FontFormatting getFontFormattingBlock() | |||
{ | |||
return fontFormatting; | |||
} | |||
/** | |||
* get the type of super or subscript for the font | |||
* | |||
* @return super or subscript option | |||
* @see #SS_NONE | |||
* @see #SS_SUPER | |||
* @see #SS_SUB | |||
*/ | |||
public short getEscapementType() | |||
{ | |||
return fontFormatting.getEscapementType(); | |||
} | |||
/** | |||
* @return font color index | |||
*/ | |||
public short getFontColorIndex() | |||
{ | |||
return fontFormatting.getFontColorIndex(); | |||
} | |||
/** | |||
* gets the height of the font in 1/20th point units | |||
* | |||
* @return fontheight (in points/20); or -1 if not modified | |||
*/ | |||
public int getFontHeight() | |||
{ | |||
return fontFormatting.getFontHeight(); | |||
} | |||
/** | |||
* get the font weight for this font (100-1000dec or 0x64-0x3e8). Default is | |||
* 0x190 for normal and 0x2bc for bold | |||
* | |||
* @return bw - a number between 100-1000 for the fonts "boldness" | |||
*/ | |||
public short getFontWeight() | |||
{ | |||
return fontFormatting.getFontWeight(); | |||
} | |||
/** | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#getRawRecord() | |||
*/ | |||
protected byte[] getRawRecord() | |||
{ | |||
return fontFormatting.getRawRecord(); | |||
} | |||
/** | |||
* get the type of underlining for the font | |||
* | |||
* @return font underlining type | |||
* | |||
* @see #U_NONE | |||
* @see #U_SINGLE | |||
* @see #U_DOUBLE | |||
* @see #U_SINGLE_ACCOUNTING | |||
* @see #U_DOUBLE_ACCOUNTING | |||
*/ | |||
public short getUnderlineType() | |||
{ | |||
return fontFormatting.getUnderlineType(); | |||
} | |||
/** | |||
* get whether the font weight is set to bold or not | |||
* | |||
* @return bold - whether the font is bold or not | |||
*/ | |||
public boolean isBold() | |||
{ | |||
return fontFormatting.isFontWeightModified() && fontFormatting.isBold(); | |||
} | |||
/** | |||
* @return true if escapement type was modified from default | |||
*/ | |||
public boolean isEscapementTypeModified() | |||
{ | |||
return fontFormatting.isEscapementTypeModified(); | |||
} | |||
/** | |||
* @return true if font cancellation was modified from default | |||
*/ | |||
public boolean isFontCancellationModified() | |||
{ | |||
return fontFormatting.isFontCancellationModified(); | |||
} | |||
/** | |||
* @return true if font outline type was modified from default | |||
*/ | |||
public boolean isFontOutlineModified() | |||
{ | |||
return fontFormatting.isFontOutlineModified(); | |||
} | |||
/** | |||
* @return true if font shadow type was modified from default | |||
*/ | |||
public boolean isFontShadowModified() | |||
{ | |||
return fontFormatting.isFontShadowModified(); | |||
} | |||
/** | |||
* @return true if font style was modified from default | |||
*/ | |||
public boolean isFontStyleModified() | |||
{ | |||
return fontFormatting.isFontStyleModified(); | |||
} | |||
/** | |||
* @return true if font style was set to <i>italic</i> | |||
*/ | |||
public boolean isItalic() | |||
{ | |||
return fontFormatting.isFontStyleModified() && fontFormatting.isItalic(); | |||
} | |||
/** | |||
* @return true if font outline is on | |||
*/ | |||
public boolean isOutlineOn() | |||
{ | |||
return fontFormatting.isFontOutlineModified() && fontFormatting.isOutlineOn(); | |||
} | |||
/** | |||
* @return true if font shadow is on | |||
*/ | |||
public boolean isShadowOn() | |||
{ | |||
return fontFormatting.isFontOutlineModified() && fontFormatting.isShadowOn(); | |||
} | |||
/** | |||
* @return true if font strikeout is on | |||
*/ | |||
public boolean isStruckout() | |||
{ | |||
return fontFormatting.isFontCancellationModified() && fontFormatting.isStruckout(); | |||
} | |||
/** | |||
* @return true if font underline type was modified from default | |||
*/ | |||
public boolean isUnderlineTypeModified() | |||
{ | |||
return fontFormatting.isUnderlineTypeModified(); | |||
} | |||
/** | |||
* @return true if font weight was modified from default | |||
*/ | |||
public boolean isFontWeightModified() | |||
{ | |||
return fontFormatting.isFontWeightModified(); | |||
} | |||
/** | |||
* set font style options. | |||
* | |||
* @param italic - if true, set posture style to italic, otherwise to normal | |||
* @param bold if true, set font weight to bold, otherwise to normal | |||
*/ | |||
public void setFontStyle(boolean italic, boolean bold) | |||
{ | |||
boolean modified = italic || bold; | |||
fontFormatting.setItalic(italic); | |||
fontFormatting.setBold(bold); | |||
fontFormatting.setFontStyleModified(modified); | |||
fontFormatting.setFontWieghtModified(modified); | |||
} | |||
/** | |||
* set font style options to default values (non-italic, non-bold) | |||
*/ | |||
public void resetFontStyle() | |||
{ | |||
setFontStyle(false,false); | |||
} | |||
/** | |||
* set the escapement type for the font | |||
* | |||
* @param escapementType super or subscript option | |||
* @see #SS_NONE | |||
* @see #SS_SUPER | |||
* @see #SS_SUB | |||
*/ | |||
public void setEscapementType(short escapementType) | |||
{ | |||
switch(escapementType) | |||
{ | |||
case HSSFFontFormatting.SS_SUB: | |||
case HSSFFontFormatting.SS_SUPER: | |||
fontFormatting.setEscapementType(escapementType); | |||
fontFormatting.setEscapementTypeModified(true); | |||
break; | |||
case HSSFFontFormatting.SS_NONE: | |||
fontFormatting.setEscapementType(escapementType); | |||
fontFormatting.setEscapementTypeModified(false); | |||
break; | |||
default: | |||
} | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setEscapementTypeModified(boolean) | |||
*/ | |||
public void setEscapementTypeModified(boolean modified) | |||
{ | |||
fontFormatting.setEscapementTypeModified(modified); | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontCancellationModified(boolean) | |||
*/ | |||
public void setFontCancellationModified(boolean modified) | |||
{ | |||
fontFormatting.setFontCancellationModified(modified); | |||
} | |||
/** | |||
* @param fci | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontColorIndex(short) | |||
*/ | |||
public void setFontColorIndex(short fci) | |||
{ | |||
fontFormatting.setFontColorIndex(fci); | |||
} | |||
/** | |||
* @param height | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontHeight(int) | |||
*/ | |||
public void setFontHeight(int height) | |||
{ | |||
fontFormatting.setFontHeight(height); | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontOutlineModified(boolean) | |||
*/ | |||
public void setFontOutlineModified(boolean modified) | |||
{ | |||
fontFormatting.setFontOutlineModified(modified); | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontShadowModified(boolean) | |||
*/ | |||
public void setFontShadowModified(boolean modified) | |||
{ | |||
fontFormatting.setFontShadowModified(modified); | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontStyleModified(boolean) | |||
*/ | |||
public void setFontStyleModified(boolean modified) | |||
{ | |||
fontFormatting.setFontStyleModified(modified); | |||
} | |||
/** | |||
* @param on | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setOutline(boolean) | |||
*/ | |||
public void setOutline(boolean on) | |||
{ | |||
fontFormatting.setOutline(on); | |||
fontFormatting.setFontOutlineModified(on); | |||
} | |||
/** | |||
* @param on | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setShadow(boolean) | |||
*/ | |||
public void setShadow(boolean on) | |||
{ | |||
fontFormatting.setShadow(on); | |||
fontFormatting.setFontShadowModified(on); | |||
} | |||
/** | |||
* @param strike | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setStrikeout(boolean) | |||
*/ | |||
public void setStrikeout(boolean strike) | |||
{ | |||
fontFormatting.setStrikeout(strike); | |||
fontFormatting.setFontCancellationModified(strike); | |||
} | |||
/** | |||
* set the type of underlining type for the font | |||
* | |||
* @param underlineType super or subscript option | |||
* | |||
* @see #U_NONE | |||
* @see #U_SINGLE | |||
* @see #U_DOUBLE | |||
* @see #U_SINGLE_ACCOUNTING | |||
* @see #U_DOUBLE_ACCOUNTING | |||
*/ | |||
public void setUnderlineType(short underlineType) | |||
{ | |||
switch(underlineType) | |||
{ | |||
case HSSFFontFormatting.U_SINGLE: | |||
case HSSFFontFormatting.U_DOUBLE: | |||
case HSSFFontFormatting.U_SINGLE_ACCOUNTING: | |||
case HSSFFontFormatting.U_DOUBLE_ACCOUNTING: | |||
fontFormatting.setUnderlineType(underlineType); | |||
setUnderlineTypeModified(true); | |||
break; | |||
case HSSFFontFormatting.U_NONE: | |||
fontFormatting.setUnderlineType(underlineType); | |||
setUnderlineTypeModified(false); | |||
break; | |||
default: | |||
} | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setUnderlineTypeModified(boolean) | |||
*/ | |||
public void setUnderlineTypeModified(boolean modified) | |||
{ | |||
fontFormatting.setUnderlineTypeModified(modified); | |||
} | |||
public final class HSSFFontFormatting implements org.apache.poi.ss.usermodel.FontFormatting { | |||
/** Underline type - None */ | |||
public final static byte U_NONE = FontFormatting.U_NONE; | |||
/** Underline type - Single */ | |||
public final static byte U_SINGLE = FontFormatting.U_SINGLE; | |||
/** Underline type - Double */ | |||
public final static byte U_DOUBLE = FontFormatting.U_DOUBLE; | |||
/** Underline type - Single Accounting */ | |||
public final static byte U_SINGLE_ACCOUNTING = FontFormatting.U_SINGLE_ACCOUNTING; | |||
/** Underline type - Double Accounting */ | |||
public final static byte U_DOUBLE_ACCOUNTING = FontFormatting.U_DOUBLE_ACCOUNTING; | |||
private final FontFormatting fontFormatting; | |||
private final HSSFWorkbook workbook; | |||
protected HSSFFontFormatting(CFRuleBase cfRuleRecord, HSSFWorkbook workbook) { | |||
this.fontFormatting = cfRuleRecord.getFontFormatting(); | |||
this.workbook = workbook; | |||
} | |||
protected FontFormatting getFontFormattingBlock() { | |||
return fontFormatting; | |||
} | |||
/** | |||
* get the type of super or subscript for the font | |||
* | |||
* @return super or subscript option | |||
* @see #SS_NONE | |||
* @see #SS_SUPER | |||
* @see #SS_SUB | |||
*/ | |||
public short getEscapementType() | |||
{ | |||
return fontFormatting.getEscapementType(); | |||
} | |||
/** | |||
* @return font color index | |||
*/ | |||
public short getFontColorIndex() | |||
{ | |||
return fontFormatting.getFontColorIndex(); | |||
} | |||
public HSSFColor getFontColor() { | |||
return workbook.getCustomPalette().getColor( | |||
getFontColorIndex() | |||
); | |||
} | |||
public void setFontColor(Color color) { | |||
HSSFColor hcolor = HSSFColor.toHSSFColor(color); | |||
if (hcolor == null) { | |||
fontFormatting.setFontColorIndex((short)0); | |||
} else { | |||
fontFormatting.setFontColorIndex(hcolor.getIndex()); | |||
} | |||
} | |||
/** | |||
* gets the height of the font in 1/20th point units | |||
* | |||
* @return fontheight (in points/20); or -1 if not modified | |||
*/ | |||
public int getFontHeight() { | |||
return fontFormatting.getFontHeight(); | |||
} | |||
/** | |||
* get the font weight for this font (100-1000dec or 0x64-0x3e8). Default is | |||
* 0x190 for normal and 0x2bc for bold | |||
* | |||
* @return bw - a number between 100-1000 for the fonts "boldness" | |||
*/ | |||
public short getFontWeight() { | |||
return fontFormatting.getFontWeight(); | |||
} | |||
/** | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#getRawRecord() | |||
*/ | |||
protected byte[] getRawRecord() { | |||
return fontFormatting.getRawRecord(); | |||
} | |||
/** | |||
* get the type of underlining for the font | |||
* | |||
* @return font underlining type | |||
* | |||
* @see #U_NONE | |||
* @see #U_SINGLE | |||
* @see #U_DOUBLE | |||
* @see #U_SINGLE_ACCOUNTING | |||
* @see #U_DOUBLE_ACCOUNTING | |||
*/ | |||
public short getUnderlineType() | |||
{ | |||
return fontFormatting.getUnderlineType(); | |||
} | |||
/** | |||
* get whether the font weight is set to bold or not | |||
* | |||
* @return bold - whether the font is bold or not | |||
*/ | |||
public boolean isBold() | |||
{ | |||
return fontFormatting.isFontWeightModified() && fontFormatting.isBold(); | |||
} | |||
/** | |||
* @return true if escapement type was modified from default | |||
*/ | |||
public boolean isEscapementTypeModified() | |||
{ | |||
return fontFormatting.isEscapementTypeModified(); | |||
} | |||
/** | |||
* @return true if font cancellation was modified from default | |||
*/ | |||
public boolean isFontCancellationModified() | |||
{ | |||
return fontFormatting.isFontCancellationModified(); | |||
} | |||
/** | |||
* @return true if font outline type was modified from default | |||
*/ | |||
public boolean isFontOutlineModified() | |||
{ | |||
return fontFormatting.isFontOutlineModified(); | |||
} | |||
/** | |||
* @return true if font shadow type was modified from default | |||
*/ | |||
public boolean isFontShadowModified() | |||
{ | |||
return fontFormatting.isFontShadowModified(); | |||
} | |||
/** | |||
* @return true if font style was modified from default | |||
*/ | |||
public boolean isFontStyleModified() | |||
{ | |||
return fontFormatting.isFontStyleModified(); | |||
} | |||
/** | |||
* @return true if font style was set to <i>italic</i> | |||
*/ | |||
public boolean isItalic() | |||
{ | |||
return fontFormatting.isFontStyleModified() && fontFormatting.isItalic(); | |||
} | |||
/** | |||
* @return true if font outline is on | |||
*/ | |||
public boolean isOutlineOn() | |||
{ | |||
return fontFormatting.isFontOutlineModified() && fontFormatting.isOutlineOn(); | |||
} | |||
/** | |||
* @return true if font shadow is on | |||
*/ | |||
public boolean isShadowOn() | |||
{ | |||
return fontFormatting.isFontOutlineModified() && fontFormatting.isShadowOn(); | |||
} | |||
/** | |||
* @return true if font strikeout is on | |||
*/ | |||
public boolean isStruckout() | |||
{ | |||
return fontFormatting.isFontCancellationModified() && fontFormatting.isStruckout(); | |||
} | |||
/** | |||
* @return true if font underline type was modified from default | |||
*/ | |||
public boolean isUnderlineTypeModified() | |||
{ | |||
return fontFormatting.isUnderlineTypeModified(); | |||
} | |||
/** | |||
* @return true if font weight was modified from default | |||
*/ | |||
public boolean isFontWeightModified() | |||
{ | |||
return fontFormatting.isFontWeightModified(); | |||
} | |||
/** | |||
* set font style options. | |||
* | |||
* @param italic - if true, set posture style to italic, otherwise to normal | |||
* @param bold if true, set font weight to bold, otherwise to normal | |||
*/ | |||
public void setFontStyle(boolean italic, boolean bold) | |||
{ | |||
boolean modified = italic || bold; | |||
fontFormatting.setItalic(italic); | |||
fontFormatting.setBold(bold); | |||
fontFormatting.setFontStyleModified(modified); | |||
fontFormatting.setFontWieghtModified(modified); | |||
} | |||
/** | |||
* set font style options to default values (non-italic, non-bold) | |||
*/ | |||
public void resetFontStyle() | |||
{ | |||
setFontStyle(false,false); | |||
} | |||
/** | |||
* set the escapement type for the font | |||
* | |||
* @param escapementType super or subscript option | |||
* @see #SS_NONE | |||
* @see #SS_SUPER | |||
* @see #SS_SUB | |||
*/ | |||
public void setEscapementType(short escapementType) { | |||
switch(escapementType) { | |||
case HSSFFontFormatting.SS_SUB: | |||
case HSSFFontFormatting.SS_SUPER: | |||
fontFormatting.setEscapementType(escapementType); | |||
fontFormatting.setEscapementTypeModified(true); | |||
break; | |||
case HSSFFontFormatting.SS_NONE: | |||
fontFormatting.setEscapementType(escapementType); | |||
fontFormatting.setEscapementTypeModified(false); | |||
break; | |||
default: | |||
} | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setEscapementTypeModified(boolean) | |||
*/ | |||
public void setEscapementTypeModified(boolean modified) { | |||
fontFormatting.setEscapementTypeModified(modified); | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontCancellationModified(boolean) | |||
*/ | |||
public void setFontCancellationModified(boolean modified) | |||
{ | |||
fontFormatting.setFontCancellationModified(modified); | |||
} | |||
/** | |||
* @param fci | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontColorIndex(short) | |||
*/ | |||
public void setFontColorIndex(short fci) | |||
{ | |||
fontFormatting.setFontColorIndex(fci); | |||
} | |||
/** | |||
* @param height | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontHeight(int) | |||
*/ | |||
public void setFontHeight(int height) | |||
{ | |||
fontFormatting.setFontHeight(height); | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontOutlineModified(boolean) | |||
*/ | |||
public void setFontOutlineModified(boolean modified) | |||
{ | |||
fontFormatting.setFontOutlineModified(modified); | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontShadowModified(boolean) | |||
*/ | |||
public void setFontShadowModified(boolean modified) | |||
{ | |||
fontFormatting.setFontShadowModified(modified); | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setFontStyleModified(boolean) | |||
*/ | |||
public void setFontStyleModified(boolean modified) | |||
{ | |||
fontFormatting.setFontStyleModified(modified); | |||
} | |||
/** | |||
* @param on | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setOutline(boolean) | |||
*/ | |||
public void setOutline(boolean on) | |||
{ | |||
fontFormatting.setOutline(on); | |||
fontFormatting.setFontOutlineModified(on); | |||
} | |||
/** | |||
* @param on | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setShadow(boolean) | |||
*/ | |||
public void setShadow(boolean on) | |||
{ | |||
fontFormatting.setShadow(on); | |||
fontFormatting.setFontShadowModified(on); | |||
} | |||
/** | |||
* @param strike | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setStrikeout(boolean) | |||
*/ | |||
public void setStrikeout(boolean strike) | |||
{ | |||
fontFormatting.setStrikeout(strike); | |||
fontFormatting.setFontCancellationModified(strike); | |||
} | |||
/** | |||
* set the type of underlining type for the font | |||
* | |||
* @param underlineType super or subscript option | |||
* | |||
* @see #U_NONE | |||
* @see #U_SINGLE | |||
* @see #U_DOUBLE | |||
* @see #U_SINGLE_ACCOUNTING | |||
* @see #U_DOUBLE_ACCOUNTING | |||
*/ | |||
public void setUnderlineType(short underlineType) { | |||
switch(underlineType) { | |||
case HSSFFontFormatting.U_SINGLE: | |||
case HSSFFontFormatting.U_DOUBLE: | |||
case HSSFFontFormatting.U_SINGLE_ACCOUNTING: | |||
case HSSFFontFormatting.U_DOUBLE_ACCOUNTING: | |||
fontFormatting.setUnderlineType(underlineType); | |||
setUnderlineTypeModified(true); | |||
break; | |||
case HSSFFontFormatting.U_NONE: | |||
fontFormatting.setUnderlineType(underlineType); | |||
setUnderlineTypeModified(false); | |||
break; | |||
default: | |||
} | |||
} | |||
/** | |||
* @param modified | |||
* @see org.apache.poi.hssf.record.cf.FontFormatting#setUnderlineTypeModified(boolean) | |||
*/ | |||
public void setUnderlineTypeModified(boolean modified) | |||
{ | |||
fontFormatting.setUnderlineTypeModified(modified); | |||
} | |||
} |
@@ -0,0 +1,81 @@ | |||
/* ==================================================================== | |||
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.usermodel; | |||
import org.apache.poi.hssf.record.CFRule12Record; | |||
import org.apache.poi.hssf.record.cf.IconMultiStateFormatting; | |||
import org.apache.poi.hssf.record.cf.Threshold; | |||
import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold; | |||
/** | |||
* High level representation for Icon / Multi-State Formatting | |||
* component of Conditional Formatting settings | |||
*/ | |||
public final class HSSFIconMultiStateFormatting implements org.apache.poi.ss.usermodel.IconMultiStateFormatting { | |||
private final HSSFSheet sheet; | |||
private final CFRule12Record cfRule12Record; | |||
private final IconMultiStateFormatting iconFormatting; | |||
protected HSSFIconMultiStateFormatting(CFRule12Record cfRule12Record, HSSFSheet sheet) { | |||
this.sheet = sheet; | |||
this.cfRule12Record = cfRule12Record; | |||
this.iconFormatting = this.cfRule12Record.getMultiStateFormatting(); | |||
} | |||
public IconSet getIconSet() { | |||
return iconFormatting.getIconSet(); | |||
} | |||
public void setIconSet(IconSet set) { | |||
iconFormatting.setIconSet(set); | |||
} | |||
public boolean isIconOnly() { | |||
return iconFormatting.isIconOnly(); | |||
} | |||
public void setIconOnly(boolean only) { | |||
iconFormatting.setIconOnly(only); | |||
} | |||
public boolean isReversed() { | |||
return iconFormatting.isReversed(); | |||
} | |||
public void setReversed(boolean reversed) { | |||
iconFormatting.setReversed(reversed); | |||
} | |||
public HSSFConditionalFormattingThreshold[] getThresholds() { | |||
Threshold[] t = iconFormatting.getThresholds(); | |||
HSSFConditionalFormattingThreshold[] ht = new HSSFConditionalFormattingThreshold[t.length]; | |||
for (int i=0; i<t.length; i++) { | |||
ht[i] = new HSSFConditionalFormattingThreshold(t[i], sheet); | |||
} | |||
return ht; | |||
} | |||
public void setThresholds(ConditionalFormattingThreshold[] thresholds) { | |||
Threshold[] t = new Threshold[thresholds.length]; | |||
for (int i=0; i<t.length; i++) { | |||
t[i] = ((HSSFConditionalFormattingThreshold)thresholds[i]).getThreshold(); | |||
} | |||
iconFormatting.setThresholds(t); | |||
} | |||
public HSSFConditionalFormattingThreshold createThreshold() { | |||
return new HSSFConditionalFormattingThreshold(new Threshold(), sheet); | |||
} | |||
} |
@@ -45,7 +45,6 @@ import org.apache.poi.hssf.record.ObjRecord; | |||
import org.apache.poi.hssf.util.CellReference; | |||
import org.apache.poi.poifs.filesystem.DirectoryEntry; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.apache.poi.ss.usermodel.Chart; | |||
import org.apache.poi.ss.usermodel.ClientAnchor; | |||
import org.apache.poi.ss.usermodel.Drawing; |
@@ -17,88 +17,116 @@ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.hssf.record.CFRuleRecord; | |||
import org.apache.poi.hssf.record.CFRuleBase; | |||
import org.apache.poi.hssf.record.cf.PatternFormatting; | |||
import org.apache.poi.hssf.util.HSSFColor; | |||
import org.apache.poi.ss.usermodel.Color; | |||
/** | |||
* High level representation for Conditional Formatting settings | |||
*/ | |||
public class HSSFPatternFormatting implements org.apache.poi.ss.usermodel.PatternFormatting | |||
{ | |||
private final CFRuleRecord cfRuleRecord; | |||
private final PatternFormatting patternFormatting; | |||
protected HSSFPatternFormatting(CFRuleRecord cfRuleRecord) | |||
{ | |||
this.cfRuleRecord = cfRuleRecord; | |||
this.patternFormatting = cfRuleRecord.getPatternFormatting(); | |||
} | |||
protected PatternFormatting getPatternFormattingBlock() | |||
{ | |||
return patternFormatting; | |||
} | |||
/** | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#getFillBackgroundColor() | |||
*/ | |||
public short getFillBackgroundColor() | |||
{ | |||
return (short)patternFormatting.getFillBackgroundColor(); | |||
} | |||
/** | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#getFillForegroundColor() | |||
*/ | |||
public short getFillForegroundColor() | |||
{ | |||
return (short)patternFormatting.getFillForegroundColor(); | |||
} | |||
/** | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#getFillPattern() | |||
*/ | |||
public short getFillPattern() | |||
{ | |||
return (short)patternFormatting.getFillPattern(); | |||
} | |||
/** | |||
* @param bg | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#setFillBackgroundColor(int) | |||
*/ | |||
public void setFillBackgroundColor(short bg) | |||
{ | |||
patternFormatting.setFillBackgroundColor(bg); | |||
if( bg != 0) | |||
{ | |||
cfRuleRecord.setPatternBackgroundColorModified(true); | |||
} | |||
} | |||
/** | |||
* @param fg | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#setFillForegroundColor(int) | |||
*/ | |||
public void setFillForegroundColor(short fg) | |||
{ | |||
patternFormatting.setFillForegroundColor(fg); | |||
if( fg != 0) | |||
{ | |||
cfRuleRecord.setPatternColorModified(true); | |||
} | |||
} | |||
/** | |||
* @param fp | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#setFillPattern(int) | |||
*/ | |||
public void setFillPattern(short fp) | |||
{ | |||
patternFormatting.setFillPattern(fp); | |||
if( fp != 0) | |||
{ | |||
cfRuleRecord.setPatternStyleModified(true); | |||
} | |||
} | |||
public class HSSFPatternFormatting implements org.apache.poi.ss.usermodel.PatternFormatting { | |||
private final HSSFWorkbook workbook; | |||
private final CFRuleBase cfRuleRecord; | |||
private final PatternFormatting patternFormatting; | |||
protected HSSFPatternFormatting(CFRuleBase cfRuleRecord, HSSFWorkbook workbook) { | |||
this.workbook = workbook; | |||
this.cfRuleRecord = cfRuleRecord; | |||
this.patternFormatting = cfRuleRecord.getPatternFormatting(); | |||
} | |||
protected PatternFormatting getPatternFormattingBlock() | |||
{ | |||
return patternFormatting; | |||
} | |||
public HSSFColor getFillBackgroundColorColor() { | |||
return workbook.getCustomPalette().getColor(getFillBackgroundColor()); | |||
} | |||
public HSSFColor getFillForegroundColorColor() { | |||
return workbook.getCustomPalette().getColor(getFillForegroundColor()); | |||
} | |||
/** | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#getFillBackgroundColor() | |||
*/ | |||
public short getFillBackgroundColor() | |||
{ | |||
return (short)patternFormatting.getFillBackgroundColor(); | |||
} | |||
/** | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#getFillForegroundColor() | |||
*/ | |||
public short getFillForegroundColor() | |||
{ | |||
return (short)patternFormatting.getFillForegroundColor(); | |||
} | |||
/** | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#getFillPattern() | |||
*/ | |||
public short getFillPattern() | |||
{ | |||
return (short)patternFormatting.getFillPattern(); | |||
} | |||
public void setFillBackgroundColor(Color bg) { | |||
HSSFColor hcolor = HSSFColor.toHSSFColor(bg); | |||
if (hcolor == null) { | |||
setFillBackgroundColor((short)0); | |||
} else { | |||
setFillBackgroundColor(hcolor.getIndex()); | |||
} | |||
} | |||
public void setFillForegroundColor(Color fg) { | |||
HSSFColor hcolor = HSSFColor.toHSSFColor(fg); | |||
if (hcolor == null) { | |||
setFillForegroundColor((short)0); | |||
} else { | |||
setFillForegroundColor(hcolor.getIndex()); | |||
} | |||
} | |||
/** | |||
* @param bg | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#setFillBackgroundColor(int) | |||
*/ | |||
public void setFillBackgroundColor(short bg) | |||
{ | |||
patternFormatting.setFillBackgroundColor(bg); | |||
if( bg != 0) | |||
{ | |||
cfRuleRecord.setPatternBackgroundColorModified(true); | |||
} | |||
} | |||
/** | |||
* @param fg | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#setFillForegroundColor(int) | |||
*/ | |||
public void setFillForegroundColor(short fg) | |||
{ | |||
patternFormatting.setFillForegroundColor(fg); | |||
if( fg != 0) | |||
{ | |||
cfRuleRecord.setPatternColorModified(true); | |||
} | |||
} | |||
/** | |||
* @param fp | |||
* @see org.apache.poi.hssf.record.cf.PatternFormatting#setFillPattern(int) | |||
*/ | |||
public void setFillPattern(short fp) | |||
{ | |||
patternFormatting.setFillPattern(fp); | |||
if( fp != 0) | |||
{ | |||
cfRuleRecord.setPatternStyleModified(true); | |||
} | |||
} | |||
} |
@@ -29,13 +29,6 @@ import java.util.Map; | |||
* Factory class for producing Excel Shapes from Escher records | |||
*/ | |||
public class HSSFShapeFactory { | |||
private final static short OBJECT_TYPE_LINE = 1; | |||
private final static short OBJECT_TYPE_RECTANGLE = 2; | |||
private final static short OBJECT_TYPE_OVAL = 3; | |||
private final static short OBJECT_TYPE_ARC = 4; | |||
private final static short OBJECT_TYPE_PICTURE = 8; | |||
/** | |||
* build shape tree from escher container | |||
* @param container root escher container from which escher records must be taken | |||
@@ -81,7 +74,7 @@ public class HSSFShapeFactory { | |||
return; | |||
} | |||
CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord) objRecord.getSubRecords().get(0); | |||
HSSFShape shape; | |||
final HSSFShape shape; | |||
switch (cmo.getObjectType()) { | |||
case CommonObjectDataSubRecord.OBJECT_TYPE_PICTURE: | |||
shape = new HSSFPicture(container, objRecord); | |||
@@ -97,11 +90,15 @@ public class HSSFShapeFactory { | |||
break; | |||
case CommonObjectDataSubRecord.OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING: | |||
EscherOptRecord optRecord = container.getChildById(EscherOptRecord.RECORD_ID); | |||
EscherProperty property = optRecord.lookup(EscherProperties.GEOMETRY__VERTICES); | |||
if (null != property) { | |||
shape = new HSSFPolygon(container, objRecord, txtRecord); | |||
if(optRecord == null) { | |||
shape = new HSSFSimpleShape(container, objRecord, txtRecord); | |||
} else { | |||
shape = new HSSFSimpleShape(container, objRecord, txtRecord); | |||
EscherProperty property = optRecord.lookup(EscherProperties.GEOMETRY__VERTICES); | |||
if (null != property) { | |||
shape = new HSSFPolygon(container, objRecord, txtRecord); | |||
} else { | |||
shape = new HSSFSimpleShape(container, objRecord, txtRecord); | |||
} | |||
} | |||
break; | |||
case CommonObjectDataSubRecord.OBJECT_TYPE_TEXT: |
@@ -867,6 +867,17 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { | |||
return _sheet.getMergedRegionAt(index); | |||
} | |||
/** | |||
* @return the list of merged regions | |||
*/ | |||
public List<CellRangeAddress> getMergedRegions() { | |||
List<CellRangeAddress> addresses = new ArrayList<CellRangeAddress>(); | |||
for (int i=0; i < _sheet.getNumMergedRegions(); i++) { | |||
addresses.add(_sheet.getMergedRegionAt(i)); | |||
} | |||
return addresses; | |||
} | |||
/** | |||
* @return an iterator of the PHYSICAL rows. Meaning the 3rd element may not | |||
* be the third row if say for instance the second row is undefined. |
@@ -17,142 +17,156 @@ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.hssf.record.CFRule12Record; | |||
import org.apache.poi.hssf.record.CFRuleBase; | |||
import org.apache.poi.hssf.record.CFRuleRecord; | |||
import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate; | |||
import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable; | |||
import org.apache.poi.ss.SpreadsheetVersion; | |||
import org.apache.poi.ss.usermodel.ConditionalFormatting; | |||
import org.apache.poi.ss.usermodel.ConditionalFormattingRule; | |||
import org.apache.poi.ss.usermodel.IconMultiStateFormatting.IconSet; | |||
import org.apache.poi.ss.usermodel.SheetConditionalFormatting; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.ss.SpreadsheetVersion; | |||
/** | |||
* The 'Conditional Formatting' facet of <tt>HSSFSheet</tt> | |||
*/ | |||
public final class HSSFSheetConditionalFormatting implements SheetConditionalFormatting { | |||
private final HSSFSheet _sheet; | |||
private final ConditionalFormattingTable _conditionalFormattingTable; | |||
private final HSSFSheet _sheet; | |||
private final ConditionalFormattingTable _conditionalFormattingTable; | |||
/* package */ HSSFSheetConditionalFormatting(HSSFSheet sheet) { | |||
_sheet = sheet; | |||
_conditionalFormattingTable = sheet.getSheet().getConditionalFormattingTable(); | |||
} | |||
/** | |||
* A factory method allowing to create a conditional formatting rule | |||
* with a cell comparison operator<p/> | |||
* TODO - formulas containing cell references are currently not parsed properly | |||
* | |||
* @param comparisonOperation - a constant value from | |||
* <tt>{@link org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator}</tt>: <p> | |||
* <ul> | |||
* <li>BETWEEN</li> | |||
* <li>NOT_BETWEEN</li> | |||
* <li>EQUAL</li> | |||
* <li>NOT_EQUAL</li> | |||
* <li>GT</li> | |||
* <li>LT</li> | |||
* <li>GE</li> | |||
* <li>LE</li> | |||
* </ul> | |||
* </p> | |||
* @param formula1 - formula for the valued, compared with the cell | |||
* @param formula2 - second formula (only used with | |||
* {@link org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator#BETWEEN}) and | |||
* {@link org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator#NOT_BETWEEN} operations) | |||
*/ | |||
public HSSFConditionalFormattingRule createConditionalFormattingRule( | |||
byte comparisonOperation, | |||
String formula1, | |||
String formula2) { | |||
HSSFWorkbook wb = _sheet.getWorkbook(); | |||
CFRuleRecord rr = CFRuleRecord.create(_sheet, comparisonOperation, formula1, formula2); | |||
return new HSSFConditionalFormattingRule(wb, rr); | |||
} | |||
/* package */ HSSFSheetConditionalFormatting(HSSFSheet sheet) { | |||
_sheet = sheet; | |||
_conditionalFormattingTable = sheet.getSheet().getConditionalFormattingTable(); | |||
} | |||
/** | |||
* A factory method allowing to create a conditional formatting rule | |||
* with a cell comparison operator<p/> | |||
* TODO - formulas containing cell references are currently not parsed properly | |||
* | |||
* @param comparisonOperation - a constant value from | |||
* <tt>{@link org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator}</tt>: <p> | |||
* <ul> | |||
* <li>BETWEEN</li> | |||
* <li>NOT_BETWEEN</li> | |||
* <li>EQUAL</li> | |||
* <li>NOT_EQUAL</li> | |||
* <li>GT</li> | |||
* <li>LT</li> | |||
* <li>GE</li> | |||
* <li>LE</li> | |||
* </ul> | |||
* </p> | |||
* @param formula1 - formula for the valued, compared with the cell | |||
* @param formula2 - second formula (only used with | |||
* {@link org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator#BETWEEN}) and | |||
* {@link org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator#NOT_BETWEEN} operations) | |||
*/ | |||
public HSSFConditionalFormattingRule createConditionalFormattingRule( | |||
byte comparisonOperation, | |||
String formula1) { | |||
String formula1, | |||
String formula2) { | |||
CFRuleRecord rr = CFRuleRecord.create(_sheet, comparisonOperation, formula1, formula2); | |||
return new HSSFConditionalFormattingRule(_sheet, rr); | |||
} | |||
HSSFWorkbook wb = _sheet.getWorkbook(); | |||
public HSSFConditionalFormattingRule createConditionalFormattingRule( | |||
byte comparisonOperation, | |||
String formula1) { | |||
CFRuleRecord rr = CFRuleRecord.create(_sheet, comparisonOperation, formula1, null); | |||
return new HSSFConditionalFormattingRule(wb, rr); | |||
} | |||
/** | |||
* A factory method allowing to create a conditional formatting rule with a formula.<br> | |||
* | |||
* The formatting rules are applied by Excel when the value of the formula not equal to 0.<p/> | |||
* TODO - formulas containing cell references are currently not parsed properly | |||
* @param formula - formula for the valued, compared with the cell | |||
*/ | |||
public HSSFConditionalFormattingRule createConditionalFormattingRule(String formula) { | |||
HSSFWorkbook wb = _sheet.getWorkbook(); | |||
CFRuleRecord rr = CFRuleRecord.create(_sheet, formula); | |||
return new HSSFConditionalFormattingRule(wb, rr); | |||
} | |||
/** | |||
* Adds a copy of HSSFConditionalFormatting object to the sheet | |||
* <p>This method could be used to copy HSSFConditionalFormatting object | |||
* from one sheet to another. For example: | |||
* <pre> | |||
* HSSFConditionalFormatting cf = sheet.getConditionalFormattingAt(index); | |||
* newSheet.addConditionalFormatting(cf); | |||
* </pre> | |||
* | |||
* @param cf HSSFConditionalFormatting object | |||
* @return index of the new Conditional Formatting object | |||
*/ | |||
public int addConditionalFormatting( HSSFConditionalFormatting cf ) { | |||
CFRecordsAggregate cfraClone = cf.getCFRecordsAggregate().cloneCFAggregate(); | |||
return _conditionalFormattingTable.add(cfraClone); | |||
} | |||
return new HSSFConditionalFormattingRule(_sheet, rr); | |||
} | |||
/** | |||
* A factory method allowing to create a conditional formatting rule with a formula.<br> | |||
* | |||
* The formatting rules are applied by Excel when the value of the formula not equal to 0.<p/> | |||
* TODO - formulas containing cell references are currently not parsed properly | |||
* @param formula - formula for the valued, compared with the cell | |||
*/ | |||
public HSSFConditionalFormattingRule createConditionalFormattingRule(String formula) { | |||
CFRuleRecord rr = CFRuleRecord.create(_sheet, formula); | |||
return new HSSFConditionalFormattingRule(_sheet, rr); | |||
} | |||
/** | |||
* A factory method allowing the creation of conditional formatting | |||
* rules using an Icon Set / Multi-State formatting. | |||
* The thresholds for it will be created, but will be empty | |||
* and require configuring with | |||
* {@link HSSFConditionalFormattingRule#getMultiStateFormatting()} | |||
* then | |||
* {@link HSSFIconMultiStateFormatting#getThresholds()} | |||
*/ | |||
public HSSFConditionalFormattingRule createConditionalFormattingRule( | |||
IconSet iconSet) { | |||
CFRule12Record rr = CFRule12Record.create(_sheet, iconSet); | |||
return new HSSFConditionalFormattingRule(_sheet, rr); | |||
} | |||
// TODO Support types beyond CELL_VALUE_IS and FORMULA and ICONs | |||
/** | |||
* Adds a copy of HSSFConditionalFormatting object to the sheet | |||
* <p>This method could be used to copy HSSFConditionalFormatting object | |||
* from one sheet to another. For example: | |||
* <pre> | |||
* HSSFConditionalFormatting cf = sheet.getConditionalFormattingAt(index); | |||
* newSheet.addConditionalFormatting(cf); | |||
* </pre> | |||
* | |||
* @param cf HSSFConditionalFormatting object | |||
* @return index of the new Conditional Formatting object | |||
*/ | |||
public int addConditionalFormatting( HSSFConditionalFormatting cf ) { | |||
CFRecordsAggregate cfraClone = cf.getCFRecordsAggregate().cloneCFAggregate(); | |||
return _conditionalFormattingTable.add(cfraClone); | |||
} | |||
public int addConditionalFormatting( ConditionalFormatting cf ) { | |||
return addConditionalFormatting((HSSFConditionalFormatting)cf); | |||
} | |||
/** | |||
* @deprecated use <tt>CellRangeAddress</tt> instead of <tt>Region</tt> | |||
*/ | |||
public int addConditionalFormatting(org.apache.poi.ss.util.Region[] regions, HSSFConditionalFormattingRule[] cfRules) { | |||
return addConditionalFormatting(org.apache.poi.ss.util.Region.convertRegionsToCellRanges(regions), cfRules); | |||
} | |||
/** | |||
* Allows to add a new Conditional Formatting set to the sheet. | |||
* | |||
* @param regions - list of rectangular regions to apply conditional formatting rules | |||
* @param cfRules - set of up to three conditional formatting rules | |||
* | |||
* @return index of the newly created Conditional Formatting object | |||
*/ | |||
public int addConditionalFormatting(CellRangeAddress[] regions, HSSFConditionalFormattingRule[] cfRules) { | |||
if (regions == null) { | |||
throw new IllegalArgumentException("regions must not be null"); | |||
} | |||
for(CellRangeAddress range : regions) range.validate(SpreadsheetVersion.EXCEL97); | |||
if (cfRules == null) { | |||
throw new IllegalArgumentException("cfRules must not be null"); | |||
} | |||
if (cfRules.length == 0) { | |||
throw new IllegalArgumentException("cfRules must not be empty"); | |||
} | |||
if (cfRules.length > 3) { | |||
throw new IllegalArgumentException("Number of rules must not exceed 3"); | |||
} | |||
CFRuleRecord[] rules = new CFRuleRecord[cfRules.length]; | |||
for (int i = 0; i != cfRules.length; i++) { | |||
rules[i] = cfRules[i].getCfRuleRecord(); | |||
} | |||
CFRecordsAggregate cfra = new CFRecordsAggregate(regions, rules); | |||
return _conditionalFormattingTable.add(cfra); | |||
} | |||
/** | |||
* @deprecated use <tt>CellRangeAddress</tt> instead of <tt>Region</tt> | |||
*/ | |||
public int addConditionalFormatting(org.apache.poi.ss.util.Region[] regions, HSSFConditionalFormattingRule[] cfRules) { | |||
return addConditionalFormatting(org.apache.poi.ss.util.Region.convertRegionsToCellRanges(regions), cfRules); | |||
} | |||
/** | |||
* Allows to add a new Conditional Formatting set to the sheet. | |||
* | |||
* @param regions - list of rectangular regions to apply conditional formatting rules | |||
* @param cfRules - set of up to three conditional formatting rules | |||
* | |||
* @return index of the newly created Conditional Formatting object | |||
*/ | |||
public int addConditionalFormatting(CellRangeAddress[] regions, HSSFConditionalFormattingRule[] cfRules) { | |||
if (regions == null) { | |||
throw new IllegalArgumentException("regions must not be null"); | |||
} | |||
for(CellRangeAddress range : regions) range.validate(SpreadsheetVersion.EXCEL97); | |||
if (cfRules == null) { | |||
throw new IllegalArgumentException("cfRules must not be null"); | |||
} | |||
if (cfRules.length == 0) { | |||
throw new IllegalArgumentException("cfRules must not be empty"); | |||
} | |||
if (cfRules.length > 3) { | |||
throw new IllegalArgumentException("Number of rules must not exceed 3"); | |||
} | |||
CFRuleBase[] rules = new CFRuleBase[cfRules.length]; | |||
for (int i = 0; i != cfRules.length; i++) { | |||
rules[i] = cfRules[i].getCfRuleRecord(); | |||
} | |||
CFRecordsAggregate cfra = new CFRecordsAggregate(regions, rules); | |||
return _conditionalFormattingTable.add(cfra); | |||
} | |||
public int addConditionalFormatting(CellRangeAddress[] regions, ConditionalFormattingRule[] cfRules) { | |||
HSSFConditionalFormattingRule[] hfRules; | |||
@@ -164,70 +178,61 @@ public final class HSSFSheetConditionalFormatting implements SheetConditionalFor | |||
return addConditionalFormatting(regions, hfRules); | |||
} | |||
public int addConditionalFormatting(CellRangeAddress[] regions, | |||
HSSFConditionalFormattingRule rule1) | |||
{ | |||
return addConditionalFormatting(regions, | |||
rule1 == null ? null : new HSSFConditionalFormattingRule[] | |||
{ | |||
rule1 | |||
}); | |||
} | |||
public int addConditionalFormatting(CellRangeAddress[] regions, | |||
HSSFConditionalFormattingRule rule1) { | |||
return addConditionalFormatting(regions, rule1 == null ? | |||
null : new HSSFConditionalFormattingRule[] { rule1 } | |||
); | |||
} | |||
public int addConditionalFormatting(CellRangeAddress[] regions, | |||
ConditionalFormattingRule rule1) | |||
{ | |||
ConditionalFormattingRule rule1) { | |||
return addConditionalFormatting(regions, (HSSFConditionalFormattingRule)rule1); | |||
} | |||
public int addConditionalFormatting(CellRangeAddress[] regions, | |||
HSSFConditionalFormattingRule rule1, | |||
HSSFConditionalFormattingRule rule2) | |||
{ | |||
return addConditionalFormatting(regions, | |||
new HSSFConditionalFormattingRule[] | |||
{ | |||
rule1, rule2 | |||
}); | |||
} | |||
public int addConditionalFormatting(CellRangeAddress[] regions, | |||
HSSFConditionalFormattingRule rule1, | |||
HSSFConditionalFormattingRule rule2) { | |||
return addConditionalFormatting(regions, | |||
new HSSFConditionalFormattingRule[] { rule1, rule2 }); | |||
} | |||
public int addConditionalFormatting(CellRangeAddress[] regions, | |||
ConditionalFormattingRule rule1, | |||
ConditionalFormattingRule rule2) | |||
{ | |||
ConditionalFormattingRule rule2) { | |||
return addConditionalFormatting(regions, | |||
(HSSFConditionalFormattingRule)rule1, | |||
(HSSFConditionalFormattingRule)rule2 | |||
); | |||
} | |||
/** | |||
* gets Conditional Formatting object at a particular index | |||
* | |||
* @param index | |||
* of the Conditional Formatting object to fetch | |||
* @return Conditional Formatting object | |||
*/ | |||
public HSSFConditionalFormatting getConditionalFormattingAt(int index) { | |||
CFRecordsAggregate cf = _conditionalFormattingTable.get(index); | |||
if (cf == null) { | |||
return null; | |||
} | |||
return new HSSFConditionalFormatting(_sheet.getWorkbook(), cf); | |||
} | |||
/** | |||
* @return number of Conditional Formatting objects of the sheet | |||
*/ | |||
public int getNumConditionalFormattings() { | |||
return _conditionalFormattingTable.size(); | |||
} | |||
/** | |||
* removes a Conditional Formatting object by index | |||
* @param index of a Conditional Formatting object to remove | |||
*/ | |||
public void removeConditionalFormatting(int index) { | |||
_conditionalFormattingTable.remove(index); | |||
} | |||
); | |||
} | |||
/** | |||
* gets Conditional Formatting object at a particular index | |||
* | |||
* @param index | |||
* of the Conditional Formatting object to fetch | |||
* @return Conditional Formatting object | |||
*/ | |||
public HSSFConditionalFormatting getConditionalFormattingAt(int index) { | |||
CFRecordsAggregate cf = _conditionalFormattingTable.get(index); | |||
if (cf == null) { | |||
return null; | |||
} | |||
return new HSSFConditionalFormatting(_sheet, cf); | |||
} | |||
/** | |||
* @return number of Conditional Formatting objects of the sheet | |||
*/ | |||
public int getNumConditionalFormattings() { | |||
return _conditionalFormattingTable.size(); | |||
} | |||
/** | |||
* removes a Conditional Formatting object by index | |||
* @param index of a Conditional Formatting object to remove | |||
*/ | |||
public void removeConditionalFormatting(int index) { | |||
_conditionalFormattingTable.remove(index); | |||
} | |||
} |
@@ -65,6 +65,7 @@ import org.apache.poi.hssf.record.UnknownRecord; | |||
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; | |||
import org.apache.poi.hssf.record.common.UnicodeString; | |||
import org.apache.poi.hssf.util.CellReference; | |||
import org.apache.poi.poifs.crypt.Decryptor; | |||
import org.apache.poi.poifs.filesystem.DirectoryEntry; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
import org.apache.poi.poifs.filesystem.EntryUtils; | |||
@@ -205,6 +206,19 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss | |||
public HSSFWorkbook(POIFSFileSystem fs) throws IOException { | |||
this(fs,true); | |||
} | |||
/** | |||
* Given a POI POIFSFileSystem object, read in its Workbook along | |||
* with all related nodes, and populate the high and low level models. | |||
* <p>This calls {@link #HSSFWorkbook(POIFSFileSystem, boolean)} with | |||
* preserve nodes set to true. | |||
* | |||
* @see #HSSFWorkbook(POIFSFileSystem, boolean) | |||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem | |||
* @exception IOException if the stream cannot be read | |||
*/ | |||
public HSSFWorkbook(NPOIFSFileSystem fs) throws IOException { | |||
this(fs.getRoot(),true); | |||
} | |||
/** | |||
* Given a POI POIFSFileSystem object, read in its Workbook and populate | |||
@@ -248,7 +262,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss | |||
// check for an encrypted .xlsx file - they get OLE2 wrapped | |||
try { | |||
directory.getEntry("EncryptedPackage"); | |||
directory.getEntry(Decryptor.DEFAULT_POIFS_ENTRY); | |||
throw new EncryptedDocumentException("The supplied spreadsheet seems to be an Encrypted .xlsx file. " + | |||
"It must be decrypted before use by XSSF, it cannot be used by HSSF"); | |||
} catch (FileNotFoundException e) { | |||
@@ -378,7 +392,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss | |||
public HSSFWorkbook(InputStream s, boolean preserveNodes) | |||
throws IOException | |||
{ | |||
this(new POIFSFileSystem(s), preserveNodes); | |||
this(new NPOIFSFileSystem(s).getRoot(), preserveNodes); | |||
} | |||
/** | |||
@@ -1276,7 +1290,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss | |||
throws IOException | |||
{ | |||
byte[] bytes = getBytes(); | |||
POIFSFileSystem fs = new POIFSFileSystem(); | |||
NPOIFSFileSystem fs = new NPOIFSFileSystem(); | |||
// For tracking what we've written out, used if we're | |||
// going to be preserving nodes | |||
@@ -1843,7 +1857,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss | |||
throws IOException { | |||
// check if we were created by POIFS otherwise create a new dummy POIFS for storing the package data | |||
if (directory == null) { | |||
directory = new POIFSFileSystem().getRoot(); | |||
directory = new NPOIFSFileSystem().getRoot(); | |||
preserveNodes = true; | |||
} | |||
@@ -204,6 +204,13 @@ public class HSSFColor implements Color { | |||
{ | |||
return BLACK.hexString; | |||
} | |||
public static HSSFColor toHSSFColor(Color color) { | |||
if (color != null && !(color instanceof HSSFColor)) { | |||
throw new IllegalArgumentException("Only HSSFColor objects are supported"); | |||
} | |||
return (HSSFColor)color; | |||
} | |||
/** | |||
* Class BLACK |
@@ -16,6 +16,8 @@ | |||
==================================================================== */ | |||
package org.apache.poi.poifs.crypt; | |||
import static org.apache.poi.poifs.crypt.Decryptor.DEFAULT_POIFS_ENTRY; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileOutputStream; | |||
@@ -132,8 +134,8 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { | |||
super.close(); | |||
int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE); | |||
calculateChecksum(fileOut, oleStreamSize); | |||
dir.createDocument("EncryptedPackage", oleStreamSize, new EncryptedPackageWriter()); | |||
calculateChecksum(fileOut, (int)_pos); | |||
dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter()); | |||
createEncryptionInfoEntry(dir, fileOut); | |||
} catch (GeneralSecurityException e) { | |||
throw new IOException(e); |
@@ -36,7 +36,7 @@ public class DataSpaceMapUtils { | |||
public static void addDefaultDataSpace(DirectoryEntry dir) throws IOException { | |||
DataSpaceMapEntry dsme = new DataSpaceMapEntry( | |||
new int[]{ 0 } | |||
, new String[]{ "EncryptedPackage" } | |||
, new String[]{ Decryptor.DEFAULT_POIFS_ENTRY } | |||
, "StrongEncryptionDataSpace" | |||
); | |||
DataSpaceMap dsm = new DataSpaceMap(new DataSpaceMapEntry[]{dsme}); |
@@ -25,10 +25,12 @@ import javax.crypto.SecretKey; | |||
import org.apache.poi.EncryptedDocumentException; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.OPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
public abstract class Decryptor { | |||
public static final String DEFAULT_PASSWORD="VelvetSweatshop"; | |||
public static final String DEFAULT_POIFS_ENTRY="EncryptedPackage"; | |||
protected final EncryptionInfoBuilder builder; | |||
private SecretKey secretKey; | |||
@@ -83,7 +85,9 @@ public abstract class Decryptor { | |||
public InputStream getDataStream(NPOIFSFileSystem fs) throws IOException, GeneralSecurityException { | |||
return getDataStream(fs.getRoot()); | |||
} | |||
public InputStream getDataStream(OPOIFSFileSystem fs) throws IOException, GeneralSecurityException { | |||
return getDataStream(fs.getRoot()); | |||
} | |||
public InputStream getDataStream(POIFSFileSystem fs) throws IOException, GeneralSecurityException { | |||
return getDataStream(fs.getRoot()); | |||
} |
@@ -26,6 +26,7 @@ import java.io.IOException; | |||
import org.apache.poi.EncryptedDocumentException; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.OPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
@@ -68,14 +69,27 @@ public class EncryptionInfo { | |||
public static BitField flagAES = BitFieldFactory.getInstance(0x20); | |||
/** | |||
* Opens for decryption | |||
*/ | |||
public EncryptionInfo(POIFSFileSystem fs) throws IOException { | |||
this(fs.getRoot()); | |||
} | |||
/** | |||
* Opens for decryption | |||
*/ | |||
public EncryptionInfo(OPOIFSFileSystem fs) throws IOException { | |||
this(fs.getRoot()); | |||
} | |||
/** | |||
* Opens for decryption | |||
*/ | |||
public EncryptionInfo(NPOIFSFileSystem fs) throws IOException { | |||
this(fs.getRoot()); | |||
} | |||
/** | |||
* Opens for decryption | |||
*/ | |||
public EncryptionInfo(DirectoryNode dir) throws IOException { | |||
this(dir.createDocumentInputStream("EncryptionInfo"), false); | |||
} | |||
@@ -131,7 +145,7 @@ public class EncryptionInfo { | |||
} | |||
/** | |||
* @deprecated use constructor without fs parameter | |||
* @deprecated Use {@link #EncryptionInfo(EncryptionMode)} (fs parameter no longer required) | |||
*/ | |||
@Deprecated | |||
public EncryptionInfo(POIFSFileSystem fs, EncryptionMode encryptionMode) { | |||
@@ -139,7 +153,7 @@ public class EncryptionInfo { | |||
} | |||
/** | |||
* @deprecated use constructor without fs parameter | |||
* @deprecated Use {@link #EncryptionInfo(EncryptionMode)} (fs parameter no longer required) | |||
*/ | |||
@Deprecated | |||
public EncryptionInfo(NPOIFSFileSystem fs, EncryptionMode encryptionMode) { | |||
@@ -147,7 +161,7 @@ public class EncryptionInfo { | |||
} | |||
/** | |||
* @deprecated use constructor without dir parameter | |||
* @deprecated Use {@link #EncryptionInfo(EncryptionMode)} (dir parameter no longer required) | |||
*/ | |||
@Deprecated | |||
public EncryptionInfo(DirectoryNode dir, EncryptionMode encryptionMode) { | |||
@@ -155,7 +169,7 @@ public class EncryptionInfo { | |||
} | |||
/** | |||
* @deprecated use constructor without fs parameter | |||
* @deprecated use {@link #EncryptionInfo(EncryptionMode, CipherAlgorithm, HashAlgorithm, int, int, ChainingMode)} | |||
*/ | |||
@Deprecated | |||
public EncryptionInfo( | |||
@@ -171,7 +185,7 @@ public class EncryptionInfo { | |||
} | |||
/** | |||
* @deprecated use constructor without fs parameter | |||
* @deprecated use {@link #EncryptionInfo(EncryptionMode, CipherAlgorithm, HashAlgorithm, int, int, ChainingMode)} | |||
*/ | |||
@Deprecated | |||
public EncryptionInfo( | |||
@@ -187,7 +201,7 @@ public class EncryptionInfo { | |||
} | |||
/** | |||
* @deprecated use constructor without dir parameter | |||
* @deprecated use {@link #EncryptionInfo(EncryptionMode, CipherAlgorithm, HashAlgorithm, int, int, ChainingMode)} | |||
*/ | |||
@Deprecated | |||
public EncryptionInfo( | |||
@@ -202,6 +216,11 @@ public class EncryptionInfo { | |||
this(encryptionMode, cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode); | |||
} | |||
/** | |||
* Prepares for encryption, using the given Encryption Mode, and | |||
* all other parameters as default. | |||
* @see #EncryptionInfo(EncryptionMode, CipherAlgorithm, HashAlgorithm, int, int, ChainingMode) | |||
*/ | |||
public EncryptionInfo(EncryptionMode encryptionMode) { | |||
this(encryptionMode, null, null, -1, -1, null); | |||
} |
@@ -24,9 +24,11 @@ import javax.crypto.SecretKey; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.OPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
public abstract class Encryptor { | |||
protected static final String DEFAULT_POIFS_ENTRY = Decryptor.DEFAULT_POIFS_ENTRY; | |||
private SecretKey secretKey; | |||
/** | |||
@@ -50,7 +52,9 @@ public abstract class Encryptor { | |||
public OutputStream getDataStream(NPOIFSFileSystem fs) throws IOException, GeneralSecurityException { | |||
return getDataStream(fs.getRoot()); | |||
} | |||
public OutputStream getDataStream(OPOIFSFileSystem fs) throws IOException, GeneralSecurityException { | |||
return getDataStream(fs.getRoot()); | |||
} | |||
public OutputStream getDataStream(POIFSFileSystem fs) throws IOException, GeneralSecurityException { | |||
return getDataStream(fs.getRoot()); | |||
} |
@@ -118,7 +118,7 @@ public class BinaryRC4Decryptor extends Decryptor { | |||
public InputStream getDataStream(DirectoryNode dir) throws IOException, | |||
GeneralSecurityException { | |||
DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage"); | |||
DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY); | |||
_length = dis.readLong(); | |||
BinaryRC4CipherInputStream cipherStream = new BinaryRC4CipherInputStream(dis, _length); | |||
return cipherStream; | |||
@@ -131,4 +131,4 @@ public class BinaryRC4Decryptor extends Decryptor { | |||
return _length; | |||
} | |||
} | |||
} |
@@ -40,7 +40,7 @@ import org.apache.poi.poifs.crypt.HashAlgorithm; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
import org.apache.poi.poifs.filesystem.DocumentInputStream; | |||
import org.apache.poi.poifs.filesystem.DocumentNode; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.BoundedInputStream; | |||
@@ -200,7 +200,7 @@ public class CryptoAPIDecryptor extends Decryptor { | |||
@SuppressWarnings("unused") | |||
public InputStream getDataStream(DirectoryNode dir) | |||
throws IOException, GeneralSecurityException { | |||
POIFSFileSystem fsOut = new POIFSFileSystem(); | |||
NPOIFSFileSystem fsOut = new NPOIFSFileSystem(); | |||
DocumentNode es = (DocumentNode) dir.getEntry("EncryptedSummary"); | |||
DocumentInputStream dis = dir.createDocumentInputStream(es); | |||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |||
@@ -240,6 +240,7 @@ public class CryptoAPIDecryptor extends Decryptor { | |||
sbis = null; | |||
bos.reset(); | |||
fsOut.writeFilesystem(bos); | |||
fsOut.close(); | |||
_length = bos.size(); | |||
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); | |||
return bis; |
@@ -123,7 +123,7 @@ public class StandardDecryptor extends Decryptor { | |||
} | |||
public InputStream getDataStream(DirectoryNode dir) throws IOException { | |||
DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage"); | |||
DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY); | |||
_length = dis.readLong(); | |||
@@ -166,7 +166,7 @@ public class StandardEncryptor extends Encryptor { | |||
void writeToPOIFS() throws IOException { | |||
int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE); | |||
dir.createDocument("EncryptedPackage", oleStreamSize, this); | |||
dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, this); | |||
// TODO: any properties??? | |||
} | |||
@@ -16,27 +16,54 @@ | |||
==================================================================== */ | |||
package org.apache.poi.poifs.dev; | |||
import org.apache.poi.poifs.filesystem.*; | |||
import java.io.FileInputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.FileInputStream; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.lang.reflect.Field; | |||
import java.nio.ByteBuffer; | |||
import java.util.Iterator; | |||
import org.apache.poi.poifs.common.POIFSConstants; | |||
import org.apache.poi.poifs.filesystem.DirectoryEntry; | |||
import org.apache.poi.poifs.filesystem.DocumentInputStream; | |||
import org.apache.poi.poifs.filesystem.DocumentNode; | |||
import org.apache.poi.poifs.filesystem.Entry; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.NPOIFSStream; | |||
import org.apache.poi.poifs.property.NPropertyTable; | |||
import org.apache.poi.poifs.storage.HeaderBlock; | |||
/** | |||
* | |||
* Dump internal structure of a OLE2 file into file system | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class POIFSDump { | |||
public static void main(String[] args) throws Exception { | |||
if (args.length == 0) { | |||
System.err.println("Must specify at least one file to dump"); | |||
System.exit(1); | |||
} | |||
boolean dumpProps = false, dumpMini = false; | |||
for (int i = 0; i < args.length; i++) { | |||
if (args[i].equalsIgnoreCase("-dumprops") || | |||
args[i].equalsIgnoreCase("-dump-props") || | |||
args[i].equalsIgnoreCase("-dump-properties")) { | |||
dumpProps = true; | |||
continue; | |||
} | |||
if (args[i].equalsIgnoreCase("-dumpmini") || | |||
args[i].equalsIgnoreCase("-dump-mini") || | |||
args[i].equalsIgnoreCase("-dump-ministream") || | |||
args[i].equalsIgnoreCase("-dump-mini-stream")) { | |||
dumpMini = true; | |||
continue; | |||
} | |||
System.out.println("Dumping " + args[i]); | |||
FileInputStream is = new FileInputStream(args[i]); | |||
POIFSFileSystem fs = new POIFSFileSystem(is); | |||
NPOIFSFileSystem fs = new NPOIFSFileSystem(is); | |||
is.close(); | |||
DirectoryEntry root = fs.getRoot(); | |||
@@ -44,13 +71,39 @@ public class POIFSDump { | |||
file.mkdir(); | |||
dump(root, file); | |||
if (dumpProps) { | |||
HeaderBlock header = getHeaderBlock(fs); | |||
dump(fs, header.getPropertyStart(), "properties", file); | |||
} | |||
if (dumpMini) { | |||
NPropertyTable props = getPropertyTable(fs); | |||
int startBlock = props.getRoot().getStartBlock(); | |||
if (startBlock == POIFSConstants.END_OF_CHAIN) { | |||
System.err.println("No Mini Stream in file"); | |||
} else { | |||
dump(fs, startBlock, "mini-stream", file); | |||
} | |||
} | |||
} | |||
} | |||
protected static HeaderBlock getHeaderBlock(NPOIFSFileSystem fs) throws Exception { | |||
Field headerF = NPOIFSFileSystem.class.getDeclaredField("_header"); | |||
headerF.setAccessible(true); | |||
HeaderBlock header = (HeaderBlock)headerF.get(fs); | |||
return header; | |||
} | |||
protected static NPropertyTable getPropertyTable(NPOIFSFileSystem fs) throws Exception { | |||
Field ptF = NPOIFSFileSystem.class.getDeclaredField("_property_table"); | |||
ptF.setAccessible(true); | |||
NPropertyTable table = (NPropertyTable)ptF.get(fs); | |||
return table; | |||
} | |||
public static void dump(DirectoryEntry root, File parent) throws IOException { | |||
for(Iterator it = root.getEntries(); it.hasNext();){ | |||
Entry entry = (Entry)it.next(); | |||
for(Iterator<Entry> it = root.getEntries(); it.hasNext();){ | |||
Entry entry = it.next(); | |||
if(entry instanceof DocumentNode){ | |||
DocumentNode node = (DocumentNode)entry; | |||
DocumentInputStream is = new DocumentInputStream(node); | |||
@@ -58,9 +111,12 @@ public class POIFSDump { | |||
is.read(bytes); | |||
is.close(); | |||
FileOutputStream out = new FileOutputStream(new File(parent, node.getName().trim())); | |||
out.write(bytes); | |||
out.close(); | |||
OutputStream out = new FileOutputStream(new File(parent, node.getName().trim())); | |||
try { | |||
out.write(bytes); | |||
} finally { | |||
out.close(); | |||
} | |||
} else if (entry instanceof DirectoryEntry){ | |||
DirectoryEntry dir = (DirectoryEntry)entry; | |||
File file = new File(parent, entry.getName()); | |||
@@ -71,4 +127,17 @@ public class POIFSDump { | |||
} | |||
} | |||
} | |||
public static void dump(NPOIFSFileSystem fs, int startBlock, String name, File parent) throws IOException { | |||
File file = new File(parent, name); | |||
FileOutputStream out = new FileOutputStream(file); | |||
NPOIFSStream stream = new NPOIFSStream(fs, startBlock); | |||
byte[] b = new byte[fs.getBigBlockSize()]; | |||
for (ByteBuffer bb : stream) { | |||
int len = bb.remaining(); | |||
bb.get(b); | |||
out.write(b, 0, len); | |||
} | |||
out.close(); | |||
} | |||
} |
@@ -24,9 +24,10 @@ import java.lang.reflect.Method; | |||
import org.apache.poi.poifs.common.POIFSBigBlockSize; | |||
import org.apache.poi.poifs.common.POIFSConstants; | |||
import org.apache.poi.poifs.property.DirectoryProperty; | |||
import org.apache.poi.poifs.property.Property; | |||
import org.apache.poi.poifs.property.PropertyTable; | |||
import org.apache.poi.poifs.storage.BlockAllocationTableReader; | |||
import org.apache.poi.poifs.storage.BlockList; | |||
import org.apache.poi.poifs.storage.HeaderBlock; | |||
import org.apache.poi.poifs.storage.ListManagedBlock; | |||
import org.apache.poi.poifs.storage.RawDataBlockList; | |||
@@ -42,111 +43,146 @@ import org.apache.poi.util.IntList; | |||
* down the source of corruption in a file. | |||
*/ | |||
public class POIFSHeaderDumper { | |||
/** | |||
* Display the entries of multiple POIFS files | |||
* | |||
* @param args the names of the files to be displayed | |||
*/ | |||
public static void main(final String args[]) throws Exception { | |||
if (args.length == 0) { | |||
System.err.println("Must specify at least one file to view"); | |||
System.exit(1); | |||
} | |||
for (int j = 0; j < args.length; j++) { | |||
viewFile(args[j]); | |||
} | |||
} | |||
public static void viewFile(final String filename) throws Exception { | |||
InputStream inp = new FileInputStream(filename); | |||
// Header | |||
HeaderBlock header_block = new HeaderBlock(inp); | |||
displayHeader(header_block); | |||
// Raw blocks | |||
POIFSBigBlockSize bigBlockSize = header_block.getBigBlockSize(); | |||
RawDataBlockList data_blocks = new RawDataBlockList(inp, bigBlockSize); | |||
displayRawBlocksSummary(data_blocks); | |||
// Main FAT Table | |||
BlockAllocationTableReader batReader = | |||
new BlockAllocationTableReader( | |||
header_block.getBigBlockSize(), | |||
header_block.getBATCount(), | |||
header_block.getBATArray(), | |||
header_block.getXBATCount(), | |||
header_block.getXBATIndex(), | |||
data_blocks); | |||
displayBATReader(batReader); | |||
// Properties Table | |||
PropertyTable properties = | |||
new PropertyTable(header_block, data_blocks); | |||
// Mini Fat | |||
BlockList sbat = | |||
SmallBlockTableReader.getSmallDocumentBlocks( | |||
bigBlockSize, data_blocks, properties.getRoot(), | |||
header_block.getSBATStart() | |||
); | |||
} | |||
public static void displayHeader(HeaderBlock header_block) throws Exception { | |||
System.out.println("Header Details:"); | |||
System.out.println(" Block size: " + header_block.getBigBlockSize().getBigBlockSize()); | |||
System.out.println(" BAT (FAT) header blocks: " + header_block.getBATArray().length); | |||
System.out.println(" BAT (FAT) block count: " + header_block.getBATCount()); | |||
System.out.println(" XBAT (FAT) block count: " + header_block.getXBATCount()); | |||
System.out.println(" XBAT (FAT) block 1 at: " + header_block.getXBATIndex()); | |||
System.out.println(" SBAT (MiniFAT) block count: " + header_block.getSBATCount()); | |||
System.out.println(" SBAT (MiniFAT) block 1 at: " + header_block.getSBATStart()); | |||
System.out.println(" Property table at: " + header_block.getPropertyStart()); | |||
System.out.println(""); | |||
} | |||
public static void displayRawBlocksSummary(RawDataBlockList data_blocks) throws Exception { | |||
System.out.println("Raw Blocks Details:"); | |||
System.out.println(" Number of blocks: " + data_blocks.blockCount()); | |||
Method gbm = data_blocks.getClass().getSuperclass().getDeclaredMethod("get", int.class); | |||
gbm.setAccessible(true); | |||
for(int i=0; i<Math.min(16, data_blocks.blockCount()); i++) { | |||
ListManagedBlock block = (ListManagedBlock)gbm.invoke(data_blocks, Integer.valueOf(i)); | |||
byte[] data = new byte[Math.min(48, block.getData().length)]; | |||
System.arraycopy(block.getData(), 0, data, 0, data.length); | |||
System.out.println(" Block #" + i + ":"); | |||
System.out.println(HexDump.dump(data, 0, 0)); | |||
} | |||
System.out.println(""); | |||
} | |||
public static void displayBATReader(BlockAllocationTableReader batReader) throws Exception { | |||
System.out.println("Sectors, as referenced from the FAT:"); | |||
Field entriesF = batReader.getClass().getDeclaredField("_entries"); | |||
entriesF.setAccessible(true); | |||
IntList entries = (IntList)entriesF.get(batReader); | |||
for(int i=0; i<entries.size(); i++) { | |||
int bn = entries.get(i); | |||
String bnS = Integer.toString(bn); | |||
if(bn == POIFSConstants.END_OF_CHAIN) { | |||
bnS = "End Of Chain"; | |||
} else if(bn == POIFSConstants.DIFAT_SECTOR_BLOCK) { | |||
bnS = "DI Fat Block"; | |||
} else if(bn == POIFSConstants.FAT_SECTOR_BLOCK) { | |||
bnS = "Normal Fat Block"; | |||
} else if(bn == POIFSConstants.UNUSED_BLOCK) { | |||
bnS = "Block Not Used (Free)"; | |||
} | |||
System.out.println(" Block # " + i + " -> " + bnS); | |||
} | |||
System.out.println(""); | |||
} | |||
/** | |||
* Display the entries of multiple POIFS files | |||
* | |||
* @param args the names of the files to be displayed | |||
*/ | |||
public static void main(final String args[]) throws Exception { | |||
if (args.length == 0) { | |||
System.err.println("Must specify at least one file to view"); | |||
System.exit(1); | |||
} | |||
for (int j = 0; j < args.length; j++) { | |||
viewFile(args[j]); | |||
} | |||
} | |||
public static void viewFile(final String filename) throws Exception { | |||
System.out.println("Dumping headers from: " + filename); | |||
InputStream inp = new FileInputStream(filename); | |||
// Header | |||
HeaderBlock header_block = new HeaderBlock(inp); | |||
displayHeader(header_block); | |||
// Raw blocks | |||
POIFSBigBlockSize bigBlockSize = header_block.getBigBlockSize(); | |||
RawDataBlockList data_blocks = new RawDataBlockList(inp, bigBlockSize); | |||
displayRawBlocksSummary(data_blocks); | |||
// Main FAT Table | |||
BlockAllocationTableReader batReader = | |||
new BlockAllocationTableReader( | |||
header_block.getBigBlockSize(), | |||
header_block.getBATCount(), | |||
header_block.getBATArray(), | |||
header_block.getXBATCount(), | |||
header_block.getXBATIndex(), | |||
data_blocks); | |||
displayBATReader("Big Blocks", batReader); | |||
// Properties Table | |||
PropertyTable properties = | |||
new PropertyTable(header_block, data_blocks); | |||
// Mini Fat | |||
BlockAllocationTableReader sbatReader = | |||
SmallBlockTableReader._getSmallDocumentBlockReader( | |||
bigBlockSize, data_blocks, properties.getRoot(), | |||
header_block.getSBATStart() | |||
); | |||
displayBATReader("Small Blocks", sbatReader); | |||
// Summary of the properties | |||
displayPropertiesSummary(properties); | |||
} | |||
public static void displayHeader(HeaderBlock header_block) throws Exception { | |||
System.out.println("Header Details:"); | |||
System.out.println(" Block size: " + header_block.getBigBlockSize().getBigBlockSize()); | |||
System.out.println(" BAT (FAT) header blocks: " + header_block.getBATArray().length); | |||
System.out.println(" BAT (FAT) block count: " + header_block.getBATCount()); | |||
if (header_block.getBATCount() > 0) | |||
System.out.println(" BAT (FAT) block 1 at: " + header_block.getBATArray()[0]); | |||
System.out.println(" XBAT (FAT) block count: " + header_block.getXBATCount()); | |||
System.out.println(" XBAT (FAT) block 1 at: " + header_block.getXBATIndex()); | |||
System.out.println(" SBAT (MiniFAT) block count: " + header_block.getSBATCount()); | |||
System.out.println(" SBAT (MiniFAT) block 1 at: " + header_block.getSBATStart()); | |||
System.out.println(" Property table at: " + header_block.getPropertyStart()); | |||
System.out.println(""); | |||
} | |||
public static void displayRawBlocksSummary(RawDataBlockList data_blocks) throws Exception { | |||
System.out.println("Raw Blocks Details:"); | |||
System.out.println(" Number of blocks: " + data_blocks.blockCount()); | |||
Method gbm = data_blocks.getClass().getSuperclass().getDeclaredMethod("get", int.class); | |||
gbm.setAccessible(true); | |||
for(int i=0; i<Math.min(16, data_blocks.blockCount()); i++) { | |||
ListManagedBlock block = (ListManagedBlock)gbm.invoke(data_blocks, Integer.valueOf(i)); | |||
byte[] data = new byte[Math.min(48, block.getData().length)]; | |||
System.arraycopy(block.getData(), 0, data, 0, data.length); | |||
System.out.println(" Block #" + i + ":"); | |||
System.out.println(HexDump.dump(data, 0, 0)); | |||
} | |||
System.out.println(""); | |||
} | |||
public static void displayBATReader(String type, BlockAllocationTableReader batReader) throws Exception { | |||
System.out.println("Sectors, as referenced from the "+type+" FAT:"); | |||
Field entriesF = batReader.getClass().getDeclaredField("_entries"); | |||
entriesF.setAccessible(true); | |||
IntList entries = (IntList)entriesF.get(batReader); | |||
for(int i=0; i<entries.size(); i++) { | |||
int bn = entries.get(i); | |||
String bnS = Integer.toString(bn); | |||
if(bn == POIFSConstants.END_OF_CHAIN) { | |||
bnS = "End Of Chain"; | |||
} else if(bn == POIFSConstants.DIFAT_SECTOR_BLOCK) { | |||
bnS = "DI Fat Block"; | |||
} else if(bn == POIFSConstants.FAT_SECTOR_BLOCK) { | |||
bnS = "Normal Fat Block"; | |||
} else if(bn == POIFSConstants.UNUSED_BLOCK) { | |||
bnS = "Block Not Used (Free)"; | |||
} | |||
System.out.println(" Block # " + i + " -> " + bnS); | |||
} | |||
System.out.println(""); | |||
} | |||
public static void displayPropertiesSummary(PropertyTable properties) { | |||
System.out.println("Mini Stream starts at " + properties.getRoot().getStartBlock()); | |||
System.out.println("Mini Stream length is " + properties.getRoot().getSize()); | |||
System.out.println(); | |||
System.out.println("Properties and their block start:"); | |||
displayProperties(properties.getRoot(), ""); | |||
System.out.println(""); | |||
} | |||
public static void displayProperties(DirectoryProperty prop, String indent) { | |||
String nextIndent = indent + " "; | |||
System.out.println(indent + "-> " + prop.getName()); | |||
for (Property cp : prop) { | |||
if (cp instanceof DirectoryProperty) { | |||
displayProperties((DirectoryProperty)cp, nextIndent); | |||
} else { | |||
System.out.println(nextIndent + "=> " + cp.getName()); | |||
System.out.print(nextIndent + " " + cp.getSize() + " bytes in "); | |||
if (cp.shouldUseSmallBlocks()) { | |||
System.out.print("mini"); | |||
} else { | |||
System.out.print("main"); | |||
} | |||
System.out.println(" stream, starts at " + cp.getStartBlock()); | |||
} | |||
} | |||
} | |||
} |