Jak aplikovat vlastní logiku na nespojené oblasti

Contents
[ ]

Existují situace, kdy úplné odstranění nespojených oblastí z dokumentu během Mail Merge není žádoucí nebo vede k tomu, že dokument vypadá neúplně. K tomu může dojít, když by měla být uživateli zobrazena absence vstupních dat ve formě zprávy namísto úplného odstranění oblasti.

Jsou také chvíle, kdy samotné odstranění nevyužité oblasti nestačí, například pokud oblasti předchází název nebo je oblast obsažena v tabulce. Pokud je tato oblast nevyužitá, zůstane název a tabulka i po odstranění oblasti, která bude v dokumentu vypadat nepatřičně.

Tento článek poskytuje řešení pro ruční definování způsobu zpracování nepoužívaných oblastí v dokumentu. Základní kód pro tuto funkci je dodáván a lze jej snadno znovu použít v jiném projektu.

Logika, která se má použít pro každou oblast, je definována uvnitř třídy, která implementuje rozhraní IFieldMergingCallback. Stejným způsobem lze nastavit obslužnou rutinu Mail Merge, která řídí, jak je každé pole sloučeno, tuto obslužnou rutinu lze nastavit tak, aby prováděla akce na každém poli v nepoužívané oblasti nebo v oblasti jako celku. V rámci této obslužné rutiny můžete nastavit kód pro změnu textu oblasti, odstranění uzlů nebo prázdných řádků a buněk atd.

V této ukázce použijeme níže uvedený dokument. Obsahuje vnořené oblasti a oblast obsaženou v tabulce.

apply-custom-logic-to-unmerged-regions-aspose-words-java

Jako rychlou demonstraci můžeme spustit ukázkovou databázi na ukázkovém dokumentu s povoleným příznakem MailMergeCleanupOptions.REMOVE_UNUSED_REGIONS. Tato vlastnost automaticky odstraní nespojené oblasti z dokumentu během mail merge.

Zdroj dat obsahuje dva záznamy pro oblast StoreDetails, ale záměrně má jakákoli data pro podřízené oblasti ContactDetails pro jeden ze záznamů. Kromě toho oblast Suppliers nemá žádné datové řádky. To způsobí, že v dokumentu zůstanou nevyužité oblasti. Výsledek po sloučení dokumentu s tímto zdrojem dat je níže.

merged-regions-aspose-words-java

Jak je uvedeno na obrázku, můžete vidět, že ContactDetails oblast pro druhý záznam a Suppliers oblasti byly automaticky odstraněny motorem Mail Merge, protože nemají žádná data. Existuje však několik problémů, díky nimž tento výstupní dokument vypadá neúplně:

  • Oblast ContactDetails stále ponechává odstavec s textem “kontaktní údaje”.
  • Ve stejném případě nic nenasvědčuje tomu, že neexistují žádná telefonní čísla, pouze prázdné místo, které by mohlo vést ke zmatku.
  • Tabulka a název související s oblastí Suppliers také zůstanou po odstranění oblasti uvnitř tabulky.

Technika uvedená v tomto článku ukazuje, jak aplikovat vlastní logiku na každou nespojenou oblast, aby se těmto problémům zabránilo.

Řešení

Chcete-li ručně použít logiku pro každou nepoužitou oblast v dokumentu, využíváme výhod funkcí, které jsou již k dispozici v Aspose.Words.

Motor Mail Merge poskytuje vlastnost pro odstranění nepoužívaných oblastí pomocí příznaku MailMergeCleanupOptions.RemoveUnusedRegions. To lze deaktivovat tak, aby tyto oblasti zůstaly nedotčeny během mail merge. To nám umožňuje ponechat nespojené oblasti v dokumentu a místo toho je zpracovat ručně.

Poté můžeme využít vlastnosti MailMerge.FieldMergingCallback jako prostředek k aplikaci naší vlastní logiky na tyto nespojené oblasti během Mail Merge pomocí třídy handler implementující rozhraní IFieldMergingCallback.

Tento kód ve třídě handler je jedinou třídou, kterou budete muset upravit, abyste mohli ovládat logiku aplikovanou na nespojené oblasti. Druhý kód v tomto vzorku lze znovu použít bez úprav v jakémkoli projektu.

Tento ukázkový projekt demonstruje tuto techniku. Zahrnuje následující kroky:

  1. Spusťte Mail Merge na dokumentu pomocí zdroje dat. Příznak MailMergeCleanupOptions.RemoveUnusedRegions je prozatím deaktivován chceme, aby regiony zůstaly, abychom je mohli zpracovat ručně. Všechny oblasti bez dat zůstanou v dokumentu nespojené.
  2. Zavolejte metodu ExecuteCustomLogicOnEmptyRegions. Tato metoda je uvedena v tomto vzorku. Provádí akce, které umožňují volání zadané obslužné rutiny pro každou nespojenou oblast. Tato metoda je opakovaně použitelná a lze ji beze změny zkopírovat do jakéhokoli projektu, který ji vyžaduje (spolu s jakýmikoli závislými metodami).Tato metoda provede následující kroky:
    1. Nastaví obslužnou rutinu určenou uživatelem na vlastnost MailMerge.FieldMergingCallback.
    2. Volá metodu CreateDataSourceFromDocumentRegions, která přijímá názvy oblastí obsahující uživatele Document a ArrayList. Tato metoda vytvoří fiktivní zdroj dat obsahující tabulky pro každou nespojenou oblast v dokumentu.
    3. Provede Mail Merge na dokumentu pomocí fiktivního zdroje dat. Když je Mail Merge spuštěn s tímto zdrojem dat, umožňuje volat uživatelem zadanou obslužnou rutinu pro každou oblast unmerge a použít vlastní logiku

Kód

Implementace metody ExecuteCustomLogicOnEmptyRegions je uvedena níže. Tato metoda přijímá několik parametrů:

  1. Objekt Document obsahující nespojené oblasti, které mají být zpracovány předanou obslužnou rutinou.
  2. Třída obslužné rutiny, která definuje logiku, která se má použít na nespojené oblasti. Tento psovod musí implementovat IFieldMergingCallback rozhraní.
  3. Pomocí vhodného přetížení může metoda také přijmout třetí parametr-seznam názvů oblastí jako řetězce. Pokud je toto zadáno, budou ručně zpracovány pouze názvy oblastí zbývající v dokumentu zadaném v seznamu. Ostatní oblasti, které se vyskytnou, nebudou volány obslužnou rutinou a automaticky odstraněny. Když je zadáno přetížení pouze se dvěma parametry, každá zbývající oblast v dokumentu je zahrnuta metodou, která má být zpracována ručně.

Příklad

Ukazuje, jak spustit vlastní logiku v nepoužívaných oblastech pomocí zadané obslužné rutiny.

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java
/**
* Applies logic defined in the passed handler class to all unused regions
* in the document. This allows to manually control how unused regions are
* handled in the document.
*
* @param doc The document containing unused regions.
* @param handler The handler which implements the IFieldMergingCallback
* interface and defines the logic to be applied to each unmerged
* region.
*/
public static void executeCustomLogicOnEmptyRegions(Document doc, IFieldMergingCallback handler) throws Exception {
executeCustomLogicOnEmptyRegions(doc, handler, null); // Pass null to handle all regions found in the document.
}
/**
* Applies logic defined in the passed handler class to specific unused
* regions in the document as defined in regionsList. This allows to
* manually control how unused regions are handled in the document.
*
* @param doc The document containing unused regions.
* @param handler The handler which implements the IFieldMergingCallback
* interface and defines the logic to be applied to each unmerged
* region.
* @param regionsList A list of strings corresponding to the region names that are
* to be handled by the supplied handler class. Other regions
* encountered will not be handled and are removed automatically.
*/
public static void executeCustomLogicOnEmptyRegions(Document doc, IFieldMergingCallback handler, ArrayList regionsList) throws Exception {
// Certain regions can be skipped from applying logic to by not adding the table name inside the CreateEmptyDataSource method.
// Enable this cleanup option so any regions which are not handled by the user's logic are removed automatically.
doc.getMailMerge().setCleanupOptions(MailMergeCleanupOptions.REMOVE_UNUSED_REGIONS);
// Set the user's handler which is called for each unmerged region.
doc.getMailMerge().setFieldMergingCallback(handler);
// Execute mail merge using the dummy dataset. The dummy data source contains the table names of
// each unmerged region in the document (excluding ones that the user may have specified to be skipped). This will allow the handler
// to be called for each field in the unmerged regions.
doc.getMailMerge().executeWithRegions(createDataSourceFromDocumentRegions(doc, regionsList));
}
/**
* A helper method that creates an empty Java disconnected ResultSet with
* the specified columns.
*/
private static ResultSet createCachedRowSet(String[] columnNames) throws Exception {
RowSetMetaDataImpl metaData = new RowSetMetaDataImpl();
metaData.setColumnCount(columnNames.length);
for (int i = 0; i < columnNames.length; i++) {
metaData.setColumnName(i + 1, columnNames[i]);
metaData.setColumnType(i + 1, java.sql.Types.VARCHAR);
}
CachedRowSet rowSet = RowSetProvider.newFactory().createCachedRowSet();
;
rowSet.setMetaData(metaData);
return rowSet;
}
/**
* A helper method that adds a new row with the specified values to a
* disconnected ResultSet.
*/
private static void addRow(ResultSet resultSet, String[] values) throws Exception {
resultSet.moveToInsertRow();
for (int i = 0; i < values.length; i++)
resultSet.updateString(i + 1, values[i]);
resultSet.insertRow();
// This "dance" is needed to add rows to the end of the result set properly.
// If I do something else then rows are either added at the front or the result
// set throws an exception about a deleted row during mail merge.
resultSet.moveToCurrentRow();
resultSet.last();
}

Příklad

Definuje metodu použitou k ručnímu zpracování nespojených oblastí.

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java
/**
* Returns a DataSet object containing a DataTable for the unmerged regions
* in the specified document. If regionsList is null all regions found
* within the document are included. If an ArrayList instance is present the
* only the regions specified in the list that are found in the document are
* added.
*/
private static DataSet createDataSourceFromDocumentRegions(Document doc, ArrayList regionsList) throws Exception {
final String TABLE_START_MARKER = "TableStart:";
DataSet dataSet = new DataSet();
String tableName = null;
for (String fieldName : doc.getMailMerge().getFieldNames()) {
if (fieldName.contains(TABLE_START_MARKER)) {
tableName = fieldName.substring(TABLE_START_MARKER.length());
} else if (tableName != null) {
// Only add the table as a new DataTable if it doesn't already exists in the DataSet.
if (dataSet.getTables().get(tableName) == null) {
ResultSet resultSet = createCachedRowSet(new String[]{fieldName});
// We only need to add the first field for the handler to be called for the fields in the region.
if (regionsList == null || regionsList.contains(tableName)) {
addRow(resultSet, new String[]{"FirstField"});
}
dataSet.getTables().add(new DataTable(resultSet, tableName));
}
tableName = null;
}
}
return dataSet;
}

Tato metoda zahrnuje nalezení všech nespojených oblastí v dokumentu. To se provádí pomocí metody MailMerge.GetFieldNames. Tato metoda vrací všechna slučovací pole v dokumentu, včetně značek začátku a konce regionu (reprezentovaných sloučenými poli s předponou TableStart nebo TableEnd).

Když se objeví pole sloučení TableStart, přidá se jako nové DataTable do DataSet. Protože se oblast může objevit více než jednou (například proto, že se jedná o vnořenou oblast, kde byla nadřazená oblast sloučena s více záznamy), tabulka se vytvoří a přidá pouze v případě, že již neexistuje v DataSet.

Když byl nalezen příslušný začátek oblasti a přidán do databáze, přidá se do DataTable další pole (které odpovídá prvnímu poli v oblasti). Pro každé pole v oblasti, které má být sloučeno a předáno obslužné rutině, je nutné přidat pouze první pole.

Také jsme nastavili hodnotu pole prvního pole na " FirstField", abychom usnadnili použití logiky na první nebo jiná pole v oblasti. Zahrnutím to znamená, že není nutné tvrdě kódovat název prvního pole nebo implementovat další kód, abyste zkontrolovali, zda je aktuální pole první v kódu obslužné rutiny.

Níže uvedený kód ukazuje, jak tento systém funguje. Dokument zobrazený na začátku tohoto článku je remerged se stejným zdrojem dat, ale tentokrát, nevyužité oblasti jsou zpracovány vlastním kódem.

Příklad

Ukazuje, jak zacházet s nespojenými oblastmi po Mail Merge s uživatelem definovaným kódem.

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java
// Open the document.
Document doc = new Document(dataDir + "TestFile.doc");
// Create a data source which has some data missing.
// This will result in some regions that are merged and some that remain after executing mail merge.
DataSet data = getDataSource();
// Make sure that we have not set the removal of any unused regions as we will handle them manually.
// We achieve this by removing the RemoveUnusedRegions flag from the cleanup options by using the AND and NOT bitwise operators.
doc.getMailMerge().setCleanupOptions(doc.getMailMerge().getCleanupOptions() & ~MailMergeCleanupOptions.REMOVE_UNUSED_REGIONS);
// Execute mail merge. Some regions will be merged with data, others left unmerged.
doc.getMailMerge().executeWithRegions(data);
// The regions which contained data now would of been merged. Any regions which had no data and were
// not merged will still remain in the document.
Document mergedDoc = doc.deepClone(); //ExSkip
// Apply logic to each unused region left in the document using the logic set out in the handler.
// The handler class must implement the IFieldMergingCallback interface.
executeCustomLogicOnEmptyRegions(doc, new EmptyRegionsHandler());
// Save the output document to disk.
doc.save(dataDir + "TestFile.CustomLogicEmptyRegions1 Out.doc");

Kód provádí různé operace na základě názvu oblasti načtené pomocí vlastnosti FieldMergingArgs.TableName. Všimněte si, že v závislosti na dokumentu a oblastech můžete kódovat obslužnou rutinu a spouštět logiku závislou na každé oblasti nebo kódu, který platí pro každou nespojenou oblast v dokumentu nebo kombinaci obou.

Logika pro oblast ContactDetails zahrnuje změnu textu každého pole v oblasti ContactDetails s příslušnou zprávou, že neexistují žádná data. Názvy každého pole jsou porovnány v obslužné rutině pomocí vlastnosti FieldMergingArgs.FieldName.

Podobný proces se aplikuje na oblast Suppliers s přidáním dalšího kódu pro zpracování tabulky, která oblast obsahuje. Kód zkontroluje, zda je oblast obsažena v tabulce (protože již mohla být odstraněna). Pokud ano, odstraní z dokumentu celou tabulku i odstavec, který jí předchází, pokud je naformátován stylem nadpisu, např. “Heading 1”.

Příklad

Ukazuje, jak definovat vlastní logiku v obslužné rutině implementující IFieldMergingCallback, která je spuštěna pro nespojené oblasti v dokumentu.

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java
public static class EmptyRegionsHandler implements IFieldMergingCallback {
/**
* Called for each field belonging to an unmerged region in the
* document.
*/
public void fieldMerging(FieldMergingArgs args) throws Exception {
// Change the text of each field of the ContactDetails region individually.
if ("ContactDetails".equals(args.getTableName())) {
// Set the text of the field based off the field name.
if ("Name".equals(args.getFieldName()))
args.setText("(No details found)");
else if ("Number".equals(args.getFieldName()))
args.setText("(N/A)");
}
// Remove the entire table of the Suppliers region. Also check if the previous paragraph
// before the table is a heading paragraph and if so remove that too.
if ("Suppliers".equals(args.getTableName())) {
Table table = (Table) args.getField().getStart().getAncestor(NodeType.TABLE);
// Check if the table has been removed from the document already.
if (table.getParentNode() != null) {
// Try to find the paragraph which precedes the table before the table is removed from the document.
if (table.getPreviousSibling() != null && table.getPreviousSibling().getNodeType() == NodeType.PARAGRAPH) {
Paragraph previousPara = (Paragraph) table.getPreviousSibling();
if (isHeadingParagraph(previousPara))
previousPara.remove();
}
table.remove();
}
}
}
/**
* Returns true if the paragraph uses any Heading style e.g Heading 1 to
* Heading 9
*/
private boolean isHeadingParagraph(Paragraph para) throws Exception {
return (para.getParagraphFormat().getStyleIdentifier() >= StyleIdentifier.HEADING_1 && para.getParagraphFormat().getStyleIdentifier() <= StyleIdentifier.HEADING_9);
}
public void imageFieldMerging(ImageFieldMergingArgs args) throws Exception {
// Do Nothing
}
}

Výsledek výše uvedeného kódu je uveden níže. Nespojená pole v první oblasti jsou nahrazena informativním textem a odstranění tabulky a nadpisu umožňuje, aby dokument vypadal jako úplný.

apply-custom-logic-to-unmerged-regions-aspose-words-java-2

Kód, který odebere nadřazenou tabulku, lze také spustit v každé nepoužívané oblasti místo pouze v konkrétní oblasti odstraněním kontroly názvu tabulky. V tomto případě, pokud žádná oblast uvnitř tabulky nebyla sloučena s žádnými daty, bude oblast i tabulka kontejnerů automaticky odebrána.

Do obslužné rutiny můžeme vložit jiný kód, abychom určili, jak se zachází s nespojenými oblastmi. Použití kódu níže v obslužné rutině místo toho změní text v prvním odstavci oblasti na užitečnou zprávu, zatímco všechny následující odstavce v oblasti budou odstraněny. Tyto další odstavce jsou odstraněny, protože by zůstaly v regionu po sloučení naší zprávy.

Náhradní text je sloučen do prvního pole nastavením zadaného textu do vlastnosti FieldMergingArgs.Text. Text z této vlastnosti je sloučen do pole motorem Mail Merge.

Kód to použije pouze pro první pole v regionu zaškrtnutím vlastnosti FieldMergingArgs.FieldValue. Hodnota pole prvního pole v oblasti je označena “FirstField”. To usnadňuje implementaci tohoto typu logiky v mnoha regionech, protože není vyžadován žádný další kód.

Příklad

Ukazuje, jak nahradit nepoužívanou oblast zprávou a odstranit další odstavce.

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java
// Store the parent paragraph of the current field for easy access.
Paragraph parentParagraph = args.getField().getStart().getParentParagraph();
// Define the logic to be used when the ContactDetails region is encountered.
// The region is removed and replaced with a single line of text stating that there are no records.
if ("ContactDetails".equals(args.getTableName())) {
// Called for the first field encountered in a region. This can be used to execute logic on the first field
// in the region without needing to hard code the field name. Often the base logic is applied to the first field and
// different logic for other fields. The rest of the fields in the region will have a null FieldValue.
if ("FirstField".equals(args.getFieldValue())) {
FindReplaceOptions opts = new FindReplaceOptions();
opts.setMatchCase(false);
opts.setFindWholeWordsOnly(false);
// Remove the "Name:" tag from the start of the paragraph
parentParagraph.getRange().replace("Name:", "", opts);
// Set the text of the first field to display a message stating that there are no records.
args.setText("No records to display");
} else {
// We have already inserted our message in the paragraph belonging to the first field. The other paragraphs in the region
// will still remain so we want to remove these. A check is added to ensure that the paragraph has not already been removed.
// which may happen if more than one field is included in a paragraph.
if (parentParagraph.getParentNode() != null)
parentParagraph.remove();
}
}

Výsledný dokument po provedení výše uvedeného kódu je uveden níže. Nevyužitá oblast je nahrazena zprávou, že nejsou k dispozici žádné záznamy k zobrazení.

apply-custom-logic-to-unmerged-regions-aspose-words-java-3

Jako další příklad můžeme vložit níže uvedený kód místo kódu původně zpracovávajícího SuppliersRegion. Zobrazí se Zpráva v tabulce a sloučí se buňky namísto odebrání tabulky z dokumentu. Vzhledem k tomu, že oblast se nachází v tabulce s více buňkami, vypadá hezčí, když se buňky tabulky spojí dohromady a zpráva se vycentruje.

Příklad

Ukazuje, jak sloučit všechny nadřazené buňky nepoužívané oblasti a zobrazit zprávu v tabulce.

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java
// Replace the unused region in the table with a "no records" message and merge all cells into one.
if ("Suppliers".equals(args.getTableName())) {
if ("FirstField".equals(args.getFieldValue())) {
// We will use the first paragraph to display our message. Make it centered within the table. The other fields in other cells
// within the table will be merged and won't be displayed so we don't need to do anything else with them.
parentParagraph.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
args.setText("No records to display");
}
// Merge the cells of the table together.
Cell cell = (Cell) parentParagraph.getAncestor(NodeType.CELL);
if (cell != null) {
if (cell.isFirstCell())
cell.getCellFormat().setHorizontalMerge(CellMerge.FIRST); // If this cell is the first cell in the table then the merge is started using "CellMerge.First".
else
cell.getCellFormat().setHorizontalMerge(CellMerge.PREVIOUS); // Otherwise the merge is continued using "CellMerge.Previous".
}
}

Výsledný dokument po provedení výše uvedeného kódu je uveden níže.

apply-custom-logic-to-unmerged-regions-aspose-words-java-4

Nakonec můžeme zavolat metodu ExecuteCustomLogicOnEmptyRegions a určit názvy tabulek, které by měly být zpracovány v rámci naší metody obslužné rutiny, zatímco určíme ostatní, které mají být automaticky odstraněny.

Příklad

Ukazuje, jak určit pouze oblast ContactDetails, která má být zpracována pomocí třídy obslužné rutiny.

// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java
ArrayList<String> regions = new ArrayList<String>();
regions.add("ContactDetails");
executeCustomLogicOnEmptyRegions(doc, new EmptyRegionsHandler(), regions);

Volání tohoto přetížení zadaným ArrayList vytvoří zdroj dat, který obsahuje pouze datové řádky pro zadané oblasti. Jiné oblasti než oblast ContactDetails nebudou zpracovány a místo toho budou automaticky odstraněny motorem Mail Merge. Výsledek výše uvedeného volání pomocí kódu v našem původním obslužném programu je uveden níže.

apply-custom-logic-to-unmerged-regions-aspose-words-java-5