blob: 8646ba0c72aaf3418f2d33e989a1b4b1f102348c [file] [log] [blame]
/*
* Copyright 2000-2014 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.fileTypes.impl;
import com.intellij.openapi.fileTypes.ExactFileNameMatcher;
import com.intellij.openapi.fileTypes.ExtensionFileNameMatcher;
import com.intellij.openapi.fileTypes.FileNameMatcher;
import com.intellij.openapi.fileTypes.FileNameMatcherEx;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.util.ArrayUtil;
import com.intellij.util.text.CharSequenceHashingStrategy;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author max
*/
public class FileTypeAssocTable<T> {
private final Map<CharSequence, T> myExtensionMappings;
private final Map<CharSequence, T> myExactFileNameMappings;
private final Map<CharSequence, T> myExactFileNameAnyCaseMappings;
private boolean myHasAnyCaseExactMappings;
private final List<Pair<FileNameMatcher, T>> myMatchingMappings;
private FileTypeAssocTable(Map<CharSequence, T> extensionMappings, Map<CharSequence, T> exactFileNameMappings, Map<CharSequence, T> exactFileNameAnyCaseMappings, List<Pair<FileNameMatcher, T>> matchingMappings) {
myExtensionMappings = new THashMap<CharSequence, T>(extensionMappings, CharSequenceHashingStrategy.CASE_INSENSITIVE);
myExactFileNameMappings = new THashMap<CharSequence, T>(exactFileNameMappings, CharSequenceHashingStrategy.CASE_SENSITIVE);
myExactFileNameAnyCaseMappings = new THashMap<CharSequence, T>(exactFileNameAnyCaseMappings, CharSequenceHashingStrategy.CASE_INSENSITIVE) {
@Override
public T remove(Object key) {
T removed = super.remove(key);
myHasAnyCaseExactMappings = size() > 0;
return removed;
}
@Override
public T put(CharSequence key, T value) {
T result = super.put(key, value);
myHasAnyCaseExactMappings = true;
return result;
}
};
myMatchingMappings = new ArrayList<Pair<FileNameMatcher, T>>(matchingMappings);
}
public FileTypeAssocTable() {
this(Collections.<CharSequence, T>emptyMap(), Collections.<CharSequence, T>emptyMap(), Collections.<CharSequence, T>emptyMap(), Collections.<Pair<FileNameMatcher, T>>emptyList());
}
public boolean isAssociatedWith(T type, FileNameMatcher matcher) {
if (matcher instanceof ExtensionFileNameMatcher || matcher instanceof ExactFileNameMatcher) {
return findAssociatedFileType(matcher) == type;
}
for (Pair<FileNameMatcher, T> mapping : myMatchingMappings) {
if (matcher.equals(mapping.getFirst()) && type == mapping.getSecond()) return true;
}
return false;
}
public void addAssociation(FileNameMatcher matcher, T type) {
if (matcher instanceof ExtensionFileNameMatcher) {
myExtensionMappings.put(((ExtensionFileNameMatcher)matcher).getExtension(), type);
}
else if (matcher instanceof ExactFileNameMatcher) {
final ExactFileNameMatcher exactFileNameMatcher = (ExactFileNameMatcher)matcher;
if (exactFileNameMatcher.isIgnoreCase()) {
myExactFileNameAnyCaseMappings.put(exactFileNameMatcher.getFileName(), type);
} else {
myExactFileNameMappings.put(exactFileNameMatcher.getFileName(), type);
}
} else {
myMatchingMappings.add(Pair.create(matcher, type));
}
}
public boolean removeAssociation(FileNameMatcher matcher, T type) {
if (matcher instanceof ExtensionFileNameMatcher) {
String extension = ((ExtensionFileNameMatcher)matcher).getExtension();
if (myExtensionMappings.get(extension) == type) {
myExtensionMappings.remove(extension);
return true;
}
return false;
}
if (matcher instanceof ExactFileNameMatcher) {
final ExactFileNameMatcher exactFileNameMatcher = (ExactFileNameMatcher)matcher;
final Map<CharSequence, T> mapToUse;
String fileName = exactFileNameMatcher.getFileName();
if (exactFileNameMatcher.isIgnoreCase()) {
mapToUse = myExactFileNameAnyCaseMappings;
} else {
mapToUse = myExactFileNameMappings;
}
if(mapToUse.get(fileName) == type) {
mapToUse.remove(fileName);
return true;
}
return false;
}
List<Pair<FileNameMatcher, T>> copy = new ArrayList<Pair<FileNameMatcher, T>>(myMatchingMappings);
for (Pair<FileNameMatcher, T> assoc : copy) {
if (matcher.equals(assoc.getFirst())) {
myMatchingMappings.remove(assoc);
return true;
}
}
return false;
}
public boolean removeAllAssociations(T type) {
boolean changed = removeAssociationsFromMap(myExtensionMappings, type, false);
changed = removeAssociationsFromMap(myExactFileNameAnyCaseMappings, type, changed);
changed = removeAssociationsFromMap(myExactFileNameMappings, type, changed);
List<Pair<FileNameMatcher, T>> copy = new ArrayList<Pair<FileNameMatcher, T>>(myMatchingMappings);
for (Pair<FileNameMatcher, T> assoc : copy) {
if (assoc.getSecond() == type) {
myMatchingMappings.remove(assoc);
changed = true;
}
}
return changed;
}
private boolean removeAssociationsFromMap(Map<CharSequence, T> extensionMappings, T type, boolean changed) {
Set<CharSequence> exts = extensionMappings.keySet();
CharSequence[] extsStrings = exts.toArray(new CharSequence[exts.size()]);
for (CharSequence s : extsStrings) {
if (extensionMappings.get(s) == type) {
extensionMappings.remove(s);
changed = true;
}
}
return changed;
}
@Nullable
public T findAssociatedFileType(@NotNull @NonNls CharSequence fileName) {
T t = myExactFileNameMappings.get(fileName);
if (t != null) return t;
if (myHasAnyCaseExactMappings) { // even hash lookup with case insensitive hasher is costly for isIgnored checks during compile
t = myExactFileNameAnyCaseMappings.get(fileName);
if (t != null) return t;
}
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = myMatchingMappings.size(); i < n; i++) {
final Pair<FileNameMatcher, T> mapping = myMatchingMappings.get(i);
if (FileNameMatcherEx.acceptsCharSequence(mapping.getFirst(), fileName)) return mapping.getSecond();
}
return myExtensionMappings.get(FileUtilRt.getExtension(fileName));
}
@Nullable
public T findAssociatedFileType(final FileNameMatcher matcher) {
if (matcher instanceof ExtensionFileNameMatcher) {
return myExtensionMappings.get(((ExtensionFileNameMatcher)matcher).getExtension());
}
if (matcher instanceof ExactFileNameMatcher) {
final ExactFileNameMatcher exactFileNameMatcher = (ExactFileNameMatcher)matcher;
if (exactFileNameMatcher.isIgnoreCase()) {
return myExactFileNameAnyCaseMappings.get(exactFileNameMatcher.getFileName());
} else {
return myExactFileNameMappings.get(exactFileNameMatcher.getFileName());
}
}
for (Pair<FileNameMatcher, T> mapping : myMatchingMappings) {
if (matcher.equals(mapping.getFirst())) return mapping.getSecond();
}
return null;
}
@Deprecated
@NotNull
public String[] getAssociatedExtensions(T type) {
Map<CharSequence, T> extMap = myExtensionMappings;
List<String> exts = new ArrayList<String>();
for (CharSequence ext : extMap.keySet()) {
if (extMap.get(ext) == type) {
exts.add(ext.toString());
}
}
return ArrayUtil.toStringArray(exts);
}
@NotNull
public FileTypeAssocTable<T> copy() {
return new FileTypeAssocTable<T>(myExtensionMappings, myExactFileNameMappings, myExactFileNameAnyCaseMappings, myMatchingMappings);
}
@NotNull
public List<FileNameMatcher> getAssociations(final T type) {
List<FileNameMatcher> result = new ArrayList<FileNameMatcher>();
for (Pair<FileNameMatcher, T> mapping : myMatchingMappings) {
if (mapping.getSecond() == type) {
result.add(mapping.getFirst());
}
}
for (Map.Entry<CharSequence, T> entries : myExactFileNameMappings.entrySet()) {
if (entries.getValue() == type) {
result.add(new ExactFileNameMatcher(entries.getKey().toString()));
}
}
for (Map.Entry<CharSequence, T> entries : myExactFileNameAnyCaseMappings.entrySet()) {
if (entries.getValue() == type) {
result.add(new ExactFileNameMatcher(entries.getKey().toString(), true));
}
}
for (Map.Entry<CharSequence, T> entries : myExtensionMappings.entrySet()) {
if (entries.getValue() == type) {
result.add(new ExtensionFileNameMatcher(entries.getKey().toString()));
}
}
return result;
}
public boolean hasAssociationsFor(final T fileType) {
if (myExtensionMappings.values().contains(fileType)) return true;
if (myExactFileNameMappings.values().contains(fileType)) return true;
if (myExactFileNameAnyCaseMappings.values().contains(fileType)) return true;
for (Pair<FileNameMatcher, T> mapping : myMatchingMappings) {
if (mapping.getSecond() == fileType) return true;
}
return false;
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final FileTypeAssocTable that = (FileTypeAssocTable)o;
if (!myExtensionMappings.equals(that.myExtensionMappings)) return false;
if (!myMatchingMappings.equals(that.myMatchingMappings)) return false;
if (!myExactFileNameMappings.equals(that.myExactFileNameMappings)) return false;
if (!myExactFileNameAnyCaseMappings.equals(that.myExactFileNameAnyCaseMappings)) return false;
return true;
}
public int hashCode() {
int result;
result = myExtensionMappings.hashCode();
result = 31 * result + myMatchingMappings.hashCode();
result = 31 * result + myExactFileNameMappings.hashCode();
result = 31 * result + myExactFileNameAnyCaseMappings.hashCode();
return result;
}
}