content: package in.oneton.idea.spring.assistant.plugin.suggestion.metadata;
  
  import com.intellij.openapi.module.Module;
  import in.oneton.idea.spring.assistant.plugin.suggestion.Suggestion;
  import in.oneton.idea.spring.assistant.plugin.suggestion.SuggestionNode;
  import in.oneton.idea.spring.assistant.plugin.suggestion.completion.FileType;
  import org.jetbrains.annotations.Contract;
  import org.jetbrains.annotations.NotNull;
  
  import javax.annotation.Nullable;
  import java.util.List;
  import java.util.Set;
  import java.util.SortedSet;
  import java.util.Stack;
  
  import static java.util.stream.Collectors.joining;
  
  public abstract class MetadataSuggestionNode implements SuggestionNode {
  
    //  static MetadataSuggestionNode NULL_NODE = null;
  
    /**
     * If {@code matchAllSegments} is true, all {@code pathSegments} starting from {@code pathSegmentStartIndex} will be attempted to be matched. If a result is found, it will be returned. Else null
     * Else, method should attempt to match as deep as it can & return that match
     *
     * @param pathSegments          path segments to match against
     * @param pathSegmentStartIndex index within {@code pathSegments} to start match from
     * @param matchAllSegments      should all {@code pathSegments} be matched or not
     * @return leaf suggestion node that matches path segments starting with {@code pathSegmentStartIndex} or null otherwise
     */
    @Contract("_, _, true -> null; _, _, false -> !null")
    public abstract MetadataSuggestionNode findDeepestMetadataNode(String[] pathSegments,
        int pathSegmentStartIndex, boolean matchAllSegments);
  
    @Override
    public SortedSet<Suggestion> findKeySuggestionsForQueryPrefix(Module module, FileType fileType,
        List<SuggestionNode> matchesRootTillMe, int numOfAncestors, String[] querySegmentPrefixes,
        int querySegmentPrefixStartIndex) {
      return findKeySuggestionsForQueryPrefix(module, fileType, matchesRootTillMe, numOfAncestors,
          querySegmentPrefixes, querySegmentPrefixStartIndex, null);
    }
  
    @Nullable
    @Override
    public SortedSet<Suggestion> findValueSuggestionsForPrefix(Module module, FileType fileType,
        List<SuggestionNode> matchesRootTillMe, String prefix) {
      return findValueSuggestionsForPrefix(module, fileType, matchesRootTillMe, prefix, null);
    }
  
    public void addRefCascadeTillRoot(String containerPath) {
      MetadataSuggestionNode node = this;
      do {
        if (node.getBelongsTo().contains(containerPath)) {
          break;
        }
        node.getBelongsTo().add(containerPath);
        node = node.getParent();
      } while (node != null && !node.isRoot());
    }
  
    public abstract Set<String> getBelongsTo();
  
    /**
     * @param containerPath Represents path to the metadata file container
     * @return true if no children left & this item does not belong to any other source
     */
    public abstract boolean removeRefCascadeDown(String containerPath);
  
    /**
     * During reindexing lets make sure that we refresh references to proxies so that subsequent searches would be faster
     *
     * @param module idea module
     */
    public abstract void refreshClassProxy(Module module);
  
    protected abstract String getName();
  
    @NotNull
    public abstract String getOriginalName();
  
    @Nullable
    protected abstract MetadataNonPropertySuggestionNode getParent();
  
    protected abstract boolean isRoot();
  
    /**
     * @return whether the node expects any children or not
     */
    public abstract boolean isGroup();
  
    /**
     * @return whether the node expects any children or not
     */
    public abstract boolean isProperty();
  
    protected abstract boolean hasOnlyOneChild(Module module);
  
    public int numOfHopesToRoot() {
      int hopCount = 0;
      MetadataSuggestionNode current = getParent();
      while (current != null) {
        hopCount++;
        current = current.getParent();
      }
      return hopCount;
    }
  
    public String getPathFromRoot(Module module) {
      Stack<String> leafTillRoot = new Stack<>();
      MetadataSuggestionNode current = this;
      do {
        leafTillRoot.push(current.getOriginalName());
        current = current.getParent();
      } while (current != null);
      return leafTillRoot.stream().collect(joining("."));
    }
  
    @Override
    public boolean supportsDocumentation() {
      return isGroup() || isProperty();
    }
  
    @Nullable
    @Override
    public String getNameForDocumentation(Module module) {
      return getSuggestionNodeType(module).representsArrayOrCollection() ?
          getOriginalName() + "[]" :
          getOriginalName();
    }
  
    public abstract String toTree();
  
  }
  