/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* 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 com.intellij.openapi.util.io;
import com.intellij.openapi.util.text.StringUtil;
import gnu.trove.THashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* @author yole
*/
public class UniqueNameBuilder<T> {
private static final String VFS_SEPARATOR = "/";
private final Map<T, String> myPaths = new THashMap<T, String>();
private final String mySeparator;
private final int myMaxLength;
private final String myRoot;
public UniqueNameBuilder(String root, String separator, int maxLength) {
myRoot = root;
mySeparator = separator;
myMaxLength = maxLength;
}
public boolean contains(T file) {
return myPaths.containsKey(file);
}
private static class Node {
final String myText;
final THashMap<String, Node> myChildren;
final Node myParentNode;
int myNestedChildrenCount;
Node(String text, Node parentNode) {
myText = text;
myParentNode = parentNode;
myChildren = new THashMap<String, Node>(1);
}
Node findOrAddChild(String word) {
Node node = myChildren.get(word);
if (node == null) myChildren.put(word, node = new Node(word, this));
return node;
}
}
private final Node myRootNode = new Node("", null);
// Build a trie from path components starting from end
// E.g. following try will be build from example
// |<-------[/fabrique] <- [/idea]
// /idea/pycharm/download/index.html |
// /idea/fabrique/download/index.html [RootNode] <- [/index.html] <- [/download] <- [/pycharm] <- [/idea]
// /idea/pycharm/documentation/index.html |
// |<------[/documentation] <- [/pycharm] <- [/idea]
public void addPath(T key, String path) {
path = StringUtil.trimStart(path, myRoot);
myPaths.put(key, path);
Node current = myRootNode;
Iterator<String> pathComponentsIterator = new PathComponentsIterator(path);
while(pathComponentsIterator.hasNext()) {
String word = pathComponentsIterator.next();
current = current.findOrAddChild(word);
}
for(Node c = current; c != null; c = c.myParentNode) ++c.myNestedChildrenCount;
}
public String getShortPath(T key) {
String path = myPaths.get(key);
if (path == null) return key.toString();
Node current = myRootNode;
Node firstNodeWithBranches = null;
Node firstNodeBeforeNodeWithBranches = null;
Node fileNameNode = null;
Iterator<String> pathComponentsIterator = new PathComponentsIterator(path);
while(pathComponentsIterator.hasNext()) {
String pathComponent = pathComponentsIterator.next();
current = current.findOrAddChild(pathComponent);
if (fileNameNode == null) fileNameNode = current;
if (firstNodeBeforeNodeWithBranches == null &&
firstNodeWithBranches != null &&
current.myChildren.size() <= 1) {
if(current.myParentNode.myNestedChildrenCount - current.myParentNode.myChildren.size() < 1) {
firstNodeBeforeNodeWithBranches = current;
}
}
if (current.myChildren.size() != 1 && firstNodeWithBranches == null) {
firstNodeWithBranches = current;
}
}
StringBuilder b = new StringBuilder();
if (firstNodeBeforeNodeWithBranches == null) {
firstNodeBeforeNodeWithBranches = current;
}
boolean skipFirstSeparator = true;
for(Node c = firstNodeBeforeNodeWithBranches; c!= myRootNode; c = c.myParentNode) {
if (c != fileNameNode && c != firstNodeBeforeNodeWithBranches && c.myParentNode.myChildren.size() == 1) {
b.append(mySeparator);
b.append("\u2026");
while(c.myParentNode != fileNameNode && c.myParentNode.myChildren.size() == 1) c = c.myParentNode;
} else {
if (c.myText.startsWith(VFS_SEPARATOR)) {
if (!skipFirstSeparator) b.append(mySeparator);
skipFirstSeparator = false;
b.append(c.myText, VFS_SEPARATOR.length(), c.myText.length());
} else {
b.append(c.myText);
}
}
}
return b.toString();
}
public String getSeparator() {
return mySeparator;
}
private static class PathComponentsIterator implements Iterator<String> {
private final String myPath;
private int myLastPos;
private int mySeparatorPos;
PathComponentsIterator(String path) {
myPath = path;
myLastPos = path.length();
mySeparatorPos = path.lastIndexOf(VFS_SEPARATOR);
}
@Override
public boolean hasNext() {
return myLastPos != 0;
}
@Override
public String next() {
if (myLastPos == 0) throw new NoSuchElementException();
String pathComponent;
if (mySeparatorPos != -1) {
pathComponent = myPath.substring(mySeparatorPos, myLastPos);
myLastPos = mySeparatorPos;
mySeparatorPos = myPath.lastIndexOf(VFS_SEPARATOR, myLastPos - 1);
} else {
pathComponent = myPath.substring(0, myLastPos);
if (!pathComponent.startsWith(VFS_SEPARATOR)) pathComponent = VFS_SEPARATOR + pathComponent;
myLastPos = 0;
}
return pathComponent;
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
}
}