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

wycc.util.Trie Maven / Gradle / Ivy

// Copyright 2011 The Whiley Project Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package wycc.util;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Iterator;
import java.util.regex.Pattern;

/**
 * 

* Provides a standard implementation of the Path.ID and Path.Filter interfaces. * It employs the flyweight pattern to ensure every ID can only ever * correspond to a single instanceof of Trie. That, it ensures that any two * instances which represent the same Path.ID are, in fact, the same instance. *

*

* As the name suggests, the implementation is based around a n-ary tree where * each node stores a component of a path. There is a single master root of all * possible paths and all possible instances of Trie extend this. *

*

* NOTE: the implementation does not currently attempt to garbage collect * tries. Therefore, the memory consumed is proportional to the total number of * distinct tries created throughout the program's life *

* * @author David J. Pearce * */ public final class Trie implements Iterable, Comparable { // ========================================================= // Private Constants // ========================================================= private static final Trie[] ONE_CHILD = new Trie[1]; // ========================================================= // Public Constants // ========================================================= public static final Trie ROOT = new Trie(null,""); public static final Trie STAR = fromString("*"); public static final Trie STARSTAR = fromString("**"); /** * A default filter which recursively matches everything */ public static final Trie EVERYTHING = Trie.fromString("**/*"); // ========================================================= // Private State // ========================================================= private final Trie parent; private final String component; private final int depth; private final boolean isConcrete; private Trie[] children; private int nchildren; // ========================================================= // Public Methods // ========================================================= Trie(final Trie parent, final String component) { this.parent = parent; this.component = component; if(parent != null) { this.depth = parent.depth + 1; } else { this.depth = -1; } this.children = ONE_CHILD; this.nchildren = 0; this.isConcrete = (parent == null || parent.isConcrete) && !component.contains("*"); } public int size() { return depth + 1; } public boolean isConcrete() { return isConcrete; } public String get(final int index) { if(index == depth) { return component; } else if(index > depth) { throw new IllegalArgumentException("index out-of-bounds"); } else { return parent.get(index); } } public Path toPath() { if(parent == null) { return Paths.get(component); } else { return parent.toPath().resolve(component); } } public boolean matches(Trie id) { return match(id, 0, 0, false); } public String last() { return component; } public Trie parent() { return parent; } public Trie subpath(int start, int end) { Trie id = Trie.ROOT; for(int i=start;i!=end;++i) { // TODO: this could be made more efficient id = id.append(get(i)); } return id; } public Trie parent(int depth) { if(this.depth < depth) { return this; } else { return parent.parent(depth); } } @Override public Iterator iterator() { return new InternalIterator(this); } @Override public int compareTo(final Trie o) { if(o instanceof Trie) { // We can be efficient here Trie t1 = this; Trie t2 = o; while(t1.depth > t2.depth) { t1 = t1.parent; } while(t2.depth > t1.depth) { t2 = t2.parent; } while(t1.parent != t2.parent) { t1 = t1.parent; t2 = t2.parent; } int c = t1.component.compareTo(t2.component); if(c != 0) { return c; } // assert t2 == o int tDepth = t2.depth; if(depth < tDepth) { return -1; } else if(depth > tDepth) { return 1; } else { return 0; } } else { throw new IllegalArgumentException("Attempting to compare Trie with some other Path.ID"); } } @Override public int hashCode() { int hc = component.hashCode(); if(parent != null) { hc = hc ^ parent.hashCode(); } return hc; } @Override public boolean equals(final Object o) { return this == o; } public Trie append(final String component) { int index = binarySearch(children, nchildren, component); if(index >= 0) { return children[index]; } Trie nt = new Trie(this,component); index = -index - 1; // calculate insertion point if((nchildren+1) < children.length) { System.arraycopy(children, index, children, index+1, nchildren - index); } else { Trie[] tmp = new Trie[children.length * 2]; System.arraycopy(children, 0, tmp, 0, index); System.arraycopy(children, index, tmp, index+1, nchildren - index); children = tmp; } children[index] = nt; nchildren++; return nt; } public Trie append(final Trie t) { Trie r = this; // TODO: this could be more efficient for (int i = 0; i != t.size(); ++i) { r = r.append(t.get(i)); } return r; } @Override public String toString() { if(parent == null || parent == ROOT) { return component; } else { return parent.toString() + "/" + component; } } public String toNativeString() { if(parent == null || parent == ROOT) { return component; } else { return parent.toNativeString() + File.separatorChar + component; } } /** * Construct a Trie from a string, where '/' is the separator. * s * @param str * @return */ public static Trie fromString(String str) { String[] components = str.split("/"); Trie r = ROOT; for(int i=0;i!=components.length;++i) { r = r.append(components[i]); } return r; } public static Trie fromStrings(String... components) { Trie r = ROOT; for(int i=0;i!=components.length;++i) { r = r.append(components[i]); } return r; } /** * Construct a Trie from a native string, where the separator is determined by * the native filesystem separator char. s * * @param str * @return */ public static Trie fromNativeString(String str) { String[] components = str.split(Pattern.quote(File.separator)); Trie r = ROOT; for(int i=0;i!=components.length;++i) { r = r.append(components[i]); } return r; } // ========================================================= // Private Methods // ========================================================= private boolean match(Trie t, int idIndex, int myIndex, boolean submatch) { int mySize = depth + 1; if (myIndex == mySize && idIndex == t.size()) { return true; } else if(idIndex == t.size()) { return submatch; } else if (myIndex == mySize) { return false; } String myComponent = get(myIndex); if (myComponent.equals("*")) { return match(t, idIndex + 1, myIndex + 1, submatch); } else if (myComponent.equals("**")) { myIndex++; for (int i = idIndex; i <= t.size(); ++i) { if (match(t, i, myIndex, submatch)) { return true; } } return false; } else { return myComponent.equals(t.get(idIndex)) && match(t, idIndex + 1, myIndex + 1, submatch); } } private static final int binarySearch(final Trie[] children, final int nchildren, final String key) { int low = 0; int high = nchildren-1; while (low <= high) { int mid = (low + high) >> 1; int c = children[mid].component.compareTo(key); if (c < 0) { low = mid + 1; } else if (c > 0) { high = mid - 1; } else { return mid; } } return -(low + 1); } private static final class InternalIterator implements Iterator { private final Trie id; private int index; public InternalIterator(Trie id) { this.id = id; this.index = 0; } @Override public boolean hasNext() { return index <= id.depth; } @Override public String next() { return id.get(index++); } @Override public void remove() { throw new UnsupportedOperationException(); } } public static void main(String[] args) { Trie t1 = ROOT.append("Hello"); Trie t2 = t1.append("*"); Trie t3 = t2.append("Blah"); System.out.println("T1: " + t3.parent(1)); System.out.println("T2: " + t3.parent(2)); System.out.println("T3: " + t3.parent(3)); Trie[] ids = {ROOT,t2,t3,t1}; for(Trie id : ids) { System.out.println(id + "(" + id.size() + ")"); } Arrays.sort(ids); for(Trie id : ids) { System.out.println(id); } for(String c : t3) { System.out.println(c); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy