p2_fix_poi3.py
7.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
r"""
P2 纠偏:把存量 POI 3.x 的写法改到 POI 4.x。
## 为什么必须改
`parking-dependencies` 里 `poi-ooxml` 定的是 **4.1.2**(被 EasyExcel 3.3.4 要求,见 BOM 注释)。
但 BOM 注释里那句「3.9→4.1 未破坏」是**错的**:POI 4.0 一次性删掉了 3.x 里所有
「常量式」的旧 API,只保留枚举版。已用 javap 对 4.1.2 的 jar 逐个核对:
| 3.x 写法 | 4.1.2 实况 | 改成 |
|---|---|---|
| `CellStyle.BORDER_THIN` / `BORDER_DASHED` | 已删 | `BorderStyle.THIN` / `DASHED` |
| `Cell.CELL_TYPE_STRING` / `HSSFCell.CELL_TYPE_*` / `XSSFCell.CELL_TYPE_*` | 已删(`getCellType()` 现在返回 `CellType`) | `CellType.STRING` / … |
| `CellStyle.ALIGN_CENTER/LEFT/RIGHT` | 已删 | `HorizontalAlignment.CENTER/LEFT/RIGHT` |
| `CellStyle.VERTICAL_CENTER` | 已删 | `VerticalAlignment.CENTER` |
| `CellStyle.SOLID_FOREGROUND` | 已删 | `FillPatternType.SOLID_FOREGROUND` |
| `HSSFColor.GREY_25_PERCENT` / `ROSE` / `WHITE` | 内嵌类已删,改为枚举 `HSSFColorPredefined` | `HSSFColor.HSSFColorPredefined.XXX.getIndex()` |
| `font.setBoldweight(Font.BOLDWEIGHT_BOLD)` | 方法已删;`Font.BOLDWEIGHT_BOLD` 也没了(`HSSFFont` 那份变成包内可见) | `font.setBold(true)` |
⚠️ 最后一行的语义确认:`setBoldweight(BOLDWEIGHT_BOLD)` 就是「加粗」,等价于
`setBold(true)`;本仓库里 `setBoldweight` 的实参**只有** `BOLDWEIGHT_BOLD`
(`HSSFFont` 4 处 + `Font` 7 处,共 11 处,已全量核对),所以没有丢分支的风险。
⚠️ `setCellType(...)` **本身**在 4.1.2 还在(签名已改成收 `CellType`),所以只改传进去的常量。
## 为什么有两个包
`com/clouds/common/utils/` 下有 **两份**同源代码:`excle/`(6 个文件)与
`poiexcle/`(3 个文件)。第一次编译只报了 `excle/` 的 200 条错——
那是 javac 的默认 `-Xmaxerrs 200` **截断**,`poiexcle/` 的错被挡在后面没打印出来。
所以本脚本两个包一起改,不能只看报错文件清单。
## 幂等
全部规则都是「找不到就不改」,可重复执行。已改过的文件不会二次变形。
用法: python3 -I tools/p2_fix_poi3.py [--dry-run]
"""
import argparse
import os
import re
import sys
CLOUD = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE = os.path.join(
CLOUD,
"parking-framework/parking-legacy-common/src/main/java/com/clouds/common/utils",
)
DIRS = ["excle", "poiexcle"]
# 需要补的 import:枚举类型名 -> 全限定名
ENUM_IMPORTS = {
"BorderStyle": "org.apache.poi.ss.usermodel.BorderStyle",
"CellType": "org.apache.poi.ss.usermodel.CellType",
"FillPatternType": "org.apache.poi.ss.usermodel.FillPatternType",
"HorizontalAlignment": "org.apache.poi.ss.usermodel.HorizontalAlignment",
"VerticalAlignment": "org.apache.poi.ss.usermodel.VerticalAlignment",
}
# ---- 规则:(说明, 正则, 替换函数或字符串) ----
# 顺序有意义:CELL_TYPE / BORDER / ALIGN 的限定符可能是「类名」也可能是「实例变量名」
# (如 `hssfCell.CELL_TYPE_BOOLEAN`),所以 CELL_TYPE 用 \w+ 兜住两种情况,
# 其余三个按实测的类名枚举限定(避免误伤同名的业务常量)。
RULES = [
(
"Cell.CELL_TYPE_* → CellType.*",
re.compile(r"\b[A-Za-z_]\w*\.CELL_TYPE_([A-Z_]+)\b"),
lambda m: "CellType." + m.group(1),
),
(
"CellStyle.BORDER_* → BorderStyle.*",
re.compile(r"\b(?:HSSFCellStyle|XSSFCellStyle|CellStyle)\.BORDER_([A-Z_]+)\b"),
lambda m: "BorderStyle." + m.group(1),
),
(
"CellStyle.ALIGN_* → HorizontalAlignment.*",
re.compile(r"\b(?:HSSFCellStyle|XSSFCellStyle|CellStyle)\.ALIGN_([A-Z_]+)\b"),
lambda m: "HorizontalAlignment." + m.group(1),
),
(
"CellStyle.VERTICAL_* → VerticalAlignment.*",
re.compile(r"\b(?:HSSFCellStyle|XSSFCellStyle|CellStyle)\.VERTICAL_([A-Z_]+)\b"),
lambda m: "VerticalAlignment." + m.group(1),
),
(
"CellStyle.SOLID_FOREGROUND → FillPatternType.SOLID_FOREGROUND",
re.compile(r"\b(?:HSSFCellStyle|XSSFCellStyle|CellStyle)\.(SOLID_FOREGROUND)\b"),
lambda m: "FillPatternType." + m.group(1),
),
(
"HSSFColor.XXX.index → HSSFColorPredefined.XXX.getIndex()",
re.compile(r"\bHSSFColor\.([A-Z][A-Z0-9_]*)\.index\b"),
lambda m: f"HSSFColor.HSSFColorPredefined.{m.group(1)}.getIndex()",
),
(
"setBoldweight(X.BOLDWEIGHT_BOLD) → setBold(true)",
re.compile(
r"\.setBoldweight\(\s*(?:HSSFFont|Font)\.BOLDWEIGHT_BOLD\s*\)"
),
lambda m: ".setBold(true)",
),
]
def add_imports(src, needed):
"""把缺的枚举 import 插进 import 区。已是 FQN 或已存在的不重复插。"""
lines = src.split("\n")
existing = {ln.strip() for ln in lines}
missing = [
(name, fqn)
for name, fqn in sorted(ENUM_IMPORTS.items())
if name in needed and f"import {fqn};" not in existing
]
if not missing:
return src, []
# 定位插入点:最后一个 import 行之后;没有 import 就放在 package 行之后
last_import = -1
pkg = -1
for i, ln in enumerate(lines):
s = ln.strip()
if s.startswith("import "):
last_import = i
elif s.startswith("package ") and pkg < 0:
pkg = i
at = (last_import if last_import >= 0 else pkg) + 1
lines[at:at] = [f"import {fqn};" for _, fqn in missing]
return "\n".join(lines), [n for n, _ in missing]
def migrate(path, dry):
with open(path, encoding="utf-8", errors="surrogateescape") as fh:
src = original = fh.read()
hits = []
for desc, pat, repl in RULES:
src, n = pat.subn(repl, src)
if n:
hits.append(f"{desc} x{n}")
# 兜底:还有 setBoldweight 就说明实参不是 BOLDWEIGHT_BOLD,需人工确认
if ".setBoldweight(" in src:
raise SystemExit(f"!! {path}: 仍有 setBoldweight 调用且实参非 BOLDWEIGHT_BOLD,需人工处理")
if src == original:
return None
needed = {name for name in ENUM_IMPORTS if re.search(rf"\b{name}\.", src)}
src, added = add_imports(src, needed)
if added:
hits.append("补 import: " + ", ".join(added))
if not dry:
with open(path, "w", encoding="utf-8", errors="surrogateescape") as fh:
fh.write(src)
return hits
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()
changed = 0
for d in DIRS:
base = os.path.join(BASE, d)
if not os.path.isdir(base):
print(f"!! 目录不存在: {base}")
continue
for f in sorted(os.listdir(base)):
if not f.endswith(".java"):
continue
p = os.path.join(base, f)
hits = migrate(p, args.dry_run)
if hits:
changed += 1
print(f" {d}/{f}")
for h in hits:
print(f" · {h}")
else:
print(f" {d}/{f} (无需改动)")
print(f"\n改动文件 {changed} 个" + ("(--dry-run,未落盘)" if args.dry_run else ""))
if __name__ == "__main__":
main()