| /* |
| * 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.psi.impl.file; |
| |
| import com.intellij.ide.util.PsiNavigationSupport; |
| import com.intellij.lang.ASTNode; |
| import com.intellij.lang.Language; |
| import com.intellij.navigation.ItemPresentation; |
| import com.intellij.navigation.ItemPresentationProviders; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.fileEditor.FileDocumentManager; |
| import com.intellij.openapi.fileEditor.impl.LoadTextUtil; |
| import com.intellij.openapi.progress.ProgressIndicatorProvider; |
| import com.intellij.openapi.ui.Queryable; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.NonPhysicalFileSystem; |
| import com.intellij.openapi.vfs.VfsBundle; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.CheckUtil; |
| import com.intellij.psi.impl.PsiElementBase; |
| import com.intellij.psi.impl.PsiManagerImpl; |
| import com.intellij.psi.impl.source.PsiFileImpl; |
| import com.intellij.psi.impl.source.SourceTreeToPsiMap; |
| import com.intellij.psi.impl.source.tree.ChangeUtil; |
| import com.intellij.psi.impl.source.tree.TreeElement; |
| import com.intellij.psi.search.PsiElementProcessor; |
| import com.intellij.psi.search.PsiFileSystemItemProcessor; |
| import com.intellij.psi.util.PsiUtilCore; |
| import com.intellij.testFramework.LightVirtualFile; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.PlatformIcons; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Map; |
| |
| public class PsiDirectoryImpl extends PsiElementBase implements PsiDirectory, Queryable { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.file.PsiDirectoryImpl"); |
| |
| private final PsiManagerImpl myManager; |
| private final VirtualFile myFile; |
| |
| public PsiDirectoryImpl(PsiManagerImpl manager, @NotNull VirtualFile file) { |
| myManager = manager; |
| myFile = file; |
| } |
| |
| @Override |
| @NotNull |
| public VirtualFile getVirtualFile() { |
| return myFile; |
| } |
| |
| @Override |
| public boolean isDirectory() { |
| return true; |
| } |
| |
| @Override |
| public boolean isValid() { |
| return myFile.isValid(); |
| } |
| |
| @Override |
| @NotNull |
| public Language getLanguage() { |
| return Language.ANY; |
| } |
| |
| @Override |
| public PsiManager getManager() { |
| return myManager; |
| } |
| |
| @Override |
| @NotNull |
| public String getName() { |
| return myFile.getName(); |
| } |
| |
| @Override |
| @NotNull |
| public PsiElement setName(@NotNull String name) throws IncorrectOperationException { |
| checkSetName(name); |
| |
| /* |
| final String oldName = myFile.getName(); |
| PsiTreeChangeEventImpl event = new PsiTreeChangeEventImpl(myManager); |
| event.setElement(this); |
| event.setPropertyName(PsiTreeChangeEvent.PROP_DIRECTORY_NAME); |
| event.setOldValue(oldName); |
| myManager.beforePropertyChange(event); |
| */ |
| |
| try { |
| myFile.rename(myManager, name); |
| } |
| catch (IOException e) { |
| throw new IncorrectOperationException(e.toString()); |
| } |
| |
| /* |
| PsiUndoableAction undoableAction = new PsiUndoableAction(){ |
| public void undo() throws IncorrectOperationException { |
| if (!PsiDirectoryImpl.this.isValid()){ |
| throw new IncorrectOperationException(); |
| } |
| setName(oldName); |
| } |
| }; |
| */ |
| |
| /* |
| event = new PsiTreeChangeEventImpl(myManager); |
| event.setElement(this); |
| event.setPropertyName(PsiTreeChangeEvent.PROP_DIRECTORY_NAME); |
| event.setOldValue(oldName); |
| event.setNewValue(name); |
| event.setUndoableAction(undoableAction); |
| myManager.propertyChanged(event); |
| */ |
| return this; |
| } |
| |
| @Override |
| public void checkSetName(String name) throws IncorrectOperationException { |
| //CheckUtil.checkIsIdentifier(name); |
| CheckUtil.checkWritable(this); |
| VirtualFile parentFile = myFile.getParent(); |
| if (parentFile == null) { |
| throw new IncorrectOperationException(VfsBundle.message("cannot.rename.root.directory")); |
| } |
| VirtualFile child = parentFile.findChild(name); |
| if (child != null && !child.equals(myFile)) { |
| throw new IncorrectOperationException(VfsBundle.message("file.already.exists.error", child.getPresentableUrl())); |
| } |
| } |
| |
| @Override |
| public PsiDirectory getParentDirectory() { |
| VirtualFile parentFile = myFile.getParent(); |
| if (parentFile == null) return null; |
| return myManager.findDirectory(parentFile); |
| } |
| |
| @Override |
| @NotNull |
| public PsiDirectory[] getSubdirectories() { |
| VirtualFile[] files = myFile.getChildren(); |
| ArrayList<PsiDirectory> dirs = new ArrayList<PsiDirectory>(); |
| for (VirtualFile file : files) { |
| PsiDirectory dir = myManager.findDirectory(file); |
| if (dir != null) { |
| dirs.add(dir); |
| } |
| } |
| return dirs.toArray(new PsiDirectory[dirs.size()]); |
| } |
| |
| @Override |
| @NotNull |
| public PsiFile[] getFiles() { |
| LOG.assertTrue(myFile.isValid()); |
| VirtualFile[] files = myFile.getChildren(); |
| ArrayList<PsiFile> psiFiles = new ArrayList<PsiFile>(); |
| for (VirtualFile file : files) { |
| PsiFile psiFile = myManager.findFile(file); |
| if (psiFile != null) { |
| psiFiles.add(psiFile); |
| } |
| } |
| return PsiUtilCore.toPsiFileArray(psiFiles); |
| } |
| |
| @Override |
| public PsiDirectory findSubdirectory(@NotNull String name) { |
| VirtualFile childVFile = myFile.findChild(name); |
| if (childVFile == null) return null; |
| return myManager.findDirectory(childVFile); |
| } |
| |
| @Override |
| public PsiFile findFile(@NotNull String name) { |
| VirtualFile childVFile = myFile.findChild(name); |
| if (childVFile == null) return null; |
| return myManager.findFile(childVFile); |
| } |
| |
| @Override |
| public boolean processChildren(PsiElementProcessor<PsiFileSystemItem> processor) { |
| checkValid(); |
| ProgressIndicatorProvider.checkCanceled(); |
| |
| for (VirtualFile vFile : myFile.getChildren()) { |
| boolean isDir = vFile.isDirectory(); |
| if (processor instanceof PsiFileSystemItemProcessor && |
| !((PsiFileSystemItemProcessor)processor).acceptItem(vFile.getName(), isDir)) { |
| continue; |
| } |
| if (isDir) { |
| PsiDirectory dir = myManager.findDirectory(vFile); |
| if (dir != null) { |
| if (!processor.execute(dir)) return false; |
| } |
| } |
| else { |
| PsiFile file = myManager.findFile(vFile); |
| if (file != null) { |
| if (!processor.execute(file)) return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| @NotNull |
| public PsiElement[] getChildren() { |
| checkValid(); |
| |
| VirtualFile[] files = myFile.getChildren(); |
| final ArrayList<PsiElement> children = new ArrayList<PsiElement>(files.length); |
| processChildren(new PsiElementProcessor<PsiFileSystemItem>() { |
| @Override |
| public boolean execute(@NotNull final PsiFileSystemItem element) { |
| children.add(element); |
| return true; |
| } |
| }); |
| |
| return PsiUtilCore.toPsiElementArray(children); |
| } |
| |
| private void checkValid() { |
| if (!isValid()) { |
| throw new PsiInvalidElementAccessException(this); |
| } |
| } |
| |
| @Override |
| public PsiDirectory getParent() { |
| return getParentDirectory(); |
| } |
| |
| @Override |
| public PsiFile getContainingFile() { |
| return null; |
| } |
| |
| @Override |
| public TextRange getTextRange() { |
| return null; |
| } |
| |
| @Override |
| public int getStartOffsetInParent() { |
| return -1; |
| } |
| |
| @Override |
| public int getTextLength() { |
| return -1; |
| } |
| |
| @Override |
| public PsiElement findElementAt(int offset) { |
| return null; |
| } |
| |
| @Override |
| public int getTextOffset() { |
| return -1; |
| } |
| |
| @Override |
| public String getText() { |
| return ""; // TODO throw new InsupportedOperationException() |
| } |
| |
| @Override |
| @NotNull |
| public char[] textToCharArray() { |
| return ArrayUtil.EMPTY_CHAR_ARRAY; // TODO throw new InsupportedOperationException() |
| } |
| |
| @Override |
| public boolean textMatches(@NotNull CharSequence text) { |
| return false; |
| } |
| |
| @Override |
| public boolean textMatches(@NotNull PsiElement element) { |
| return false; |
| } |
| |
| @Override |
| public final boolean isWritable() { |
| return myFile.isWritable(); |
| } |
| |
| @Override |
| public boolean isPhysical() { |
| return !(myFile.getFileSystem() instanceof NonPhysicalFileSystem) && !myFile.getFileSystem().getProtocol().equals("temp"); |
| } |
| |
| /** |
| * @not_implemented |
| */ |
| @Override |
| public PsiElement copy() { |
| LOG.error("not implemented"); |
| return null; |
| } |
| |
| |
| @Override |
| @NotNull |
| public PsiDirectory createSubdirectory(@NotNull String name) throws IncorrectOperationException { |
| checkCreateSubdirectory(name); |
| |
| try { |
| VirtualFile file = getVirtualFile().createChildDirectory(myManager, name); |
| PsiDirectory directory = myManager.findDirectory(file); |
| if (directory == null) throw new IncorrectOperationException("Cannot find directory in '" + file.getPresentableUrl() + "'"); |
| return directory; |
| } |
| catch (IOException e) { |
| throw new IncorrectOperationException(e.toString()); |
| } |
| } |
| |
| @Override |
| public void checkCreateSubdirectory(@NotNull String name) throws IncorrectOperationException { |
| // TODO : another check? |
| //CheckUtil.checkIsIdentifier(name); |
| VirtualFile existingFile = getVirtualFile().findChild(name); |
| if (existingFile != null) { |
| throw new IncorrectOperationException(VfsBundle.message("file.already.exists.error", existingFile.getPresentableUrl())); |
| } |
| CheckUtil.checkWritable(this); |
| } |
| |
| @Override |
| @NotNull |
| public PsiFile createFile(@NotNull String name) throws IncorrectOperationException { |
| checkCreateFile(name); |
| |
| try { |
| VirtualFile vFile = getVirtualFile().createChildData(myManager, name); |
| return myManager.findFile(vFile); |
| } |
| catch (IOException e) { |
| throw new IncorrectOperationException(e.toString()); |
| } |
| } |
| |
| @Override |
| @NotNull |
| public PsiFile copyFileFrom(@NotNull String newName, @NotNull PsiFile originalFile) throws IncorrectOperationException { |
| checkCreateFile(newName); |
| |
| final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(originalFile); |
| if (document != null) { |
| FileDocumentManager.getInstance().saveDocument(document); |
| } |
| |
| final VirtualFile parent = getVirtualFile(); |
| try { |
| final VirtualFile vFile = originalFile.getVirtualFile(); |
| if (vFile == null) throw new IncorrectOperationException("Cannot copy nonphysical file"); |
| VirtualFile copyVFile; |
| if (parent.getFileSystem() == vFile.getFileSystem()) { |
| copyVFile = vFile.copy(this, parent, newName); |
| } |
| else if (vFile instanceof LightVirtualFile) { |
| copyVFile = parent.createChildData(this, newName); |
| copyVFile.setBinaryContent(originalFile.getText().getBytes(copyVFile.getCharset())); |
| } |
| else { |
| copyVFile = VfsUtilCore.copyFile(this, vFile, parent, newName); |
| } |
| LOG.assertTrue(copyVFile != null, "File was not copied: " + vFile); |
| final PsiFile copyPsi = myManager.findFile(copyVFile); |
| if (copyPsi == null) { |
| LOG.error("Could not find file '" + copyVFile + "' after copying '" + vFile + "'"); |
| } |
| updateAddedFile(copyPsi); |
| return copyPsi; |
| } |
| catch (IOException e) { |
| throw new IncorrectOperationException(e); |
| } |
| } |
| |
| private static void updateAddedFile(@NotNull PsiFile copyPsi) throws IncorrectOperationException { |
| final UpdateAddedFileProcessor processor = UpdateAddedFileProcessor.forElement(copyPsi); |
| if (processor != null) { |
| final TreeElement tree = (TreeElement)SourceTreeToPsiMap.psiElementToTree(copyPsi); |
| if (tree != null) { |
| ChangeUtil.encodeInformation(tree); |
| } |
| processor.update(copyPsi, null); |
| if (tree != null) { |
| ChangeUtil.decodeInformation(tree); |
| } |
| } |
| } |
| |
| @Override |
| public void checkCreateFile(@NotNull String name) throws IncorrectOperationException { |
| VirtualFile existingFile = getVirtualFile().findChild(name); |
| if (existingFile != null) { |
| throw new IncorrectOperationException(VfsBundle.message("file.already.exists.error", existingFile.getPresentableUrl())); |
| } |
| CheckUtil.checkWritable(this); |
| } |
| |
| |
| @Override |
| public PsiElement add(@NotNull PsiElement element) throws IncorrectOperationException { |
| checkAdd(element); |
| if (element instanceof PsiDirectory) { |
| LOG.error("not implemented"); |
| return null; |
| } |
| else if (element instanceof PsiFile) { |
| PsiFile originalFile = (PsiFile)element; |
| |
| try { |
| VirtualFile newVFile; |
| final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(myManager.getProject()); |
| if (originalFile instanceof PsiFileImpl) { |
| newVFile = myFile.createChildData(myManager, originalFile.getName()); |
| String text = originalFile.getText(); |
| final PsiFile psiFile = getManager().findFile(newVFile); |
| final Document document = psiFile == null ? null : psiDocumentManager.getDocument(psiFile); |
| final FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); |
| if (document != null) { |
| document.setText(text); |
| fileDocumentManager.saveDocument(document); |
| } |
| else { |
| String lineSeparator = fileDocumentManager.getLineSeparator(newVFile, getProject()); |
| if (!lineSeparator.equals("\n")) { |
| text = StringUtil.convertLineSeparators(text, lineSeparator); |
| } |
| |
| LoadTextUtil.write(getProject(), newVFile, myManager, text, -1); |
| } |
| } |
| else { |
| byte[] storedContents = ((PsiBinaryFileImpl)originalFile).getStoredContents(); |
| if (storedContents != null) { |
| newVFile = myFile.createChildData(myManager, originalFile.getName()); |
| newVFile.setBinaryContent(storedContents); |
| } |
| else { |
| newVFile = VfsUtilCore.copyFile(null, originalFile.getVirtualFile(), myFile); |
| } |
| } |
| psiDocumentManager.commitAllDocuments(); |
| |
| PsiFile newFile = myManager.findFile(newVFile); |
| updateAddedFile(newFile); |
| |
| return newFile; |
| } |
| catch (IOException e) { |
| throw new IncorrectOperationException(e); |
| } |
| } |
| else { |
| LOG.assertTrue(false); |
| return null; |
| } |
| } |
| |
| @Override |
| public void checkAdd(@NotNull PsiElement element) throws IncorrectOperationException { |
| CheckUtil.checkWritable(this); |
| if (element instanceof PsiDirectory) { |
| String name = ((PsiDirectory)element).getName(); |
| PsiDirectory[] subpackages = getSubdirectories(); |
| for (PsiDirectory dir : subpackages) { |
| if (Comparing.strEqual(dir.getName(), name)) { |
| throw new IncorrectOperationException(VfsBundle.message("dir.already.exists.error", dir.getVirtualFile().getPresentableUrl())); |
| } |
| } |
| } |
| else if (element instanceof PsiFile) { |
| String name = ((PsiFile)element).getName(); |
| PsiFile[] files = getFiles(); |
| for (PsiFile file : files) { |
| if (Comparing.strEqual(file.getName(), name, SystemInfo.isFileSystemCaseSensitive)) { |
| throw new IncorrectOperationException(VfsBundle.message("file.already.exists.error", file.getVirtualFile().getPresentableUrl())); |
| } |
| } |
| } |
| else { |
| throw new IncorrectOperationException(); |
| } |
| } |
| |
| @Override |
| public PsiElement addBefore(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException { |
| throw new IncorrectOperationException(); |
| } |
| |
| @Override |
| public PsiElement addAfter(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException { |
| throw new IncorrectOperationException(); |
| } |
| |
| @Override |
| public void delete() throws IncorrectOperationException { |
| checkDelete(); |
| //PsiDirectory parent = getParentDirectory(); |
| |
| /* |
| PsiTreeChangeEventImpl event = new PsiTreeChangeEventImpl(myManager); |
| event.setParent(parent); |
| event.setChild(this); |
| myManager.beforeChildRemoval(event); |
| */ |
| |
| try { |
| myFile.delete(myManager); |
| } |
| catch (IOException e) { |
| throw new IncorrectOperationException(e); |
| } |
| |
| /* |
| //TODO : allow undo |
| PsiTreeChangeEventImpl treeEvent = new PsiTreeChangeEventImpl(myManager); |
| treeEvent.setParent(parent); |
| treeEvent.setChild(this); |
| treeEvent.setUndoableAction(null); |
| myManager.childRemoved(treeEvent); |
| */ |
| } |
| |
| @Override |
| public void checkDelete() throws IncorrectOperationException { |
| CheckUtil.checkDelete(myFile); |
| } |
| |
| /** |
| * @not_implemented |
| */ |
| @Override |
| public PsiElement replace(@NotNull PsiElement newElement) throws IncorrectOperationException { |
| LOG.error("not implemented"); |
| return null; |
| } |
| |
| @Override |
| public void accept(@NotNull PsiElementVisitor visitor) { |
| visitor.visitDirectory(this); |
| } |
| |
| public String toString() { |
| return "PsiDirectory:" + myFile.getPresentableUrl(); |
| } |
| |
| @Override |
| public ASTNode getNode() { |
| return null; |
| } |
| |
| @Override |
| public boolean canNavigateToSource() { |
| return false; |
| } |
| |
| @Override |
| public ItemPresentation getPresentation() { |
| return ItemPresentationProviders.getItemPresentation(this); |
| } |
| |
| @Override |
| public void navigate(boolean requestFocus) { |
| PsiNavigationSupport.getInstance().navigateToDirectory(this, requestFocus); |
| } |
| |
| @Override |
| protected Icon getElementIcon(final int flags) { |
| return PlatformIcons.DIRECTORY_CLOSED_ICON; |
| } |
| |
| @Override |
| public void putInfo(@NotNull Map<String, String> info) { |
| info.put("fileName", getName()); |
| } |
| } |