update with work status (not synchronized with my prev. version here)

This commit is contained in:
2026-02-21 15:16:22 +01:00
parent 3bb37ef10d
commit 16ef02b533
20 changed files with 529 additions and 167 deletions

View File

@@ -0,0 +1,23 @@
SELECT
ts.Start_Time AS Datum
, CASE
WHEN ts.NOTE_TEXT LIKE '%Silke TGIF%' THEN 'F-Tag'
WHEN ts.NOTE_TEXT LIKE '%Silke Home Office%' THEN 'Home Office'
WHEN ts.NOTE_TEXT LIKE '%Silke %rotag%' THEN 'Buerotag'
WHEN ts.NOTE_TEXT LIKE '%Silke Dienstreise%' THEN 'Dienstreise'
WHEN lower(ts.NOTE_TEXT) LIKE '%Silke krank%' THEN 'Krank'
WHEN ts.NOTE_TEXT LIKE '%Silke Urlaubs%' THEN 'Urlaub'
WHEN ts.NOTE_TEXT LIKE '%Urlaubstag Silke%' THEN 'Urlaub'
WHEN ts.NOTE_TEXT LIKE '%Urlaubsanfang Silke%' THEN 'Urlaub'
WHEN ts.NOTE_TEXT LIKE '%Urlaubsende Silke%' THEN 'Urlaub'
WHEN ts.NOTE_TEXT LIKE '%ilke F Tag%' THEN 'F-Tag'
WHEN ts.NOTE_TEXT LIKE '%ilke Tag frei%' THEN 'F-Tag'
WHEN ts.NOTE_TEXT LIKE '%ilke frei%' THEN 'F-Tag'
WHEN ts.NOTE_TEXT LIKE '%Tag frei Silke%' THEN 'F-Tag'
WHEN ts.NOTE_TEXT LIKE '%ilke Kurzarbeit%' THEN 'Kurzarbeit'
ELSE 'Sonstiges'
END AS Category
, 'Silke' AS Person
, ts.NOTE_TEXT
FROM note ts
WHERE CLASSTYPE = 'Note' AND ts.note_text like '%ilke%'

View File

@@ -8,22 +8,26 @@ import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import de.lcag.common.AbstractTable.ListColumn;
import de.lcag.common.HashTable; import de.lcag.common.HashTable;
import de.lcag.common.Table.TableColumn;
import routines.LcagFileTools; import routines.LcagFileTools;
import routines.LcagLogger; import routines.LcagLogger;
public class CommentExtractor { public class CommentExtractor {
private static final String BUGFIX_COMMAND = "BUGFIX_"; // Fix of a Agile Defect in Trackspace
private static final String STORY_COMMAND = "BUGFIX_"; // User Story in Trackspace to implement
private static final String HOW_TO_COMMAND = "HOWTO_"; private static final String HOW_TO_COMMAND = "HOWTO_";
private static final String DOCUMENT_FLOW_COMMAND = "FLOW_"; private static final String DOCUMENT_FLOW_COMMAND = "FLOW_";
private static final String TODO_COMMAND = "TODO_"; private static final String TODO_COMMAND = "TODO_";
private static final String[] COMMAD_LIST = new String[] { HOW_TO_COMMAND, DOCUMENT_FLOW_COMMAND, TODO_COMMAND }; private static final String[] COMMAD_LIST = new String[] { HOW_TO_COMMAND, DOCUMENT_FLOW_COMMAND, TODO_COMMAND,
BUGFIX_COMMAND, STORY_COMMAND };
private static final String COL_TYPE = "Type"; private static final String COL_TYPE = "Type";
private static final String COL_ID = "Id"; private static final String COL_ID = "Id";
@@ -73,7 +77,7 @@ public class CommentExtractor {
try { try {
extractCodeExamples(rootDirPath); extractCodeExamples(rootDirPath);
ListColumn sortedResults = resultTable.sort(COL_ID, COL_STEP); TableColumn sortedResults = resultTable.sort(COL_ID, COL_STEP);
String txt = resultTable.asCSV(sortedResults); String txt = resultTable.asCSV(sortedResults);
String fileName = DOC_FILE + ".csv"; String fileName = DOC_FILE + ".csv";
@@ -87,11 +91,13 @@ public class CommentExtractor {
} }
} }
private static void writeHtmlFile(ListColumn sortedResults) throws IOException { private static void writeHtmlFile(TableColumn sortedResults) throws IOException {
String fileName = DOC_FILE + ".html"; String fileName = DOC_FILE + ".html";
for (int i = 0; i < resultTable.lenght(); i++) { for (int i = 0; i < resultTable.lenght(); i++) {
int lineNo = (int) resultTable.getCellValue(i, COL_LINE_NO); int lineNo = (int) resultTable.getCellValue(i, COL_LINE_NO);
String colId = (String) resultTable.getCellValue(i, COL_ID);
String filePath = (String) resultTable.getCellValue(i, COL_FILE); String filePath = (String) resultTable.getCellValue(i, COL_FILE);
String gitURL = String.format(CODE_BASE_GITHUB_DIR, filePath, gitBranchName, lineNo, lineNo + 1); String gitURL = String.format(CODE_BASE_GITHUB_DIR, filePath, gitBranchName, lineNo, lineNo + 1);
String lineNoURL = String.format("<html><a href=\"%s\">%d</a></html>", gitURL, lineNo); String lineNoURL = String.format("<html><a href=\"%s\">%d</a></html>", gitURL, lineNo);
@@ -111,6 +117,16 @@ public class CommentExtractor {
if (kind.equals("Title")) { if (kind.equals("Title")) {
String disc = String.format("<html><b>%s</b></html>", (String) resultTable.getCellValue(i, COL_DESC)); String disc = String.format("<html><b>%s</b></html>", (String) resultTable.getCellValue(i, COL_DESC));
resultTable.setCellValue(i, COL_DESC, disc); resultTable.setCellValue(i, COL_DESC, disc);
// For BUGFIX or STORY: Add link into trackspace:
String digits = colId.replaceAll(".*[BS](\\d+).*", "$1"); // Extract digits after 'B'
if (!Objects.equals(colId, digits)) {
String htmlLink = "<html><a href=\"https://trackspace.lhsystems.com/browse/MDMGMT-" + digits
+ "\" target=\"_blank\">" + colId + "</a></html>";
resultTable.setCellValue(i, COL_ID, htmlLink);
}
} }
} }

View File

@@ -1,10 +1,22 @@
package application; package application;
import java.awt.BorderLayout;
import java.awt.Desktop; import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.GridLayout; import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import javax.naming.ConfigurationException;
import javax.swing.BorderFactory;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JFrame; import javax.swing.JFrame;
@@ -13,88 +25,316 @@ import javax.swing.JMenu;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import de.lcag.common.ExcelWorkbook;
import de.lcag.common.LcagProperties;
import de.lcag.common.MappingTable;
import de.lcag.common.Table;
import de.lcag.common.WorkbookComparisonResult;
import routines.LcagFileTools;
import routines.LcagStringTools;
public class FileActions extends JFrame { public class FileActions extends JFrame {
private JLabel file1Label, file2Label; private static final int FRAME_HEIGHT = 150;
private JButton selectFile1, selectFile2; private static String MASTER_FILE_LABEL = "Main Excel File";
private File file1, file2; private static String CONFIG_FILE_LABEL = "Configuration File";
private JLabel masterFileLabel, clientFileLabel, configFileLabel;
private JButton selectMasterFile, selectClientFile, selectConfigFile;
private Path masterFile, clientFile, configFile;
private JMenuBar menuBar; private JMenuBar menuBar;
private JMenu actionsMenu; private JMenu actionsMenu;
private JMenuItem openInExcelItem; private JMenuItem mergeAction;
private JPopupMenu contextMenuMasterFile, contextMenuClientFile;
private File configFileDirectoryPath = null;
private LcagProperties config = null;
private File masterDirecory = null;
public FileActions() { public FileActions() {
setTitle("Datei-Auswahl und Aktionen"); setTitle("Compare 2 Excel Files");
setSize(400, 200); setSize(800, FRAME_HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(3, 2));
file1Label = new JLabel("Datei 1: Keine ausgew<65>hlt"); JPanel mainPanel = new JPanel(new BorderLayout());
file2Label = new JLabel("Datei 2: Keine ausgew<65>hlt"); JPanel leftButtonPanel = new JPanel(new GridLayout(3, 1, 5, 5));
selectFile1 = new JButton("Datei 1 ausw<73>hlen"); JPanel rightTextPanel = new JPanel(new GridLayout(3, 1, 5, 5));
selectFile2 = new JButton("Datei 2 ausw<73>hlen"); // setLayout(new GridLayout(4, 2));
// File Chooser f<>r Datei 1 (nur Excel-Dateien) leftButtonPanel.setPreferredSize(new Dimension(200, FRAME_HEIGHT)); // Fixed width
selectFile1.addActionListener(e -> {
// Add an empty border to create a gap on the right side of leftPanel
leftButtonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); // right gap: 10px
rightTextPanel.setPreferredSize(new Dimension(500, FRAME_HEIGHT));
mainPanel.add(leftButtonPanel, BorderLayout.WEST);
mainPanel.add(rightTextPanel, BorderLayout.EAST);
configFileLabel = new JLabel(String.format(" %s: Not selected", CONFIG_FILE_LABEL));
selectConfigFile = new JButton("Select " + CONFIG_FILE_LABEL);
leftButtonPanel.add(selectConfigFile);
rightTextPanel.add(configFileLabel);
// File Chooser for config file
selectConfigFile.setEnabled(true);
selectConfigFile.addActionListener(e -> {
if (!configFileDirectoryPath.isDirectory()) {
System.err.println(String.format("ERROR: Config directory %s does not exist",
configFileDirectoryPath.getAbsolutePath()));
System.exit(1);
}
JFileChooser fileChooser = new JFileChooser(configFileDirectoryPath);
FileNameExtensionFilter filter = new FileNameExtensionFilter("Config Files", "properties");
fileChooser.setFileFilter(filter);
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
String clientDirString = null;
configFile = selectFile(fileChooser);
config = new LcagProperties(configFile.toString());
configFileLabel.setText(String.format(" %s: %s", CONFIG_FILE_LABEL, configFile));
masterFile = config.getOptionalFilePathProperty("main_file");
clientFile = config.getOptionalFilePathProperty("compare_file");
if (clientFile.isAbsolute()) {
clientDirString = clientFile.getParent().toString();
} else {
// client file is relative expect it is same directory as master
Path dirPath = masterFile.getParent();
clientDirString = "same directory as 1st file";
clientFile = dirPath.resolve(clientFile);
}
// After user selected a config file enable buttons to set files to compare (can
// overwrite files listed in config file):
selectMasterFile.setEnabled(true);
if (clientFile != null) {
String fileName = clientFile.getFileName().toString();
clientFileLabel
.setText(String.format("<html>Selected: %s<br>in: %s</html>", fileName, clientDirString));
selectClientFile.setEnabled(true);
}
if (masterFile != null) {
String fileName = masterFile.getFileName().toString();
masterDirecory = new File(masterFile.getParent().toString());
masterFileLabel.setText(String.format("<html>Selected: %s<br>in: %s</html>", fileName,
masterFile.getParent().toString()));
selectClientFile.setEnabled(true);
}
}
});
// Kontextmen<65>s erstellen
contextMenuMasterFile = new JPopupMenu();
contextMenuClientFile = new JPopupMenu();
JMenuItem renameFile1 = new JMenuItem("Rename File");
JMenuItem renameFile2 = new JMenuItem("Rename File");
contextMenuMasterFile.add(renameFile1);
contextMenuClientFile.add(renameFile2);
masterFileLabel = new JLabel(String.format(" %s: %s", MASTER_FILE_LABEL,
masterFile == null ? "Not selected" : masterFile.getFileName()));
selectMasterFile = new JButton("Select " + MASTER_FILE_LABEL);
leftButtonPanel.add(selectMasterFile);
rightTextPanel.add(masterFileLabel);
clientFileLabel = new JLabel(String.format(" %s: %s", "Excel to compare",
clientFile == null ? "Not selected" : clientFile.getFileName()));
selectClientFile = new JButton("Select Excel to Compare");
// MouseListener f<>r Kontextmen<65> auf den Labels
masterFileLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e) && masterFile != null) {
contextMenuMasterFile.show(masterFileLabel, e.getX(), e.getY());
}
}
});
clientFileLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e) && clientFile != null) {
contextMenuClientFile.show(clientFileLabel, e.getX(), e.getY());
}
}
});
leftButtonPanel.add(selectClientFile);
rightTextPanel.add(clientFileLabel);
renameFile1.addActionListener(e -> renameFile(masterFile, masterFileLabel, "Datei 1"));
renameFile2.addActionListener(e -> renameFile(clientFile, clientFileLabel, "Datei 2"));
// File Chooser f<>r Datei 1 (nur Excel-Dateien)
selectMasterFile.setEnabled(false);
selectMasterFile.addActionListener(e -> {
JFileChooser fileChooser = new JFileChooser(); JFileChooser fileChooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("Excel-Dateien", "xls", "xlsx"); FileNameExtensionFilter filter = new FileNameExtensionFilter("Excel-Files", "xls", "xlsx");
fileChooser.setFileFilter(filter); fileChooser.setFileFilter(filter);
if (masterFile != null) {
masterDirecory = new File(masterFile.getParent().toString());
fileChooser.setCurrentDirectory(masterDirecory);
}
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
file1 = fileChooser.getSelectedFile(); masterFile = selectFile(fileChooser);
file1Label.setText("Datei 1: " + file1.getName()); masterDirecory = new File(masterFile.getParent().toString());
selectFile2.setEnabled(true); masterFileLabel.setText(String.format(" %s: %s", MASTER_FILE_LABEL, masterFile.getFileName()));
selectClientFile.setEnabled(true);
} }
}); });
// File Chooser f<>r Datei 2 (im selben Verzeichnis wie Datei 1, nur // File Chooser f<>r Datei 2 (im selben Verzeichnis wie Datei 1, nur
// Excel-Dateien) // Excel-Dateien)
selectFile2.setEnabled(false); selectClientFile.setEnabled(false);
selectFile2.addActionListener(e -> { selectClientFile.addActionListener(e -> {
if (file1 != null) { if (masterDirecory != null) {
JFileChooser fileChooser = new JFileChooser(file1.getParentFile()); JFileChooser fileChooser = new JFileChooser(masterDirecory);
FileNameExtensionFilter filter = new FileNameExtensionFilter("Excel-Dateien", "xls", "xlsx"); FileNameExtensionFilter filter = new FileNameExtensionFilter("Excel-Dateien", "xls", "xlsx");
fileChooser.setFileFilter(filter); fileChooser.setFileFilter(filter);
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
file2 = fileChooser.getSelectedFile(); clientFile = selectFile(fileChooser);
file2Label.setText("Datei 2: " + file2.getName()); clientFileLabel.setText("Datei 2: " + clientFile.getFileName());
} }
} }
}); });
add(selectFile1); // Men<65> erstellen
add(file1Label);
add(selectFile2);
add(file2Label);
// Men<65> erstellen
menuBar = new JMenuBar(); menuBar = new JMenuBar();
actionsMenu = new JMenu("Aktionen"); actionsMenu = new JMenu("Actions");
openInExcelItem = new JMenuItem("Erste Datei in Excel <20>ffnen"); mergeAction = new JMenuItem("Merge 2nd into Main file");
actionsMenu.add(openInExcelItem); mergeAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.CTRL_MASK));
mergeAction.addActionListener(e -> {
System.out.println("Ctrl-C pressed on menu");
});
actionsMenu.add(mergeAction);
menuBar.add(actionsMenu); menuBar.add(actionsMenu);
setJMenuBar(menuBar); setJMenuBar(menuBar);
// Aktion: Erste Datei in Excel <20>ffnen // Aktion: Erste Datei in Excel <20>ffnen
openInExcelItem.addActionListener(e -> { mergeAction.addActionListener(e -> {
if (file1 != null) { mergeFiles(config, masterFile, clientFile);
});
add(mainPanel);
pack();
setVisible(true);
}
private Path selectFile(JFileChooser fileChooser) {
File result = fileChooser.getSelectedFile();
return Path.of(result.getAbsolutePath());
}
private void mergeFiles(LcagProperties pConfig, Path pMasterFilePath, Path pClientFilePath) {
if (pConfig != null) {
String keyFieldString = pConfig.getProperty("list_of_keys");
List<String> keyFields = LcagStringTools.asList(keyFieldString, ",\\s*");
if (pMasterFilePath != null && pClientFilePath != null)
try { try {
Desktop.getDesktop().open(file1); String masterFileName = pMasterFilePath.getFileName().toString();
Path resultFilePath = LcagFileTools.appendStringToFilePath(pMasterFilePath, "-merged");
String fieldMapping = pConfig.getProperty("column.mapping.main2compare");
Files.copy(pMasterFilePath, resultFilePath, StandardCopyOption.REPLACE_EXISTING);
ExcelWorkbook excel1 = new ExcelWorkbook(resultFilePath.toString());
ExcelWorkbook excel2 = new ExcelWorkbook(pClientFilePath.toString());
excel1.setKeyColumns(keyFields);
excel1.read();
excel2.setKeyColumns(keyFields);
excel2.read();
excel1.setOption(keyFieldString, FRAME_HEIGHT);
if (fieldMapping != null) {
MappingTable mappingTable = new MappingTable(fieldMapping, ";", "->");
excel2.setOption(Table.OPTION_USE_MAPPING, mappingTable);
}
WorkbookComparisonResult compareResult = excel1.compareTo(excel2);
ExcelWorkbook integratedTable = (ExcelWorkbook) compareResult.integrateDifferencesIntoMaster();
integratedTable.save();
Desktop.getDesktop().open(resultFilePath.toFile());
} catch (IOException ex) { } catch (IOException ex) {
JOptionPane.showMessageDialog(this, "Fehler beim <20>ffnen der Datei in Excel.", "Fehler", JOptionPane.showMessageDialog(this, "Cannot open comparison result file.", "Error",
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
} catch (ConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
} else { } else {
JOptionPane.showMessageDialog(this, "Bitte w<>hle zuerst eine Datei aus!", "Fehler", JOptionPane.showMessageDialog(this, "Pls select at least a config file!", "Error",
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
} }
}); }
private Path getPath(LcagProperties config, String pProperty, File pMasterFile) {
Path result = null;
String filePathString = (pMasterFile == null) ? config.getProperty(pProperty) : pMasterFile.getAbsolutePath();
if (!LcagStringTools.isEmpty(filePathString)) {
result = Path.of(filePathString);
}
return result;
}
private Path renameFile(Path pFile, JLabel label, String prefix) {
Path newPath = null;
if (pFile == null)
return newPath;
File file = new File(pFile.toString());
String newName = JOptionPane.showInputDialog(this, "Enter new name:", file.getName());
if (!LcagStringTools.isEmpty(newName)) {
newPath = pFile.getParent().resolve(newName);
File newFile = new File(newPath.toString());
if (file.renameTo(newFile)) {
file = newFile;
label.setText(prefix + ": " + newFile.getName());
if (prefix.equals("Datei 1"))
this.masterFile = newPath;
else
this.clientFile = newPath;
} else {
JOptionPane.showMessageDialog(this, "Umbenennung fehlgeschlagen!", "Fehler", JOptionPane.ERROR_MESSAGE);
}
}
return newPath;
} }
public static void main(String[] args) { public static void main(String[] args) {
String cfgFileDirectory = args.length < 1 ? System.getenv("_MY_DATA") + "/Tools" : args[0];
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
FileActions gui = new FileActions(); FileActions gui = new FileActions();
gui.configFileDirectoryPath = new File(cfgFileDirectory);
gui.setVisible(true); gui.setVisible(true);
}); });
} }

View File

@@ -96,6 +96,12 @@ public abstract class AbstractTable implements Table {
return result; return result;
} }
public String aggregate(String pSeparator) {
String result = LcagStringTools.joinIgnoreNull(pSeparator, this.data);
return result;
}
public void setValue(int rowNo, Object v) { public void setValue(int rowNo, Object v) {
int n = this.data.size(); int n = this.data.size();
@@ -339,6 +345,12 @@ public abstract class AbstractTable implements Table {
} }
} }
public void setCellValue(int row, int pColumnNumber, Object v) {
String colName = this.getColumnNameByPosition(pColumnNumber);
this.setCellValue(row, colName, v);
}
@Override @Override
public void setCellValue(int row, String colName, Object v) { public void setCellValue(int row, String colName, Object v) {
TableColumn col = getColumnByLabel(colName); TableColumn col = getColumnByLabel(colName);
@@ -385,8 +397,14 @@ public abstract class AbstractTable implements Table {
@Override @Override
public <V> V getCellValue(int row, String colLabel) { public <V> V getCellValue(int row, String colLabel) {
TableColumn col = getColumnByLabel(colLabel); TableColumn col = getColumnByLabel(colLabel);
V result = null;
V result = col.getValue(row); if (col == null) {
// FIXME: Shall throw a TableColumnException (bigger impact)
log.fatal("Column '%s' does not exist in %s", colLabel, this.getName());
} else {
result = col.getValue(row);
}
return result; return result;
} }
@@ -606,17 +624,14 @@ public abstract class AbstractTable implements Table {
@Override @Override
public TableColumn getColumnByLabel(String pFormattedColName) { public TableColumn getColumnByLabel(String pFormattedColName) {
String colName = pFormattedColName.split(PARAMETER_STYLE)[0]; String colName = pFormattedColName.split(PARAMETER_STYLE)[0];
TableColumn result = this.rowMap.get(getColumnNameByLabel(colName));
MappingTable mapping = getOption(OPTION_USE_MAPPING); MappingTable mapping = getOption(OPTION_USE_MAPPING);
if (mapping != null) { if (mapping != null) {
String mappedName = mapping.getColumnLableFor(colName); String colLabel = mapping.getColumnLableFor(colName);
result = this.rowMap.get(getColumnNameByLabel(colLabel));
if (mappedName != null)
colName = mappedName;
} }
TableColumn result = this.rowMap.get(getColumnNameByLabel(colName));
return result; return result;
} }
@@ -627,6 +642,17 @@ public abstract class AbstractTable implements Table {
} }
} }
public String aggregateColumn(String pColName, String pSeparator) {
String result = "";
ListColumn col = (ListColumn) this.getColumnByLabel(getColumnNameByLabel(pColName));
if (col != null) {
result = col.aggregate(pSeparator);
}
return result;
}
public String asCsvData(TableColumn pOutputOrdering) { public String asCsvData(TableColumn pOutputOrdering) {
String result = ""; String result = "";
boolean numberRows = this.optionIsSet(OPTION_NUMBER_ROWS); boolean numberRows = this.optionIsSet(OPTION_NUMBER_ROWS);
@@ -797,33 +823,20 @@ public abstract class AbstractTable implements Table {
return asHTML(null); return asHTML(null);
} }
private int createHeaderRow(Collection<String> colNames) {
int noColumnsCreated = 0;
for (String colName : colNames) {
if (this.colNames.get(colName) == null) {
this.addColumn(colName);
noColumnsCreated += 1;
}
}
return noColumnsCreated;
}
public void append(Table pSrcTable) { public void append(Table pSrcTable) {
int noRows = pSrcTable.lenght(); int noRows = pSrcTable.lenght();
Collection<String> colNames = pSrcTable.getColumnNames(); Collection<String> colNames = pSrcTable.getColumnNames();
createHeaderRow(colNames); for (int rowNo = pSrcTable.getFirstRowNo(); rowNo < noRows; rowNo++) {
int firstRowNoInSrcTable = pSrcTable.getFirstRowNo();
for (int rowNo = firstRowNoInSrcTable; rowNo < noRows; rowNo++) {
int newRowNo = this.newRow(); int newRowNo = this.newRow();
for (String colName : colNames) { for (String colName : colNames) {
Object value = pSrcTable.getCellValue(rowNo, colName); Object value = pSrcTable.getCellValue(rowNo, colName);
if (rowNo == 0 && this.colNames.get(colName) == null) {
this.addColumn(colName);
}
this.setCellValue(newRowNo, colName, value); this.setCellValue(newRowNo, colName, value);
} }
} }
@@ -1032,7 +1045,8 @@ public abstract class AbstractTable implements Table {
} }
public String getColumnNameByLabel(String pStyledColName) { public String getColumnNameByLabel(String pStyledColName) {
String colName = pStyledColName.split(PARAMETER_STYLE)[0]; final String[] split = pStyledColName.split(PARAMETER_STYLE);
String colName = split[0];
return colName.toUpperCase(); return colName.toUpperCase();
} }
@@ -1056,7 +1070,9 @@ public abstract class AbstractTable implements Table {
} }
public int getColumnPosition(TableColumn pCol) { public int getColumnPosition(TableColumn pCol) {
return this.rowMap.getPositionOfValue(pCol); final Integer positionOfValue = this.rowMap.getPositionOfValue(pCol);
return positionOfValue;
} }
@Override @Override

View File

@@ -36,7 +36,6 @@ import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import de.lcag.common.ExcelWorkbook.ExcelTable.ExcelColumn;
import de.lcag.common.TableComparisonResult.CellPair; import de.lcag.common.TableComparisonResult.CellPair;
import routines.LcagGeneral; import routines.LcagGeneral;
import routines.LcagLogger; import routines.LcagLogger;
@@ -59,6 +58,7 @@ public class ExcelWorkbook implements Table, Storage {
private static String dateFormatString = "yyyy-MM-dd"; private static String dateFormatString = "yyyy-MM-dd";
private FormulaEvaluator evaluator = null; private FormulaEvaluator evaluator = null;
private CreationHelper factory = null; private CreationHelper factory = null;
private String name = null;
// Map styles as Excel limits to 64000: // Map styles as Excel limits to 64000:
private HashMap<Object, CellStyle> styleMap = new HashMap<>(); private HashMap<Object, CellStyle> styleMap = new HashMap<>();
@@ -307,8 +307,7 @@ public class ExcelWorkbook implements Table, Storage {
@Override @Override
public String getName() { public String getName() {
// TODO Auto-generated method stub return ExcelWorkbook.this.getName() + "." + sheet.getSheetName();
return null;
} }
@Override @Override
@@ -348,8 +347,16 @@ public class ExcelWorkbook implements Table, Storage {
srcColumnNames.add(colName); srcColumnNames.add(colName);
if (useColumn(colName)) { if (useColumn(colName)) {
int colDublicateNo = 1;
String newColname = colName;
ExcelColumn col = new ExcelColumn(); ExcelColumn col = new ExcelColumn();
this.addColumn(colName, col);
// Add the new column to the table. Number column name if the same name already
// exists.
while (this.addColumn(newColname, col) < 0) {
colDublicateNo += 1;
newColname = colName + "_" + colDublicateNo;
}
} }
} }
@@ -434,29 +441,18 @@ public class ExcelWorkbook implements Table, Storage {
@Override @Override
public int addColumn(String pStyledColumnLabel, TableColumn pColumn, String pAfterColumnNamed) { public int addColumn(String pStyledColumnLabel, TableColumn pColumn, String pAfterColumnNamed) {
int firstRowNo = 0;
Row r = sheet.getRow(firstRowNo);
if (r == null)
sheet.createRow(firstRowNo);
int colPos = super.addColumn(pStyledColumnLabel, pColumn, pAfterColumnNamed); int colPos = super.addColumn(pStyledColumnLabel, pColumn, pAfterColumnNamed);
pColumn.setValue(firstRowNo, pStyledColumnLabel); if (colPos >= 0)
pColumn.setValue(0, pStyledColumnLabel);
return colPos; return colPos;
} }
@Override @Override
public void addColumnAtPosition(String pStyledColumnLabel, TableColumn pColumn, int pColumnPosition) { public void addColumnAtPosition(String pStyledColumnLabel, TableColumn pColumn, int pColumnPosition) {
int firstRowNo = 0;
Row r = sheet.getRow(firstRowNo);
if (r == null)
sheet.createRow(firstRowNo);
super.addColumnAtPosition(pStyledColumnLabel, pColumn, pColumnPosition); super.addColumnAtPosition(pStyledColumnLabel, pColumn, pColumnPosition);
pColumn.setValue(firstRowNo, pStyledColumnLabel); pColumn.setValue(0, pStyledColumnLabel);
} }
private void colorizeRow(int pRowNo, int noCells, IndexedColors pColor) { private void colorizeRow(int pRowNo, int noCells, IndexedColors pColor) {
@@ -662,10 +658,11 @@ public class ExcelWorkbook implements Table, Storage {
} }
} }
Map<String, ExcelTable> tables = new LinkedHashMap<>(); Map<String, AbstractTable> tables = new LinkedHashMap<>();
public ExcelWorkbook(String aFilePath, String... pColumnNameList) throws InvalidFormatException, IOException { public ExcelWorkbook(String aFilePath, String... pColumnNameList) throws InvalidFormatException, IOException {
this.filePath = AbstractTable.resolveFilePath(aFilePath, new HashMap<String, Object>()); this.filePath = AbstractTable.resolveFilePath(aFilePath, new HashMap<String, Object>());
this.name = this.filePath.getFileName().toString();
workbook = new XSSFWorkbook(); workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("Table1"); XSSFSheet sheet = workbook.createSheet("Table1");
@@ -684,6 +681,7 @@ public class ExcelWorkbook implements Table, Storage {
public ExcelWorkbook(String aFilePath, Map<String, Object> pParams) { public ExcelWorkbook(String aFilePath, Map<String, Object> pParams) {
this.filePath = AbstractTable.resolveFilePath(aFilePath, pParams); this.filePath = AbstractTable.resolveFilePath(aFilePath, pParams);
this.name = this.filePath.getFileName().toString();
} }
public ExcelWorkbook(String aFilePath) { public ExcelWorkbook(String aFilePath) {
@@ -699,8 +697,19 @@ public class ExcelWorkbook implements Table, Storage {
return filePath; return filePath;
} }
public Sheet getSheet(String pSheetName) { public List<String> getSheetNames() {
return workbook.getSheet(pSheetName); List<String> result = new ArrayList<>();
int n = workbook.getNumberOfSheets();
for (int i = 0; i < n; i++) {
result.add(workbook.getSheetName(i));
}
return result;
}
public AbstractTable getSheet(String pSheetName) {
return tables.get(pSheetName);
} }
public ExcelTable addSheet(String pSheetName) { public ExcelTable addSheet(String pSheetName) {
@@ -710,6 +719,16 @@ public class ExcelWorkbook implements Table, Storage {
return newTable; return newTable;
} }
public AbstractTable addSheet(String pSheetName, AbstractTable pTable) {
this.tables.put(pSheetName, pTable);
return pTable;
}
public void addSheet(String pSheetName, GenericComparisonResultTable pCmpResult) {
// TODO Auto-generated method stub, should add the data to a new Excel Sheet
}
@Override @Override
public void saveAs(String pFilePath) throws IOException { public void saveAs(String pFilePath) throws IOException {
File aFile = new File(pFilePath); File aFile = new File(pFilePath);
@@ -815,13 +834,13 @@ public class ExcelWorkbook implements Table, Storage {
} }
@Override @Override
public ExcelColumn addColumn(String aName) { public TableColumn addColumn(String aName) {
return getFirstTable().addColumn(aName); return getFirstTable().addColumn(aName);
} }
@Override @Override
public String getName() { public String getName() {
return getFirstTable().getName(); return name;
} }
@Override @Override
@@ -835,33 +854,17 @@ public class ExcelWorkbook implements Table, Storage {
return getFirstTable().newRow(); return getFirstTable().newRow();
} }
ExcelTable getFirstTable() { public AbstractTable getFirstTable() {
ExcelTable result = firstTable; AbstractTable result = firstTable;
if (result == null) { if (result == null) {
String filePathString = this.filePath.toString(); String filePathString = this.filePath.toString();
File file = new File(filePathString); FileInputStream inputStream;
XSSFWorkbook aWorkbook = null;
try { try {
if (file.canRead()) { inputStream = new FileInputStream(new File(filePathString));
FileInputStream inputStream = new FileInputStream(file); initWorkbook(new XSSFWorkbook(inputStream));
aWorkbook = new XSSFWorkbook(inputStream);
inputStream.close();
// Workbook workbook = // Workbook workbook =
// StreamingReader.builder().rowCacheSize(100).bufferSize(4096).open(inputStream); // StreamingReader.builder().rowCacheSize(100).bufferSize(4096).open(inputStream);
} else {
aWorkbook = new XSSFWorkbook();
aWorkbook.createSheet(EXCEL_DEFAULT_SHEET_NAME);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
initWorkbook(aWorkbook);
Sheet sheet = workbook.getSheetAt(0); Sheet sheet = workbook.getSheetAt(0);
@@ -871,8 +874,13 @@ public class ExcelWorkbook implements Table, Storage {
this.tables.put(sheetName, firstTable); this.tables.put(sheetName, firstTable);
// workbook.close(); // workbook.close();
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Collection<ExcelTable> tableList = this.tables.values(); Collection<AbstractTable> tableList = this.tables.values();
result = tableList.iterator().next(); result = tableList.iterator().next();
} }
@@ -923,8 +931,8 @@ public class ExcelWorkbook implements Table, Storage {
public void integrateDifferences(WorkbookComparisonResult pDiff) throws IOException { public void integrateDifferences(WorkbookComparisonResult pDiff) throws IOException {
for (String tableName : pDiff.getDifferencesPerTable().keySet()) { for (String tableName : pDiff.getDifferencesPerTable().keySet()) {
ExcelTable myTable = this.tables.get(tableName); AbstractTable myTable = this.tables.get(tableName);
ExcelTable cmpTable = pDiff.getClientWorkbook().tables.get(tableName); AbstractTable cmpTable = pDiff.getClientWorkbook().tables.get(tableName);
TableComparisonResult diff = pDiff.getDifferencesPerTable().get(tableName); TableComparisonResult diff = pDiff.getDifferencesPerTable().get(tableName);
@@ -979,7 +987,7 @@ public class ExcelWorkbook implements Table, Storage {
} }
public void setMaxRows(int pMaxRows) { public void setMaxRows(int pMaxRows) {
for (ExcelTable t : tables.values()) { for (AbstractTable t : tables.values()) {
t.setMaxRows(pMaxRows); t.setMaxRows(pMaxRows);
} }
} }
@@ -1044,8 +1052,8 @@ public class ExcelWorkbook implements Table, Storage {
this.compareStructureTo(result, pCmpWorkbook); this.compareStructureTo(result, pCmpWorkbook);
for (String tableName : result.getCommonTables()) { for (String tableName : result.getCommonTables()) {
ExcelTable myTable = this.tables.get(tableName); AbstractTable myTable = this.tables.get(tableName);
ExcelTable cmpTable = pCmpWorkbook.tables.get(tableName); AbstractTable cmpTable = pCmpWorkbook.tables.get(tableName);
TableComparisonResult diff = myTable.compareTo(cmpTable); TableComparisonResult diff = myTable.compareTo(cmpTable);
@@ -1075,8 +1083,7 @@ public class ExcelWorkbook implements Table, Storage {
} }
public void append(CsvTable csv) { public void append(CsvTable csv) {
ExcelTable table = getFirstTable(); getFirstTable().append(csv);
table.append(csv);
} }
@Override @Override
@@ -1088,8 +1095,8 @@ public class ExcelWorkbook implements Table, Storage {
getFirstTable().setKey(rowNo); getFirstTable().setKey(rowNo);
} }
public void setOption(MappingTable mappingTable) { public void setOption(String optionUseMapping, MappingTable mappingTable) {
getFirstTable().setOption(Table.OPTION_USE_MAPPING, mappingTable); getFirstTable().setOption(optionUseMapping, mappingTable);
} }
@@ -1112,4 +1119,5 @@ public class ExcelWorkbook implements Table, Storage {
public int getNoColumns() { public int getNoColumns() {
return getFirstTable().getNoColumns(); return getFirstTable().getNoColumns();
} }
} }

View File

@@ -4,7 +4,7 @@ import java.util.Set;
import routines.LcagGeneral; import routines.LcagGeneral;
public class GenericComparisonResultTable extends HashTable { public class GenericComparisonResultTable extends ExcelHashTable {
public static final String FILE_PATH_COLUMN = "FilePath"; public static final String FILE_PATH_COLUMN = "FilePath";
public static final String SOURCE_ROW_NUMBER_COLUMN = "Src_RowNo"; public static final String SOURCE_ROW_NUMBER_COLUMN = "Src_RowNo";
public static final String COMPARE_ROW_NUMBER_COLUMN = "Cmp_RowNo"; public static final String COMPARE_ROW_NUMBER_COLUMN = "Cmp_RowNo";
@@ -27,6 +27,16 @@ public class GenericComparisonResultTable extends HashTable {
compareTables(); compareTables();
} }
public GenericComparisonResultTable(ExcelWorkbook pSrcTable, ExcelWorkbook pCmpTable) {
super(KEY_COLUMN, SOURCE_ROW_NUMBER_COLUMN, COMPARE_ROW_NUMBER_COLUMN, COLUMN_NAME_COLUMN, SOURCE_VALUE_COLUMN,
COMPARE_VALUE_COLUMN, SOURCE_COLUMN_NUMBER_COLUMN, COMPARE_COLUMN_NUMBER_COLUMN);
this.sourceTable = pSrcTable.getFirstTable();
this.compareTable = pCmpTable.getFirstTable();
compareTables();
}
private void compareTables() { private void compareTables() {
int noRows = sourceTable.lenght(); int noRows = sourceTable.lenght();
Set<String> columnNames = sourceTable.getColumnNames(); Set<String> columnNames = sourceTable.getColumnNames();

View File

@@ -19,6 +19,14 @@ public class GenericTable extends HashTable {
convertToGenericTable(); convertToGenericTable();
} }
public GenericTable(ExcelWorkbook pSrcTable) {
super(KEY_COLUMN, ROW_NUMBER_COLUMN, COLUMN_NUMBER_COLUMN, COLUMN_NAME_COLUMN, COLUMN_VALUE_COLUMN);
this.sourceTable = pSrcTable.getFirstTable();
convertToGenericTable();
}
private void convertToGenericTable() { private void convertToGenericTable() {
AbstractTable srcTable = this.sourceTable; AbstractTable srcTable = this.sourceTable;
int noRows = srcTable.lenght(); int noRows = srcTable.lenght();

View File

@@ -0,0 +1,8 @@
package de.lcag.common;
// TODO Not yet used. In future use it if a table column cannot be found by name.
public class TableColumnException extends Exception {
TableColumnException(String s) {
super(s);
}
}

View File

@@ -11,16 +11,20 @@ import java.util.List;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import de.lcag.common.Table.TableColumn; import de.lcag.common.Table.TableColumn;
import routines.LcagFileTools; import routines.LcagFileTools;
import testutils.CleanupBeforeTestCase;
class ExcelWorkbookTest extends CleanupBeforeTestCase { class ExcelWorkbookTest {
private Path resDir = Path.of("src/test/resources/ExcelTable"); private Path resDir = Path.of("src/test/resources/ExcelTable");
@BeforeAll
static void setUpBeforeClass() throws Exception {
}
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
} }
@@ -28,18 +32,12 @@ class ExcelWorkbookTest extends CleanupBeforeTestCase {
@Test @Test
void testSave() throws IOException { void testSave() throws IOException {
CsvTable csv = new CsvTable("CsvTable/NameList.csv"); CsvTable csv = new CsvTable("CsvTable/NameList.csv");
int noRows, expectedNoRows;
csv.read(); csv.read();
expectedNoRows = csv.lenght();
ExcelWorkbook excel = new ExcelWorkbook("src/test/resources/results/NameList.xlsx"); ExcelWorkbook excel = new ExcelWorkbook("src/test/resources/results/NameList.xlsx");
excel.append(csv); excel.append(csv);
excel.save(); excel.save();
noRows = excel.lenght();
assertEquals(expectedNoRows, noRows, "Creating an Excel from a CSV should have the same number of rows");
} }
@Test @Test
@@ -68,28 +66,20 @@ class ExcelWorkbookTest extends CleanupBeforeTestCase {
void testRead() throws IOException, ConfigurationException { void testRead() throws IOException, ConfigurationException {
ExcelWorkbook excel; ExcelWorkbook excel;
int noRows = -1; int noRows = -1;
String value = null; CsvTable mappingTableCSV = new CsvTable("EntityFieldMapping.csv");
excel = new ExcelWorkbook("src/test/resources/ExcelTable/NameList.xlsx"); mappingTableCSV.read();
noRows = mappingTableCSV.lenght();
assertEquals(37, noRows);
excel = new ExcelWorkbook("src/test/resources/results/NameList.xlsx");
excel.read(); excel.read();
noRows = excel.lenght(); noRows = excel.lenght();
assertEquals(4, noRows, "Table should have 4 rows"); assertEquals(4, noRows);
MappingTable mappingTable = new MappingTable("Nachname->LastName", ",", "->"); // TODO: Test read using mapping table
excel.setOption(mappingTable); MappingTable mappingTable = new MappingTable(mappingTableCSV);
excel.read();
noRows = excel.lenght();
assertEquals(4, noRows, "Re-reding the table should not change the number of rows");
value = excel.getFirstTable().getCellValue(2, "Nachname");
assertEquals("Schulze", value, "Sould resolve mapped column name and get correct value");
value = excel.getFirstTable().getCellValue(2, "LastName");
assertEquals("Schulze", value, "Using the direct name istead of mapped one should find the same value");
value = excel.getFirstTable().getCellValue(2, "FirstName");
assertEquals("Hermann", value, "Using the name of a non-mapped field should find the correct value");
} }
@Test @Test

View File

@@ -1,7 +1,6 @@
package de.lcag.common; package de.lcag.common;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -77,7 +76,7 @@ class GenericComparisonResultTableTest {
noRows = aTable2.lenght(); noRows = aTable2.lenght();
assertEquals(9087, noRows); assertEquals(9087, noRows);
//fail("Comparing with may lines not yet implemented"); // fail("Comparing with may lines not yet implemented");
GenericComparisonResultTable result = new GenericComparisonResultTable(aTable1, aTable2); GenericComparisonResultTable result = new GenericComparisonResultTable(aTable1, aTable2);
result.saveAs("result.csv"); result.saveAs("result.csv");

View File

@@ -1,7 +1,7 @@
package de.lcag.common; package de.lcag.common;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
import java.util.HashMap; import java.util.HashMap;

View File

@@ -0,0 +1,12 @@
# Configuration file for S:\WS\eclipse-2024\MDM-LCAG-JavaFunctions\src\main\java\application\FileActions.java
main_file = S:/WS/eclipse-2024/MDM-LCAG-JavaFunctions/src/test/resources/ExcelTable/CompareMasterOrig.xlsx
compare_file = CompareClient1.xlsx
list_of_keys = Name, FirstName
# Not yet implemented:
# Sheet mappings: Values
# "use sequence": compare first sheets in files, then second sheets, ...
# <main_file_sheet_name1> -> <compare_file_sheet_name1> ; <main_file_sheet_name2> -> <compare_file_sheet_name2>; ...
# Default: Sheet names must match 1:1
sheets.mapping = use sequence

View File

@@ -0,0 +1,12 @@
# Configuration file for S:\WS\eclipse-2024\MDM-LCAG-JavaFunctions\src\main\java\application\FileActions.java
main_file = S:/WS/eclipse-2024/MDM-LCAG-JavaFunctions/src/test/resources/ExcelTable/CompareMasterOrig.xlsx
compare_file = CompareClient1.xlsx
list_of_keys = Name, FirstName
# Not yet implemented:
# Sheet mappings: Values
# "use sequence": compare first sheets in files, then second sheets, ...
# <main_file_sheet_name1> -> <compare_file_sheet_name1> ; <main_file_sheet_name2> -> <compare_file_sheet_name2>; ...
# Default: Sheet names must match 1:1
sheets.mapping = M1 -> C1; M1->C2

View File

@@ -0,0 +1,9 @@
# Configuration file for S:\WS\eclipse-2024\MDM-LCAG-JavaFunctions\src\main\java\application\FileActions.java
main_file = S:/WS/eclipse-2024/MDM-LCAG-JavaFunctions/src/test/resources/ExcelTable/CompareMasterOrig.xlsx
compare_file = CompareClient1.xlsx
list_of_keys = Name, FirstName
# Not yet implemented:
main_file.colums.ignore =
compare_file.colums.ignore =

View File

@@ -0,0 +1,11 @@
# Configuration file for S:\WS\eclipse-2024\MDM-LCAG-JavaFunctions\src\main\java\application\FileActions.java
main_file = S:/WS/eclipse-2024/MDM-LCAG-JavaFunctions/src/test/resources/ExcelTable/CompareMasterOrig.xlsx
compare_file = CompareClient1.xlsx
list_of_keys = Name, FirstName
# Not yet implemented:
main_file.columns.mapping = A->A1; B->A2
compare_file.columns.mapping =
main_file.columns.mapping = <sheet-name1>:A->A1;<sheet-name2>:B->A1