All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.hm.hafner.util.TreeString Maven / Gradle / Ivy

package edu.hm.hafner.util;

import java.io.Serializable;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import edu.umd.cs.findbugs.annotations.CheckForNull;

/**
 * {@link TreeString} is an alternative string representation that saves the memory when you have a large number of
 * strings that share common prefixes (such as various file names.)
 *
 * 

* {@link TreeString} can be built with {@link TreeStringBuilder}. *

* * @author Kohsuke Kawaguchi */ public final class TreeString implements Serializable { private static final long serialVersionUID = 3621959682117480904L; /** Parent node that represents the prefix. */ @CheckForNull private TreeString parent; /** {@link #parent} + {@code label} is the string value of this node. */ private char[] label; /** * Creates a new root {@link TreeString}. */ TreeString() { this(null, ""); } /** * Creates a new {@link TreeString} with the given parent and suffix. * * @param parent * the parent * @param label * the suffix */ @SuppressWarnings("NullAway") TreeString(@CheckForNull final TreeString parent, final String label) { Ensure.that(parent == null || !label.isEmpty()) .isTrue("if there's a parent '%s', label '%s' can't be empty", parent, label); this.parent = parent; this.label = label.toCharArray(); // string created as a substring of another string can have a lot of garbage attached to it. } String getLabel() { return String.valueOf(label); } /** * Inserts a new node between this node and its parent, and returns the newly inserted node. * *

* This operation doesn't change the string representation of this node. *

* * @param prefix * the prefix to remove * * @return the new node in the middle */ TreeString split(final String prefix) { Ensure.that(getLabel().startsWith(prefix)).isTrue(); char[] suffix = new char[label.length - prefix.length()]; System.arraycopy(label, prefix.length(), suffix, 0, suffix.length); var middle = new TreeString(parent, prefix); label = suffix; parent = middle; return middle; } @VisibleForTesting @CheckForNull TreeString getParent() { return parent; } /** * How many nodes do we have from the root to this node (including 'this' itself?). Thus depth of the root node is * 1. * * @return the depth */ private int depth() { int i = 0; for (TreeString p = this; p != null; p = p.parent) { i++; } return i; } @Override public boolean equals(@CheckForNull final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } return toString().equals(((TreeString) o).toString()); } @Override public int hashCode() { return toString().hashCode(); } /** * Returns the full string representation. */ @Override public String toString() { char[][] tokens = new char[depth()][]; int i = tokens.length; int sz = 0; for (TreeString p = this; p != null; p = p.parent) { tokens[--i] = p.label; sz += p.label.length; } var buf = new StringBuilder(sz); for (char[] token : tokens) { buf.append(token); } return buf.toString(); } /** * Interns {@link #label}. * * @param table * the table containing the existing strings */ void dedup(final Map table) { String l = getLabel(); char[] v = table.get(l); if (v == null) { table.put(l, label); } else { label = v; } } public boolean isBlank() { return StringUtils.isBlank(toString()); } /** * Creates a {@link TreeString}. Useful if you need to create one-off {@link TreeString} without {@link * TreeStringBuilder}. Memory consumption is still about the same to {@code new String(string)}. * * @param string * the tree string * * @return the new {@link TreeString} */ public static TreeString valueOf(final String string) { return new TreeString(null, string); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy