ocr_recognition/src/main/java/com/ytlk/ocr/OCRSwingArea.java

536 lines
25 KiB
Java
Raw Normal View History

2025-03-13 17:45:37 +08:00
package com.ytlk.ocr;
import com.ytlk.huidan.table.ButtonRenderer;
import com.ytlk.huidan.table.LabelRenderer;
import com.ytlk.ocr.table.*;
import com.ytlk.ocr.util.FileUtils;
import com.ytlk.ocr.vo.UserErrorVo;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
public class OCRSwingArea extends JFrame {
private static OCRSwingArea instance = null;
public static JTable table;
public static JTable table2;
public static final int MAX_FILE_NUM = 5;
private OCRSwingArea() {
setOpacity(0.0f);
this.setTitle("工资发放OCR识别与核对工具");
this.setResizable(false);
// this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
2025-03-14 13:53:38 +08:00
this.setBounds(0, 0, 1000, 565);
2025-03-13 17:45:37 +08:00
setLocationRelativeTo(null);
getContentPane().setLayout(null);
getContentPane().setBackground(new Color(236, 236, 248));
/**********************工资册源版文件导入Excel表格*******************/
JLabel label1 = new JLabel("工资册源版文件导入:");
label1.setHorizontalAlignment(JLabel.LEFT);
label1.setFont(new Font("微软雅黑", Font.BOLD, 14));
label1.setBounds(15, 10, 160, 30);
label1.setVerticalAlignment(SwingConstants.CENTER); // 设置垂直对齐方式
getContentPane().add(label1);
JLabel label2 = new JLabel("(Excel表格)");
label2.setHorizontalAlignment(JLabel.LEFT);
label2.setFont(new Font("微软雅黑", Font.BOLD, 14));
label2.setBounds(45, 30, 100, 30);
label2.setVerticalAlignment(SwingConstants.CENTER); // 设置垂直对齐方式
getContentPane().add(label2);
// 添加文件按钮
JButton selectFile = new JButton("添加");
selectFile.setFont(new Font("微软雅黑", Font.PLAIN, 15));
selectFile.setBounds(200, 20, 80, 30);
getContentPane().add(selectFile);
// 重置文件按钮
JButton resetFile = new JButton("重置");
resetFile.setFont(new Font("微软雅黑", Font.PLAIN, 15));
resetFile.setBounds(300, 20, 80, 30);
getContentPane().add(resetFile);
// 文件表格
FileTableModel fileTableModel = new FileTableModel();
table = new JTable(fileTableModel);
table.setRowSelectionAllowed(false);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumn labelColumn = table.getColumn("文件");
labelColumn.setCellRenderer(new LabelRenderer());
2025-03-14 13:53:38 +08:00
labelColumn.setPreferredWidth(390);
2025-03-13 17:45:37 +08:00
TableColumn buttonColumn = table.getColumn("操作");
buttonColumn.setPreferredWidth(80);
buttonColumn.setCellRenderer(new ButtonRenderer());
buttonColumn.setCellEditor(new OCRTableCellEditorButton());
JScrollPane statusScrollPane1 = new JScrollPane(table, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
2025-03-14 13:53:38 +08:00
statusScrollPane1.setBounds(15, 60, 480, 350);
2025-03-13 17:45:37 +08:00
getContentPane().add(statusScrollPane1);
/**********************工资册源版文件导入:添加按钮监听事件*******************/
selectFile.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
fc.setMultiSelectionEnabled(true);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setFileFilter(new FileNameExtensionFilter("Excel Files (*.xls, *.xlsx)", "xls", "xlsx"));
fc.setAcceptAllFileFilterUsed(false);
int val = fc.showOpenDialog(null); // 文件打开对话框
if (val == fc.APPROVE_OPTION) {
// 正常选择文件
File[] fs = fc.getSelectedFiles();
boolean flag = FileUtils.getTableFileName(fileTableModel, fs);
if (flag) {
JOptionPane.showMessageDialog(null, "请勿选择相同工资册源版文件");
return;
}
int isExceedFileNum = FileUtils.isExceedFileNum(fileTableModel, fs);
if (isExceedFileNum > MAX_FILE_NUM) {
JOptionPane.showMessageDialog(null, "工资册源版文件超出" + MAX_FILE_NUM + "个文件限制");
return;
}
for (File f : fs) {
fileTableModel.addRow(new Object[]{f, ""});
OCRSwingArea.table.setValueAt(0, table.getRowCount() - 1, 1);
}
}
}
});
/**********************工资册源版文件导入:重置按钮监听事件*******************/
resetFile.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rows = fileTableModel.getRowCount();
if (rows > 0) {
for (int i = rows - 1; i >= 0; i--) {
fileTableModel.removeRow(i);
}
}
}
});
/**********************工资册签字版导入PDF*******************/
JLabel label3 = new JLabel("工资册签字版导入:");
label3.setHorizontalAlignment(JLabel.LEFT);
label3.setFont(new Font("微软雅黑", Font.BOLD, 14));
2025-03-14 13:53:38 +08:00
label3.setBounds(500, 10, 160, 30);
2025-03-13 17:45:37 +08:00
label3.setVerticalAlignment(SwingConstants.CENTER); // 设置垂直对齐方式
getContentPane().add(label3);
JLabel label4 = new JLabel("PDF");
label4.setHorizontalAlignment(JLabel.LEFT);
label4.setFont(new Font("微软雅黑", Font.BOLD, 14));
2025-03-14 13:53:38 +08:00
label4.setBounds(530, 30, 100, 30);
2025-03-13 17:45:37 +08:00
label4.setVerticalAlignment(SwingConstants.CENTER); // 设置垂直对齐方式
getContentPane().add(label4);
// 添加文件按钮2
JButton selectFile2 = new JButton("添加");
selectFile2.setFont(new Font("微软雅黑", Font.PLAIN, 15));
2025-03-14 13:53:38 +08:00
selectFile2.setBounds(650, 20, 80, 30);
2025-03-13 17:45:37 +08:00
getContentPane().add(selectFile2);
// 重置文件按钮2
JButton resetFile2 = new JButton("重置");
resetFile2.setFont(new Font("微软雅黑", Font.PLAIN, 15));
2025-03-14 13:53:38 +08:00
resetFile2.setBounds(750, 20, 80, 30);
2025-03-13 17:45:37 +08:00
getContentPane().add(resetFile2);
// 文件表格2
FileTableModel fileTableModel2 = new FileTableModel();
table2 = new JTable(fileTableModel2);
table2.setRowSelectionAllowed(false);
table2.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumn buttonColumn2 = table2.getColumn("操作");
buttonColumn2.setPreferredWidth(80);
buttonColumn2.setCellRenderer(new ButtonRenderer());
buttonColumn2.setCellEditor(new OCRTableCellEditorButton2());
TableColumn labelColumn2 = table2.getColumn("文件");
labelColumn2.setCellRenderer(new LabelRenderer());
2025-03-14 13:53:38 +08:00
labelColumn2.setPreferredWidth(390);
2025-03-13 17:45:37 +08:00
JScrollPane statusScrollPane2 = new JScrollPane(table2, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
2025-03-14 13:53:38 +08:00
statusScrollPane2.setBounds(500, 60, 480, 350);
2025-03-13 17:45:37 +08:00
getContentPane().add(statusScrollPane2);
/**********************工资册签字版导入:添加按钮监听事件*******************/
selectFile2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
fc.setMultiSelectionEnabled(true);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
2025-04-16 18:10:22 +08:00
fc.setFileFilter(new FileNameExtensionFilter("PDF Documents", "pdf"));
// fc.setFileFilter(new FileNameExtensionFilter("Excel Files (*.xls, *.xlsx)", "xls", "xlsx"));
2025-03-13 17:45:37 +08:00
fc.setAcceptAllFileFilterUsed(false);
int val = fc.showOpenDialog(null); // 文件打开对话框
if (val == fc.APPROVE_OPTION) {
// 正常选择文件
File[] fs = fc.getSelectedFiles();
boolean flag = FileUtils.getTableFileName(fileTableModel2, fs);
if (flag) {
JOptionPane.showMessageDialog(null, "请勿选择相同工资册签字版文件");
return;
}
int isExceedFileNum = FileUtils.isExceedFileNum(fileTableModel2, fs);
if (isExceedFileNum > MAX_FILE_NUM) {
JOptionPane.showMessageDialog(null, "工资册签字版文件超出" + MAX_FILE_NUM + "个文件限制");
return;
}
for (File f : fs) {
fileTableModel2.addRow(new Object[]{f, ""});
OCRSwingArea.table2.setValueAt(0, table2.getRowCount() - 1, 1);
}
}
}
});
/**********************工资册签字版导入:重置按钮监听事件*******************/
resetFile2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rows = fileTableModel2.getRowCount();
if (rows > 0) {
for (int i = rows - 1; i >= 0; i--) {
fileTableModel2.removeRow(i);
}
}
}
});
/**********************提示*******************/
JLabel label5 = new JLabel("1.支持批量导入进行核定");
label5.setHorizontalAlignment(JLabel.LEFT);
label5.setFont(new Font("微软雅黑", Font.PLAIN, 14));
label5.setBounds(15, 430, 350, 30);
label5.setVerticalAlignment(SwingConstants.CENTER); // 设置垂直对齐方式
label5.setForeground(Color.RED);
getContentPane().add(label5);
JLabel label6 = new JLabel("2.请保证需核对的Excel与PDF文件名称一致");
label6.setHorizontalAlignment(JLabel.LEFT);
label6.setFont(new Font("微软雅黑", Font.PLAIN, 14));
label6.setBounds(43, 450, 350, 30);
label6.setVerticalAlignment(SwingConstants.CENTER); // 设置垂直对齐方式
label6.setForeground(Color.RED);
getContentPane().add(label6);
JLabel label7 = new JLabel("3.请保证文件中包含有【姓名】【实发工资】字段");
label7.setHorizontalAlignment(JLabel.LEFT);
label7.setFont(new Font("微软雅黑", Font.PLAIN, 14));
label7.setBounds(43, 470, 350, 30);
label7.setVerticalAlignment(SwingConstants.CENTER); // 设置垂直对齐方式
label7.setForeground(Color.RED);
getContentPane().add(label7);
2025-03-13 18:06:15 +08:00
JLabel label8 = new JLabel("4.Excel文件、PDF文件最多只能各上传五个文件");
label8.setHorizontalAlignment(JLabel.LEFT);
label8.setFont(new Font("微软雅黑", Font.PLAIN, 14));
label8.setBounds(43, 490, 350, 30);
label8.setVerticalAlignment(SwingConstants.CENTER); // 设置垂直对齐方式
label8.setForeground(Color.RED);
getContentPane().add(label8);
2025-03-13 17:45:37 +08:00
/**********************开始核对*******************/
JButton startCheckBtn = new JButton("开 始 核 对");
startCheckBtn.setFont(new Font("微软雅黑", Font.BOLD, 18));
2025-03-14 13:53:38 +08:00
startCheckBtn.setBounds(500, 450, 400, 40);
2025-03-13 17:45:37 +08:00
getContentPane().add(startCheckBtn);
startCheckBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowCount = fileTableModel.getRowCount();
int rowCount2 = fileTableModel2.getRowCount();
setButtonDisable(selectFile, selectFile2, resetFile, resetFile2, startCheckBtn);
if (rowCount == 0) {
JOptionPane.showMessageDialog(null, "请先上传工资册源版文件");
setButtonEnable(selectFile, selectFile2, resetFile, resetFile2, startCheckBtn);
return;
}
if (rowCount2 == 0) {
JOptionPane.showMessageDialog(null, "请先上传工资册签字版文件");
setButtonEnable(selectFile, selectFile2, resetFile, resetFile2, startCheckBtn);
return;
}
boolean flag = FileUtils.fileIsEquality(fileTableModel, fileTableModel2);
if (flag) {
JOptionPane.showMessageDialog(null, "请确保工资册源版文件与工资册签字版文件数量相等");
setButtonEnable(selectFile, selectFile2, resetFile, resetFile2, startCheckBtn);
return;
}
boolean flag2 = FileUtils.fileNameIsEquality(fileTableModel, fileTableModel2);
if (flag2) {
JOptionPane.showMessageDialog(null, "请确保工资册源版文件与工资册签字版文件文件名称一致");
setButtonEnable(selectFile, selectFile2, resetFile, resetFile2, startCheckBtn);
return;
}
showLoadingDialog(selectFile, selectFile2, resetFile, resetFile2, startCheckBtn);
}
});
// 添加 WindowAdapter 关闭的监听事件
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("窗口正在关闭");
// 在这里执行关闭前的操作
int rows = fileTableModel.getRowCount();
if (rows > 0) {
for (int i = rows - 1; i >= 0; i--) {
fileTableModel.removeRow(i);
}
}
int rows2 = fileTableModel2.getRowCount();
if (rows2 > 0) {
for (int i = rows2 - 1; i >= 0; i--) {
fileTableModel2.removeRow(i);
}
}
}
});
}
public void setButtonEnable(JButton selectFile, JButton selectFile2, JButton resetFile, JButton resetFile2, JButton startCheckBtn) {
selectFile.setEnabled(true);
selectFile2.setEnabled(true);
resetFile.setEnabled(true);
resetFile2.setEnabled(true);
startCheckBtn.setEnabled(true);
}
public void setButtonDisable(JButton selectFile, JButton selectFile2, JButton resetFile, JButton resetFile2, JButton startCheckBtn) {
selectFile.setEnabled(false);
selectFile2.setEnabled(false);
resetFile.setEnabled(false);
resetFile2.setEnabled(false);
startCheckBtn.setEnabled(false);
}
/**
* 执行比较结果
*
* @param selectFile
* @param selectFile2
* @param resetFile
* @param resetFile2
* @param startCheckBtn
* @return void
* @author cwchen
* @date 2025/3/11 17:19
*/
public void showLoadingDialog(JButton selectFile, JButton selectFile2, JButton resetFile, JButton resetFile2, JButton startCheckBtn) {
JFrame parent = this;
// 创建加载框
JDialog loadingDialog = new JDialog(parent, "核对中", true);
loadingDialog.setSize(400, 150);
loadingDialog.setLocationRelativeTo(parent);
// 禁用关闭按钮
loadingDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
// 添加加载提示
JLabel label = new JLabel("正在处理中,请稍后...", SwingConstants.CENTER);
label.setFont(new Font("微软雅黑", Font.PLAIN, 16));
label.setBounds(15, 10, 400, 30);
JLabel label2 = new JLabel("等待时间可能较长正在OCR识别、表格提取、数据比对中...", SwingConstants.CENTER);
label2.setFont(new Font("微软雅黑", Font.PLAIN, 12));
label2.setBounds(15, 30, 400, 30);
loadingDialog.add(label);
loadingDialog.add(label2);
new Thread(() -> {
try {
Object obj = HandleDataUtil.handleData();
if (obj instanceof String) {
String checkExcelData = (String) obj;
if (StringUtils.isNotBlank(checkExcelData)) {
// 任务完成后关闭加载框
loadingDialog.dispose();
setButtonEnable(selectFile, selectFile2, resetFile, resetFile2, startCheckBtn);
JOptionPane.showMessageDialog(null, checkExcelData);
}
} else if (obj instanceof List) {
loadingDialog.dispose();
setButtonEnable(selectFile, selectFile2, resetFile, resetFile2, startCheckBtn);
List<Map<String, Object>> errorDataList = (List<Map<String, Object>>) obj;
showModalDialog(errorDataList);
}
} catch (Exception e) {
e.printStackTrace();
// 任务完成后关闭加载框
loadingDialog.dispose();
setButtonEnable(selectFile, selectFile2, resetFile, resetFile2, startCheckBtn);
}
}).start();
// 显示加载框
loadingDialog.setVisible(true);
}
public void showModalDialog(List<Map<String, Object>> errorDataList) {
JFrame parent = this;
// 创建模态对话框
JDialog dialog = new JDialog(parent, "比对结果", true); // true表示模态
dialog.setSize(690, 450);
dialog.setLocationRelativeTo(parent); // 相对于父窗口居中显示
dialog.setLayout(new FlowLayout());
JPanel parentContentPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0; // 列索引
gbc.fill = GridBagConstraints.HORIZONTAL; // 水平填充
gbc.weightx = 1; // 水平权重
parentContentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // 设置内边距
2025-03-17 14:08:47 +08:00
String[] columnNames = {"姓名", "Excel表格实发工资", "PDF实发工资","异常项"};
2025-03-13 17:45:37 +08:00
for (int i = 0; i < errorDataList.size(); i++) {
Map<String, Object> map = errorDataList.get(i);
JPanel childContentPanel = new JPanel();
childContentPanel.setLayout(new BoxLayout(childContentPanel, BoxLayout.Y_AXIS)); // 垂直布局
String fileName = (String) map.get("fileName");
List<UserErrorVo> errorList = (List<UserErrorVo>) map.get("errorList");
JLabel label = new JLabel(fileName);
JLabel label2 = new JLabel(" 异常数据" + errorList.size() + "");
JButton toggleButton = new JButton("收起/展开");
toggleButton.setPreferredSize(new Dimension(80, 30));
label2.setForeground(Color.RED);
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); // 左对齐
panel.add(label);
panel.add(label2);
panel.add(toggleButton);
childContentPanel.add(panel);
if (CollectionUtils.isNotEmpty(errorList)) {
ErrorTableModel errorTableModel = new ErrorTableModel(errorList, columnNames);
JTable errorTable = new JTable(errorTableModel);
errorTable.setRowSelectionAllowed(false);
errorTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
errorTable.setRowHeight(25); // 设置行高
TableColumn cellColumn = errorTable.getColumn("姓名");
2025-03-17 14:08:47 +08:00
cellColumn.setPreferredWidth(100);
2025-03-13 17:45:37 +08:00
TableColumn cellColumn2 = errorTable.getColumn("Excel表格实发工资");
2025-03-17 14:08:47 +08:00
cellColumn2.setPreferredWidth(150);
2025-03-13 17:45:37 +08:00
TableColumn cellColumn3 = errorTable.getColumn("PDF实发工资");
2025-03-17 14:08:47 +08:00
cellColumn3.setPreferredWidth(150);
TableColumn cellColumn4 = errorTable.getColumn("异常项");
cellColumn4.setPreferredWidth(200);
2025-03-13 17:45:37 +08:00
JScrollPane jScrollPane = new JScrollPane(errorTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JPanel panel2 = new JPanel();
panel2.setLayout(new BoxLayout(panel2, BoxLayout.Y_AXIS)); // 垂直排列
panel2.add(jScrollPane);
// 计算表格总高度
int rowHeight = errorTable.getRowHeight();
int rowCount = errorTable.getRowCount();
int tableHeight = rowHeight * rowCount + 50;
panel2.setPreferredSize(new Dimension(600, tableHeight));
toggleButton.addActionListener(new ToggleListener(panel2)); // 为每个按钮添加监听器
childContentPanel.add(panel2);
}
gbc.gridy = i; // 行索引
parentContentPanel.add(childContentPanel,gbc);
}
gbc.gridy = errorDataList.size() + 1;
gbc.weighty = 1; // 垂直权重,使组件占满剩余空间
gbc.fill = GridBagConstraints.BOTH; // 组件水平和垂直填充
parentContentPanel.add(new JLabel(""), gbc); // 添加一个空标签占位
// 创建JScrollPane并设置其滚动条策略可选
JScrollPane scrollPane = new JScrollPane(parentContentPanel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); // 水平滚动条按需显示
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); // 垂直滚动条按需显示
scrollPane.setPreferredSize(new Dimension(660, 400)); // 设置滚动面板的尺寸,以适应内容或手动设置大小
dialog.add(scrollPane);
dialog.setVisible(true);
}
public String getTime(String time) {
if (time.startsWith("0")) {
return time.substring(1);
} else {
return time;
}
}
public File getCurrenPath() {
try {
String path = this.getClass().getProtectionDomain().getCodeSource().getLocation().getFile();
path = java.net.URLDecoder.decode(path, "UTF-8");
File file = new File(path);
if (file.isFile()) {
return new File(file.getParent());
} else {
return file;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public void deleteTemp() {
File path = getCurrenPath();
File dir = path.getParentFile();
for (File f : dir.listFiles()) {
if (!f.getAbsoluteFile().equals(path.getAbsolutePath())) {
File zb = new File(f.getAbsoluteFile() + File.separator + "hdpdf.jar");
if (zb.exists()) {
for (File t : f.listFiles()) {
t.delete();
}
f.delete();
}
}
}
}
public File getCurrentJarDir() {
try {
File f = new File(".");
if (f.exists()) {
return f;
}
return getCurrenPath();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private JPanel createLabel(String v, List<String> kws, JPanel sp) {
// Icon icon = new ImageIcon(getClass().getResource("/close.jpg"));
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
JLabel lbl = new JLabel(v);
lbl.setFont(new Font("微软雅黑", Font.PLAIN, 16));
// p.setBorder(BorderFactory.createLineBorder(Color.gray));
p.add(lbl, BorderLayout.CENTER);
JButton bt = new JButton("X");
// bt.setIcon(icon);
bt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
remove(v, kws, sp);
}
});
p.add(bt, BorderLayout.EAST);
return p;
}
private void remove(String v, List<String> keywords, JPanel sp) {
for (int i = keywords.size() - 1; i >= 0; i--) {
if (keywords.get(i).equals(v)) {
keywords.remove(i);
break;
}
}
sp.removeAll();
for (String k : keywords) {
JPanel p = createLabel(k, keywords, sp);
sp.add(p);
}
sp.revalidate();
sp.repaint();
}
public static OCRSwingArea getInstance() {
if (null == instance) {
synchronized (OCRSwingArea.class) {
if (null == instance) {
instance = new OCRSwingArea();
}
}
}
return instance;
}
public void initUI() {
this.setVisible(true);
deleteTemp();
}
}