diff --git a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/items/SuperItemUtility.java b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/items/SuperItemUtility.java index 3bf05a9..c55f556 100644 --- a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/items/SuperItemUtility.java +++ b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/items/SuperItemUtility.java @@ -1,27 +1,21 @@ package com.nuix.superutilities.items; +import com.nuix.superutilities.SuperUtilities; +import nuix.Case; +import nuix.Item; +import nuix.ItemUtility; +import nuix.TreePosition; +import org.jetbrains.annotations.NotNull; + import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Collectors; -import com.nuix.superutilities.SuperUtilities; - -import nuix.Case; -import nuix.Item; -import nuix.ItemUtility; -import nuix.TreePosition; - /*** * Offers methods for working with items and item collections in the same spirit of the * ItemUtility offered in the Nuix API. @@ -29,275 +23,316 @@ * */ public class SuperItemUtility { - - private static SuperItemUtility instance = null; - - protected SuperItemUtility(){} - - public static SuperItemUtility getInstance(){ - if(instance == null){ - instance = new SuperItemUtility(); - } - return instance; - } - - /*** - * Unions multiple collections of items into a single Set of items. - * @param itemCollections A list of item collections to union into a single final Set - * @return A set at most 1 of each item in the provided input item collections - */ - public Set unionMany(List> itemCollections){ - ItemUtility iutil = SuperUtilities.getItemUtility(); - Set result = new HashSet(); - for (int i = 0; i < itemCollections.size(); i++) { - result = iutil.union(result, itemCollections.get(i)); - } - return result; - } - - /*** - * Traverses up an item's path to locate the ancestor physical file item (flag:physical_file) or - * null if there is no physical file ancestor item. - * @param item The item to find the physical file ancestor for. - * @return The physical file ancestor item (if there is on) otherwise null - */ - public Item findPhysicalFileAncestor(Item item){ - Item physicalFileAncestor = null; - List pathItems = item.getPath(); - for (int i = pathItems.size()-1; i >= 0; i--) { - Item currentItem = pathItems.get(i); - if(currentItem.isPhysicalFile() && currentItem != item){ - physicalFileAncestor = currentItem; - break; - } - } - return physicalFileAncestor; - } - - /*** - * Similar to {@link #findPhysicalFileAncestor(Item)}, but resolves physical file ancestor items - * for multiple items at once. Resulting set will not contain nulls. - * @param items The items to resolve the physical file ancestors of. - * @return The physical file ancestors of the input items (if there were any) - */ - public Set findPhysicalFileAncestors(Collection items){ - Set result = items.parallelStream() - .map(i -> findPhysicalFileAncestor(i)) - .filter(i -> i != null) - .collect(Collectors.toSet()); - return result; - } - - /*** - * Traverses up an item's path to locate the nearest ancestor container item (kind:container) or - * null if there is no container ancestor item. - * @param item The item to resolve the nearest container ancestor of. - * @return The nearest container ancestor of the input item or null if no container ancestor could be found - */ - public Item findContainerAncestor(Item item){ - Item containerAncestor = null; - List pathItems = item.getPath(); - for (int i = pathItems.size()-1; i >= 0; i--) { - Item currentItem = pathItems.get(i); - if(currentItem.isKind("container") && currentItem != item){ - containerAncestor = currentItem; - break; - } - } - return containerAncestor; - } - - /*** - * Similar to {@link #findContainerAncestor(Item)}, but resolves nearest container ancestor items - * for multiple items at once. Resulting set will not contain nulls. - * @param items The items to resolve the nearest container ancestors of. - * @return The nearest container ancestors of the input items. - */ - public Set findContainerAncestors(Collection items){ - Set result = items.parallelStream() - .map(i -> findContainerAncestor(i)) - .filter(i -> i != null) - .collect(Collectors.toSet()); - return result; - } - - /*** - * Splits a collection of items into a target chunk size, while maintaining families. Each chunk is - * passed as an argument to the provided chunkConsumer. - * @param items The items to split - * @param targetChunkSize The target chunk size. Actual chunks may differ from this size based on where - * family boundaries land. - * @param chunkConsumer A Consumer<Collection<Item>> which will receive each chunk as it is built. - */ - public void splitAndMaintainFamilies(Collection items, int targetChunkSize, Consumer> chunkConsumer){ - List currentSubSet = new ArrayList(); - List sortedItems = new ArrayList(items); - sortedItems = SuperUtilities.getItemUtility().sortItemsByPosition(sortedItems); - - Item previousItem = null; - for (int i = 0; i < sortedItems.size(); i++) { - Item currentItem = sortedItems.get(i); - boolean canCutHere = previousItem == null || currentItem.getTopLevelItem() == null || (currentItem.getTopLevelItem() != previousItem.getTopLevelItem()); - if(currentSubSet.size() >= targetChunkSize && canCutHere){ - chunkConsumer.accept(currentSubSet); - currentSubSet = new ArrayList(); - } - currentSubSet.add(currentItem); - } - - if(currentSubSet.size() > 0){ - chunkConsumer.accept(currentSubSet); - } - } - - /*** - * Convenience method for removing items responsive to a query from another collection of items. Internally this - * method runs a search for the given query and then uses ItemUtility.difference to remove them from the provided - * input items, returning the differenced result. - * @param nuixCase The Nuix case (needed to run the search) - * @param inputItems The items from which you wish to remove items which are responsive to the given query - * @param itemsToRemoveQuery The query used to define items you wish to have removed form the input items - * @return The input items, with items responsive to the query removed - * @throws IOException Likely thrown if there is an issue with the provided query - */ - public Set removeItemsResponsiveToQuery(Case nuixCase, Collection inputItems, String itemsToRemoveQuery) throws IOException { - Set toRemove = nuixCase.searchUnsorted(itemsToRemoveQuery); - return SuperUtilities.getItemUtility().difference(inputItems, toRemove); - } - - public Set findFamiliesWithoutItemsResponsiveToQuery(Case nuixCase, Collection inputItems, String itemsToRemoveQuery) throws IOException { - Set topLevelItems = SuperUtilities.getItemUtility().findTopLevelItems(inputItems); - Set families = SuperUtilities.getItemUtility().findItemsAndDescendants(topLevelItems); - return removeItemsResponsiveToQuery(nuixCase,families,itemsToRemoveQuery); - } - - /*** - * Returns the file system path of an item's physical file ancestor. Begins by calling {@link #findPhysicalFileAncestor(Item)}. - * If an physical file ancestor is located, then gets its URI and attempts to convert that to an actual file system path. - * @param item The item to resolve the physical file ancestor file system path of. - * @return The physical file ancestor file system path (if possible), otherwise null - */ - public String getPhysicalAncestorPath(Item item){ - Item physicalAncestor = findPhysicalFileAncestor(item); - if(physicalAncestor == null){ - return ""; - } - - String uri = physicalAncestor.getUri(); - if(uri == null){ - return ""; - } else { - uri = uri.replaceAll("\\\\+", "%2B"); - try { - String path = URLDecoder.decode(uri,"UTF-8"); - path = path.replaceAll("^file:\\\\/\\\\/\\\\/", ""); - path = path.replaceAll("\\\\/", "\\\\"); - return path; - } catch (UnsupportedEncodingException e) { - return "Error decoding: "+uri+", "+e.getMessage(); - } - } - } - - /*** - * Custom deduplication implementation allowing code to specify which original is kept. The normal behavior - * of ItemUtility.deduplicate is that the original with the earliest position value, amongst the items with the same - * MD5, is considered the original. This implementation allows code to provide a 2 argument function which will - * determine the winner. It may be a good idea for the custom function provided to default to the default position - * behavior when all other comparisons it may perform are equal, to mimic the behavior of the API. Like the API - * deduplicate method, items without an MD5 value are automatically included in the results and therefore never - * sent to the tie breaker function. - * @param items The items to deduplicate - * @param tieBreaker A function which is provided 2 items with the same MD5, the first argument is the current - * "champion" item (the item currently considered original) and the second argument is the "contender" item - * (the item which may become the new champion). If function returns the "contender" item, then it becomes - * the new "champion", any other result (including null) leaves the current "champion" in place. - * @return A custom deduplicated set of items - */ - public Set deduplicateCustomTieBreaker(Collection items, BiFunction tieBreaker){ - Map working = new HashMap(); - List noMd5 = new ArrayList(); - for(Item item : items){ - String md5 = item.getDigests().getMd5(); - - if(md5 == null || md5.trim().isEmpty()){ - noMd5.add(item); - continue; - } - - if(!working.containsKey(md5)){ - working.put(md5,item); - continue; - } else { - Item currentChampion = working.get(md5); - Item contender = item; - Item winner = tieBreaker.apply(currentChampion, contender); - if(winner == contender){ - working.put(md5,winner); - } - } - } - Set result = new HashSet(working.values()); - result.addAll(noMd5); - return result; - } - - /*** - * Tests whether 2 items have the same parent by comparing their tree position values - * @param a The first item to compare - * @param b The second item to compare - * @return True if items appear to have the same parent item based on their tree position values - */ - public boolean itemsAreSiblings(Item a, Item b){ - TreePosition posA = a.getPosition(); - TreePosition posB = b.getPosition(); - int[] arrA = posA.toArray(); - int[] arrB = posB.toArray(); - - // If position array length differs they are at - // different depths and therefore cannot be siblings - if(arrA.length != arrB.length){ - return false; - } - - // Test to make sure position values match up to - // but not including the last position int - for (int i = 0; i < arrA.length-1; i++) { - if(arrA[i] != arrB[i]) return false; - } - - return true; - } - - /*** - * Gets items and sibling items within a certain ordinal distance. - * @param items The items to obtain the neighboring siblings of - * @param itemsBefore How many siblings before each item to include - * @param itemsAfter How many siblings after each item to include - * @return A new list of items which includes both input items and neighboring siblings, sorted by position - */ - public List getItemsAndNeighboringSiblings(List items, int itemsBefore, int itemsAfter){ - if(itemsBefore < 0) { itemsBefore = 0; } - if(itemsAfter < 0) { itemsAfter = 0; } - - if(items.size() < 1) { return Collections.emptyList(); } - else { - Set tempSet = new HashSet(); - for(Item item : items) { - Item parent = item.getParent(); - List siblings = parent.getChildren(); - int itemIndex = siblings.indexOf(item); - int first = itemIndex - itemsBefore; - int last = itemIndex + itemsAfter; - if(first < 0) { first = 0; } - if(last > items.size() - 1) { last = items.size() - 1; } - for (int i = first; i <= last; i++) { - tempSet.add(items.get(i)); - } - } - List result = new ArrayList(); - result.addAll(tempSet); - return SuperUtilities.getItemUtility().sortItemsByPosition(result); - } - } + + private static SuperItemUtility instance = null; + + protected SuperItemUtility() { + } + + public static SuperItemUtility getInstance() { + if (instance == null) { + instance = new SuperItemUtility(); + } + return instance; + } + + /*** + * Unions multiple collections of items into a single Set of items. + * @param itemCollections A list of item collections to union into a single final Set + * @return A set at most 1 of each item in the provided input item collections + */ + public Set unionMany(List> itemCollections) { + ItemUtility iutil = SuperUtilities.getItemUtility(); + Set result = new HashSet(); + for (int i = 0; i < itemCollections.size(); i++) { + result = iutil.union(result, itemCollections.get(i)); + } + return result; + } + + /*** + * Traverses up an item's path to locate the ancestor physical file item (flag:physical_file) or + * null if there is no physical file ancestor item. + * @param item The item to find the physical file ancestor for. + * @return The physical file ancestor item (if there is on) otherwise null + */ + public Item findPhysicalFileAncestor(Item item) { + return findAncestor(item, Item::isPhysicalFile); + } + + /*** + * Traverses an item's ancestors bottom up, until the first item for which the predicate yields true + * or null if no ancestor matches the predicate. + * @param item The item whose ancestors will be inspected + * @param ancestorPredicate A predicate for matching the target ancestor + * @return The first ancestor matched walking bottom up or null if none matched the predicate + */ + public Item findAncestor(@NotNull Item item, @NotNull Predicate ancestorPredicate) { + Item matchedAncestor = null; + List pathItems = item.getPath(); + for (int i = pathItems.size() - 1; i >= 0; i--) { + Item currentItem = pathItems.get(i); + if (ancestorPredicate.test(currentItem) && currentItem != item) { + matchedAncestor = currentItem; + break; + } + } + return matchedAncestor; + } + + /*** + * Similar to {@link #findPhysicalFileAncestor(Item)}, but resolves physical file ancestor items + * for multiple items at once. Resulting set will not contain nulls. + * @param items The items to resolve the physical file ancestors of. + * @return The physical file ancestors of the input items (if there were any) + */ + public Set findPhysicalFileAncestors(Collection items) { + return items.parallelStream() + .map(this::findPhysicalFileAncestor) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + /*** + * Traverses up an item's path to locate the nearest ancestor container item (kind:container) or + * null if there is no container ancestor item. + * @param item The item to resolve the nearest container ancestor of. + * @return The nearest container ancestor of the input item or null if no container ancestor could be found + */ + public Item findContainerAncestor(Item item) { + return findAncestor(item, ancestor -> ancestor.isKind("container")); + } + + /*** + * Similar to {@link #findContainerAncestor(Item)}, but resolves nearest container ancestor items + * for multiple items at once. Resulting set will not contain nulls. + * @param items The items to resolve the nearest container ancestors of. + * @return The nearest container ancestors of the input items. + */ + public Set findContainerAncestors(Collection items) { + return items.parallelStream() + .map(this::findContainerAncestor) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + /*** + * Resolves input collection of items to parent items. + * @param items The items to resolve to parent items + * @return The parent items of the provide input items + */ + public Set findParents(Collection items) { + return items.parallelStream() + .map(Item::getParent) + .collect(Collectors.toSet()); + } + + /*** + * Resolves input collection of items to a set which includes both parents of + * input items and the input items themselves. + * @param items The items to resolve the parents of + * @return The input items and their parent items + */ + public Set findItemsAndParents(Collection items) { + return items.parallelStream() + .flatMap(item -> { + List opResult = new ArrayList<>(); + opResult.add(item); + Item parent = item.getParent(); + if (parent != null) { + opResult.add(parent); + } + return opResult.stream(); + }) + .collect(Collectors.toSet()); + } + + /*** + * Splits a collection of items into a target chunk size, while maintaining families. Each chunk is + * passed as an argument to the provided chunkConsumer. + * @param items The items to split + * @param targetChunkSize The target chunk size. Actual chunks may differ from this size based on where + * family boundaries land. + * @param chunkConsumer A Consumer<Collection<Item>> which will receive each chunk as it is built. + */ + public void splitAndMaintainFamilies(Collection items, int targetChunkSize, Consumer> chunkConsumer) { + List currentSubSet = new ArrayList(); + List sortedItems = new ArrayList(items); + sortedItems = SuperUtilities.getItemUtility().sortItemsByPosition(sortedItems); + + Item previousItem = null; + for (int i = 0; i < sortedItems.size(); i++) { + Item currentItem = sortedItems.get(i); + boolean canCutHere = previousItem == null || currentItem.getTopLevelItem() == null || (currentItem.getTopLevelItem() != previousItem.getTopLevelItem()); + if (currentSubSet.size() >= targetChunkSize && canCutHere) { + chunkConsumer.accept(currentSubSet); + currentSubSet = new ArrayList(); + } + currentSubSet.add(currentItem); + } + + if (currentSubSet.size() > 0) { + chunkConsumer.accept(currentSubSet); + } + } + + /*** + * Convenience method for removing items responsive to a query from another collection of items. Internally this + * method runs a search for the given query and then uses ItemUtility.difference to remove them from the provided + * input items, returning the differenced result. + * @param nuixCase The Nuix case (needed to run the search) + * @param inputItems The items from which you wish to remove items which are responsive to the given query + * @param itemsToRemoveQuery The query used to define items you wish to have removed form the input items + * @return The input items, with items responsive to the query removed + * @throws IOException Likely thrown if there is an issue with the provided query + */ + public Set removeItemsResponsiveToQuery(Case nuixCase, Collection inputItems, String itemsToRemoveQuery) throws IOException { + Set toRemove = nuixCase.searchUnsorted(itemsToRemoveQuery); + return SuperUtilities.getItemUtility().difference(inputItems, toRemove); + } + + public Set findFamiliesWithoutItemsResponsiveToQuery(Case nuixCase, Collection inputItems, String itemsToRemoveQuery) throws IOException { + Set topLevelItems = SuperUtilities.getItemUtility().findTopLevelItems(inputItems); + Set families = SuperUtilities.getItemUtility().findItemsAndDescendants(topLevelItems); + return removeItemsResponsiveToQuery(nuixCase, families, itemsToRemoveQuery); + } + + /*** + * Returns the file system path of an item's physical file ancestor. Begins by calling {@link #findPhysicalFileAncestor(Item)}. + * If an physical file ancestor is located, then gets its URI and attempts to convert that to an actual file system path. + * @param item The item to resolve the physical file ancestor file system path of. + * @return The physical file ancestor file system path (if possible), otherwise null + */ + public String getPhysicalAncestorPath(Item item) { + Item physicalAncestor = findPhysicalFileAncestor(item); + if (physicalAncestor == null) { + return ""; + } + + String uri = physicalAncestor.getUri(); + if (uri == null) { + return ""; + } else { + uri = uri.replaceAll("\\\\+", "%2B"); + try { + String path = URLDecoder.decode(uri, "UTF-8"); + path = path.replaceAll("^file:\\\\/\\\\/\\\\/", ""); + path = path.replaceAll("\\\\/", "\\\\"); + return path; + } catch (UnsupportedEncodingException e) { + return "Error decoding: " + uri + ", " + e.getMessage(); + } + } + } + + /*** + * Custom deduplication implementation allowing code to specify which original is kept. The normal behavior + * of ItemUtility.deduplicate is that the original with the earliest position value, amongst the items with the same + * MD5, is considered the original. This implementation allows code to provide a 2 argument function which will + * determine the winner. It may be a good idea for the custom function provided to default to the default position + * behavior when all other comparisons it may perform are equal, to mimic the behavior of the API. Like the API + * deduplicate method, items without an MD5 value are automatically included in the results and therefore never + * sent to the tie breaker function. + * @param items The items to deduplicate + * @param tieBreaker A function which is provided 2 items with the same MD5, the first argument is the current + * "champion" item (the item currently considered original) and the second argument is the "contender" item + * (the item which may become the new champion). If function returns the "contender" item, then it becomes + * the new "champion", any other result (including null) leaves the current "champion" in place. + * @return A custom deduplicated set of items + */ + public Set deduplicateCustomTieBreaker(Collection items, BiFunction tieBreaker) { + Map working = new HashMap(); + List noMd5 = new ArrayList(); + for (Item item : items) { + String md5 = item.getDigests().getMd5(); + + if (md5 == null || md5.trim().isEmpty()) { + noMd5.add(item); + continue; + } + + if (!working.containsKey(md5)) { + working.put(md5, item); + continue; + } else { + Item currentChampion = working.get(md5); + Item contender = item; + Item winner = tieBreaker.apply(currentChampion, contender); + if (winner == contender) { + working.put(md5, winner); + } + } + } + Set result = new HashSet(working.values()); + result.addAll(noMd5); + return result; + } + + /*** + * Tests whether 2 items have the same parent by comparing their tree position values + * @param a The first item to compare + * @param b The second item to compare + * @return True if items appear to have the same parent item based on their tree position values + */ + public boolean itemsAreSiblings(Item a, Item b) { + TreePosition posA = a.getPosition(); + TreePosition posB = b.getPosition(); + int[] arrA = posA.toArray(); + int[] arrB = posB.toArray(); + + // If position array length differs they are at + // different depths and therefore cannot be siblings + if (arrA.length != arrB.length) { + return false; + } + + // Test to make sure position values match up to + // but not including the last position int + for (int i = 0; i < arrA.length - 1; i++) { + if (arrA[i] != arrB[i]) return false; + } + + return true; + } + + /*** + * Gets items and sibling items within a certain ordinal distance. + * @param items The items to obtain the neighboring siblings of + * @param itemsBefore How many siblings before each item to include + * @param itemsAfter How many siblings after each item to include + * @return A new list of items which includes both input items and neighboring siblings, sorted by position + */ + public List getItemsAndNeighboringSiblings(List items, int itemsBefore, int itemsAfter) { + if (itemsBefore < 0) { + itemsBefore = 0; + } + if (itemsAfter < 0) { + itemsAfter = 0; + } + + if (items.size() < 1) { + return Collections.emptyList(); + } else { + Set tempSet = new HashSet(); + for (Item item : items) { + Item parent = item.getParent(); + List siblings = parent.getChildren(); + int itemIndex = siblings.indexOf(item); + int first = itemIndex - itemsBefore; + int last = itemIndex + itemsAfter; + if (first < 0) { + first = 0; + } + if (last > items.size() - 1) { + last = items.size() - 1; + } + for (int i = first; i <= last; i++) { + tempSet.add(items.get(i)); + } + } + List result = new ArrayList(); + result.addAll(tempSet); + return SuperUtilities.getItemUtility().sortItemsByPosition(result); + } + } } diff --git a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/BoundedProgressInfo.java b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/BoundedProgressInfo.java index 7ac2b6b..d56d7e1 100644 --- a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/BoundedProgressInfo.java +++ b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/BoundedProgressInfo.java @@ -3,7 +3,7 @@ import lombok.Getter; /*** - * Represents progress of an operation which has a bounded value, that is, we known what progress + * Represents progress of an operation which has a bounded value, that is, we know what progress * value is considered maximum/done. */ @Getter diff --git a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PeriodicGatedConsumer.java b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PeriodicGatedConsumer.java index 621fa80..48c3f67 100644 --- a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PeriodicGatedConsumer.java +++ b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PeriodicGatedConsumer.java @@ -8,7 +8,7 @@ /*** * A wrapper for a {@link Consumer} instance which will only periodically forward call to accept - * method of wrapped instance. Created for taking per item events that Nuix publishes and turning them + * method of wrapped instance. Created for taking frequent events that are published and turning them * into periodic progress reporters. * @param The type of the {@link Consumer} being wrapped */ diff --git a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PlaceholderResolver.java b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PlaceholderResolver.java index d94b513..f463ae5 100644 --- a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PlaceholderResolver.java +++ b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PlaceholderResolver.java @@ -5,6 +5,7 @@ package com.nuix.superutilities.misc; +import com.nuix.superutilities.items.SuperItemUtility; import lombok.Getter; import nuix.Case; import nuix.Item; @@ -52,12 +53,43 @@ private void recordPlaceholderPattern(String key) { * {evidence_name} - The name of the evidence the item belongs to.
* {item_date_short} - The item's item date formatted YYYYMMDD or NO_DATE for items without an item date.
* {item_date_long} - The item's item date formatted YYYYMMdd-HHmmss or NO_DATE for items without an item date.
- * {item_date_year} - The item's item date 4 digit year or NO_DATE for items without an item date.
- * {item_date_month} - The item's item date 2 digit month or NO_DATE for items without an item date.
- * {item_date_day} - The item's item date 2 digit day of the month or NO_DATE for items without an item date.
- * {top_level_guid} - The GUID of the provided item's top level item or ABOVE_TOP_LEVEL for items which are above top level.
- * {top_level_name} - The name (via Item.getLocalisedName) of the provided item's top level item or ABOVE_TOP_LEVEL for items which are above top level.
- * {top_level_kind} - The kind (via ItemType.getKind.getName) of the provided item's top level item or ABOVE_TOP_LEVEL for items which are above top level.
+ * {item_date_year} - The item's item date 4-digit year or NO_DATE for items without an item date.
+ * {item_date_month} - The item's item date 2-digit month or NO_DATE for items without an item date.
+ * {item_date_day} - The item's item date 2-digit day of the month or NO_DATE for items without an item date.
+ * + * {top_level_guid} - The GUID of the associated top level item
+ * {top_level_name} - The name (via Item.getLocalisedName) of the associated top level item
+ * {top_level_kind} - The kind (via ItemType.getKind.getName) of the associated top level item
+ * {top_level_type} - The localised (human friendly) name of the mime type attributed to the associated top level item.
+ * {top_level_mimetype} - The mime type of the associated top level item.
+ * {top_level_short_date} - The short format (YYYYMMDD) item date of the associated top level item.
+ * {top_level_long_date} - The long format (YYYYMMDD-HHMMSS) item date of the associated top level item.
+ * {top_level_year} - The item date year (YYYY) of the associated top level item.
+ * {top_level_month} - The item date month (MM) of the associated top level item.
+ * {top_level_day} - The item date day (DD) of the associated top level item.
+ * + * {parent_guid} - The GUID of the associated parent item
+ * {parent_name} - The name (via Item.getLocalisedName) of the associated parent item
+ * {parent_kind} - The kind (via ItemType.getKind.getName) of the associated parent item
+ * {parent_type} - The localised (human friendly) name of the mime type attributed to the associated parent item.
+ * {parent_mimetype} - The mime type of the associated parent item.
+ * {parent_short_date} - The short format (YYYYMMDD) item date of the associated parent item.
+ * {parent_long_date} - The long format (YYYYMMDD-HHMMSS) item date of the associated parent item.
+ * {parent_year} - The item date year (YYYY) of the associated parent item.
+ * {parent_month} - The item date month (MM) of the associated parent item.
+ * {parent_day} - The item date day (DD) of the associated parent item.
+ * + * {container_guid} - The GUID of the associated nearest ancestor container item
+ * {container_name} - The name (via Item.getLocalisedName) of the associated nearest ancestor container item
+ * {container_kind} - The kind (via ItemType.getKind.getName) of the associated nearest ancestor container item
+ * {container_type} - The localised (human friendly) name of the mime type attributed to the associated nearest ancestor container item.
+ * {container_mimetype} - The mime type of the associated nearest ancestor container item.
+ * {container_short_date} - The short format (YYYYMMDD) item date of the associated nearest ancestor container item.
+ * {container_long_date} - The long format (YYYYMMDD-HHMMSS) item date of the associated nearest ancestor container item.
+ * {container_year} - The item date year (YYYY) of the associated nearest ancestor container item.
+ * {container_month} - The item date month (MM) of the associated nearest ancestor container item.
+ * {container_day} - The item date day (DD) of the associated nearest ancestor container item.
+ * * {original_extension} - The original extension as obtained from Nuix via Item.getOriginalExtension or NO_ORIGINAL_EXTENSION for items where Nuix does not have an original extension value.
* {corrected_extension} - The corrected extension as obtained from Nuix via Item.getCorrectedExtension or NO_CORRECTED_EXTENSION for items where Nuix does not have a corrected extension value.
* @param item The item used to set all the item based placeholder values. @@ -101,16 +133,9 @@ public void setFromItem(Item item) { set("item_date_day", itemDate.toString("dd")); } - Item topLevelItem = item.getTopLevelItem(); - if (topLevelItem == null) { - set("top_level_guid", "ABOVE_TOP_LEVEL"); - set("top_level_name", "ABOVE_TOP_LEVEL"); - set("top_level_kind", "ABOVE_TOP_LEVEL"); - } else { - set("top_level_guid", topLevelItem.getGuid()); - set("top_level_name", topLevelItem.getLocalisedName()); - set("top_level_kind", topLevelItem.getType().getKind().getName()); - } + addCommonFieldsOfRelatedItem(item.getTopLevelItem(), "top_level", "ABOVE_TOP_LEVEL"); + addCommonFieldsOfRelatedItem(item.getParent(), "parent", "HAS_NO_PARENT"); + addCommonFieldsOfRelatedItem(SuperItemUtility.getInstance().findContainerAncestor(item), "container", "NO_ANCESTOR_CONTAINER"); String originalExtension = item.getOriginalExtension(); if (originalExtension == null || originalExtension.trim().isEmpty()) { @@ -125,6 +150,38 @@ public void setFromItem(Item item) { set("corrected_extension", correctedExtension); } + public void addCommonFieldsOfRelatedItem(Item relatedItem, String prefix, String valueIfItemNull) { + set(prefix + "_guid", relatedItem == null ? valueIfItemNull : relatedItem.getGuid()); + set(prefix + "_name", relatedItem == null ? valueIfItemNull : relatedItem.getLocalisedName()); + set(prefix + "_kind", relatedItem == null ? valueIfItemNull : relatedItem.getType().getKind().getName()); + set(prefix + "_type", relatedItem == null ? valueIfItemNull : relatedItem.getType().getLocalisedName()); + set(prefix + "_mimetype", relatedItem == null ? valueIfItemNull : relatedItem.getType().getName()); + + // Date values of related items + if (relatedItem != null) { + DateTime itemDate = relatedItem.getDate(); + if (itemDate == null) { + set(prefix + "_short_date", "000000"); + set(prefix + "_long_date", "000000-000000"); + set(prefix + "_year", "0000"); + set(prefix + "_month", "00"); + set(prefix + "_day", "00"); + } else { + set(prefix + "_short_date", itemDate.toString("YYYYMMdd")); + set(prefix + "_long_date", itemDate.toString("YYYYMMdd-HHmmss")); + set(prefix + "_year", itemDate.toString("YYYY")); + set(prefix + "_month", itemDate.toString("MM")); + set(prefix + "_day", itemDate.toString("dd")); + } + } else { + set(prefix + "_short_date", valueIfItemNull); + set(prefix + "_long_date", valueIfItemNull); + set(prefix + "_year", valueIfItemNull); + set(prefix + "_month", valueIfItemNull); + set(prefix + "_day", valueIfItemNull); + } + } + /*** * Convenience method for setting various standard values:

* {date_short} - The datetime of invocation, formatted YYYYMMDD