org.apache.commons.collections4.Trie Java Examples

The following examples show how to use org.apache.commons.collections4.Trie. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: SuggestionServiceImpl.java    From intellij-spring-assistant with MIT License 6 votes vote down vote up
private void removeReferences(Map<String, MetadataContainerInfo> containerPathToContainerInfo,
    Trie<String, MetadataSuggestionNode> rootSearchIndex,
    MetadataContainerInfo metadataContainerInfo) {
  debug(() -> log.debug("Removing references to " + metadataContainerInfo));
  String containerPath = metadataContainerInfo.getContainerArchiveOrFileRef();
  containerPathToContainerInfo.remove(containerPath);

  Iterator<String> searchIndexIterator = rootSearchIndex.keySet().iterator();
  while (searchIndexIterator.hasNext()) {
    SuggestionNode root = rootSearchIndex.get(searchIndexIterator.next());
    if (root != null) {
      boolean removeTree = MetadataSuggestionNode.class.cast(root)
          .removeRefCascadeDown(metadataContainerInfo.getContainerArchiveOrFileRef());
      if (removeTree) {
        searchIndexIterator.remove();
      }
    }
  }
}
 
Example #2
Source File: PatriciaTrieTest.java    From jesterj with Apache License 2.0 6 votes vote down vote up
public void testPrefixMapClearUsingRemove() {
    final Trie<CharSequence, Integer> trie = new PatriciaTrie<>();
    trie.put("Anna", 1);
    trie.put("Anael", 2);
    trie.put("Analu", 3);
    trie.put("Andreas", 4);
    trie.put("Andrea", 5);
    trie.put("Andres", 6);
    trie.put("Anatole", 7);
    final SortedMap<CharSequence, Integer> prefixMap = trie.prefixMap("And");
    assertEquals(new HashSet<>(Arrays.asList("Andrea", "Andreas", "Andres")), prefixMap.keySet());
    assertEquals(Arrays.asList(5, 4, 6), new ArrayList<>(prefixMap.values()));

    final Set<CharSequence> keys = new HashSet<>(prefixMap.keySet());
    for (final CharSequence key : keys) {
        prefixMap.remove(key);
    }
    assertTrue(prefixMap.keySet().isEmpty());
    assertTrue(prefixMap.values().isEmpty());
    assertEquals(new HashSet<>(Arrays.asList("Anael", "Analu", "Anatole", "Anna")), trie.keySet());
    assertEquals(Arrays.asList(2, 3, 7, 1), new ArrayList<>(trie.values()));
}
 
Example #3
Source File: PatriciaTrieTest.java    From jesterj with Apache License 2.0 6 votes vote down vote up
public void testPrefixMapClear() {
    final Trie<CharSequence, Integer> trie = new PatriciaTrie<>();
    trie.put("Anna", 1);
    trie.put("Anael", 2);
    trie.put("Analu", 3);
    trie.put("Andreas", 4);
    trie.put("Andrea", 5);
    trie.put("Andres", 6);
    trie.put("Anatole", 7);
    final SortedMap<CharSequence, Integer> prefixMap = trie.prefixMap("And");
    assertEquals(new HashSet<>(Arrays.asList("Andrea", "Andreas", "Andres")), prefixMap.keySet());
    assertEquals(Arrays.asList(5, 4, 6), new ArrayList<>(prefixMap.values()));

    prefixMap.clear();
    assertTrue(prefixMap.isEmpty());
    assertTrue(prefixMap.keySet().isEmpty());
    assertTrue(prefixMap.values().isEmpty());
    assertEquals(new HashSet<>(Arrays.asList("Anael", "Analu", "Anatole", "Anna")), trie.keySet());
    assertEquals(Arrays.asList(2, 3, 7, 1), new ArrayList<>(trie.values()));
}
 
Example #4
Source File: MPQViewer.java    From riiablo with Apache License 2.0 6 votes vote down vote up
private void treeify(Trie<String, Node> nodes, Node root, String path) {
  Node parent = root;
  String[] parts = path.split("\\\\");
  StringBuilder builder = new StringBuilder(path.length());
  for (String part : parts) {
    if (part.isEmpty()) {
      break;
    }

    builder.append(part).append("\\");
    String partPath = builder.toString();
    Node node = nodes.get(partPath);
    if (node == null) {
      node = new BaseNode(new VisLabel(part));
      nodes.put(partPath, node);
      parent.add(node);
    }

    parent = node;
  }
}
 
Example #5
Source File: BinList.java    From zap-extensions with Apache License 2.0 6 votes vote down vote up
private static Trie<String, BinRecord> createTrie() {
    Trie<String, BinRecord> trie = new PatriciaTrie<>();
    Iterable<CSVRecord> records;
    try (InputStream in = BinList.class.getResourceAsStream(BINLIST);
            BOMInputStream bomStream = new BOMInputStream(in);
            InputStreamReader inStream =
                    new InputStreamReader(bomStream, StandardCharsets.UTF_8)) {
        records = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(inStream).getRecords();
    } catch (NullPointerException | IOException e) {
        LOGGER.warn("Exception while loading: " + BINLIST, e);
        return trie;
    }

    for (CSVRecord record : records) {
        trie.put(
                record.get("bin"),
                new BinRecord(
                        record.get("bin"),
                        record.get("brand"),
                        record.get("category"),
                        record.get("issuer")));
    }
    return trie;
}
 
Example #6
Source File: SuggestionServiceImpl.java    From intellij-spring-assistant with MIT License 5 votes vote down vote up
private void addHintsToIndex(Module module, Trie<String, MetadataSuggestionNode> rootSearchIndex,
    SpringConfigurationMetadata springConfigurationMetadata, String containerPath) {
  List<SpringConfigurationMetadataHint> hints = springConfigurationMetadata.getHints();
  if (hints != null) {
    hints.sort(comparing(SpringConfigurationMetadataHint::getName));
    for (SpringConfigurationMetadataHint hint : hints) {
      String[] pathSegments = toSanitizedPathSegments(hint.getExpectedPropertyName());
      MetadataSuggestionNode closestMetadata =
          findDeepestMetadataMatch(rootSearchIndex, pathSegments, true);
      if (closestMetadata != null) {
        if (!closestMetadata.isProperty()) {
          log.warn(
              "Unexpected hint " + hint.getName() + " is assigned to  group " + closestMetadata
                  .getPathFromRoot(module)
                  + " found. Hints can be only assigned to property. Ignoring the hint completely.Existing group belongs to ("
                  + closestMetadata.getBelongsTo().stream().collect(joining(","))
                  + "), New hint belongs " + containerPath);
        } else {
          MetadataPropertySuggestionNode propertySuggestionNode =
              MetadataPropertySuggestionNode.class.cast(closestMetadata);
          if (hint.representsValueOfMap()) {
            propertySuggestionNode.getProperty().setValueHint(hint);
          } else {
            propertySuggestionNode.getProperty().setGenericOrKeyHint(hint);
          }
        }
      }
    }
  }
}
 
Example #7
Source File: PatriciaTrieTest.java    From JQF with BSD 2-Clause "Simplified" License 5 votes vote down vote up
@Fuzz
public void testCopy(Map<String, Integer> map, String key) {
    assumeTrue(map.containsKey(key));
    // Create new trie with input `map`
    Trie trie = new PatriciaTrie(map);
    // The key should exist in the trie as well
    assertTrue(trie.containsKey(key));
}
 
Example #8
Source File: SuggestionServiceImpl.java    From intellij-spring-assistant with MIT License 5 votes vote down vote up
private void buildMetadataHierarchy(Module module,
    Trie<String, MetadataSuggestionNode> rootSearchIndex,
    MetadataContainerInfo metadataContainerInfo,
    SpringConfigurationMetadata springConfigurationMetadata) {
  debug(() -> log.debug("Adding container to index " + metadataContainerInfo));
  String containerPath = metadataContainerInfo.getContainerArchiveOrFileRef();
  addGroupsToIndex(module, rootSearchIndex, springConfigurationMetadata, containerPath);
  addPropertiesToIndex(module, rootSearchIndex, springConfigurationMetadata, containerPath);
  addHintsToIndex(module, rootSearchIndex, springConfigurationMetadata, containerPath);
  debug(() -> log.debug("Done adding container to index"));
}
 
Example #9
Source File: SuggestionServiceImpl.java    From intellij-spring-assistant with MIT License 5 votes vote down vote up
private void reindexModule(List<MetadataContainerInfo> newProjectSourcesToProcess,
    List<MetadataContainerInfo> projectContainersToRemove, Module module) {
  Map<String, MetadataContainerInfo> moduleSeenContainerPathToSeenContainerInfo =
      moduleNameToSeenContainerPathToContainerInfo
          .computeIfAbsent(module.getName(), k -> new THashMap<>());

  Trie<String, MetadataSuggestionNode> moduleRootSearchIndex =
      moduleNameToRootSearchIndex.get(module.getName());
  if (moduleRootSearchIndex == null) {
    moduleRootSearchIndex = new PatriciaTrie<>();
    moduleNameToRootSearchIndex.put(module.getName(), moduleRootSearchIndex);
  }

  OrderEnumerator moduleOrderEnumerator = OrderEnumerator.orderEntries(module);

  List<MetadataContainerInfo> newModuleContainersToProcess =
      computeNewContainersToProcess(moduleOrderEnumerator,
          moduleSeenContainerPathToSeenContainerInfo);
  newModuleContainersToProcess.addAll(newProjectSourcesToProcess);

  List<MetadataContainerInfo> moduleContainersToRemove =
      computeContainersToRemove(moduleOrderEnumerator,
          moduleSeenContainerPathToSeenContainerInfo);
  moduleContainersToRemove.addAll(projectContainersToRemove);

  processContainers(module, newModuleContainersToProcess, moduleContainersToRemove,
      moduleSeenContainerPathToSeenContainerInfo, moduleRootSearchIndex);
}
 
Example #10
Source File: UnmodifiableTrie.java    From jesterj with Apache License 2.0 5 votes vote down vote up
/**
 * Constructor that wraps (not copies).
 *
 * @param trie  the trie to decorate, must not be null
 * @throws NullPointerException if trie is null
 */
public UnmodifiableTrie(final Trie<K, ? extends V> trie) {
    if (trie == null) {
        throw new NullPointerException("Trie must not be null");
    }
    @SuppressWarnings("unchecked") // safe to upcast
    final Trie<K, V> tmpTrie = (Trie<K, V>) trie;
    this.delegate = tmpTrie;
}
 
Example #11
Source File: SuggestionServiceImpl.java    From intellij-spring-assistant with MIT License 5 votes vote down vote up
private void processContainers(Module module, List<MetadataContainerInfo> containersToProcess,
    List<MetadataContainerInfo> containersToRemove,
    Map<String, MetadataContainerInfo> seenContainerPathToContainerInfo,
    Trie<String, MetadataSuggestionNode> rootSearchIndex) {
  // Lets remove references to files that are no longer present in classpath
  containersToRemove.forEach(
      container -> removeReferences(seenContainerPathToContainerInfo, rootSearchIndex,
          container));

  for (MetadataContainerInfo metadataContainerInfo : containersToProcess) {
    // lets remove existing references from search index, as these files are modified, so that we can rebuild index
    if (seenContainerPathToContainerInfo
        .containsKey(metadataContainerInfo.getContainerArchiveOrFileRef())) {
      removeReferences(seenContainerPathToContainerInfo, rootSearchIndex, metadataContainerInfo);
    }

    String metadataFilePath = metadataContainerInfo.getFileUrl();
    try (InputStream inputStream = metadataContainerInfo.getMetadataFile().getInputStream()) {
      GsonBuilder gsonBuilder = new GsonBuilder();
      // register custom mapper adapters
      gsonBuilder.registerTypeAdapter(SpringConfigurationMetadataValueProviderType.class,
          new SpringConfigurationMetadataValueProviderTypeDeserializer());
      gsonBuilder.registerTypeAdapterFactory(new GsonPostProcessEnablingTypeFactory());
      SpringConfigurationMetadata springConfigurationMetadata = gsonBuilder.create()
          .fromJson(new BufferedReader(new InputStreamReader(inputStream)),
              SpringConfigurationMetadata.class);
      buildMetadataHierarchy(module, rootSearchIndex, metadataContainerInfo,
          springConfigurationMetadata);

      seenContainerPathToContainerInfo
          .put(metadataContainerInfo.getContainerArchiveOrFileRef(), metadataContainerInfo);
    } catch (IOException e) {
      log.error("Exception encountered while processing metadata file: " + metadataFilePath, e);
      removeReferences(seenContainerPathToContainerInfo, rootSearchIndex, metadataContainerInfo);
    }
  }
}
 
Example #12
Source File: PatriciaTrieTest.java    From jesterj with Apache License 2.0 5 votes vote down vote up
public void testPrefixMapClearNothing() {
    final Trie<CharSequence, Integer> trie = new PatriciaTrie<>();
    final SortedMap<CharSequence, Integer> prefixMap = trie.prefixMap("And");
    assertEquals(new HashSet<String>(), prefixMap.keySet());
    assertEquals(new ArrayList<Integer>(0), new ArrayList<>(prefixMap.values()));

    prefixMap.clear();
    assertTrue(prefixMap.isEmpty());
    assertTrue(prefixMap.keySet().isEmpty());
    assertTrue(prefixMap.values().isEmpty());
    assertEquals(new HashSet<String>(), trie.keySet());
    assertEquals(new ArrayList<Integer>(0), new ArrayList<>(trie.values()));
}
 
Example #13
Source File: SuggestionServiceImpl.java    From intellij-spring-assistant with MIT License 4 votes vote down vote up
private void addPropertiesToIndex(Module module,
    Trie<String, MetadataSuggestionNode> rootSearchIndex,
    SpringConfigurationMetadata springConfigurationMetadata, String containerArchiveOrFileRef) {
  List<SpringConfigurationMetadataProperty> properties =
      springConfigurationMetadata.getProperties();
  properties.sort(comparing(SpringConfigurationMetadataProperty::getName));
  for (SpringConfigurationMetadataProperty property : properties) {
    String[] pathSegments = toSanitizedPathSegments(property.getName());
    String[] rawPathSegments = toRawPathSegments(property.getName());
    MetadataSuggestionNode closestMetadata =
        findDeepestMetadataMatch(rootSearchIndex, pathSegments, false);

    int startIndex;
    if (closestMetadata == null) { // path does not have a corresponding root element
      boolean onlyRootSegmentExists = pathSegments.length == 1;
      if (onlyRootSegmentExists) {
        closestMetadata = MetadataPropertySuggestionNode
            .newInstance(rawPathSegments[0], property, null, containerArchiveOrFileRef);
      } else {
        closestMetadata = MetadataNonPropertySuggestionNode
            .newInstance(rawPathSegments[0], null, containerArchiveOrFileRef);
      }
      rootSearchIndex.put(pathSegments[0], closestMetadata);

      // since we already handled the root level item, let addChildren start from index 1 of pathSegments
      startIndex = 1;
    } else {
      startIndex = closestMetadata.numOfHopesToRoot() + 1;
    }

    boolean haveMoreSegmentsLeft = startIndex < rawPathSegments.length;

    if (haveMoreSegmentsLeft) {
      if (!closestMetadata.isProperty()) {
        MetadataNonPropertySuggestionNode.class.cast(closestMetadata)
            .addChildren(property, rawPathSegments, startIndex, containerArchiveOrFileRef);
      } else {
        log.warn("Detected conflict between a new group & existing property for suggestion path "
            + closestMetadata.getPathFromRoot(module)
            + ". Ignoring property. Existing non property node belongs to (" + closestMetadata
            .getBelongsTo().stream().collect(joining(",")) + "), New property belongs to "
            + containerArchiveOrFileRef);
      }
    } else {
      if (!closestMetadata.isProperty()) {
        log.warn(
            "Detected conflict between a new metadata property & existing non property node for suggestion path "
                + closestMetadata.getPathFromRoot(module)
                + ". Ignoring property. Existing non property node belongs to (" + closestMetadata
                .getBelongsTo().stream().collect(joining(",")) + "), New property belongs to "
                + containerArchiveOrFileRef);
      } else {
        closestMetadata.addRefCascadeTillRoot(containerArchiveOrFileRef);
        log.debug("Detected a duplicate metadata property for suggestion path " + closestMetadata
            .getPathFromRoot(module) + ". Ignoring property. Existing property belongs to ("
            + closestMetadata.getBelongsTo().stream().collect(joining(","))
            + "), New property belongs to " + containerArchiveOrFileRef);
      }
    }
  }
}
 
Example #14
Source File: SuggestionServiceImpl.java    From intellij-spring-assistant with MIT License 4 votes vote down vote up
private void addGroupsToIndex(Module module, Trie<String, MetadataSuggestionNode> rootSearchIndex,
    SpringConfigurationMetadata springConfigurationMetadata, String containerArchiveOrFileRef) {
  List<SpringConfigurationMetadataGroup> groups = springConfigurationMetadata.getGroups();
  if (groups != null) {
    groups.sort(comparing(SpringConfigurationMetadataGroup::getName));
    for (SpringConfigurationMetadataGroup group : groups) {
      String[] pathSegments = toSanitizedPathSegments(group.getName());
      String[] rawPathSegments = toRawPathSegments(group.getName());

      MetadataSuggestionNode closestMetadata = MetadataSuggestionNode.class
          .cast(findDeepestMetadataMatch(rootSearchIndex, pathSegments, false));

      int startIndex;
      if (closestMetadata == null) { // path does not have a corresponding root element
        // lets build just the root element. Rest of the path segments will be taken care of by the addChildren method
        boolean onlyRootSegmentExists = pathSegments.length == 1;
        MetadataNonPropertySuggestionNode newGroupSuggestionNode =
            MetadataNonPropertySuggestionNode
                .newInstance(rawPathSegments[0], null, containerArchiveOrFileRef);
        if (onlyRootSegmentExists) {
          newGroupSuggestionNode.setGroup(module, group);
        }
        rootSearchIndex.put(pathSegments[0], newGroupSuggestionNode);

        closestMetadata = newGroupSuggestionNode;
        // since we already handled the root level item, let addChildren start from index 1 of pathSegments
        startIndex = 1;
      } else {
        startIndex = closestMetadata.numOfHopesToRoot() + 1;
      }

      if (closestMetadata.isProperty()) {
        log.warn(
            "Detected conflict between an existing metadata property & new group for suggestion path "
                + closestMetadata.getPathFromRoot(module)
                + ". Ignoring new group. Existing Property belongs to (" + closestMetadata
                .getBelongsTo().stream().collect(joining(",")) + "), New Group belongs to "
                + containerArchiveOrFileRef);
      } else {
        // lets add container as a reference till root
        MetadataNonPropertySuggestionNode groupSuggestionNode =
            MetadataNonPropertySuggestionNode.class.cast(closestMetadata);
        groupSuggestionNode.addRefCascadeTillRoot(containerArchiveOrFileRef);

        boolean haveMoreSegmentsLeft = startIndex < rawPathSegments.length;
        if (haveMoreSegmentsLeft) {
          groupSuggestionNode
              .addChildren(module, group, rawPathSegments, startIndex, containerArchiveOrFileRef);
        } else {
          // Node is an intermediate node that has neither group nor property assigned to it, lets assign this group to it
          // Can happen when `a.b.c` is already added to the metadata tree from an earlier metadata source & now we are trying to add a group for `a.b`
          // In this e.g, startIndex would be 2. So, there is no point in adding children. We only need to update the tree appropriately
          groupSuggestionNode.setGroup(module, group);
        }
      }
    }
  }
}
 
Example #15
Source File: SuggestionServiceImpl.java    From intellij-spring-assistant with MIT License 4 votes vote down vote up
@Override
public boolean canProvideSuggestions(Project project, Module module) {
  Trie<String, MetadataSuggestionNode> rootSearchIndex =
      moduleNameToRootSearchIndex.get(module.getName());
  return rootSearchIndex != null && rootSearchIndex.size() != 0;
}
 
Example #16
Source File: SuggestionServiceImpl.java    From intellij-spring-assistant with MIT License 4 votes vote down vote up
private List<LookupElementBuilder> doFindSuggestionsForQueryPrefix(Module module,
    Trie<String, MetadataSuggestionNode> rootSearchIndex, FileType fileType, PsiElement element,
    @Nullable List<String> ancestralKeys, String queryWithDotDelimitedPrefixes,
    @Nullable Set<String> siblingsToExclude) {
  debug(() -> log.debug("Search requested for " + queryWithDotDelimitedPrefixes));
  StopWatch timer = new StopWatch();
  timer.start();
  try {
    String[] querySegmentPrefixes = toSanitizedPathSegments(queryWithDotDelimitedPrefixes);
    Set<Suggestion> suggestions = null;
    if (ancestralKeys != null) {
      String[] ancestralKeySegments =
          ancestralKeys.stream().flatMap(key -> stream(toRawPathSegments(key)))
              .toArray(String[]::new);
      MetadataSuggestionNode rootNode = rootSearchIndex.get(sanitise(ancestralKeySegments[0]));
      if (rootNode != null) {
        List<SuggestionNode> matchesRootToDeepest;
        SuggestionNode startSearchFrom = null;
        if (ancestralKeySegments.length > 1) {
          String[] sanitisedAncestralPathSegments =
              stream(ancestralKeySegments).map(SuggestionNode::sanitise).toArray(String[]::new);
          matchesRootToDeepest = rootNode
              .findDeepestSuggestionNode(module, modifiableList(rootNode),
                  sanitisedAncestralPathSegments, 1);
          if (matchesRootToDeepest != null && matchesRootToDeepest.size() != 0) {
            startSearchFrom = matchesRootToDeepest.get(matchesRootToDeepest.size() - 1);
          }
        } else {
          startSearchFrom = rootNode;
          matchesRootToDeepest = singletonList(rootNode);
        }

        if (startSearchFrom != null) {
          // if search start node is a leaf, this means, the user is looking for values for the given key, lets find the suggestions for values
          if (startSearchFrom.isLeaf(module)) {
            suggestions = startSearchFrom.findValueSuggestionsForPrefix(module, fileType,
                unmodifiableList(matchesRootToDeepest),
                sanitise(truncateIdeaDummyIdentifier(element.getText())), siblingsToExclude);
          } else {
            suggestions = startSearchFrom.findKeySuggestionsForQueryPrefix(module, fileType,
                unmodifiableList(matchesRootToDeepest), matchesRootToDeepest.size(),
                querySegmentPrefixes, 0, siblingsToExclude);
          }
        }
      }
    } else {
      String rootQuerySegmentPrefix = querySegmentPrefixes[0];
      SortedMap<String, MetadataSuggestionNode> topLevelQueryResults =
          rootSearchIndex.prefixMap(rootQuerySegmentPrefix);

      Collection<MetadataSuggestionNode> childNodes;
      int querySegmentPrefixStartIndex;

      // If no results are found at the top level, let dive deeper and find matches
      if (topLevelQueryResults == null || topLevelQueryResults.size() == 0) {
        childNodes = rootSearchIndex.values();
        querySegmentPrefixStartIndex = 0;
      } else {
        childNodes = topLevelQueryResults.values();
        querySegmentPrefixStartIndex = 1;
      }

      Collection<MetadataSuggestionNode> nodesToSearchAgainst;
      if (siblingsToExclude != null) {
        Set<MetadataSuggestionNode> nodesToExclude = siblingsToExclude.stream()
            .flatMap(exclude -> rootSearchIndex.prefixMap(exclude).values().stream())
            .collect(toSet());
        nodesToSearchAgainst =
            childNodes.stream().filter(node -> !nodesToExclude.contains(node)).collect(toList());
      } else {
        nodesToSearchAgainst = childNodes;
      }

      suggestions = doFindSuggestionsForQueryPrefix(module, fileType, nodesToSearchAgainst,
          querySegmentPrefixes, querySegmentPrefixStartIndex);
    }

    if (suggestions != null) {
      return toLookupElementBuilders(suggestions);
    }
    return null;
  } finally {
    timer.stop();
    debug(() -> log.debug("Search took " + timer.toString()));
  }
}
 
Example #17
Source File: UnmodifiableTrie.java    From jesterj with Apache License 2.0 3 votes vote down vote up
/**
 * Factory method to create a unmodifiable trie.
 *
 * @param <K>  the key type
 * @param <V>  the value type
 * @param trie  the trie to decorate, must not be null
 * @return a new unmodifiable trie
 * @throws NullPointerException if trie is null
 */
public static <K, V> Trie<K, V> unmodifiableTrie(final Trie<K, ? extends V> trie) {
    if (trie instanceof Unmodifiable) {
        @SuppressWarnings("unchecked") // safe to upcast
        final Trie<K, V> tmpTrie = (Trie<K, V>) trie;
        return tmpTrie;
    }
    return new UnmodifiableTrie<K, V>(trie);
}