From 3c9a8569603831c40af98a7de3b2c8ee10ad0769 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 22 Jan 2014 14:10:25 +0400 Subject: [PATCH] assertion for EA-52184 - IAE: FileTypeManagerImpl.isFileIgnored --- .../openapi/vfs/VirtualFilePropertyEvent.java | 4 +- .../events/VFilePropertyChangeEvent.java | 30 +++- .../newvfs/persistent/PersistentFSImpl.java | 49 ++++--- .../util/containers/EmptyIntHashSet.java | 128 ++++++++++++++++++ 4 files changed, 191 insertions(+), 20 deletions(-) create mode 100644 platform/util/src/com/intellij/util/containers/EmptyIntHashSet.java diff --git a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFilePropertyEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFilePropertyEvent.java index f4433aac2de0..91af87f6daca 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFilePropertyEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFilePropertyEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vfs; +import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent; import org.jetbrains.annotations.NotNull; /** @@ -33,6 +34,7 @@ public class VirtualFilePropertyEvent extends VirtualFileEvent { myPropertyName = propertyName; myOldValue = oldValue; myNewValue = newValue; + VFilePropertyChangeEvent.checkPropertyValuesCorrect(requestor, propertyName, oldValue, newValue); } /** diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFilePropertyChangeEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFilePropertyChangeEvent.java index ac7803c9dc63..360ce833a28b 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFilePropertyChangeEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFilePropertyChangeEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -19,8 +19,10 @@ */ package com.intellij.openapi.vfs.newvfs.events; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileSystem; +import com.intellij.util.FileContentUtilCore; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,6 +44,32 @@ public class VFilePropertyChangeEvent extends VFileEvent { myPropertyName = propertyName; myOldValue = oldValue; myNewValue = newValue; + checkPropertyValuesCorrect(requestor, propertyName, oldValue, newValue); + } + + public static void checkPropertyValuesCorrect(Object requestor, @NotNull String propertyName, Object oldValue, Object newValue) { + if (Comparing.equal(oldValue, newValue) && FileContentUtilCore.FORCE_RELOAD_REQUESTOR != requestor) { + throw new IllegalArgumentException("Values must be different, got the same: " + oldValue); + } + if (VirtualFile.PROP_NAME.equals(propertyName)) { + if (oldValue == null) throw new IllegalArgumentException("oldName must not be null"); + if (newValue == null) throw new IllegalArgumentException("newName must not be null"); + } + else if (VirtualFile.PROP_ENCODING.equals(propertyName)) { + if (oldValue == null) throw new IllegalArgumentException("oldCharset must not be null"); + } + else if (VirtualFile.PROP_WRITABLE.equals(propertyName)) { + if (!(oldValue instanceof Boolean)) throw new IllegalArgumentException("oldWriteable must be boolean, got "+oldValue); + if (!(newValue instanceof Boolean)) throw new IllegalArgumentException("newWriteable must be boolean, got "+newValue); + } + else if (VirtualFile.PROP_HIDDEN.equals(propertyName)) { + if (!(oldValue instanceof Boolean)) throw new IllegalArgumentException("oldHidden must be boolean, got "+oldValue); + if (!(newValue instanceof Boolean)) throw new IllegalArgumentException("newHidden must be boolean, got "+newValue); + } + else if (VirtualFile.PROP_SYMLINK_TARGET.equals(propertyName)) { + if (oldValue != null && !(oldValue instanceof String)) throw new IllegalArgumentException("oldSymTarget must be String, got "+oldValue); + if (newValue != null && !(newValue instanceof String)) throw new IllegalArgumentException("newSymTarget must be String, got "+newValue); + } } @NotNull diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java index c9c0579146ce..b8be33704761 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java @@ -34,6 +34,7 @@ import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry; import com.intellij.util.*; import com.intellij.util.containers.ConcurrentIntObjectMap; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.EmptyIntHashSet; import com.intellij.util.containers.StripedLockIntObjectConcurrentHashMap; import com.intellij.util.io.ReplicatorInputStream; import com.intellij.util.io.URLUtil; @@ -361,7 +362,10 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone @Override public void setWritable(@NotNull final VirtualFile file, final boolean writableFlag) throws IOException { getDelegate(file).setWritable(file, writableFlag); - processEvent(new VFilePropertyChangeEvent(this, file, VirtualFile.PROP_WRITABLE, isWritable(file), writableFlag, false)); + boolean oldWritable = isWritable(file); + if (oldWritable != writableFlag) { + processEvent(new VFilePropertyChangeEvent(this, file, VirtualFile.PROP_WRITABLE, oldWritable, writableFlag, false)); + } } @Override @@ -458,7 +462,10 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone @Override public void renameFile(final Object requestor, @NotNull VirtualFile file, @NotNull String newName) throws IOException { getDelegate(file).renameFile(requestor, file, newName); - processEvent(new VFilePropertyChangeEvent(requestor, file, VirtualFile.PROP_NAME, file.getName(), newName, false)); + String oldName = file.getName(); + if (!newName.equals(oldName)) { + processEvent(new VFilePropertyChangeEvent(requestor, file, VirtualFile.PROP_NAME, oldName, newName, false)); + } } @Override @@ -695,28 +702,34 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone } } - ContainerUtil.quickSort(deletionEvents, DEPTH_COMPARATOR); + final TIntHashSet invalidIDs; + if (deletionEvents.isEmpty()) { + invalidIDs = EmptyIntHashSet.INSTANCE; + } + else { + ContainerUtil.quickSort(deletionEvents, DEPTH_COMPARATOR); - final TIntHashSet invalidIDs = new TIntHashSet(deletionEvents.size()); - final Set dirsToBeDeleted = new THashSet(deletionEvents.size()); - nextEvent: - for (EventWrapper wrapper : deletionEvents) { - final VirtualFile candidate = wrapper.event.getFile(); - VirtualFile parent = candidate; - while (parent != null) { - if (dirsToBeDeleted.contains(parent)) { - invalidIDs.add(wrapper.id); - continue nextEvent; + invalidIDs = new TIntHashSet(deletionEvents.size()); + final Set dirsToBeDeleted = new THashSet(deletionEvents.size()); + nextEvent: + for (EventWrapper wrapper : deletionEvents) { + final VirtualFile candidate = wrapper.event.getFile(); + VirtualFile parent = candidate; + while (parent != null) { + if (dirsToBeDeleted.contains(parent)) { + invalidIDs.add(wrapper.id); + continue nextEvent; + } + parent = parent.getParent(); } - parent = parent.getParent(); - } - if (candidate.isDirectory()) { - dirsToBeDeleted.add(candidate); + if (candidate.isDirectory()) { + dirsToBeDeleted.add(candidate); + } } } - final List filtered = ContainerUtil.newArrayListWithCapacity(events.size() - invalidIDs.size()); + final List filtered = new ArrayList(events.size() - invalidIDs.size()); for (int i = 0, size = events.size(); i < size; i++) { final VFileEvent event = events.get(i); if (event.isValid() && !(event instanceof VFileDeleteEvent && invalidIDs.contains(i))) { diff --git a/platform/util/src/com/intellij/util/containers/EmptyIntHashSet.java b/platform/util/src/com/intellij/util/containers/EmptyIntHashSet.java new file mode 100644 index 000000000000..71241648fcf1 --- /dev/null +++ b/platform/util/src/com/intellij/util/containers/EmptyIntHashSet.java @@ -0,0 +1,128 @@ +/* + * 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.util.containers; + +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import gnu.trove.TIntHashSet; +import gnu.trove.TIntIterator; +import gnu.trove.TIntProcedure; + +import java.util.NoSuchElementException; + +public class EmptyIntHashSet extends TIntHashSet { + public static final TIntHashSet INSTANCE = new EmptyIntHashSet(); + public static final TIntIterator EMPTY_INT_ITERATOR = new TIntIterator(INSTANCE) { + @Override + public int next() { + throw new NoSuchElementException(); + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public void remove() { + throw new NoSuchElementException(); + } + }; + + private EmptyIntHashSet() { + super(0); + } + + @Override + public boolean add(int val) { + throw new IncorrectOperationException(); + } + + @Override + public int[] toArray() { + return ArrayUtil.EMPTY_INT_ARRAY; + } + + @Override + public TIntIterator iterator() { + return EMPTY_INT_ITERATOR; + } + + @Override + public void clear() { + throw new IncorrectOperationException(); + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean remove(int val) { + throw new IncorrectOperationException(); + } + + @Override + public boolean containsAll(int[] array) { + return false; + } + + @Override + public boolean addAll(int[] array) { + throw new IncorrectOperationException(); + } + + @Override + public boolean removeAll(int[] array) { + throw new IncorrectOperationException(); + } + + @Override + public boolean retainAll(int[] array) { + throw new IncorrectOperationException(); + } + + @Override + public boolean contains(int val) { + return false; + } + + @Override + public boolean forEach(TIntProcedure procedure) { + return true; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public int size() { + return 0; + } + + @Override + public void compact() { + throw new IncorrectOperationException(); + } + + @Override + public String toString() { + return "Empty Int Hash Set"; + } +}