diff --git a/domino-jna/src/main/java/com/mindoo/domino/jna/NotesDatabase.java b/domino-jna/src/main/java/com/mindoo/domino/jna/NotesDatabase.java index 5ad98993..8f025ff2 100755 --- a/domino-jna/src/main/java/com/mindoo/domino/jna/NotesDatabase.java +++ b/domino-jna/src/main/java/com/mindoo/domino/jna/NotesDatabase.java @@ -29,9 +29,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; -import com.mindoo.domino.jna.NotesSearch.ISearchMatch; import com.mindoo.domino.jna.NotesReplicationHistorySummary.ReplicationDirection; +import com.mindoo.domino.jna.NotesSearch.ISearchMatch; import com.mindoo.domino.jna.constants.AclFlag; import com.mindoo.domino.jna.constants.AclLevel; import com.mindoo.domino.jna.constants.CopyDatabase; @@ -50,6 +52,7 @@ import com.mindoo.domino.jna.constants.ReplicateOption; import com.mindoo.domino.jna.constants.Search; import com.mindoo.domino.jna.constants.UpdateNote; +import com.mindoo.domino.jna.design.NotesForm; import com.mindoo.domino.jna.directory.DirectoryScanner; import com.mindoo.domino.jna.directory.DirectoryScanner.DatabaseData; import com.mindoo.domino.jna.directory.DirectoryScanner.SearchResultData; @@ -66,6 +69,7 @@ import com.mindoo.domino.jna.gc.NotesGC; import com.mindoo.domino.jna.internal.DisposableMemory; import com.mindoo.domino.jna.internal.FTSearchResultsDecoder; +import com.mindoo.domino.jna.internal.Handle; import com.mindoo.domino.jna.internal.INotesNativeAPI32; import com.mindoo.domino.jna.internal.INotesNativeAPI64; import com.mindoo.domino.jna.internal.Mem32; @@ -102,6 +106,7 @@ import com.mindoo.domino.jna.utils.NotesNamingUtils.Privileges; import com.mindoo.domino.jna.utils.NotesStringUtils; import com.mindoo.domino.jna.utils.PlatformUtils; +import com.mindoo.domino.jna.utils.Ref; import com.mindoo.domino.jna.utils.SignalHandlerUtil; import com.mindoo.domino.jna.utils.SignalHandlerUtil.IBreakHandler; import com.mindoo.domino.jna.utils.StringTokenizerExt; @@ -2560,6 +2565,10 @@ public NotesNote findDesignNote(String name, NoteClass noteType) { * @return note id or 0 if not found */ public int findDesignNoteId(String name, NoteClass noteType) { + if (StringUtil.isEmpty(name)) { + return 0; + } + checkHandle(); IntByReference retNoteID = new IntByReference(); retNoteID.setValue(0); @@ -2572,7 +2581,7 @@ public int findDesignNoteId(String name, NoteClass noteType) { flagsPatternMem = NotesStringUtils.toLMBCS(NotesConstants.DFLAGPAT_VIEWS_AND_FOLDERS_DESIGN, true); } else if (noteType == NoteClass.FILTER) { - flagsPatternMem = NotesStringUtils.toLMBCS(NotesConstants.DFLAGPAT_TOOLSRUNMACRO, true); + flagsPatternMem = NotesStringUtils.toLMBCS(NotesConstants.DFLAGPAT_AGENTSLIST, true); } short result; @@ -2582,8 +2591,31 @@ else if (noteType == NoteClass.FILTER) { else { result = NotesNativeAPI32.get().NIFFindDesignNoteExt(m_hDB32, nameMem, noteTypeShort, flagsPatternMem, retNoteID, 0); } - - if ((result & NotesConstants.ERR_MASK)==1028) { + + if (noteType==NoteClass.FORM) { + if (((result & NotesConstants.ERR_MASK)==INotesErrorConstants.ERR_NOT_FOUND) || + ((result & NotesConstants.ERR_MASK)==INotesErrorConstants.ERR_INVALID_NOTE) || + retNoteID.getValue()==0) { + + IntByReference retbIsPrivate = new IntByReference(); + + if (PlatformUtils.is64Bit()) { + result = NotesNativeAPI64.get().DesignLookupNameFE(m_hDB64, noteTypeShort, + null, nameMem, (short) ((nameMem.size()-1) & 0xffff), + NotesConstants.DGN_STRIPUNDERS | NotesConstants.DGN_SKIPSYNONYMS | NotesConstants.DGN_ONLYSHARED, + retNoteID, retbIsPrivate, null, null); + } + else { + result = NotesNativeAPI32.get().DesignLookupNameFE(m_hDB32, noteTypeShort, + null, nameMem, (short) ((nameMem.size()-1) & 0xffff), + NotesConstants.DGN_STRIPUNDERS | NotesConstants.DGN_SKIPSYNONYMS | NotesConstants.DGN_ONLYSHARED, + retNoteID, retbIsPrivate, null, null); + } + } + } + + if (((result & NotesConstants.ERR_MASK)==INotesErrorConstants.ERR_NOT_FOUND) || + ((result & NotesConstants.ERR_MASK)==INotesErrorConstants.ERR_INVALID_NOTE)) { return 0; } @@ -4196,7 +4228,7 @@ public void hardDeleteNote(int softDelNoteId) { /** * This function reads a "soft deleted" note into memory.
* Its input is a database handle and a note ID within that database.
- * Use {@link NotesNote#update(EnumSet)} to restore this "soft deleted" note. + * Use {@link NotesNote#update(Set)} to restore this "soft deleted" note. * @param noteId The ID of the "soft deleted" note to open * @return note @@ -4313,7 +4345,7 @@ public NotesNote openNoteById(int noteId) { * @param openFlags Flags that control the manner in which the note is opened. This, in turn, controls what information about the note is available to you and how it is structured. The flags are defined in {@link OpenNote}. * @return note or null if not found */ - public NotesNote openNoteById(int noteId, EnumSet openFlags) { + public NotesNote openNoteById(int noteId, Set openFlags) { checkHandle(); int openFlagsBitmask = toNoteOpenOptions(openFlags); @@ -7837,4 +7869,290 @@ public NotesIDTable getIDTableForFolder(int folderNoteId, boolean validateIds) { public NotesNoteCollection createNoteCollection() { return new NotesNoteCollection(this); } + + private NotesIDTable getAllDesignElement(NoteClass noteClass) { + checkHandle(); + + Ref idTable = new Ref<>(new NotesIDTable()); + + boolean designEnumSuccess = false; + + short result = 0; + + NotesCallbacks.b64_DESIGNENUMPROC callback64; + NotesCallbacks.b32_DESIGNENUMPROC callback32; + + if (PlatformUtils.is64Bit()) { + callback32 = null; + + callback64 = new NotesCallbacks.b64_DESIGNENUMPROC() { + + @Override + public short invoke(Pointer routineParameter, long hDB, int noteID, + NotesUniversalNoteIdStruct noteUNID, short noteClass, Pointer summary, int designType) { + + idTable.get().addNote(noteID); + return 0; + } + }; + } + else { + callback64 = null; + + if (PlatformUtils.isWin32()) { + callback32 = new Win32NotesCallbacks.DESIGNENUMPROCWin32() { + + @Override + public short invoke(Pointer routineParameter, int hDB, int noteID, + NotesUniversalNoteIdStruct noteUNID, short noteClass, Pointer summary, int designType) { + + idTable.get().addNote(noteID); + return 0; + } + + }; + } + else { + callback32 = new NotesCallbacks.b32_DESIGNENUMPROC() { + + @Override + public short invoke(Pointer routineParameter, int hDB, int noteID, + NotesUniversalNoteIdStruct noteUNID, short noteClass, Pointer summary, int designType) { + + idTable.get().addNote(noteID); + return 0; + } + }; + } + } + + if (noteClass == NoteClass.VIEW || noteClass == NoteClass.FILTER || + noteClass == NoteClass.FORM) { + + String designFlags; + if (noteClass == NoteClass.VIEW) { + designFlags = NotesConstants.DFLAGPAT_VIEWS_AND_FOLDERS_DESIGN; + } + else if (noteClass == NoteClass.FILTER) { + designFlags = NotesConstants.DFLAGPAT_AGENTSLIST; + } + else { + designFlags = NotesConstants.DFLAGPAT_VIEWFORM_ALL_VERSIONS; + } + Memory designFlagsMem = NotesStringUtils.toLMBCS(designFlags, true); + + if (PlatformUtils.is64Bit()) { + result = AccessController.doPrivileged((PrivilegedAction) () -> { + return NotesNativeAPI64.get().DesignEnum2(m_hDB64, (short) (noteClass.getValue() &0xffff), + designFlagsMem, + NotesConstants.DGN_ONLYSHARED, callback64, null, null, null); + }); + + if (result==0 && noteClass == NoteClass.FORM) { + //add subforms + Memory subformDesignFlagsMem = NotesStringUtils.toLMBCS(NotesConstants.DFLAGPAT_SUBFORM_ALL_VERSIONS, true); + + result = AccessController.doPrivileged((PrivilegedAction) () -> { + return NotesNativeAPI64.get().DesignEnum2(m_hDB64, (short) (noteClass.getValue() &0xffff), + subformDesignFlagsMem, + NotesConstants.DGN_ONLYSHARED, callback64, null, null, null); + }); + } + + if (result == 0) { + designEnumSuccess = true; + } + } + else { + result = AccessController.doPrivileged((PrivilegedAction) () -> { + return NotesNativeAPI32.get().DesignEnum2(m_hDB32, (short) (noteClass.getValue() &0xffff), + designFlagsMem, + NotesConstants.DGN_ONLYSHARED, callback32, null, null, null); + }); + NotesErrorUtils.checkResult(result); + + if (result==0 && noteClass == NoteClass.FORM) { + //add subforms + Memory subformDesignFlagsMem = NotesStringUtils.toLMBCS(NotesConstants.DFLAGPAT_SUBFORM_ALL_VERSIONS, true); + + result = AccessController.doPrivileged((PrivilegedAction) () -> { + return NotesNativeAPI32.get().DesignEnum2(m_hDB32, (short) (noteClass.getValue() &0xffff), + subformDesignFlagsMem, + NotesConstants.DGN_ONLYSHARED, callback32, null, null, null); + }); + NotesErrorUtils.checkResult(result); + } + + if (result == 0) { + designEnumSuccess = true; + } + } + + if (result != INotesErrorConstants.ERR_NOACCESS) { + NotesErrorUtils.checkResult(result); + } + } + + if (!designEnumSuccess) { + idTable.get().recycle(); + + // non-views, and v3 servers + // first get all the public design notes + Handle hIDTable; + if (PlatformUtils.is64Bit()) { + LongByReference rethIDTable = new LongByReference(); + result = NotesNativeAPI64.get().DesignGetNoteTable(m_hDB64, + (short) (noteClass.getValue() & 0xffff), rethIDTable); + NotesErrorUtils.checkResult(result); + hIDTable = new Handle(rethIDTable.getValue()); + } + else { + IntByReference rethIDTable = new IntByReference(); + result = NotesNativeAPI32.get().DesignGetNoteTable(m_hDB32, + (short) (noteClass.getValue() & 0xffff), rethIDTable); + NotesErrorUtils.checkResult(result); + hIDTable = new Handle(rethIDTable.getValue()); + } + + idTable.set(new NotesIDTable(hIDTable, false)); + + // if we're getting views, remove any navigator notes; + // if we're doing agents, remove script libs + + if (noteClass == NoteClass.VIEW || noteClass == NoteClass.FILTER || + noteClass == NoteClass.FORM) { + + boolean isView = noteClass == NoteClass.VIEW; + boolean isForm = noteClass == NoteClass.FORM; + + Set noteIdsToRemove = new HashSet<>(); + + for (Integer currNoteId : idTable.get()) { + NotesNote currNote = null; + try { + currNote = openNoteById(currNoteId, EnumSet.of(OpenNote.SUMMARY)); + } + catch (NotesError e) { + //ignore + } + + if (currNote!=null) { + String flags = currNote.getItemValueString(NotesConstants.DESIGN_FLAGS); + + if (isView && flags.contains(NotesConstants.DESIGN_FLAG_VIEWMAP)) { + noteIdsToRemove.add(currNoteId); + } + else if (isForm && ( + flags.contains(NotesConstants.DESIGN_FLAG_IMAGE_RESOURCE) || + flags.contains(NotesConstants.DESIGN_FLAG_WEBPAGE) || + flags.contains(NotesConstants.DESIGN_FLAG_JAVA_RESOURCE) || + flags.contains(NotesConstants.DESIGN_FLAG_FRAMESET) || + flags.contains(NotesConstants.DESIGN_FLAG_HTMLFILE) || + flags.contains(NotesConstants.DESIGN_FLAG_JSP) || + flags.contains(NotesConstants.DESIGN_FLAG_SACTIONS) || + flags.contains(NotesConstants.DESIGN_FLAG_STYLESHEET_RESOURCE) + )) { + noteIdsToRemove.add(currNoteId); + } + else if (!isView && !isForm && ( + flags.contains(NotesConstants.DESIGN_FLAG_SCRIPTLIB) || + flags.contains(NotesConstants.DESIGN_FLAG_DATABASESCRIPT) || + flags.contains(NotesConstants.DESIGN_FLAG_SITEMAP) + )) { + noteIdsToRemove.add(currNoteId); + } + currNote.recycle(); + } + } + } + } + + // Now use a lower level one to get private notes + + String designFlags; + + /* this call might fail if the db is on a V3 server. Ignore ERR_NOACCESS return code */ + if (noteClass == NoteClass.VIEW) { + designFlags = NotesConstants.DFLAGPAT_VIEWS_AND_FOLDERS; + } + else if (noteClass == NoteClass.FILTER) { + designFlags = NotesConstants.DFLAGPAT_AGENTSLIST; + } + else { + designFlags = null; + } + Memory designFlagsMem = designFlags==null ? null : NotesStringUtils.toLMBCS(designFlags, true); + + if (PlatformUtils.is64Bit()) { + result = AccessController.doPrivileged((PrivilegedAction) ()->{ + return NotesNativeAPI64.get().DesignEnum2(m_hDB64, (short) (noteClass.getValue() & 0xffff), + designFlagsMem, NotesConstants.DGN_ONLYPRIVATE | NotesConstants.DGN_ALLPRIVATE, + callback64, null, null, null); + }); + } + else { + result = AccessController.doPrivileged((PrivilegedAction) ()->{ + return NotesNativeAPI32.get().DesignEnum2(m_hDB32, (short) (noteClass.getValue() & 0xffff), + designFlagsMem, NotesConstants.DGN_ONLYPRIVATE | NotesConstants.DGN_ALLPRIVATE, + callback32, null, null, null); + }); + } + + if (result==0 || result==INotesErrorConstants.ERR_NOACCESS) { + return idTable.get(); + } + NotesErrorUtils.checkResult(result); + return new NotesIDTable(); //should not be reached, because checkResult throws an Exception + } + + /** + * Returns the note ids of all forms and subforms + * + * @return note ids + */ + public NotesIDTable getFormNoteIds() { + return getAllDesignElement(NoteClass.FORM); + } + + /** + * Returns a stream of database forms (lazily loaded) + * + * @return forms + */ + public Stream getForms() { + NotesIDTable idTable = getFormNoteIds(); + return StreamSupport + .stream(idTable.spliterator(), false) + .map((noteId) -> { + return openNoteById(noteId); + }) + .filter((note) -> { + return note!=null; + }) + .map((note) -> { + return new NotesForm(note); + }); + } + + /** + * Finds a form or subform in a database given the form name + * + * @param formName form name + * @return form or null if not found + */ + public NotesForm getForm(String formName) { + if (StringUtil.isEmpty(formName)) { + return null; + } + + int formNoteId = findDesignNoteId(formName, NoteClass.FORM); + if (formNoteId!=0) { + NotesNote formNote = openNoteById(formNoteId); + if (formNote!=null) { + return new NotesForm(formNote); + } + } + + return null; + } } diff --git a/domino-jna/src/main/java/com/mindoo/domino/jna/design/NotesForm.java b/domino-jna/src/main/java/com/mindoo/domino/jna/design/NotesForm.java new file mode 100644 index 00000000..51657751 --- /dev/null +++ b/domino-jna/src/main/java/com/mindoo/domino/jna/design/NotesForm.java @@ -0,0 +1,264 @@ +package com.mindoo.domino.jna.design; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +import com.mindoo.domino.jna.NotesNote; +import com.mindoo.domino.jna.constants.ItemType; +import com.mindoo.domino.jna.constants.UpdateNote; +import com.mindoo.domino.jna.internal.NotesConstants; +import com.mindoo.domino.jna.richtext.FieldInfo; +import com.mindoo.domino.jna.richtext.IRichTextNavigator; +import com.mindoo.domino.jna.richtext.RichTextUtils; +import com.mindoo.domino.jna.utils.ListUtil; +import com.mindoo.domino.jna.utils.NotesNamingUtils; + +/** + * Represents a form or subform in a database + * + * @author Karsten Lehmann + */ +public class NotesForm { + private int m_formNoteId; + private NotesNote m_formNote; + private String m_formName; + private List m_formAliases; + + /** + * Creates a new instance + * + * @param formNote form note + */ + public NotesForm(NotesNote formNote) { + m_formNote = formNote; + m_formNoteId = formNote.getNoteId(); + } + + /** + * Returns the underlying form note + * + * @return note + */ + public NotesNote getNote() { + return m_formNote; + } + + public int getNoteId() { + return m_formNoteId; + } + + private void parseNameAndAlias() { + if (m_formName==null || m_formAliases==null) { + List formNameAndAliases = m_formNote.getItemValueStringList(NotesConstants.FIELD_TITLE); + if (formNameAndAliases==null || formNameAndAliases.isEmpty()) { + m_formName = ""; + m_formAliases = Collections.emptyList(); + } + else if (formNameAndAliases.size()==1) { + m_formName = formNameAndAliases.get(0); + m_formAliases = Collections.emptyList(); + } + else { + m_formName = formNameAndAliases.get(0); + m_formAliases = new ArrayList<>(); + for (int i=1; i getAliases() { + parseNameAndAlias(); + return m_formAliases; + } + + /** + * Indicates whether this is a form or a subform + * + * @return true if subform + */ + public boolean isSubForm() { + String flags = m_formNote.getItemValueString(NotesConstants.DESIGN_FLAGS); + return flags.contains(NotesConstants.DESIGN_FLAG_SUBFORM); + } + + /** + * Returns detail information about the form fields + * + * @return field infos + */ + public List getFields() { + IRichTextNavigator rtNav = m_formNote.getRichtextNavigator(NotesConstants.ITEM_NAME_TEMPLATE); + return RichTextUtils.collectFields(rtNav); + } + + /** + * Returns the contents of the $FormUsers item + * + * @return form users, not null + */ + public List getFormUsers() { + return m_formNote.getItemValueStringList(NotesConstants.ITEM_NAME_FORMUSERS); + } + + /** + * Changes the $FormUsers field. Call {@link #update()} afterwards to + * save your changes. + * + * @param users new values or null to remove the item + */ + public void setFormUsers(List users) { + m_formNote.replaceItemValue(NotesConstants.ITEM_NAME_FORMUSERS, + EnumSet.of(ItemType.SUMMARY, ItemType.NAMES), NotesNamingUtils.toCanonicalNames(users)); + } + + /** + * Returns the contents of the $Readers item + * + * @return readers, not null + */ + public List getReaders() { + return m_formNote.getItemValueStringList(NotesConstants.DESIGN_READERS); + } + + /** + * Changes the $Readers field. Call {@link #update()} afterwards to + * save your changes. + * + * @param readers readers + */ + public void setReaders(List readers) { + m_formNote.replaceItemValue(NotesConstants.DESIGN_READERS, + EnumSet.of(ItemType.SUMMARY, ItemType.NAMES), NotesNamingUtils.toCanonicalNames(readers)); + } + + /** + * Test if the item should be retained in a design refresh + * + * @param itemName item name + * @return true to protect + */ + private boolean isProtected(String itemName) { + List retainFields = m_formNote.getItemValueStringList(NotesConstants.DESIGN_RETAIN_FIELDS); + return ListUtil.containsIgnoreCase(retainFields, itemName); + } + + /** + * Helper method tp protect an item against a redesign refresh + * + * @param itemName item name + * @param protect true to protect + */ + private void setProtected(String itemName, boolean protect) { + List retainFields = m_formNote.getItemValueStringList(NotesConstants.DESIGN_RETAIN_FIELDS); + boolean isProtected = ListUtil.containsIgnoreCase(retainFields, itemName); + + if (isProtected==protect) { + return; + } + + List newRetainFields = new ArrayList<>(); + + if (protect) { + newRetainFields.addAll(retainFields); + newRetainFields.add(itemName); + } + else { + for (String currItemName : retainFields) { + if (!currItemName.equalsIgnoreCase(itemName)) { + newRetainFields.add(currItemName); + } + } + } + m_formNote.replaceItemValue(NotesConstants.DESIGN_RETAIN_FIELDS, newRetainFields); + } + + /** + * Checks if the $Readers item is protected against design refresh + * + * @return true if protected + */ + public boolean isProtectReaders() { + return isProtected(NotesConstants.DESIGN_READERS); + } + + /** + * Protects/unprotects the $Readers item against design refresh. + * Call {@link #update()} afterwards to save your changes. + * + * @param protect true to protect + */ + public void setProtectReaders(boolean protect) { + setProtected(NotesConstants.DESIGN_READERS, protect); + } + + /** + * Checks if the $FormUsers item is protected against design refresh + * + * @return true if protected + */ + public boolean isProtectUsers() { + return isProtected(NotesConstants.ITEM_NAME_FORMUSERS); + } + + /** + * Protects/unprotects the $FormUsers item against design refresh. + * Call {@link #update()} afterwards to save your changes. + * + * @param protect true to protect + */ + public void setProtectUsers(boolean protect) { + setProtected(NotesConstants.ITEM_NAME_FORMUSERS, protect); + } + + /** + * Saves the underlying note (calls {@link NotesNote#update(java.util.Set)} + * with {@link UpdateNote#FORCE}). + */ + public void update() { + m_formNote.update(EnumSet.of(UpdateNote.FORCE)); + } + + /** + * Recycles the underlying form note + */ + public void recycle() { + m_formNote.recycle(); + } + + public boolean isRecycled() { + return m_formNote.isRecycled(); + } + + @Override + public String toString() { + if (m_formNote.isRecycled()) { + return "NotesForm [recycled, noteid="+getNoteId()+"]"; + } + else { + return "NotesForm [name=" + getName() + ", aliases=" + getAliases() + + ", issubform="+isSubForm()+", noteid="+getNoteId() + + "]"; + } + } + + +} diff --git a/domino-jna/src/test/java/com/mindoo/domino/jna/test/TestDesignFormAccess.java b/domino-jna/src/test/java/com/mindoo/domino/jna/test/TestDesignFormAccess.java new file mode 100644 index 00000000..0c06cb7d --- /dev/null +++ b/domino-jna/src/test/java/com/mindoo/domino/jna/test/TestDesignFormAccess.java @@ -0,0 +1,114 @@ +package com.mindoo.domino.jna.test; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import org.junit.Assert; +import org.junit.Test; + +import com.mindoo.domino.jna.NotesDatabase; +import com.mindoo.domino.jna.design.NotesForm; +import com.mindoo.domino.jna.gc.NotesGC; +import com.mindoo.domino.jna.richtext.FieldInfo; + +import lotus.domino.Database; +import lotus.domino.Form; +import lotus.domino.Session; + +public class TestDesignFormAccess extends BaseJNATestClass { + + @Test + public void testFormAPI() { + + runWithSession(new IDominoCallable() { + + @Override + public Object call(Session session) throws Exception { + NotesGC.setDebugLoggingEnabled(false); + + AtomicInteger cnt = new AtomicInteger(); + + NotesDatabase dbMail = NotesDatabase.openMailDatabase(); + if (dbMail==null) { + //test only working in the Notes Client, because openMailDatabase reads the mail db location from + //the Notes.ini + return null; + } + System.out.println("All forms and subforms of database "+dbMail.getServer()+"!!"+dbMail.getRelativeFilePath()); + + System.out.println("Read via JNA:"); + + List jnaForms = new ArrayList<>(); + + dbMail + .getForms().forEach((form) -> { + int currCnt = cnt.incrementAndGet(); + System.out.println("#"+currCnt+" - name="+form); + + jnaForms.add(form); + }); + + cnt.set(0); + + System.out.println("Read via Notes.jar:"); + + Database db = session.getDatabase(dbMail.getServer(), dbMail.getRelativeFilePath(), false); + @SuppressWarnings("unchecked") + Vector
legacyForms = db.getForms(); + for (Form currForm : legacyForms) { + int currCnt = cnt.incrementAndGet(); + System.out.println("#"+currCnt+" - name="+currForm.getName()+", aliases="+currForm.getAliases()+", issubform="+currForm.isSubForm()); + } + + Assert.assertEquals(jnaForms.size(), legacyForms.size()); + + for (int i=0; i