| /******************************************************************************* |
| * Copyright (c) 2015, 2016 Google, Inc and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Stefan Xenos (Google) - Initial implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.nd.util; |
| |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| /** |
| * Maps IPath keys onto values. |
| */ |
| public class PathMap<T> { |
| private static class Node<T> { |
| int depth; |
| boolean exists; |
| T value; |
| Map<String, Node<T>> children; |
| |
| Node(int depth) { |
| this.depth = depth; |
| } |
| |
| String getSegment(IPath key) { |
| return key.segment(this.depth); |
| } |
| |
| Node<T> createNode(IPath key) { |
| if (this.depth == key.segmentCount()) { |
| this.exists = true; |
| return this; |
| } |
| |
| String nextSegment = getSegment(key); |
| Node<T> next = createChild(nextSegment); |
| return next.createNode(key); |
| } |
| |
| public Node<T> createChild(String nextSegment) { |
| if (this.children == null) { |
| this.children = new HashMap<>(); |
| } |
| Node<T> next = this.children.get(nextSegment); |
| if (next == null) { |
| next = new Node<>(this.depth + 1); |
| this.children.put(nextSegment, next); |
| } |
| return next; |
| } |
| |
| /** |
| * Returns this node or one of its descendants whose path is the longest-possible prefix of the given key (or |
| * equal to it). |
| */ |
| public Node<T> getMostSpecificNode(IPath key) { |
| if (this.depth == key.segmentCount()) { |
| return this; |
| } |
| String nextSegment = getSegment(key); |
| |
| Node<T> child = getChild(nextSegment); |
| if (child == null) { |
| return this; |
| } |
| Node<T> result = child.getMostSpecificNode(key); |
| if (result.exists) { |
| return result; |
| } else { |
| return this; |
| } |
| } |
| |
| Node<T> getChild(String nextSegment) { |
| if (this.children == null) { |
| return null; |
| } |
| return this.children.get(nextSegment); |
| } |
| |
| public void addAllKeys(Set<IPath> result, IPath parent) { |
| if (this.exists) { |
| result.add(parent); |
| } |
| |
| if (this.children == null) { |
| return; |
| } |
| |
| for (Entry<String, Node<T>> next : this.children.entrySet()) { |
| String key = next.getKey(); |
| IPath nextPath = buildChildPath(parent, key); |
| next.getValue().addAllKeys(result, nextPath); |
| } |
| } |
| |
| IPath buildChildPath(IPath parent, String key) { |
| IPath nextPath = parent.append(key); |
| return nextPath; |
| } |
| |
| public void toString(StringBuilder builder, IPath parentPath) { |
| if (this.exists) { |
| builder.append("["); //$NON-NLS-1$ |
| builder.append(parentPath); |
| builder.append("] = "); //$NON-NLS-1$ |
| builder.append(this.value); |
| builder.append("\n"); //$NON-NLS-1$ |
| } |
| if (this.children != null) { |
| for (Entry<String, Node<T>> next : this.children.entrySet()) { |
| String key = next.getKey(); |
| IPath nextPath = buildChildPath(parentPath, key); |
| next.getValue().toString(builder, nextPath); |
| } |
| } |
| } |
| } |
| |
| private static class DeviceNode<T> extends Node<T> { |
| Node<T> noDevice = new Node<>(0); |
| |
| DeviceNode() { |
| super(-1); |
| } |
| |
| @Override |
| String getSegment(IPath key) { |
| return key.getDevice(); |
| } |
| |
| @Override |
| public Node<T> createChild(String nextSegment) { |
| if (nextSegment == null) { |
| return this.noDevice; |
| } |
| return super.createChild(nextSegment); |
| } |
| |
| Node<T> getChild(String nextSegment) { |
| if (nextSegment == null) { |
| return this.noDevice; |
| } |
| return super.getChild(nextSegment); |
| } |
| |
| @Override |
| IPath buildChildPath(IPath parent, String key) { |
| IPath nextPath = Path.EMPTY.append(parent); |
| nextPath.setDevice(key); |
| return nextPath; |
| } |
| |
| @Override |
| public void toString(StringBuilder builder, IPath parentPath) { |
| this.noDevice.toString(builder, parentPath); |
| super.toString(builder,parentPath); |
| } |
| } |
| |
| private Node<T> root = new DeviceNode<T>(); |
| |
| /** |
| * Inserts the given key into the map. |
| */ |
| public T put(IPath key, T value) { |
| Node<T> node = this.root.createNode(key); |
| T result = node.value; |
| node.value = value; |
| return result; |
| } |
| |
| /** |
| * Returns the value associated with the given key |
| */ |
| public T get(IPath key) { |
| Node<T> node = this.root.getMostSpecificNode(key); |
| if (!node.exists || node.depth < key.segmentCount()) { |
| return null; |
| } |
| return node.value; |
| } |
| |
| /** |
| * Returns the value associated with the longest prefix of the given key |
| * that can be found in the map. |
| */ |
| public T getMostSpecific(IPath key) { |
| Node<T> node = this.root.getMostSpecificNode(key); |
| if (!node.exists) { |
| return null; |
| } |
| return node.value; |
| } |
| |
| /** |
| * Returns true iff any key in this map is a prefix of the given path. |
| */ |
| public boolean containsPrefixOf(IPath path) { |
| Node<T> node = this.root.getMostSpecificNode(path); |
| return node.exists; |
| } |
| |
| public Set<IPath> keySet() { |
| Set<IPath> result = new HashSet<>(); |
| |
| this.root.addAllKeys(result, Path.EMPTY); |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| |
| this.root.toString(builder, Path.EMPTY); |
| return builder.toString(); |
| } |
| |
| /** |
| * Returns true iff this map contains any key that starts with the given prefix. |
| */ |
| public boolean containsKeyStartingWith(IPath next) { |
| Node<T> node = this.root.getMostSpecificNode(next); |
| return node.depth == next.segmentCount(); |
| } |
| } |