diff: add annotate action to diff viewers
[idea/community.git] / platform / diff-impl / src / com / intellij / diff / DiffContentFactoryImpl.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.diff;
17
18 import com.intellij.diff.contents.*;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.editor.Document;
22 import com.intellij.openapi.editor.EditorFactory;
23 import com.intellij.openapi.fileEditor.FileDocumentManager;
24 import com.intellij.openapi.fileTypes.FileType;
25 import com.intellij.openapi.ide.CopyPasteManager;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.util.Computable;
28 import com.intellij.openapi.util.io.FileUtil;
29 import com.intellij.openapi.util.text.StringUtil;
30 import com.intellij.openapi.vcs.FilePath;
31 import com.intellij.openapi.vfs.VfsUtil;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.testFramework.BinaryLightVirtualFile;
34 import com.intellij.util.LineSeparator;
35 import com.intellij.util.PathUtil;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
38
39 import java.awt.datatransfer.DataFlavor;
40 import java.io.File;
41 import java.io.IOException;
42 import java.nio.charset.Charset;
43
44 public class DiffContentFactoryImpl extends DiffContentFactory {
45   public final Logger LOG = Logger.getInstance(DiffContentFactoryImpl.class);
46
47   @NotNull
48   public static DiffContentFactoryImpl getInstanceImpl() {
49     return (DiffContentFactoryImpl)DiffContentFactory.getInstance();
50   }
51
52   @Override
53   @NotNull
54   public EmptyContent createEmpty() {
55     return new EmptyContent();
56   }
57
58   @Override
59   @NotNull
60   public DocumentContent create(@NotNull String text) {
61     return create(text, (FileType)null);
62   }
63
64   @Override
65   @NotNull
66   public DocumentContent create(@NotNull String text, @Nullable FileType type) {
67     return create(text, type, true);
68   }
69
70   @Override
71   @NotNull
72   public DocumentContent create(@NotNull String text, @Nullable FileType type, boolean respectLineSeparators) {
73     return createImpl(text, type, null, null, respectLineSeparators, true);
74   }
75
76   @NotNull
77   public DocumentContent create(@NotNull String text, @Nullable VirtualFile highlightFile) {
78     return createImpl(text, highlightFile != null ? highlightFile.getFileType() : null, highlightFile, null, true, true);
79   }
80
81   @Override
82   @NotNull
83   public DocumentContent create(@Nullable Project project, @NotNull Document document) {
84     return create(project, document, (FileType)null);
85   }
86
87   @Override
88   @NotNull
89   public DocumentContent create(@Nullable Project project, @NotNull Document document, @Nullable FileType fileType) {
90     VirtualFile file = FileDocumentManager.getInstance().getFile(document);
91     if (file == null) return new DocumentContentImpl(document, fileType, null, null, null);
92     return create(project, document, file);
93   }
94
95   @Override
96   @NotNull
97   public DocumentContent create(@Nullable Project project, @NotNull Document document, @Nullable VirtualFile file) {
98     if (file != null) return new FileDocumentContentImpl(project, document, file);
99     return new DocumentContentImpl(document);
100   }
101
102   @Override
103   @NotNull
104   public DiffContent create(@Nullable Project project, @NotNull VirtualFile file) {
105     if (file.isDirectory()) return new DirectoryContentImpl(project, file);
106     DocumentContent content = createDocument(project, file);
107     if (content != null) return content;
108     return new FileContentImpl(project, file);
109   }
110
111   @Override
112   @Nullable
113   public DocumentContent createDocument(@Nullable Project project, @NotNull final VirtualFile file) {
114     // TODO: add notification, that file is decompiled ?
115     if (file.isDirectory()) return null;
116     Document document = ApplicationManager.getApplication().runReadAction(new Computable<Document>() {
117       @Override
118       public Document compute() {
119         return FileDocumentManager.getInstance().getDocument(file);
120       }
121     });
122     if (document == null) return null;
123     return new FileDocumentContentImpl(project, document, file);
124   }
125
126   @Override
127   @Nullable
128   public FileContent createFile(@Nullable Project project, @NotNull VirtualFile file) {
129     if (file.isDirectory()) return null;
130     return (FileContent)create(project, file);
131   }
132
133   @Override
134   @NotNull
135   public DiffContent createClipboardContent() {
136     return createClipboardContent(null);
137   }
138
139   @Override
140   @NotNull
141   public DocumentContent createClipboardContent(@Nullable DocumentContent mainContent) {
142     String text = CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor);
143
144     FileType type = mainContent != null ? mainContent.getContentType() : null;
145     VirtualFile highlightFile = mainContent != null ? mainContent.getHighlightFile() : null;
146
147     return createImpl(StringUtil.notNullize(text), type, highlightFile, null, true, false);
148   }
149
150   @NotNull
151   private static DocumentContent createImpl(@NotNull String text,
152                                             @Nullable FileType type,
153                                             @Nullable VirtualFile highlightFile,
154                                             @Nullable Charset charset,
155                                             boolean respectLineSeparators,
156                                             boolean readOnly) {
157     // TODO: detect invalid (different across the file) separators ?
158     LineSeparator separator = respectLineSeparators ? StringUtil.detectSeparators(text) : null;
159     Document document = EditorFactory.getInstance().createDocument(StringUtil.convertLineSeparators(text));
160     if (readOnly) document.setReadOnly(true);
161     return new DocumentContentImpl(document, type, highlightFile, separator, charset);
162   }
163
164   @NotNull
165   public DiffContent createFromBytes(@Nullable Project project,
166                                      @NotNull FilePath filePath,
167                                      @NotNull byte[] content) throws IOException {
168     if (filePath.getFileType().isBinary()) {
169       return DiffContentFactory.getInstance().createBinary(project, filePath.getName(), filePath.getFileType(), content);
170     }
171
172     return FileAwareDocumentContent.create(project, content, filePath);
173   }
174
175   @Override
176   @NotNull
177   public DiffContent createFromBytes(@Nullable Project project,
178                                      @NotNull VirtualFile highlightFile,
179                                      @NotNull byte[] content) throws IOException {
180     // TODO: check if FileType.UNKNOWN is actually a text ?
181     if (highlightFile.getFileType().isBinary()) {
182       return DiffContentFactory.getInstance().createBinary(project, highlightFile.getName(), highlightFile.getFileType(), content);
183     }
184
185     return FileAwareDocumentContent.create(project, content, highlightFile);
186   }
187
188   @Override
189   @NotNull
190   public DiffContent createBinary(@Nullable Project project,
191                                   @NotNull String name,
192                                   @NotNull FileType type,
193                                   @NotNull byte[] content) throws IOException {
194     boolean useTemporalFile = true; // TODO: workaround for Decompiler
195     //boolean useTemporalFile = type instanceof ArchiveFileType; // workaround - our JarFileSystem can't process non-local files
196
197     VirtualFile file;
198     if (useTemporalFile) {
199       if (type.getDefaultExtension().isEmpty()) {
200         file = createTemporalFile(project, "tmp_", "_" + name, content);
201       }
202       else {
203         file = createTemporalFile(project, name + "_", "." + type.getDefaultExtension(), content);
204       }
205     }
206     else {
207       file = new BinaryLightVirtualFile(name, type, content);
208     }
209
210     return create(project, file);
211   }
212
213   @NotNull
214   public static VirtualFile createTemporalFile(@Nullable Project project,
215                                                @NotNull String prefix,
216                                                @NotNull String suffix,
217                                                @NotNull byte[] content) throws IOException {
218     File tempFile = FileUtil.createTempFile(PathUtil.suggestFileName(prefix + "_", true, false),
219                                             PathUtil.suggestFileName("_" + suffix, true, false), true);
220     if (content.length != 0) {
221       FileUtil.writeToFile(tempFile, content);
222     }
223     VirtualFile file = VfsUtil.findFileByIoFile(tempFile, true);
224     if (file == null) {
225       throw new IOException("Can't create temp file for revision content");
226     }
227     VfsUtil.markDirtyAndRefresh(true, true, true, file);
228     return file;
229   }
230 }