Cum se aplică logica personalizată Regiunilor Nemergate

Contents
[ ]

Există unele situații în care eliminarea completă a regiunilor nemergate din document în timpul Mail Merge nu este dorită sau rezultă în documentul care pare incomplet. Acest lucru poate apărea atunci când absența datelor de intrare ar trebui să fie afișată utilizatorului sub forma unui mesaj în loc ca regiunea să fie complet eliminată.

Există, de asemenea, momente în care eliminarea regiunii neutilizate în sine nu este suficientă, de exemplu, dacă regiunea este precedată de un titlu sau dacă regiunea este conținută într-un tabel. Dacă această regiune este neutilizată, atunci titlul și tabelul vor rămâne în continuare după ce regiunea este eliminată, care va părea deplasată în document.

Acest articol oferă o soluție pentru a defini manual modul în care sunt gestionate regiunile neutilizate din document. Codul de bază pentru această funcționalitate este furnizat și poate fi reutilizat cu ușurință într-un alt proiect.

Logica care trebuie aplicată fiecărei regiuni este definită în interiorul unei clase care implementează interfața IFieldMergingCallback. În același mod, un handler Mail Merge poate fi configurat pentru a controla modul în care fiecare câmp este fuzionat, acest handler poate fi configurat pentru a efectua acțiuni pe fiecare câmp într-o regiune neutilizată sau pe regiune în ansamblu. În cadrul acestui handler, puteți seta codul pentru a schimba textul unei regiuni, pentru a elimina noduri sau rânduri și celule goale etc.

În acest eșantion, vom folosi documentul afișat mai jos. Conține regiuni imbricate și o regiune conținută într-un tabel.

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

Ca o demonstrație rapidă, putem executa o bază de date eșantion pe documentul eșantion cu indicatorul MailMergeCleanupOptions.REMOVE_UNUSED_REGIONS activat. Această proprietate va elimina automat regiunile nemergate din document în timpul unui mail merge.

Sursa de date include două înregistrări pentru regiunea StoreDetails, dar are în mod intenționat date pentru regiunile ContactDetails copil pentru una dintre înregistrări. În plus, regiunea Suppliers nu are nici rânduri de date. Acest lucru va face ca regiunile neutilizate să rămână în document. Rezultatul după îmbinarea documentului cu această sursă de date este mai jos.

merged-regions-aspose-words-java

După cum se menționează în imagine, puteți vedea că regiunea ContactDetails pentru a doua înregistrare și regiunile Suppliers au fost eliminate automat de motorul mail merge, deoarece nu au date. Cu toate acestea, există câteva probleme care fac ca acest document de ieșire să pară incomplet:

  • Regiunea ContactDetails lasă încă un paragraf cu textul"detalii de Contact".
  • În același caz, nu există niciun indiciu că nu există numere de telefon, ci doar un spațiu gol care ar putea duce la confuzie.
  • Tabelul și titlul legate de regiunea Suppliers rămân, de asemenea, după ce regiunea din interiorul tabelului este eliminată.

Tehnica furnizată în acest articol demonstrează modul de aplicare a logicii personalizate fiecărei regiuni nemergate pentru a evita aceste probleme.

Soluția

Pentru a aplica manual logica fiecărei regiuni neutilizate din document, profităm de caracteristicile deja disponibile în Aspose.Words.

Motorul Mail Merge oferă o proprietate pentru a elimina regiunile neutilizate prin steagul MailMergeCleanupOptions.RemoveUnusedRegions. Acest lucru poate fi dezactivat astfel încât astfel de regiuni să fie lăsate neatinse în timpul unui mail merge. Acest lucru ne permite să lăsăm regiunile nemergate din document și să le gestionăm manual noi înșine.

Putem profita apoi de proprietatea MailMerge.FieldMergingCallback ca mijloc de a aplica propria noastră logică personalizată acestor regiuni nemergate în timpul Mail Merge prin utilizarea unei clase de manipulare care implementează interfața IFieldMergingCallback.

Acest cod din clasa handler este singura clasă pe care va trebui să o modificați pentru a controla logica aplicată regiunilor nemergate. Celălalt cod din acest eșantion poate fi reutilizat fără modificări în niciun proiect.

Acest proiect de probă demonstrează această tehnică. Aceasta implică următorii pași:

  1. Executați Mail Merge pe document folosind sursa de date. Steagul MailMergeCleanupOptions.RemoveUnusedRegions este dezactivat deocamdată vrem ca regiunile să rămână astfel încât să le putem gestiona manual. Orice regiuni fără date vor fi lăsate nemergate în document.
  2. Apelați metoda ExecuteCustomLogicOnEmptyRegions. Această metodă este furnizată în acest eșantion. Efectuează acțiuni care permit apelarea manipulatorului specificat pentru fiecare regiune nemergată. Această metodă este reutilizabilă și poate fi copiată nealterată în orice proiect care o necesită (împreună cu orice metode dependente).Această metodă execută următorii pași:
    1. Setează manipulatorul specificat de utilizator la proprietatea MailMerge.FieldMergingCallback.
    2. Apelează metoda CreateDataSourceFromDocumentRegions care acceptă numele regiunilor Document și ArrayList ale utilizatorului care conțin. Această metodă va crea o sursă de date fictivă care conține tabele pentru fiecare regiune nemergată din document.
    3. Execută Mail Merge pe document folosind sursa de date fictivă. Când Mail Merge este executat cu această sursă de date, permite apelarea manipulatorului specificat de utilizator pentru fiecare regiune unmerge și logica personalizată aplicată

Codul

Implementarea metodei ExecuteCustomLogicOnEmptyRegions se găsește mai jos. Această metodă acceptă mai mulți parametri:

  1. Obiectul Document care conține regiuni nemergate care urmează să fie manipulate de handler-ul trecut.
  2. Clasa handler care definește logica de aplicat regiunilor nemergate. Acest handler trebuie să pună în aplicare IFieldMergingCallback interfață.
  3. Prin utilizarea supraîncărcării corespunzătoare, metoda poate accepta și un al treilea parametru-o listă de nume de regiuni ca șiruri. Dacă acest lucru este specificat, atunci numai numele regiunilor rămase din documentul specificat în listă vor fi tratate manual. Alte regiuni care sunt întâlnite nu vor fi apelate de către handler și eliminate automat. Când este specificată supraîncărcarea cu doar doi parametri, fiecare regiune rămasă în document este inclusă prin metoda care trebuie gestionată manual.

Exemplu

Arată cum să execute logica personalizat pe regiuni neutilizate folosind handler specificat.

// 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();
}

Exemplu

Definește metoda utilizată pentru a gestiona manual regiunile nemergate.

// 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;
}

Această metodă implică găsirea tuturor regiunilor nemergate din document. Acest lucru se realizează folosind metoda MailMerge.GetFieldNames. Această metodă returnează toate câmpurile de îmbinare din document, inclusiv markerii de început și de sfârșit ai regiunii (reprezentați prin câmpuri de îmbinare cu prefixul TableStart sau TableEnd).

Când se întâlnește un câmp de îmbinare TableStart, Acesta este adăugat ca un nou DataTable la DataSet. Deoarece o regiune poate apărea de mai multe ori (de exemplu, deoarece este o regiune imbricată în care regiunea părinte a fost fuzionată cu mai multe înregistrări), tabelul este creat și adăugat numai dacă nu există deja în DataSet.

Când a fost găsit un început de regiune adecvat și adăugat la baza de date, câmpul următor (care corespunde primului câmp din regiune) este adăugat la DataTable. Este necesar să se adauge doar primul câmp pentru fiecare câmp din regiune care urmează să fie fuzionat și transmis manipulatorului.

De asemenea, setăm valoarea câmpului primului câmp la “FirstField” pentru a facilita aplicarea logicii primului sau altor câmpuri din regiune. Prin includerea acestui lucru înseamnă că nu este necesar să codificați cu greu numele primului câmp sau să implementați cod suplimentar pentru a verifica dacă câmpul curent este primul din Codul handler.

Codul de mai jos demonstrează modul în care funcționează acest sistem. Documentul prezentat la începutul acestui articol este remerged cu aceeași sursă de date, dar de data aceasta, regiunile neutilizate sunt manipulate de cod personalizat.

Exemplu

Arată cum să se ocupe de regiuni unmerged după Mail Merge cu cod definit de utilizator.

// 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");

Codul efectuează diferite operații bazate pe numele regiunii preluate folosind proprietatea FieldMergingArgs.TableName. Rețineți că, în funcție de document și regiuni, puteți codifica handler-ul pentru a rula logica în funcție de fiecare regiune sau cod care se aplică fiecărei regiuni nemergate din document sau unei combinații a ambelor.

Logica pentru regiunea ContactDetails implică schimbarea textului fiecărui câmp din regiunea ContactDetails cu un mesaj adecvat care să ateste că nu există date. Numele fiecărui câmp sunt potrivite în cadrul manipulatorului folosind proprietatea FieldMergingArgs.FieldName.

Un proces similar este aplicat regiunii Suppliers cu adăugarea unui cod suplimentar pentru a gestiona tabelul care conține Regiunea. Codul va verifica dacă regiunea este conținută într-un tabel (deoarece este posibil să fi fost deja eliminată). Dacă este, va elimina întregul tabel din document, precum și paragraful care îl precede, atâta timp cât este formatat cu un stil de titlu, de exemplu “Heading 1”.

Exemplu

Arată cum se definește logica personalizată într-un handler care implementează IFieldMergingCallback care este executat pentru regiunile nemergate din document.

// 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
}
}

Rezultatul codului de mai sus este prezentat mai jos. Câmpurile nemergate din prima regiune sunt înlocuite cu text informativ, iar eliminarea tabelului și a titlului permite documentului să pară complet.

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

Codul care elimină tabelul părinte ar putea fi, de asemenea, făcut să ruleze pe fiecare regiune neutilizată în loc de doar o anumită regiune prin eliminarea cecului pentru numele tabelului. În acest caz, dacă o regiune din interiorul unui tabel nu a fost îmbinată cu nicio dată, atât Regiunea, cât și tabelul container vor fi eliminate automat.

Putem introduce cod diferit în handler pentru a controla modul în care sunt gestionate regiunile nemergate. Utilizarea codului de mai jos în handler va schimba textul din primul paragraf al regiunii într-un mesaj util, în timp ce orice paragrafe ulterioare din regiune sunt eliminate. Aceste alte paragrafe sunt eliminate, deoarece ar rămâne în regiune după îmbinarea mesajului nostru.

Textul de înlocuire este îmbinat în primul câmp prin setarea textului specificat în proprietatea FieldMergingArgs.Text. Textul din această proprietate este îmbinat în câmp de motorul mail merge.

Codul aplică acest lucru numai pentru primul câmp din regiune, verificând proprietatea FieldMergingArgs.FieldValue. Valoarea câmpului primului câmp din regiune este marcată cu" FirstField". Acest lucru face ca acest tip de logică să fie mai ușor de implementat în multe regiuni, deoarece nu este necesar un cod suplimentar.

Exemplu

Afișează cum să înlocuiți o regiune neutilizată cu un mesaj și să eliminați paragrafe suplimentare.

// 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();
}
}

Documentul rezultat după executarea codului de mai sus este prezentat mai jos. Regiunea neutilizată este înlocuită cu un mesaj care indică faptul că nu există înregistrări de afișat.

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

Ca un alt exemplu, putem introduce codul de mai jos în locul codului care a gestionat inițial SuppliersRegion. Aceasta va afișa un mesaj în tabel și va îmbina celulele în loc să elimine tabelul din document. Deoarece regiunea se află într-un tabel cu mai multe celule, pare mai frumos ca celulele tabelului să fuzioneze și mesajul să fie centrat.

Exemplu

Arată cum să îmbinați toate celulele părinte ale unei regiuni neutilizate și să afișați un mesaj în tabel.

// 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".
}
}

Documentul rezultat după executarea codului de mai sus este prezentat mai jos.

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

În cele din urmă, putem apela metoda ExecuteCustomLogicOnEmptyRegions și putem specifica numele tabelelor care ar trebui tratate în cadrul metodei noastre handler, specificând în același timp altele care vor fi eliminate automat.

Exemplu

Arată cum să specificați numai regiunea ContactDetails care trebuie gestionată prin clasa handler.

// 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);

Apelarea acestei supraîncărcări cu ArrayList specificat va crea sursa de date care conține doar rânduri de date pentru regiunile specificate. Regiunile altele decât regiunea ContactDetails nu vor fi gestionate și vor fi eliminate automat de motorul mail merge. Rezultatul apelului de mai sus folosind codul din handler-ul nostru original este prezentat mai jos.

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