fixes in module structure.

add workaround code for applet.
add method needed for applet.
This commit is contained in:
Pavel Talanov
2011-12-15 20:06:57 +04:00
parent 89e021b8e6
commit c5b8b86fd8
26 changed files with 5006 additions and 42 deletions
+2
View File
@@ -13,6 +13,8 @@
<element id="extracted-dir" path="$PROJECT_DIR$/translator/lib/junit-4.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/translator/lib/guava-10.0.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/../../Kotlin/jet/lib/asm-util-3.3.1.jar" path-in-jar="/" />
<element id="module-output" name="idea_fix" />
<element id="file-copy" path="E:/rt.jar" />
</root>
</artifact>
</component>
+1
View File
@@ -7,6 +7,7 @@
<module fileurl="file://$PROJECT_DIR$/../Kotlin/jet/compiler/frontend/frontend.iml" filepath="$PROJECT_DIR$/../Kotlin/jet/compiler/frontend/frontend.iml" />
<module fileurl="file://$PROJECT_DIR$/../../Kotlin/jet/compiler/frontend.java/frontend.java.iml" filepath="$PROJECT_DIR$/../../Kotlin/jet/compiler/frontend.java/frontend.java.iml" />
<module fileurl="file://$PROJECT_DIR$/../Kotlin/jet/compiler/frontend.java/frontend.java.iml" filepath="$PROJECT_DIR$/../Kotlin/jet/compiler/frontend.java/frontend.java.iml" />
<module fileurl="file://$PROJECT_DIR$/idea_fix/idea_fix.iml" filepath="$PROJECT_DIR$/idea_fix/idea_fix.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/jetTests.iml" filepath="$PROJECT_DIR$/examples/jetTests.iml" />
<module fileurl="file://$PROJECT_DIR$/js/js.iml" filepath="$PROJECT_DIR$/js/js.iml" />
<module fileurl="file://$PROJECT_DIR$/../../Kotlin/jet/stdlib/stdlib.iml" filepath="$PROJECT_DIR$/../../Kotlin/jet/stdlib/stdlib.iml" />
+1 -1
View File
@@ -5,7 +5,7 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
</content>
<orderEntry type="jdk" jdkName="IDEA IC-110.408" jdkType="IDEA JDK" />
<orderEntry type="jdk" jdkName="IDEA IU-112.121" jdkType="IDEA JDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="translator" />
</component>
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://E:/kotlin/kotlin-0.1.81/lib/intellij-core.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
@@ -0,0 +1,170 @@
/*
* Copyright 2000-2011 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.core;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem;
import com.intellij.openapi.vfs.local.CoreLocalFileSystem;
import com.intellij.psi.*;
import com.intellij.psi.impl.file.PsiPackageImpl;
import com.intellij.psi.impl.file.impl.JavaFileManager;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author yole
*/
public class CoreJavaFileManager implements JavaFileManager {
private final CoreLocalFileSystem myLocalFileSystem;
private final CoreJarFileSystem myJarFileSystem;
private final List<File> myClasspath = new ArrayList<File>();
private final List<String> myVirtualClasspath = new ArrayList<String>();
private final PsiManager myPsiManager;
public CoreJavaFileManager(PsiManager psiManager, CoreLocalFileSystem localFileSystem, CoreJarFileSystem jarFileSystem) {
myPsiManager = psiManager;
myLocalFileSystem = localFileSystem;
myJarFileSystem = jarFileSystem;
addToClasspath("rt.jar");
}
@Override
public PsiPackage findPackage(@NotNull String packageName) {
String dirName = packageName.replace(".", "/");
for (File file : myClasspath) {
VirtualFile classDir = findUnderClasspathEntry(file, dirName);
if (classDir != null) {
return new PsiPackageImpl(myPsiManager, packageName);
}
}
for (String str : myVirtualClasspath) {
VirtualFile file = myJarFileSystem.findFileByPath(str + "!/" + dirName);
if (file != null) {
return new PsiPackageImpl(myPsiManager, packageName);
}
}
return null;
}
@Nullable
private VirtualFile findUnderClasspathEntry(File classpathEntry, String relativeName) {
if (classpathEntry.isFile()) {
return myJarFileSystem.findFileByPath(classpathEntry.getPath() + "!/" + relativeName);
} else {
return myLocalFileSystem.findFileByPath(new File(classpathEntry, relativeName).getPath());
}
}
@Nullable
private PsiClass findInVirtualJar(String classpathEntry, String relativeName) {
String fileName = relativeName.replace(".", "/") + ".class";
VirtualFile file = myJarFileSystem.findFileByPath(classpathEntry + "!/" + fileName);
if (file != null) {
PsiFile psiFile = myPsiManager.findFile(file);
if (!(psiFile instanceof PsiJavaFile)) {
/*ErrorWriter.ERROR_WRITER.writeException(ErrorWriter.getExceptionForLog(
"UNKNOWN", new UnsupportedOperationException("no java file for .class"), classpathEntry + "!/" + relativeName
));
return null;*/
throw new UnsupportedOperationException("no java file for .class " + classpathEntry + "!/" + relativeName);
}
final PsiClass[] classes = ((PsiJavaFile) psiFile).getClasses();
if (classes.length == 1) {
return classes[0];
}
}
return null;
}
@Override
public PsiClass findClass(@NotNull String qName, @NotNull GlobalSearchScope scope) {
for (File file : myClasspath) {
final PsiClass psiClass = findClassInClasspathEntry(qName, file);
if (psiClass != null) {
return psiClass;
}
}
for (String str : myVirtualClasspath) {
final PsiClass psiClass = findInVirtualJar(str, qName);
if (psiClass != null) {
return psiClass;
}
}
return null;
}
@Nullable
private PsiClass findClassInClasspathEntry(String qName, File file) {
String fileName = qName.replace(".", "/") + ".class";
VirtualFile classFile = findUnderClasspathEntry(file, fileName);
if (classFile != null) {
PsiFile psiFile = myPsiManager.findFile(classFile);
if (!(psiFile instanceof PsiJavaFile)) {
throw new UnsupportedOperationException("no java file for .class");
}
final PsiClass[] classes = ((PsiJavaFile) psiFile).getClasses();
if (classes.length == 1) {
return classes[0];
}
}
return null;
}
@Override
public PsiClass[] findClasses(@NotNull String qName, @NotNull GlobalSearchScope scope) {
List<PsiClass> result = new ArrayList<PsiClass>();
for (File file : myClasspath) {
final PsiClass psiClass = findClassInClasspathEntry(qName, file);
if (psiClass != null) {
result.add(psiClass);
}
}
for (String str : myVirtualClasspath) {
final PsiClass psiClass = findInVirtualJar(str, qName);
if (psiClass != null) {
result.add(psiClass);
}
}
return result.toArray(new PsiClass[result.size()]);
}
@Override
public Collection<String> getNonTrivialPackagePrefixes() {
return Collections.emptyList();
}
@Override
public void initialize() {
}
public void addToClasspath(File path) {
myClasspath.add(path);
}
public void addToClasspath(String path) {
myVirtualClasspath.add(path);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,668 @@
/*
* Copyright 2000-2011 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.editor.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.impl.event.DocumentEventImpl;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.text.CharArrayCharSequence;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.CharSequenceBackedByArray;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author cdr
*/
abstract class CharArray implements CharSequenceBackedByArray {
@SuppressWarnings("UseOfArchaicSystemPropertyAccessors")
private static final boolean DISABLE_DEFERRED_PROCESSING = false;
@SuppressWarnings("UseOfArchaicSystemPropertyAccessors")
private static final boolean DEBUG_DEFERRED_PROCESSING = false;
private static final Logger LOG = Logger.getInstance("#" + CharArray.class.getName());
/**
* We can't exclude possibility of situation when <code>'defer changes'</code> state is {@link #setDeferredChangeMode(boolean) entered}
* but not exited, hence, we want to perform automatic flushing if necessary in order to avoid memory leaks. This constant holds
* a value that defines that 'automatic flushing' criteria, i.e. every time number of stored deferred changes exceeds this value,
* they are automatically flushed.
*/
private static final int MAX_DEFERRED_CHANGES_NUMBER = 10000;
private final AtomicReference<TextChangesStorage> myDeferredChangesStorage = new AtomicReference<TextChangesStorage>();
private int myStart;
/**
* This class implements {@link #subSequence(int, int)} by creating object of the same class that partially shares the same
* data as the object on which the method is called. So, this field may define interested end offset (if it's non-negative).
*/
private int myEnd = -1;
private int myCount = 0;
private CharSequence myOriginalSequence;
private char[] myArray;
private SoftReference<String> myStringRef; // buffers String value - for not to generate it every time
private int myBufferSize;
private int myDeferredShift;
private boolean myDeferredChangeMode;
// We had a problems with bulk document text processing, hence, debug facilities were introduced. The fields group below work with them.
// The main idea is to hold all history of bulk processing iteration in order to be able to retrieve it from client and reproduce the
// problem.
/**
* Flag the identifies if current char array should debug bulk processing.
*/
private final boolean myDebugDeferredProcessing;
/**
* Duplicate instance of the current char array that is used during debug processing as follows - apply every text change
* from the bulk changes group to this instance immediately in order to be able to check if the current 'deferred change-aware'
* instance functionally behaves at the same way as 'straightforward' one.
*/
private CharArray myDebugArray;
/**
* Holds deferred changes create during the current bulk processing iteration.
*/
private List<TextChangeImpl> myDebugDeferredChanges;
/**
* Document text on bulk processing start.
*/
private String myDebugTextOnBatchUpdateStart;
// max chars to hold, bufferSize == 0 means unbounded
CharArray(int bufferSize) {
this(bufferSize, new TextChangesStorage(), null, -1, -1);
}
private CharArray(final int bufferSize, @NotNull TextChangesStorage deferredChangesStorage, @Nullable char[] data, int start, int end) {
this(bufferSize, deferredChangesStorage, data, start, end, DEBUG_DEFERRED_PROCESSING);
}
private CharArray(final int bufferSize, @NotNull TextChangesStorage deferredChangesStorage, @Nullable char[] data, int start, int end,
boolean debugDeferredProcessing) {
myBufferSize = bufferSize;
myDeferredChangesStorage.set(deferredChangesStorage);
if (data == null) {
myOriginalSequence = "";
} else {
myArray = data;
myCount = end - start;
}
if (start >= 0 && end >= 0) {
myStart = start;
myEnd = end;
}
myDebugDeferredProcessing = debugDeferredProcessing;
if (myDebugDeferredProcessing) {
myDebugArray = new CharArray(bufferSize, new TextChangesStorage(), data == null ? null : Arrays.copyOf(data, data.length),
start, end, false) {
@NotNull
@Override
protected DocumentEvent beforeChangedUpdate(DocumentImpl subj,
int offset,
CharSequence oldString,
CharSequence newString,
boolean wholeTextReplaced) {
return new DocumentEventImpl(subj, offset, oldString, newString, -1, wholeTextReplaced);
}
@Override
protected void afterChangedUpdate(@NotNull DocumentEvent event, long newModificationStamp) {
}
};
myDebugDeferredChanges = new ArrayList<TextChangeImpl>();
}
}
public void setBufferSize(int bufferSize) {
myBufferSize = bufferSize;
}
@NotNull
protected abstract DocumentEvent beforeChangedUpdate(DocumentImpl subj,
int offset,
@Nullable CharSequence oldString,
@Nullable CharSequence newString,
boolean wholeTextReplaced);
protected abstract void afterChangedUpdate(@NotNull DocumentEvent event, long newModificationStamp);
public void setText(@Nullable final DocumentImpl subj, final CharSequence chars) {
myOriginalSequence = chars;
myArray = null;
myCount = chars.length();
myStringRef = null;
TextChangesStorage storage = myDeferredChangesStorage.get();
storage.getLock().lock();
try {
if (isSubSequence()) {
myDeferredChangesStorage.set(new TextChangesStorage());
myStart = 0;
myEnd = -1;
} else {
storage.clear();
}
} finally {
storage.getLock().unlock();
}
if (subj != null) {
trimToSize(subj);
}
if (myDebugDeferredProcessing) {
myDebugArray.setText(subj, chars);
myDebugDeferredChanges.clear();
}
}
public void replace(DocumentImpl subj,
int startOffset, int endOffset, CharSequence toDelete, CharSequence newString, long newModificationStamp,
boolean wholeTextReplaced) {
final DocumentEvent event = beforeChangedUpdate(subj, startOffset, toDelete, newString, wholeTextReplaced);
startOffset += myStart;
endOffset += myStart;
doReplace(startOffset, endOffset, newString);
afterChangedUpdate(event, newModificationStamp);
}
private void doReplace(int startOffset, int endOffset, CharSequence newString) {
prepareForModification();
if (isDeferredChangeMode()) {
storeChange(new TextChangeImpl(newString, startOffset, endOffset));
if (myDebugDeferredProcessing) {
myDebugArray.doReplace(startOffset, endOffset, newString);
}
return;
}
int newLength = newString.length();
int oldLength = endOffset - startOffset;
CharArrayUtil.getChars(newString, myArray, startOffset, Math.min(newLength, oldLength));
if (newLength > oldLength) {
doInsert(newString.subSequence(oldLength, newLength), endOffset);
} else if (newLength < oldLength) {
doRemove(startOffset + newLength, startOffset + oldLength);
}
}
public void remove(DocumentImpl subj, int startIndex, int endIndex, CharSequence toDelete) {
DocumentEvent event = beforeChangedUpdate(subj, startIndex, toDelete, null, false);
startIndex += myStart;
endIndex += myStart;
doRemove(startIndex, endIndex);
afterChangedUpdate(event, LocalTimeCounter.currentTime());
}
private void doRemove(final int startIndex, final int endIndex) {
if (startIndex == endIndex) {
return;
}
prepareForModification();
if (isDeferredChangeMode()) {
storeChange(new TextChangeImpl("", startIndex, endIndex));
if (myDebugDeferredProcessing) {
myDebugArray.doRemove(startIndex, endIndex);
}
return;
}
if (endIndex < myCount) {
System.arraycopy(myArray, endIndex, myArray, startIndex, myCount - endIndex);
}
myCount -= endIndex - startIndex;
}
public void insert(DocumentImpl subj, CharSequence s, int startIndex) {
DocumentEvent event = beforeChangedUpdate(subj, startIndex, null, s, false);
startIndex += myStart;
doInsert(s, startIndex);
afterChangedUpdate(event, LocalTimeCounter.currentTime());
trimToSize(subj);
}
private void doInsert(final CharSequence s, final int startIndex) {
prepareForModification();
if (isDeferredChangeMode()) {
storeChange(new TextChangeImpl(s, startIndex));
if (myDebugDeferredProcessing) {
myDebugArray.doInsert(s, startIndex);
}
return;
}
int insertLength = s.length();
myArray = relocateArray(myArray, myCount + insertLength);
if (startIndex < myCount) {
System.arraycopy(myArray, startIndex, myArray, startIndex + insertLength, myCount - startIndex);
}
CharArrayUtil.getChars(s, myArray, startIndex);
myCount += insertLength;
}
/**
* Stores given change at collection of deferred changes (merging it with others if necessary) and updates current object
* state ({@link #length() length} etc).
*
* @param change new change to store
*/
private void storeChange(@NotNull TextChangeImpl change) {
if (!change.isWithinBounds(length())) {
LOG.error(String.format(
"Invalid change attempt detected - given change bounds are not within the current char array. Change: %d:%d-%d",
change.getText().length(), change.getStart(), change.getEnd()
), dumpState());
return;
}
TextChangesStorage storage = myDeferredChangesStorage.get();
storage.getLock().lock();
try {
doStoreChange(change);
} finally {
storage.getLock().unlock();
}
}
private void doStoreChange(@NotNull TextChangeImpl change) {
TextChangesStorage storage = myDeferredChangesStorage.get();
if (storage.size() >= MAX_DEFERRED_CHANGES_NUMBER) {
flushDeferredChanged(storage);
}
storage.store(change);
myDeferredShift += change.getDiff();
if (myDebugDeferredProcessing) {
myDebugDeferredChanges.add(change);
}
}
private void prepareForModification() {
if (myOriginalSequence != null) {
myArray = new char[myOriginalSequence.length()];
CharArrayUtil.getChars(myOriginalSequence, myArray, 0);
myOriginalSequence = null;
}
myStringRef = null;
}
public CharSequence getCharArray() {
if (myOriginalSequence != null) return myOriginalSequence;
return this;
}
public String toString() {
String str = myStringRef != null ? myStringRef.get() : null;
if (str == null) {
if (myOriginalSequence != null) {
str = myOriginalSequence.toString();
} else if (!hasDeferredChanges()) {
str = new String(myArray, myStart, myCount);
} else {
str = substring(0, length()).toString();
}
myStringRef = new SoftReference<String>(str);
}
if (myDebugDeferredProcessing && isDeferredChangeMode()) {
String expected = myDebugArray.toString();
checkStrings("toString()", expected, str);
}
return str;
}
@Override
public final int length() {
final int result = myCount + myDeferredShift;
if (myDebugDeferredProcessing && isDeferredChangeMode()) {
int expected = myDebugArray.length();
if (expected != result) {
dumpDebugInfo(String.format("Incorrect length() processing. Expected: '%s', actual: '%s'", expected, result));
}
}
return result;
}
@Override
public final char charAt(int i) {
if (i < 0 || i >= length()) {
throw new IndexOutOfBoundsException("Wrong offset: " + i + "; count:" + length());
}
i += myStart;
if (myOriginalSequence != null) return myOriginalSequence.charAt(i);
final char result;
if (hasDeferredChanges()) {
TextChangesStorage storage = myDeferredChangesStorage.get();
storage.getLock().lock();
try {
result = storage.charAt(myArray, i);
} finally {
storage.getLock().unlock();
}
} else {
result = myArray[i];
}
if (myDebugDeferredProcessing && isDeferredChangeMode()) {
char expected = myDebugArray.charAt(i);
if (expected != result) {
dumpDebugInfo(
String.format("Incorrect charAt() processing for index %d. Expected: '%c', actual: '%c'", i, expected, result)
);
}
}
return result;
}
@Override
public CharSequence subSequence(final int start, final int end) {
if (start == 0 && end == length()) return this;
if (myOriginalSequence != null) {
return myOriginalSequence.subSequence(start, end);
}
if (hasDeferredChanges()) {
return new CharArray(myBufferSize, myDeferredChangesStorage.get(), myArray, myStart + start, myStart + end) {
@NotNull
@Override
protected DocumentEvent beforeChangedUpdate(DocumentImpl subj,
int offset,
CharSequence oldString,
CharSequence newString,
boolean wholeTextReplaced) {
return new DocumentEventImpl(subj, offset, oldString, newString, LocalTimeCounter.currentTime(), wholeTextReplaced);
}
@Override
protected void afterChangedUpdate(@NotNull DocumentEvent event, long newModificationStamp) {
}
@Override
public char[] getChars() {
char[] chars = CharArray.this.getChars();
char[] result = new char[end - start];
System.arraycopy(chars, start, result, 0, result.length);
return result;
}
};
} else {
// We don't use the same approach as with 'defer changes' mode because the former is the new experimental one and this one
// is rather mature, hence, we just minimizes the risks that something is wrong within the new approach.
return new CharArrayCharSequence(myArray, start, end);
}
}
private boolean isSubSequence() {
return myEnd >= 0;
}
@Override
public char[] getChars() {
if (myOriginalSequence != null) {
if (myArray == null) {
myArray = CharArrayUtil.fromSequence(myOriginalSequence);
}
}
flushDeferredChanged(myDeferredChangesStorage.get());
if (myDebugDeferredProcessing && isDeferredChangeMode()) {
char[] expected = myDebugArray.getChars();
for (int i = 0, max = length(); i < max; i++) {
if (myArray[i] != expected[i]) {
dumpDebugInfo(String.format("getChars(). Index: %d, expected: %c, actual: %c", i, expected[i], myArray[i]));
break;
}
}
}
return myArray;
}
@Override
public void getChars(final char[] dst, final int dstOffset) {
flushDeferredChanged(myDeferredChangesStorage.get());
if (myOriginalSequence != null) {
CharArrayUtil.getChars(myOriginalSequence, dst, dstOffset);
} else {
System.arraycopy(myArray, myStart, dst, dstOffset, length());
}
if (myDebugDeferredProcessing && isDeferredChangeMode()) {
char[] expected = new char[dst.length];
myDebugArray.getChars(expected, dstOffset);
for (int i = dstOffset, j = myStart; i < dst.length && j < myArray.length; i++, j++) {
if (expected[i] != myArray[j]) {
dumpDebugInfo(String.format("getChars(char[], int). Given array of length %d, offset %d. Found char '%c' at index %d, " +
"expected to find '%c'", dst.length, dstOffset, myArray[j], i, expected[i]));
break;
}
}
}
}
public CharSequence substring(final int start, final int end) {
if (start == end) return "";
final CharSequence result;
if (myOriginalSequence == null) {
TextChangesStorage storage = myDeferredChangesStorage.get();
storage.getLock().lock();
try {
result = storage.substring(myArray, start + myStart, end + myStart);
} finally {
storage.getLock().unlock();
}
} else {
result = myOriginalSequence.subSequence(start, end);
}
if (myDebugDeferredProcessing && isDeferredChangeMode()) {
String expected = myDebugArray.substring(start, end).toString();
checkStrings(String.format("substring(%d, %d)", start, end), expected, result.toString());
}
return result;
}
private static char[] relocateArray(char[] array, int index) {
if (index < array.length) {
return array;
}
int newArraySize = array.length;
if (newArraySize == 0) {
newArraySize = 16;
}
while (newArraySize <= index) {
newArraySize = newArraySize * 12 / 10 + 1;
}
char[] newArray = new char[newArraySize];
System.arraycopy(array, 0, newArray, 0, array.length);
return newArray;
}
private void trimToSize(DocumentImpl subj) {
if (myBufferSize != 0 && length() > myBufferSize) {
flushDeferredChanged(myDeferredChangesStorage.get());
// make a copy
remove(subj, 0, myCount - myBufferSize, getCharArray().subSequence(0, myCount - myBufferSize).toString());
}
}
/**
* @return <code>true</code> if this object is at {@link #setDeferredChangeMode(boolean) defer changes} mode;
* <code>false</code> otherwise
*/
public boolean isDeferredChangeMode() {
return !DISABLE_DEFERRED_PROCESSING && myDeferredChangeMode;
}
public boolean hasDeferredChanges() {
return !myDeferredChangesStorage.get().isEmpty();
}
/**
* There is a possible case that client of this class wants to perform great number of modifications in a short amount of time
* (e.g. end-user performs formatting of the document backed by the object of the current class). It may result in significant
* performance degradation is the changes are performed one by one (every time the change is applied tail content is shifted to
* the left or right). So, we may want to optimize that by avoiding actual array modification until information about
* all target changes is provided and perform array data moves only after that.
* <p/>
* This method allows to define that <code>'defer changes'</code> mode usages, i.e. expected usage pattern is as follows:
* <pre>
* <ol>
* <li>
* Client of this class enters <code>'defer changes'</code> mode (calls this method with <code>'true'</code> argument).
* That means that all subsequent changes will not actually modify backed array data and will be stored separately;
* </li>
* <li>
* Number of target changes are applied to the current object via standard API
* ({@link #insert(com.intellij.openapi.editor.impl.DocumentImpl, CharSequence, int) insert},
* {@link #remove(com.intellij.openapi.editor.impl.DocumentImpl, int, int, CharSequence) remove} and
* {@link #replace(com.intellij.openapi.editor.impl.DocumentImpl, int, int, CharSequence, CharSequence, long, boolean) replace});
* </li>
* <li>
* Client of this class indicates that <code>'massive change time'</code> is over by calling this method with <code>'false'</code>
* argument. That flushes all deferred changes (if any) to the backed data array and makes every subsequent change to
* be immediate flushed to the backed array;
* </li>
* </ol>
* </pre>
* <p/>
* <b>Note:</b> we can't exclude possibility that <code>'defer changes'</code> mode is started but inadvertently not ended
* (due to programming error, unexpected exception etc). Hence, this class is free to automatically end
* <code>'defer changes'</code> mode when necessary in order to avoid memory leak with infinite deferred changes storing.
*
* @param deferredChangeMode flag that defines if <code>'defer changes'</code> mode should be used by the current object
*/
public void setDeferredChangeMode(boolean deferredChangeMode) {
if (deferredChangeMode && myDebugDeferredProcessing) {
myDebugArray.setText(null, myDebugTextOnBatchUpdateStart = toString());
myDebugDeferredChanges.clear();
}
myDeferredChangeMode = deferredChangeMode;
if (!deferredChangeMode) {
flushDeferredChanged(myDeferredChangesStorage.get());
}
}
private void flushDeferredChanged(@NotNull TextChangesStorage storage) {
storage.getLock().lock();
try {
doFlushDeferredChanged();
} finally {
storage.getLock().unlock();
}
}
private void doFlushDeferredChanged() {
TextChangesStorage storage = myDeferredChangesStorage.get();
List<TextChangeImpl> changes = storage.getChanges();
if (changes.isEmpty()) {
return;
}
char[] beforeMerge = null;
final boolean inPlace;
if (myDebugDeferredProcessing) {
beforeMerge = new char[myArray.length];
System.arraycopy(myArray, 0, beforeMerge, 0, myArray.length);
}
BulkChangesMerger changesMerger = BulkChangesMerger.INSTANCE;
if (myArray.length < length()) {
myArray = changesMerger.mergeToCharArray(myArray, myCount, changes);
inPlace = false;
} else {
changesMerger.mergeInPlace(myArray, myCount, changes);
inPlace = true;
}
if (myDebugDeferredProcessing) {
for (int i = 0, max = length(); i < max; i++) {
if (myArray[i] != myDebugArray.myArray[i]) {
dumpDebugInfo(String.format(
"flushDeferredChanged(). Index %d, expected: '%c', actual '%c'. Text before merge: '%s', merge inplace: %b",
i, myDebugArray.myArray[i], myArray[i], Arrays.toString(beforeMerge), inPlace));
break;
}
}
}
myCount += myDeferredShift;
myDeferredShift = 0;
storage.clear();
myDeferredChangeMode = false;
}
@NotNull
public String dumpState() {
return String.format(
"deferred changes mode: %b, length: %d (data array length: %d, deferred shift: %d); view offsets: [%d; %d]; deferred changes: %s",
isDeferredChangeMode(), length(), myCount, myDeferredShift, myStart, myEnd, myDeferredChangesStorage
);
}
private void checkStrings(@NotNull String operation, @NotNull String expected, @NotNull String actual) {
if (expected.equals(actual)) {
return;
}
for (int i = 0, max = Math.min(expected.length(), actual.length()); i < max; i++) {
if (actual.charAt(i) != expected.charAt(i)) {
dumpDebugInfo(String.format(
"Incorrect %s processing. Expected length: %d, actual length: %d. Unmatched symbol at %d - expected: '%c', " +
"actual: '%c', expected document: '%s', actual document: '%s'",
operation, expected.length(), actual.length(), i, expected.charAt(i), actual.charAt(i), expected, actual
));
return;
}
}
dumpDebugInfo(String.format(
"Incorrect %s processing. Expected length: %d, actual length: %d, expected: '%s', actual: '%s'",
operation, expected.length(), actual.length(), expected, actual
));
}
private void dumpDebugInfo(@NotNull String problem) {
//LOG.error(String.format(
// "/***********************************************************\n" +
// " * Please email idea.log to Denis.Zhdanov@jetbrains.com\n" +
// " ***********************************************************/\n" +
// "Incorrect CharArray processing detected: '%s'. Start: %d, end: %d, text on batch update start: '%s', deferred changes history: %s, "
// + "current deferred changes: %s",
// problem, myStart, myEnd, myDebugTextOnBatchUpdateStart, myDebugDeferredChanges, myDeferredChangesStorage
//));
LOG.error(String.format(
"Incorrect CharArray processing detected: '%s'. Start: %d, end: %d, text on batch update start: '%s', deferred changes history: %s, "
+ "current deferred changes: %s",
problem, myStart, myEnd, myDebugTextOnBatchUpdateStart, myDebugDeferredChanges, myDeferredChangesStorage
));
}
}
@@ -0,0 +1,418 @@
/*
* Copyright 2000-2009 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.fileEditor.impl;
import com.intellij.lang.properties.charset.Native2AsciiCharset;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.BinaryFileDecompiler;
import com.intellij.openapi.fileTypes.BinaryFileTypeDecompilers;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.encoding.EncodingRegistry;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
public final class LoadTextUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.LoadTextUtil");
private static final Key<String> DETECTED_LINE_SEPARATOR_KEY = Key.create("DETECTED_LINE_SEPARATOR_KEY");
private LoadTextUtil() {
}
@NotNull
private static Pair<CharSequence, String> convertLineSeparators(@NotNull CharBuffer buffer) {
int dst = 0;
char prev = ' ';
int crCount = 0;
int lfCount = 0;
int crlfCount = 0;
final int length = buffer.length();
final char[] bufferArray = CharArrayUtil.fromSequenceWithoutCopying(buffer);
for (int src = 0; src < length; src++) {
char c = bufferArray != null ? bufferArray[src] : buffer.charAt(src);
switch (c) {
case '\r':
buffer.put(dst++, '\n');
crCount++;
break;
case '\n':
if (prev == '\r') {
crCount--;
crlfCount++;
} else {
buffer.put(dst++, '\n');
lfCount++;
}
break;
default:
buffer.put(dst++, c);
break;
}
prev = c;
}
String detectedLineSeparator = null;
if (crlfCount > crCount && crlfCount > lfCount) {
detectedLineSeparator = "\r\n";
} else if (crCount > lfCount) {
detectedLineSeparator = "\r";
} else if (lfCount > 0) {
detectedLineSeparator = "\n";
}
CharSequence result = buffer.length() == dst ? buffer : buffer.subSequence(0, dst);
return Pair.create(result, detectedLineSeparator);
}
private static Charset detectCharset(@NotNull VirtualFile virtualFile, @NotNull byte[] content) {
if (virtualFile.isCharsetSet()) return virtualFile.getCharset();
Charset charset = doDetectCharset(virtualFile, content);
charset = charset == null ? EncodingRegistry.getInstance().getDefaultCharset() : charset;
if (EncodingRegistry.getInstance().isNative2Ascii(virtualFile)) {
charset = Native2AsciiCharset.wrap(charset);
}
virtualFile.setCharset(charset);
return charset;
}
@NotNull
public static Charset detectCharsetAndSetBOM(@NotNull VirtualFile virtualFile, @NotNull byte[] content) {
return doDetectCharsetAndSetBOM(virtualFile, content).getFirst();
}
@NotNull
private static Pair<Charset, byte[]> doDetectCharsetAndSetBOM(@NotNull VirtualFile virtualFile, @NotNull byte[] content) {
Charset charset = detectCharset(virtualFile, content);
Pair<Charset, byte[]> bomAndCharset = getBOMAndCharset(content, charset);
final byte[] bom = bomAndCharset.second;
if (bom.length != 0) {
virtualFile.setBOM(bom);
}
return bomAndCharset;
}
private static Charset doDetectCharset(@NotNull VirtualFile virtualFile, @NotNull byte[] content) {
Trinity<Charset, CharsetToolkit.GuessedEncoding, byte[]> guessed = guessFromContent(virtualFile, content, content.length);
if (guessed != null && guessed.first != null) return guessed.first;
FileType fileType = virtualFile.getFileType();
String charsetName = fileType.getCharset(virtualFile, content);
if (charsetName == null) {
Charset saved = EncodingRegistry.getInstance().getEncoding(virtualFile, true);
if (saved != null) return saved;
}
return CharsetToolkit.forName(charsetName);
}
@Nullable("null means no luck, otherwise it's tuple(guessed encoding, hint about content if was unable to guess, BOM)")
public static Trinity<Charset, CharsetToolkit.GuessedEncoding, byte[]> guessFromContent(VirtualFile virtualFile, byte[] content, int length) {
EncodingRegistry settings = EncodingRegistry.getInstance();
boolean shouldGuess = settings != null && settings.isUseUTFGuessing(virtualFile);
CharsetToolkit toolkit = shouldGuess ? new CharsetToolkit(content, EncodingRegistry.getInstance().getDefaultCharset()) : null;
setCharsetWasDetectedFromBytes(virtualFile, false);
if (shouldGuess) {
toolkit.setEnforce8Bit(true);
Charset charset = toolkit.guessFromBOM();
if (charset != null) {
setCharsetWasDetectedFromBytes(virtualFile, true);
byte[] bom = CharsetToolkit.getBom(charset);
if (bom == null) bom = CharsetToolkit.UTF8_BOM;
return Trinity.create(charset, null, bom);
}
CharsetToolkit.GuessedEncoding guessed = toolkit.guessFromContent(length);
if (guessed == CharsetToolkit.GuessedEncoding.VALID_UTF8) {
setCharsetWasDetectedFromBytes(virtualFile, true);
return Trinity.create(CharsetToolkit.UTF8_CHARSET, null, null); //UTF detected, ignore all directives
}
return Trinity.create(null, guessed, null);
}
return null;
}
@NotNull
private static Pair<Charset, byte[]> getBOMAndCharset(@NotNull byte[] content, final Charset charset) {
if (charset != null && charset.name().contains(CharsetToolkit.UTF8) && CharsetToolkit.hasUTF8Bom(content)) {
return Pair.create(charset, CharsetToolkit.UTF8_BOM);
}
try {
if (CharsetToolkit.hasUTF16LEBom(content)) {
return Pair.create(CharsetToolkit.UTF_16LE_CHARSET, CharsetToolkit.UTF16LE_BOM);
}
if (CharsetToolkit.hasUTF16BEBom(content)) {
return Pair.create(CharsetToolkit.UTF_16BE_CHARSET, CharsetToolkit.UTF16BE_BOM);
}
} catch (UnsupportedCharsetException ignore) {
}
if (charset == null) {
return Pair.create(Charset.defaultCharset(), CharsetToolkit.UTF8_BOM);
}
return Pair.create(charset, ArrayUtil.EMPTY_BYTE_ARRAY);
}
/**
* Gets the <code>Writer</code> for this file and sets modification stamp and time stamp to the specified values
* after closing the Writer.<p>
* <p/>
* Normally you should not use this method.
*
* @param project
* @param virtualFile
* @param requestor any object to control who called this method. Note that
* it is considered to be an external change if <code>requestor</code> is <code>null</code>.
* See {@link com.intellij.openapi.vfs.VirtualFileEvent#getRequestor}
* @param text
* @param newModificationStamp new modification stamp or -1 if no special value should be set @return <code>Writer</code>
* @throws java.io.IOException if an I/O error occurs
* @see com.intellij.openapi.vfs.VirtualFile#getModificationStamp()
*/
@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
public static Writer getWriter(@Nullable Project project, @NotNull VirtualFile virtualFile, Object requestor, @NotNull String text, final long newModificationStamp)
throws IOException {
Charset existing = virtualFile.getCharset();
Charset specified = extractCharsetFromFileContent(project, virtualFile, text);
Charset charset = chooseMostlyHarmlessCharset(existing, specified, text);
if (charset != null) {
if (!charset.equals(existing)) {
virtualFile.setCharset(charset);
}
setDetectedFromBytesFlagBack(virtualFile, charset, text);
}
// in c ase of "UTF-16", OutputStreamWriter sometimes adds BOM on it's own.
// see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6800103
byte[] bom = virtualFile.getBOM();
Charset fromBom = bom == null ? null : CharsetToolkit.guessFromBOM(bom);
if (fromBom != null) charset = fromBom;
OutputStream outputStream = virtualFile.getOutputStream(requestor, newModificationStamp, -1);
OutputStreamWriter writer = charset == null ? new OutputStreamWriter(outputStream) : new OutputStreamWriter(outputStream, charset);
// no need to buffer ByteArrayOutputStream
return outputStream instanceof ByteArrayOutputStream ? writer : new BufferedWriter(writer);
}
private static void setDetectedFromBytesFlagBack(@NotNull VirtualFile virtualFile, @NotNull Charset charset, @NotNull String text) {
if (virtualFile.getBOM() != null) {
// prevent file to be reloaded in other encoding after save with BOM
setCharsetWasDetectedFromBytes(virtualFile, true);
return;
}
byte[] content = text.getBytes(charset);
CharsetToolkit.GuessedEncoding guessedEncoding = new CharsetToolkit(content).guessFromContent(content.length);
if (guessedEncoding == CharsetToolkit.GuessedEncoding.VALID_UTF8) {
setCharsetWasDetectedFromBytes(virtualFile, true);
}
}
private static Charset chooseMostlyHarmlessCharset(Charset existing, Charset specified, String text) {
if (existing == null) return specified;
if (specified == null) return existing;
if (specified.equals(existing)) return specified;
if (isSupported(specified, text)) return specified; //if explicitly specified encoding is safe, return it
if (isSupported(existing, text)) return existing; //otherwise stick to the old encoding if it's ok
return specified; //if both are bad there is no difference
}
private static boolean isSupported(@NotNull Charset charset, @NotNull String str) {
if (!charset.canEncode()) return false;
ByteBuffer out = charset.encode(str);
CharBuffer buffer = charset.decode(out);
return str.equals(buffer.toString());
}
public static Charset extractCharsetFromFileContent(@Nullable Project project, @NotNull VirtualFile virtualFile, @NotNull String text) {
Charset charset = charsetFromContentOrNull(project, virtualFile, text);
if (charset == null) charset = virtualFile.getCharset();
return charset;
}
@Nullable("returns null if cannot determine from content")
public static Charset charsetFromContentOrNull(@Nullable Project project, @NotNull VirtualFile virtualFile, @NotNull String text) {
FileType fileType = virtualFile.getFileType();
if (fileType instanceof LanguageFileType) {
return ((LanguageFileType) fileType).extractCharsetFromFileContent(project, virtualFile, text);
}
return null;
}
public static CharSequence loadText(@NotNull VirtualFile file) {
if (file instanceof LightVirtualFile) {
CharSequence content = ((LightVirtualFile) file).getContent();
if (StringUtil.indexOf(content, '\r') == -1) return content;
CharBuffer buffer = CharBuffer.allocate(content.length());
buffer.append(content);
buffer.rewind();
return convertLineSeparators(buffer).first;
}
assert !file.isDirectory() : "'" + file.getPresentableUrl() + "' is directory";
final FileType fileType = file.getFileType();
if (fileType.isBinary()) {
final BinaryFileDecompiler decompiler = BinaryFileTypeDecompilers.INSTANCE.forFileType(fileType);
if (decompiler != null) {
CharSequence text = decompiler.decompile(file);
StringUtil.assertValidSeparators(text);
return text;
}
throw new IllegalArgumentException("Attempt to load text for binary file, that doesn't have decompiler plugged in: " + file.getPresentableUrl());
}
try {
byte[] bytes = file.contentsToByteArray();
return getTextByBinaryPresentation(bytes, file);
} catch (IOException e) {
return ArrayUtil.EMPTY_CHAR_SEQUENCE;
}
}
@NotNull
public static CharSequence getTextByBinaryPresentation(@NotNull final byte[] bytes, @NotNull VirtualFile virtualFile) {
return getTextByBinaryPresentation(bytes, virtualFile, true);
}
@NotNull
public static CharSequence getTextByBinaryPresentation(@NotNull byte[] bytes, @NotNull VirtualFile virtualFile, final boolean rememberDetectedSeparators) {
Pair<Charset, byte[]> pair = doDetectCharsetAndSetBOM(virtualFile, bytes);
final Charset charset = pair.getFirst();
byte[] bom = pair.getSecond();
int offset = bom == null ? 0 : bom.length;
final Pair<CharSequence, String> result = convertBytes(bytes, charset, offset);
if (rememberDetectedSeparators) {
virtualFile.putUserData(DETECTED_LINE_SEPARATOR_KEY, result.getSecond());
}
return result.getFirst();
}
/**
* Get detected line separator, if the file never been loaded, is loaded if checkFile parameter is specified.
*
* @param file the file to check
* @param checkFile if the line separator was not detected before, try to detect it
* @return the detected line separator or null
*/
@Nullable
public static String detectLineSeparator(@NotNull VirtualFile file, boolean checkFile) {
String lineSeparator = getDetectedLineSeparator(file);
if (lineSeparator == null && checkFile) {
try {
getTextByBinaryPresentation(file.contentsToByteArray(), file);
lineSeparator = getDetectedLineSeparator(file);
} catch (IOException e) {
// null will be returned
}
}
return lineSeparator;
}
static String getDetectedLineSeparator(@NotNull VirtualFile file) {
return file.getUserData(DETECTED_LINE_SEPARATOR_KEY);
}
/**
* Change line separator for the file to the specified value (assumes that the documents were saved)
*
* @param project the project instance
* @param requestor the requestor for the operation
* @param file the file to convert
* @param newLineSeparator the new line separator for the file
* @throws java.io.IOException in the case of IO problem
*/
public static void changeLineSeparator(@Nullable Project project,
@Nullable Object requestor,
@NotNull VirtualFile file,
@NotNull String newLineSeparator) throws IOException {
String lineSeparator = getDetectedLineSeparator(file);
if (lineSeparator != null && lineSeparator.equals(newLineSeparator)) {
return;
}
CharSequence cs = getTextByBinaryPresentation(file.contentsToByteArray(), file);
lineSeparator = getDetectedLineSeparator(file);
if (lineSeparator == null || lineSeparator.equals(newLineSeparator)) {
return;
}
if (!newLineSeparator.equals("\n")) {
cs = StringUtil.convertLineSeparators(cs.toString(), newLineSeparator);
}
String text = cs.toString();
file.putUserData(DETECTED_LINE_SEPARATOR_KEY, newLineSeparator);
Writer w = getWriter(project, file, requestor, text, System.currentTimeMillis());
try {
w.write(text);
} finally {
w.close();
}
}
@NotNull
public static CharSequence getTextByBinaryPresentation(@NotNull byte[] bytes, Charset charset) {
Pair<Charset, byte[]> pair = getBOMAndCharset(bytes, charset);
byte[] bom = pair.getSecond();
int offset = bom == null ? 0 : bom.length;
final Pair<CharSequence, String> result = convertBytes(bytes, charset, offset);
return result.getFirst();
}
// do not need to think about BOM here. it is processed outside
@NotNull
private static Pair<CharSequence, String> convertBytes(@NotNull byte[] bytes, Charset charset, final int startOffset) {
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes, startOffset, bytes.length - startOffset);
if (charset == null) {
charset = CharsetToolkit.getDefaultSystemCharset();
}
if (charset == null) {
//noinspection HardCodedStringLiteral
charset = Charset.forName("ISO-8859-1");
}
CharBuffer charBuffer = charset.decode(byteBuffer);
return convertLineSeparators(charBuffer);
}
private static final Key<Boolean> CHARSET_WAS_DETECTED_FROM_BYTES = new Key<Boolean>("CHARSET_WAS_DETECTED_FROM_BYTES");
public static boolean wasCharsetDetectedFromBytes(@NotNull VirtualFile virtualFile) {
return virtualFile.getUserData(CHARSET_WAS_DETECTED_FROM_BYTES) != null;
}
public static void setCharsetWasDetectedFromBytes(@NotNull VirtualFile virtualFile, boolean flag) {
virtualFile.putUserData(CHARSET_WAS_DETECTED_FROM_BYTES, flag ? Boolean.TRUE : null);
}
}
@@ -0,0 +1,351 @@
/*
* Copyright 2000-2011 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;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.reference.SoftReference;
import com.intellij.util.ImageLoader;
import com.intellij.util.containers.ConcurrentHashMap;
import com.intellij.util.containers.WeakHashMap;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.lang.ref.Reference;
import java.net.URL;
import java.util.Map;
//import sun.reflect.Reflection;
public final class IconLoader {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.IconLoader");
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private static final ConcurrentHashMap<URL, Icon> ourIconsCache = new ConcurrentHashMap<URL, Icon>(100, 0.9f, 2);
/**
* This cache contains mapping between icons and disabled icons.
*/
private static final Map<Icon, Icon> ourIcon2DisabledIcon = new WeakHashMap<Icon, Icon>(200);
private static final ImageIcon EMPTY_ICON = new ImageIcon(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR)) {
@NonNls
public String toString() {
return "Empty icon " + super.toString();
}
};
private static boolean ourIsActivated = false;
private IconLoader() {
}
@Deprecated
public static Icon getIcon(@NotNull final Image image) {
return new MyImageIcon(image);
}
@NotNull
public static Icon getIcon(@NonNls final String path) {
int stackFrameCount = 2;
return IconLoader.EMPTY_ICON;
/*Class callerClass = Reflection.getCallerClass(stackFrameCount);
while (callerClass != null && callerClass.getClassLoader() == null) { // looks like a system class
callerClass = Reflection.getCallerClass(++stackFrameCount);
}
if (callerClass == null) {
callerClass = Reflection.getCallerClass(1);
}
return getIcon(path, callerClass);*/
}
@Nullable
/**
* Might return null if icon was not found.
* Use only if you expected null return value, otherwise see {@link com.intellij.openapi.util.IconLoader#getIcon(String)}
*/
public static Icon findIcon(@NonNls final String path) {
int stackFrameCount = 2;
return IconLoader.EMPTY_ICON;
/*Class callerClass = Reflection.getCallerClass(stackFrameCount);
while (callerClass != null && callerClass.getClassLoader() == null) { // looks like a system class
callerClass = Reflection.getCallerClass(++stackFrameCount);
}
if (callerClass == null) {
callerClass = Reflection.getCallerClass(1);
}
return findIcon(path, callerClass);*/
}
@NotNull
public static Icon getIcon(@NotNull String path, @NotNull final Class aClass) {
final Icon icon = findIcon(path, aClass);
if (icon == null) {
LOG.error("Icon cannot be found in '" + path + "', aClass='" + aClass + "'");
}
return icon;
}
public static void activate() {
ourIsActivated = true;
}
private static boolean isLoaderDisabled() {
return !ourIsActivated;
}
/**
* Might return null if icon was not found.
* Use only if you expected null return value, otherwise see {@link com.intellij.openapi.util.IconLoader#getIcon(String, Class)}
*/
@Nullable
public static Icon findIcon(@NotNull final String path, @NotNull final Class aClass) {
return findIcon(path, aClass, false);
}
public static Icon findIcon(@NotNull final String path, @NotNull final Class aClass, boolean computeNow) {
/*final ByClass icon = new ByClass(aClass, path);
if (computeNow || !Registry.is("ide.lazyIconLoading", true)) {
return icon.getOrComputeIcon();
}
return icon;*/
return IconLoader.EMPTY_ICON;
}
@Nullable
private static Icon findIcon(URL url) {
if (url == null) return null;
Icon icon = ourIconsCache.get(url);
if (icon == null) {
icon = new CachedImageIcon(url);
icon = ourIconsCache.cacheOrGet(url, icon);
}
return icon;
}
@Nullable
public static Icon findIcon(final String path, final ClassLoader aClassLoader) {
if (!path.startsWith("/")) return null;
final URL url = aClassLoader.getResource(path.substring(1));
return findIcon(url);
}
@Nullable
private static Icon checkIcon(final Image image, URL url) {
if (image == null || image.getHeight(LabelHolder.ourFakeComponent) < 1) { // image wasn't loaded or broken
return null;
}
final Icon icon = getIcon(image);
if (icon != null && !isGoodSize(icon)) {
LOG.error("Invalid icon: " + url); // # 22481
return EMPTY_ICON;
}
return icon;
}
public static boolean isGoodSize(@NotNull final Icon icon) {
return icon.getIconWidth() > 0 && icon.getIconHeight() > 0;
}
/**
* Gets (creates if necessary) disabled icon based on the passed one.
*
* @param icon
* @return <code>ImageIcon</code> constructed from disabled image of passed icon.
*/
@Nullable
public static Icon getDisabledIcon(final Icon icon) {
if (icon == null) {
return null;
}
Icon disabledIcon = ourIcon2DisabledIcon.get(icon);
if (disabledIcon == null) {
if (!isGoodSize(icon)) {
LOG.error(icon); // # 22481
return EMPTY_ICON;
}
final BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
final Graphics2D graphics = image.createGraphics();
graphics.setColor(UIUtil.TRANSPARENT_COLOR);
graphics.fillRect(0, 0, icon.getIconWidth(), icon.getIconHeight());
icon.paintIcon(LabelHolder.ourFakeComponent, graphics, 0, 0);
graphics.dispose();
disabledIcon = new MyImageIcon(GrayFilter.createDisabledImage(image));
ourIcon2DisabledIcon.put(icon, disabledIcon);
}
return disabledIcon;
}
public static Icon getTransparentIcon(final Icon icon) {
return getTransparentIcon(icon, 0.5f);
}
public static Icon getTransparentIcon(final Icon icon, final float alpha) {
return new Icon() {
public int getIconHeight() {
return icon.getIconHeight();
}
public int getIconWidth() {
return icon.getIconWidth();
}
public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
final Graphics2D g2 = (Graphics2D) g;
final Composite saveComposite = g2.getComposite();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
icon.paintIcon(c, g2, x, y);
g2.setComposite(saveComposite);
}
};
}
private static final class CachedImageIcon implements Icon {
private Object myRealIcon;
private final URL myUrl;
public CachedImageIcon(URL url) {
myUrl = url;
}
private synchronized Icon getRealIcon() {
if (isLoaderDisabled()) return EMPTY_ICON;
if (myRealIcon instanceof Icon) return (Icon) myRealIcon;
Icon icon;
if (myRealIcon instanceof Reference) {
icon = ((Reference<Icon>) myRealIcon).get();
if (icon != null) return icon;
}
Image image = ImageLoader.loadFromUrl(myUrl);
icon = checkIcon(image, myUrl);
if (icon != null) {
if (icon.getIconWidth() < 50 && icon.getIconHeight() < 50) {
myRealIcon = icon;
} else {
myRealIcon = new SoftReference<Icon>(icon);
}
}
return icon != null ? icon : EMPTY_ICON;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
getRealIcon().paintIcon(c, g, x, y);
}
public int getIconWidth() {
return getRealIcon().getIconWidth();
}
public int getIconHeight() {
return getRealIcon().getIconHeight();
}
}
private static final class MyImageIcon extends ImageIcon {
public MyImageIcon(final Image image) {
super(image);
}
public final synchronized void paintIcon(final Component c, final Graphics g, final int x, final int y) {
super.paintIcon(null, g, x, y);
}
}
public abstract static class LazyIcon implements Icon {
private boolean myWasComputed;
private Icon myIcon;
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
final Icon icon = getOrComputeIcon();
if (icon != null) {
icon.paintIcon(c, g, x, y);
}
}
@Override
public int getIconWidth() {
final Icon icon = getOrComputeIcon();
return icon != null ? icon.getIconWidth() : 0;
}
@Override
public int getIconHeight() {
final Icon icon = getOrComputeIcon();
return icon != null ? icon.getIconHeight() : 0;
}
protected synchronized final Icon getOrComputeIcon() {
if (!myWasComputed) {
myWasComputed = true;
myIcon = compute();
}
return myIcon;
}
public final void load() {
getIconWidth();
}
protected abstract Icon compute();
}
private static class ByClass extends LazyIcon {
private final Class myCallerClass;
private final String myPath;
public ByClass(Class aClass, String path) {
myCallerClass = aClass;
myPath = path;
}
@Override
protected Icon compute() {
URL url = myCallerClass.getResource(myPath);
return findIcon(url);
}
@Override
public String toString() {
return "icon path=" + myPath + " class=" + myCallerClass;
}
}
private static class LabelHolder {
/**
* To get disabled icon with paint it into the image. Some icons require
* not null component to paint.
*/
private static final JComponent ourFakeComponent = new JLabel();
}
}
@@ -0,0 +1,265 @@
/*
* Copyright 2000-2009 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;
//import com.intellij.util.concurrency.AtomicFieldUpdater;
import com.intellij.util.containers.StripedLockConcurrentHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.ConcurrentModificationException;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
public class UserDataHolderBase implements UserDataHolderEx, Cloneable {
private static final Key<Map<Key, Object>> COPYABLE_USER_MAP_KEY = Key.create("COPYABLE_USER_MAP_KEY");
/**
* Concurrent writes to this field are via CASes only, using the {@link #}
* When map becomes empty, this field set to null atomically
* <p/>
* Basic state transitions are as follows:
* <p/>
* (adding keyvalue) (putUserData(key,value))
* [myUserMap=null] -> [myUserMap=(key->value)]
* <p/>
* (adding another) (putUserData(key2,value2))
* [myUserMap=(key->value)] -> [myUserMap=(key->value, key2->value2)]
* <p/>
* (removing keyvalue) (putUserData(k2,null))
* [myUserMap=(key->value, k2->v2)] -> [myUserMap=(key->value)]
* <p/>
* (removing last entry) (putUserData(key,null))
* [myUserMap=(key->value)] -> [myUserMap=null]
*/
private volatile ConcurrentMap<Key, Object> myUserMap = null;
protected Object clone() {
try {
UserDataHolderBase clone = (UserDataHolderBase) super.clone();
clone.myUserMap = null;
copyCopyableDataTo(clone);
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@TestOnly
public String getUserDataString() {
final ConcurrentMap<Key, Object> userMap = myUserMap;
if (userMap == null) {
return "";
}
final Map copyableMap = getUserData(COPYABLE_USER_MAP_KEY);
return userMap.toString() + (copyableMap == null ? "" : copyableMap.toString());
}
public void copyUserDataTo(UserDataHolderBase other) {
ConcurrentMap<Key, Object> map = myUserMap;
if (map == null) {
other.myUserMap = null;
} else {
ConcurrentMap<Key, Object> fresh = createDataMap(map.size());
fresh.putAll(map);
other.myUserMap = fresh;
}
}
public <T> T getUserData(@NotNull Key<T> key) {
final Map<Key, Object> map = myUserMap;
//noinspection unchecked
return map == null ? null : (T) map.get(key);
}
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
while (true) {
try {
if (value == null) {
ConcurrentMap<Key, Object> map = myUserMap;
if (map == null) break;
@SuppressWarnings("unchecked")
T previous = (T) map.remove(key);
boolean removed = previous != null;
if (removed) {
nullifyMapFieldIfEmpty();
}
} else {
getOrCreateMap().put(key, value);
}
break;
} catch (ConcurrentModificationException ignored) {
}
}
}
private static ConcurrentMap<Key, Object> createDataMap(int initialCapacity) {
return new StripedLockConcurrentHashMap<Key, Object>(initialCapacity);
}
public <T> T getCopyableUserData(Key<T> key) {
return getCopyableUserDataImpl(key);
}
protected final <T> T getCopyableUserDataImpl(Key<T> key) {
Map map = getUserData(COPYABLE_USER_MAP_KEY);
//noinspection unchecked
return map == null ? null : (T) map.get(key);
}
public <T> void putCopyableUserData(Key<T> key, T value) {
putCopyableUserDataImpl(key, value);
}
private Map<Key, Object> getOrCreateCopyableMap(boolean create) {
Map<Key, Object> copyMap = getUserData(COPYABLE_USER_MAP_KEY);
if (copyMap == null && create) {
copyMap = createDataMap(1);
copyMap = putUserDataIfAbsent(COPYABLE_USER_MAP_KEY, copyMap);
}
return copyMap;
}
protected final <T> void putCopyableUserDataImpl(Key<T> key, T value) {
while (true) {
try {
Map<Key, Object> copyMap = getOrCreateCopyableMap(value != null);
if (copyMap == null) break;
if (value == null) {
copyMap.remove(key);
if (copyMap.isEmpty()) {
((StripedLockConcurrentHashMap<Key, Object>) copyMap).blockModification();
ConcurrentMap<Key, Object> newCopyMap;
if (copyMap.isEmpty()) {
newCopyMap = null;
} else {
newCopyMap = createDataMap(copyMap.size());
newCopyMap.putAll(copyMap);
}
boolean replaced = replace(COPYABLE_USER_MAP_KEY, copyMap, newCopyMap);
if (!replaced) continue;
}
} else {
copyMap.put(key, value);
}
break;
} catch (ConcurrentModificationException ignored) {
// someone blocked modification, retry
}
}
}
private ConcurrentMap<Key, Object> getOrCreateMap() {
while (true) {
ConcurrentMap<Key, Object> map = myUserMap;
if (map != null) return map;
map = createDataMap(2);
boolean updated = true;
//throw new UnsupportedOperationException("Exception in (my) UserDataHolderBase");
// boolean updated = updater.compareAndSet(this, null, map);
if (updated) {
return map;
}
}
}
public <T> boolean replace(@NotNull Key<T> key, @Nullable T oldValue, @Nullable T newValue) {
while (true) {
try {
ConcurrentMap<Key, Object> map = getOrCreateMap();
if (oldValue == null) {
return newValue == null || map.putIfAbsent(key, newValue) == null;
}
if (newValue == null) {
boolean removed = map.remove(key, oldValue);
if (removed) {
nullifyMapFieldIfEmpty();
}
return removed;
}
return map.replace(key, oldValue, newValue);
} catch (ConcurrentModificationException ignored) {
// someone blocked modification, retry
}
}
}
@NotNull
public <T> T putUserDataIfAbsent(@NotNull final Key<T> key, @NotNull final T value) {
Object v = getOrCreateMap().get(key);
if (v != null) {
//noinspection unchecked
return (T) v;
}
while (true) {
try {
@SuppressWarnings("unchecked")
T prev = (T) getOrCreateMap().putIfAbsent(key, value);
return prev == null ? value : prev;
} catch (ConcurrentModificationException ignored) {
// someone blocked modification, retry
}
}
}
public void copyCopyableDataTo(@NotNull UserDataHolderBase clone) {
Map<Key, Object> copyableMap = getUserData(COPYABLE_USER_MAP_KEY);
if (copyableMap != null) {
ConcurrentMap<Key, Object> copy = createDataMap(copyableMap.size());
copy.putAll(copyableMap);
copyableMap = copy;
}
clone.putUserData(COPYABLE_USER_MAP_KEY, copyableMap);
}
protected void clearUserData() {
myUserMap = null;
}
// private static final AtomicFieldUpdater<UserDataHolderBase, ConcurrentMap> updater = AtomicFieldUpdater.forFieldOfType(UserDataHolderBase.class, ConcurrentMap.class);
private void nullifyMapFieldIfEmpty() {
try {
while (true) {
StripedLockConcurrentHashMap<Key, Object> map = (StripedLockConcurrentHashMap<Key, Object>) myUserMap;
if (map == null || !map.isEmpty()) break;
map.blockModification(); // we block the map and either replace it with null or fail with replace, in both cases the map is thrown away
ConcurrentMap<Key, Object> newMap;
if (map.isEmpty()) {
newMap = null;
} else {
// someone managed to add something in the meantime
// atomically replace the blocked map with newly created map filled with the data sneaked in
newMap = createDataMap(map.size());
newMap.putAll(map);
}
boolean replaced = true;
//throw new UnsupportedOperationException("Exception in (my) UserDataHolderBase");
//boolean replaced = updater.compareAndSet(this, map, newMap);
if (replaced) break;
// else someone has replaced map already and pushing back the changes is his responsibility
}
} catch (ConcurrentModificationException ignored) {
// somebody has already blocked the map, back off
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2011 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.vfs.impl.jar;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* @author yole
*/
public class CoreJarHandler extends JarHandlerBase {
private final Map<String, VirtualFile> myFileMap = new HashMap<String, VirtualFile>();
private final CoreJarFileSystem myFileSystem;
public CoreJarHandler(CoreJarFileSystem fileSystem, String path) {
super(fileSystem, path);
myFileSystem = fileSystem;
}
@Nullable
public VirtualFile findFileByPath(String pathInJar) {
if (getZip() == null) {
return null;
}
VirtualFile file = myFileMap.get(pathInJar);
if (file == null) {
if (pathInJar.length() > 0) {
EntryInfo entryInfo = getEntryInfo(pathInJar);
if (entryInfo == null) {
return null;
}
}
file = new CoreJarVirtualFile(myFileSystem, this, pathInJar);
myFileMap.put(pathInJar, file);
}
return file;
}
}
@@ -0,0 +1,266 @@
/*
* Copyright 2000-2011 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.vfs.impl.jar;
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.TimedReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class JarHandlerBase {
protected final TimedReference<ZipInputStream> myZipFile = new TimedReference<ZipInputStream>(null);
protected SoftReference<Map<String, EntryInfo>> myRelPathsToEntries = new SoftReference<Map<String, EntryInfo>>(null);
protected final Object lock = new Object();
private InputStream inputStream;
protected final String myBasePath;
protected static class EntryInfo {
public EntryInfo(final String shortName, final EntryInfo parent, final boolean directory, byte[] content) {
this.shortName = new String(shortName);
this.parent = parent;
isDirectory = directory;
this.content = content;
}
final boolean isDirectory;
final byte[] content;
protected final String shortName;
final EntryInfo parent;
}
public JarHandlerBase(CoreJarFileSystem myFileSystem, String path) {
if (inputStream == null) {
try {
inputStream = new VirtualJarFile(myFileSystem, path).getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
myBasePath = path;
}
@NotNull
protected Map<String, EntryInfo> initEntries() {
synchronized (lock) {
Map<String, EntryInfo> map = myRelPathsToEntries.get();
if (map == null) {
final ZipInputStream zip = getZip();
map = new HashMap<String, EntryInfo>();
if (zip != null) {
map.put("", new EntryInfo("", null, true, new byte[0]));
try {
ZipEntry entry = zip.getNextEntry();
while (entry != null) {
final String name = entry.getName();
final boolean isDirectory = name.endsWith("/");
if (entry.getExtra() == null) {
byte[] cont = new byte[(int) entry.getSize()];
ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
InputStream stream = getZip();
if (stream != null) {
int tmp;
if ((tmp = stream.read(cont)) == entry.getSize()) {
byteArray.write(cont, 0, tmp);
entry.setExtra(byteArray.toByteArray());
} else {
int readFromIS = tmp;
if (tmp < entry.getSize()) {
byteArray.write(cont, 0, tmp);
while (((tmp = stream.read(cont)) != -1) && (tmp + readFromIS <= entry.getSize())) {
byteArray.write(cont, 0, tmp);
readFromIS += tmp;
}
entry.setExtra(byteArray.toByteArray());
}
}
}
}
getOrCreate(isDirectory ? name.substring(0, name.length() - 1) : name, isDirectory, map, entry.getExtra());
entry = zip.getNextEntry();
}
} catch (IOException e) {
e.printStackTrace();
}
myRelPathsToEntries = new SoftReference<Map<String, EntryInfo>>(map);
}
}
return map;
}
}
public File getMirrorFile(File originalFile) {
return originalFile;
}
@Nullable
public ZipInputStream getZip() {
ZipInputStream zip = myZipFile.get();
if (zip == null) {
if (inputStream == null) {
throw new IllegalArgumentException("Input Stream is null");
} else {
zip = new ZipInputStream(inputStream);
}
myZipFile.set(zip);
}
return zip;
}
protected InputStream getOriginalFile() {
return inputStream;
}
private static EntryInfo getOrCreate(String entryName, boolean isDirectory, Map<String, EntryInfo> map, byte[] content) {
EntryInfo info = map.get(entryName);
if (info == null) {
int idx = entryName.lastIndexOf('/');
final String parentEntryName = idx > 0 ? entryName.substring(0, idx) : "";
String shortName = idx > 0 ? entryName.substring(idx + 1) : entryName;
if (".".equals(shortName)) return getOrCreate(parentEntryName, true, map, content);
info = new EntryInfo(shortName, getOrCreate(parentEntryName, true, map, content), isDirectory, content);
map.put(entryName, info);
}
return info;
}
@NotNull
public String[] list(@NotNull final VirtualFile file) {
synchronized (lock) {
EntryInfo parentEntry = getEntryInfo(file);
Set<String> names = new HashSet<String>();
for (EntryInfo info : getEntriesMap().values()) {
if (info.parent == parentEntry) {
names.add(info.shortName);
}
}
return ArrayUtil.toStringArray(names);
}
}
protected EntryInfo getEntryInfo(final VirtualFile file) {
synchronized (lock) {
String parentPath = getRelativePath(file);
return getEntryInfo(parentPath);
}
}
public EntryInfo getEntryInfo(String parentPath) {
return getEntriesMap().get(parentPath);
}
protected Map<String, EntryInfo> getEntriesMap() {
return initEntries();
}
private String getRelativePath(final VirtualFile file) {
// throw new UnsupportedOperationException(file.getPath());
final String path = file.getPath().substring(myBasePath.length() + 1);
return path.startsWith("/") ? path.substring(1) : path;
}
@Nullable
private ZipEntry convertToEntry(VirtualFile file) {
String path = getRelativePath(file);
final ZipInputStream zip = getZip();
return null;
}
@Nullable
private EntryInfo convertToISEntry(VirtualFile file) {
String path = getRelativePath(file);
final ZipInputStream zip = getZip();
return myRelPathsToEntries.get().get(path);
}
public long getLength(@NotNull final VirtualFile file) {
synchronized (lock) {
final ZipEntry entry = convertToEntry(file);
return entry != null ? entry.getSize() : 0;
}
}
@NotNull
public InputStream getInputStream(@NotNull final VirtualFile file) throws IOException {
return new BufferExposingByteArrayInputStream(contentsToByteArray(file));
}
@NotNull
public byte[] contentsToByteArray(@NotNull final VirtualFile file) throws IOException {
synchronized (lock) {
EntryInfo info = convertToISEntry(file);
if (info == null) {
return new byte[0];
}
byte[] content = info.content;
if (content == null) {
return new byte[0];
}
return content;
}
}
public long getTimeStamp(@NotNull final VirtualFile file) {
if (file.getParent() == null) return file.getTimeStamp(); // Optimization
// if (file.getParent() == null) return getOriginalFile().lastModified(); // Optimization
synchronized (lock) {
final ZipEntry entry = convertToEntry(file);
return entry != null ? entry.getTime() : -1L;
}
}
public boolean isDirectory(@NotNull final VirtualFile file) {
if (file.getParent() == null) return true; // Optimization
synchronized (lock) {
final String path = getRelativePath(file);
final EntryInfo info = getEntryInfo(path);
return info == null || info.isDirectory;
}
}
public boolean exists(@NotNull final VirtualFile fileOrDirectory) {
if (fileOrDirectory.getParent() == null) {
// Optimization. Do not build entries if asked for jar root existence.
return myZipFile.get() != null;
// return myZipFile.get() != null || getOriginalFile().exists();
}
return getEntryInfo(fileOrDirectory) != null;
}
}
@@ -0,0 +1,117 @@
package com.intellij.openapi.vfs.impl.jar;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileSystem;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by IntelliJ IDEA.
* User: Natalia.Ukhorskaya
* Date: 12/1/11
* Time: 5:06 PM
*/
public class VirtualJarFile extends VirtualFile {
private byte[] byteArray;
private final CoreJarFileSystem myFileSystem;
private final String name;
public VirtualJarFile(CoreJarFileSystem myFileSystem, String pathInJar) {
this.myFileSystem = myFileSystem;
this.name = pathInJar;
}
@NotNull
@Override
public String getName() {
return name;
}
@NotNull
@Override
public VirtualFileSystem getFileSystem() {
return myFileSystem;
}
@Override
public String getPath() {
return name;
}
@Override
public boolean isWritable() {
return false;
}
@Override
public boolean isDirectory() {
return false;
}
@Override
public boolean isValid() {
return true;
}
@Override
public VirtualFile getParent() {
return null;
}
@Override
public VirtualFile[] getChildren() {
return VirtualFile.EMPTY_ARRAY;
}
@NotNull
@Override
public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
return System.out;
}
@NotNull
@Override
public byte[] contentsToByteArray() throws IOException {
if (byteArray.length <= 0) {
int length;
byte[] tmp = new byte[1024];
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream rtJar = VirtualJarFile.class.getResourceAsStream("/" + name);
try {
while ((length = rtJar.read(tmp)) >= 0) {
out.write(tmp, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
return new byte[0];
}
byteArray = out.toByteArray();
}
return byteArray;
}
@Override
public long getTimeStamp() {
return 0;
}
@Override
public long getLength() {
return byteArray.length;
}
@Override
public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) {
}
@Override
public InputStream getInputStream() throws IOException {
return VirtualJarFile.class.getResourceAsStream("/" + name);
}
}
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2011 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.vfs.newvfs.persistent;
public class PersistentFSConstants {
public static final long FILE_LENGTH_TO_CACHE_THRESHOLD = 20 * 1024 * 1024; // 20 megabytes
/**
* always in range [0, PersistentFS.FILE_LENGTH_TO_CACHE_THRESHOLD]
*/
public static final int MAX_INTELLISENSE_FILESIZE = maxIntellisenseFileSize();
// @NonNls private static final String MAX_INTELLISENSE_SIZE_PROPERTY = "idea.max.intellisense.filesize";
private PersistentFSConstants() {
}
private static int maxIntellisenseFileSize() {
final int maxLimitBytes = (int) FILE_LENGTH_TO_CACHE_THRESHOLD;
final String userLimitKb = "100";
// final String userLimitKb = System.getProperty(MAX_INTELLISENSE_SIZE_PROPERTY);
try {
return userLimitKb != null ? Math.min(Integer.parseInt(userLimitKb) * 1024, maxLimitBytes) : maxLimitBytes;
} catch (NumberFormatException ignored) {
return maxLimitBytes;
}
}
}
@@ -0,0 +1,535 @@
/*
* Copyright 2000-2011 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;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageParserDefinitions;
import com.intellij.lang.ParserDefinition;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.UndoConstants;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.file.exclude.ProjectFileExclusionManager;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
import com.intellij.openapi.fileTypes.*;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.vfs.NonPhysicalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSConstants;
import com.intellij.psi.impl.PsiFileEx;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.compiled.ClsFileImpl;
import com.intellij.psi.impl.file.PsiBinaryFileImpl;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.PsiPlainTextFileImpl;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.ReflectionCache;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
public class SingleRootFileViewProvider extends UserDataHolderBase implements FileViewProvider {
private static final Key<Boolean> OUR_NO_SIZE_LIMIT_KEY = Key.create("no.size.limit");
private static final Logger LOG = Logger.getInstance("#" + SingleRootFileViewProvider.class.getCanonicalName());
private final PsiManager myManager;
private final VirtualFile myVirtualFile;
private final boolean myEventSystemEnabled;
private final boolean myPhysical;
private final AtomicReference<PsiFile> myPsiFile = new AtomicReference<PsiFile>();
private volatile Content myContent;
private volatile SoftReference<Document> myDocument;
private final Language myBaseLanguage;
private final ProjectFileExclusionManager myExclusionManager;
public SingleRootFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile file) {
this(manager, file, true);
}
public SingleRootFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile virtualFile, final boolean physical) {
this(manager, virtualFile, physical, calcBaseLanguage(virtualFile, manager.getProject()));
}
protected SingleRootFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile virtualFile, final boolean physical, @NotNull Language language) {
myManager = manager;
myVirtualFile = virtualFile;
myEventSystemEnabled = physical;
myBaseLanguage = language;
setContent(new VirtualFileContent());
myPhysical = isEventSystemEnabled() &&
!(virtualFile instanceof LightVirtualFile) &&
!(virtualFile.getFileSystem() instanceof NonPhysicalFileSystem);
myExclusionManager = ProjectFileExclusionManager.SERVICE.getInstance(manager.getProject());
}
@Override
@NotNull
public Language getBaseLanguage() {
return myBaseLanguage;
}
private static Language calcBaseLanguage(@NotNull VirtualFile file, @NotNull Project project) {
if (file instanceof LightVirtualFile) {
final Language language = ((LightVirtualFile) file).getLanguage();
if (language != null) {
return language;
}
}
FileType fileType = file.getFileType();
// Do not load content
if (fileType == UnknownFileType.INSTANCE) {
fileType = FileTypeRegistry.getInstance().detectFileTypeFromContent(file);
}
if (fileType.isBinary()) return Language.ANY;
if (isTooLarge(file)) return PlainTextLanguage.INSTANCE;
if (fileType instanceof LanguageFileType) {
return LanguageSubstitutors.INSTANCE.substituteLanguage(((LanguageFileType) fileType).getLanguage(), file, project);
}
final ContentBasedFileSubstitutor[] processors = Extensions.getExtensions(ContentBasedFileSubstitutor.EP_NAME);
for (ContentBasedFileSubstitutor processor : processors) {
Language language = processor.obtainLanguageForFile(file);
if (language != null) return language;
}
return PlainTextLanguage.INSTANCE;
}
@Override
@NotNull
public Set<Language> getLanguages() {
return Collections.singleton(getBaseLanguage());
}
@Override
@Nullable
public final PsiFile getPsi(@NotNull Language target) {
/*if (!isPhysical()) {
((PsiManagerEx) myManager).getFileManager().setViewProvider(getVirtualFile(), this);
}*/
return getPsiInner(target);
}
@Override
@NotNull
public List<PsiFile> getAllFiles() {
return Collections.singletonList(getPsi(getBaseLanguage()));
}
@Nullable
protected PsiFile getPsiInner(final Language target) {
if (target != getBaseLanguage()) {
return null;
}
PsiFile psiFile = myPsiFile.get();
if (psiFile == null) {
psiFile = createFile();
boolean set = myPsiFile.compareAndSet(null, psiFile);
if (!set) {
psiFile = myPsiFile.get();
}
}
return psiFile;
}
@Override
public void beforeContentsSynchronized() {
unsetPsiContent();
}
@Override
public void contentsSynchronized() {
unsetPsiContent();
}
private void unsetPsiContent() {
if (!(myContent instanceof PsiFileContent)) return;
final Document cachedDocument = getCachedDocument();
setContent(cachedDocument == null ? new VirtualFileContent() : new DocumentContent());
}
public void beforeDocumentChanged() {
final PsiFileImpl psiFile = (PsiFileImpl) getCachedPsi(getBaseLanguage());
if (psiFile != null && psiFile.isContentsLoaded() && getContent() instanceof DocumentContent) {
setContent(new PsiFileContent(psiFile, getModificationStamp()));
}
}
@Override
public void rootChanged(PsiFile psiFile) {
if (((PsiFileEx) psiFile).isContentsLoaded()) {
setContent(new PsiFileContent((PsiFileImpl) psiFile, LocalTimeCounter.currentTime()));
}
}
@Override
public boolean isEventSystemEnabled() {
return myEventSystemEnabled;
}
@Override
public boolean isPhysical() {
return myPhysical;
}
@Override
public long getModificationStamp() {
return getContent().getModificationStamp();
}
@Override
public boolean supportsIncrementalReparse(final Language rootLanguage) {
return true;
}
public PsiFile getCachedPsi(Language target) {
return myPsiFile.get();
}
public FileElement[] getKnownTreeRoots() {
PsiFile psiFile = myPsiFile.get();
if (psiFile == null || !(psiFile instanceof PsiFileImpl)) return new FileElement[0];
if (((PsiFileImpl) psiFile).getTreeElement() == null) return new FileElement[0];
return new FileElement[]{(FileElement) psiFile.getNode()};
}
private PsiFile createFile() {
try {
final VirtualFile vFile = getVirtualFile();
if (vFile.isDirectory()) return null;
if (isIgnored()) return null;
final Project project = myManager.getProject();
if (isPhysical()) { // check directories consistency
final VirtualFile parent = vFile.getParent();
if (parent == null) return null;
final PsiDirectory psiDir = getManager().findDirectory(parent);
if (psiDir == null) return null;
}
FileType fileType = vFile.getFileType();
PsiFile file = null;
if (fileType.isBinary() || vFile.isSpecialFile()) {
//TODO check why ClsFileImpl doesn't created automatically with create method
file = new ClsFileImpl((PsiManagerImpl) getManager(), this);
//file = new PsiBinaryFileImpl((PsiManagerImpl) getManager(), this);
} else {
if (!isTooLarge(vFile)) {
final PsiFile psiFile = createFile(getBaseLanguage());
if (psiFile != null) file = psiFile;
} else {
file = new PsiPlainTextFileImpl(this);
}
}
return file;
} catch (ProcessCanceledException e) {
e.printStackTrace();
throw e;
} catch (Throwable e) {
e.printStackTrace();
LOG.error(e);
return null;
}
}
protected boolean isIgnored() {
final VirtualFile file = getVirtualFile();
if (file instanceof LightVirtualFile) return false;
if (myExclusionManager != null && myExclusionManager.isExcluded(file)) return true;
return FileTypeRegistry.getInstance().isFileIgnored(file);
}
@Nullable
protected PsiFile createFile(@NotNull Project project, @NotNull VirtualFile file, @NotNull FileType fileType) {
if (fileType.isBinary() || file.isSpecialFile()) {
return new PsiBinaryFileImpl((PsiManagerImpl) getManager(), this);
}
if (!isTooLarge(file)) {
final PsiFile psiFile = createFile(getBaseLanguage());
if (psiFile != null) return psiFile;
}
return new PsiPlainTextFileImpl(this);
}
public static boolean isTooLarge(@NotNull VirtualFile vFile) {
if (!checkFileSizeLimit(vFile)) return false;
return fileSizeIsGreaterThan(vFile, PersistentFSConstants.MAX_INTELLISENSE_FILESIZE);
}
private static boolean checkFileSizeLimit(@NotNull VirtualFile vFile) {
return !Boolean.TRUE.equals(vFile.getUserData(OUR_NO_SIZE_LIMIT_KEY));
}
public static void doNotCheckFileSizeLimit(@NotNull VirtualFile vFile) {
vFile.putUserData(OUR_NO_SIZE_LIMIT_KEY, Boolean.TRUE);
}
public static boolean isTooLarge(@NotNull VirtualFile vFile, final long contentSize) {
if (!checkFileSizeLimit(vFile)) return false;
return contentSize > PersistentFSConstants.MAX_INTELLISENSE_FILESIZE;
}
private static boolean fileSizeIsGreaterThan(@NotNull VirtualFile vFile, final long maxBytes) {
if (vFile instanceof LightVirtualFile) {
// This is optimization in order to avoid conversion of [large] file contents to bytes
final int lengthInChars = ((LightVirtualFile) vFile).getContent().length();
if (lengthInChars < maxBytes / 2) return false;
if (lengthInChars > maxBytes) return true;
}
return vFile.getLength() > maxBytes;
}
@Nullable
protected PsiFile createFile(Language lang) {
if (lang != getBaseLanguage()) return null;
final ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(lang);
if (parserDefinition != null) {
return parserDefinition.createFile(this);
}
return null;
}
@Override
@NotNull
public PsiManager getManager() {
return myManager;
}
@Override
@NotNull
public CharSequence getContents() {
return getContent().getText();
}
@Override
@NotNull
public VirtualFile getVirtualFile() {
return myVirtualFile;
}
@Nullable
private Document getCachedDocument() {
final Document document = myDocument != null ? myDocument.get() : null;
if (document != null) return document;
return FileDocumentManager.getInstance().getCachedDocument(getVirtualFile());
}
@Override
public Document getDocument() {
Document document = myDocument != null ? myDocument.get() : null;
if (document == null/* TODO[ik] make this change && isEventSystemEnabled()*/) {
document = FileDocumentManager.getInstance().getDocument(getVirtualFile());
myDocument = new SoftReference<Document>(document);
}
if (document != null && getContent() instanceof VirtualFileContent) {
setContent(new DocumentContent());
}
return document;
}
@Override
public FileViewProvider clone() {
final VirtualFile origFile = getVirtualFile();
LightVirtualFile copy = new LightVirtualFile(origFile.getName(), origFile.getFileType(), getContents(), origFile.getCharset(), getModificationStamp());
copy.setOriginalFile(origFile);
copy.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE);
copy.setCharset(origFile.getCharset());
return createCopy(copy);
}
@NotNull
@Override
public SingleRootFileViewProvider createCopy(final VirtualFile copy) {
return new SingleRootFileViewProvider(getManager(), copy, false, myBaseLanguage);
}
@Override
public PsiReference findReferenceAt(final int offset) {
final PsiFileImpl psiFile = (PsiFileImpl) getPsi(getBaseLanguage());
return findReferenceAt(psiFile, offset);
}
@Override
public PsiElement findElementAt(final int offset, final Language language) {
final PsiFile psiFile = getPsi(language);
return psiFile != null ? findElementAt(psiFile, offset) : null;
}
@Override
@Nullable
public PsiReference findReferenceAt(final int offset, @NotNull final Language language) {
final PsiFile psiFile = getPsi(language);
return psiFile != null ? findReferenceAt(psiFile, offset) : null;
}
@Nullable
private static PsiReference findReferenceAt(final PsiFile psiFile, final int offset) {
if (psiFile == null) return null;
int offsetInElement = offset;
PsiElement child = psiFile.getFirstChild();
while (child != null) {
final int length = child.getTextLength();
if (length <= offsetInElement) {
offsetInElement -= length;
child = child.getNextSibling();
continue;
}
return child.findReferenceAt(offsetInElement);
}
return null;
}
@Override
public PsiElement findElementAt(final int offset) {
return findElementAt(getPsi(getBaseLanguage()), offset);
}
@Override
public PsiElement findElementAt(int offset, Class<? extends Language> lang) {
if (!ReflectionCache.isAssignable(lang, getBaseLanguage().getClass())) return null;
return findElementAt(offset);
}
@Nullable
protected static PsiElement findElementAt(final PsiElement psiFile, final int offset) {
if (psiFile == null) return null;
int offsetInElement = offset;
PsiElement child = psiFile.getFirstChild();
while (child != null) {
final int length = child.getTextLength();
if (length <= offsetInElement) {
offsetInElement -= length;
child = child.getNextSibling();
continue;
}
return child.findElementAt(offsetInElement);
}
return null;
}
public void forceCachedPsi(final PsiFile psiFile) {
myPsiFile.set(psiFile);
((PsiManagerEx) myManager).getFileManager().setViewProvider(getVirtualFile(), this);
}
private Content getContent() {
return myContent;
}
private void setContent(final Content content) {
myContent = content;
}
private interface Content {
CharSequence getText();
long getModificationStamp();
}
private class VirtualFileContent implements Content {
@Override
public CharSequence getText() {
final VirtualFile virtualFile = getVirtualFile();
if (virtualFile instanceof LightVirtualFile) {
Document doc = getCachedDocument();
if (doc != null) return doc.getCharsSequence();
return ((LightVirtualFile) virtualFile).getContent();
}
final Document document = getDocument();
if (document == null) {
return LoadTextUtil.loadText(virtualFile);
} else {
return document.getCharsSequence();
}
}
@Override
public long getModificationStamp() {
return getVirtualFile().getModificationStamp();
}
}
private class DocumentContent implements Content {
@Override
public CharSequence getText() {
final Document document = getDocument();
assert document != null;
return document.getCharsSequence();
}
@Override
public long getModificationStamp() {
Document document = myDocument == null ? null : myDocument.get();
if (document != null) return document.getModificationStamp();
return myVirtualFile.getModificationStamp();
}
}
private class PsiFileContent implements Content {
private final PsiFileImpl myFile;
private CharSequence myContent = null;
private final long myModificationStamp;
private PsiFileContent(final PsiFileImpl file, final long modificationStamp) {
myFile = file;
myModificationStamp = modificationStamp;
}
@Override
public CharSequence getText() {
if (!myFile.isContentsLoaded()) {
unsetPsiContent();
return getContents();
}
if (myContent != null) return myContent;
return myContent = ApplicationManager.getApplication().runReadAction(new Computable<CharSequence>() {
public CharSequence compute() {
return myFile.calcTreeElement().getText();
}
});
}
@Override
public long getModificationStamp() {
if (!myFile.isContentsLoaded()) {
unsetPsiContent();
return SingleRootFileViewProvider.this.getModificationStamp();
}
return myModificationStamp;
}
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2000-2009 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.text;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.text.DiffLog;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public abstract class BlockSupport {
public static BlockSupport getInstance(Project project) {
return ServiceManager.getService(project, BlockSupport.class);
}
public abstract void reparseRange(PsiFile file, int startOffset, int endOffset, @NonNls CharSequence newText) throws IncorrectOperationException;
@NotNull
public abstract DiffLog reparseRange(@NotNull PsiFile file,
int startOffset,
int endOffset,
int lengthShift,
@NotNull CharSequence newText,
@NotNull ProgressIndicator progressIndicator) throws IncorrectOperationException;
public static final Key<Boolean> DO_NOT_REPARSE_INCREMENTALLY = Key.create("DO_NOT_REPARSE_INCREMENTALLY");
public static final Key<ASTNode> TREE_TO_BE_REPARSED = Key.create("TREE_TO_BE_REPARSED");
public static class ReparsedSuccessfullyException extends RuntimeException {
private final DiffLog myDiffLog;
public ReparsedSuccessfullyException(@NotNull DiffLog diffLog) {
myDiffLog = diffLog;
}
@NotNull
public DiffLog getDiffLog() {
return myDiffLog;
}
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
// maximal tree depth for which incremental reparse is allowed
// if tree is deeper then it will be replaced completely - to avoid SOEs
// public static final int INCREMENTAL_REPARSE_DEPTH_LIMIT = Registry.intValue("psi.incremental.reparse.depth.limit", 1000);
public static final int INCREMENTAL_REPARSE_DEPTH_LIMIT = 1000;
public static final Key<Boolean> TREE_DEPTH_LIMIT_EXCEEDED = Key.create("TREE_IS_TOO_DEEP");
public static boolean isTooDeep(final UserDataHolder element) {
return element != null && Boolean.TRUE.equals(element.getUserData(TREE_DEPTH_LIMIT_EXCEEDED));
}
}
@@ -0,0 +1,288 @@
/*
* Copyright 2000-2009 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;
import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
public class ReflectionUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.ReflectionUtil");
private ReflectionUtil() {
}
@Nullable
public static Type resolveVariable(TypeVariable variable, final Class classType) {
return resolveVariable(variable, classType, true);
}
@Nullable
public static Type resolveVariable(TypeVariable variable, final Class classType, boolean resolveInInterfacesOnly) {
final Class aClass = getRawType(classType);
int index = ArrayUtil.find(ReflectionCache.getTypeParameters(aClass), variable);
if (index >= 0) {
return variable;
}
final Class[] classes = ReflectionCache.getInterfaces(aClass);
final Type[] genericInterfaces = ReflectionCache.getGenericInterfaces(aClass);
for (int i = 0; i <= classes.length; i++) {
Class anInterface;
if (i < classes.length) {
anInterface = classes[i];
} else {
anInterface = ReflectionCache.getSuperClass(aClass);
if (resolveInInterfacesOnly || anInterface == null) {
continue;
}
}
final Type resolved = resolveVariable(variable, anInterface);
if (resolved instanceof Class || resolved instanceof ParameterizedType) {
return resolved;
}
if (resolved instanceof TypeVariable) {
final TypeVariable typeVariable = (TypeVariable) resolved;
index = ArrayUtil.find(ReflectionCache.getTypeParameters(anInterface), typeVariable);
if (index < 0) {
LOG.error("Cannot resolve type variable:\n" + "typeVariable = " + typeVariable + "\n" + "genericDeclaration = " +
declarationToString(typeVariable.getGenericDeclaration()) + "\n" + "searching in " + declarationToString(anInterface));
}
final Type type = i < genericInterfaces.length ? genericInterfaces[i] : aClass.getGenericSuperclass();
if (type instanceof Class) {
return Object.class;
}
if (type instanceof ParameterizedType) {
return getActualTypeArguments(((ParameterizedType) type))[index];
}
throw new AssertionError("Invalid type: " + type);
}
}
return null;
}
public static String declarationToString(final GenericDeclaration anInterface) {
return anInterface.toString()
+ Arrays.asList(anInterface.getTypeParameters())
+ " loaded by " + ((Class) anInterface).getClassLoader();
}
public static Class<?> getRawType(Type type) {
if (type instanceof Class) {
return (Class) type;
}
if (type instanceof ParameterizedType) {
return getRawType(((ParameterizedType) type).getRawType());
}
if (type instanceof GenericArrayType) {
//todo[peter] don't create new instance each time
return Array.newInstance(getRawType(((GenericArrayType) type).getGenericComponentType()), 0).getClass();
}
assert false : type;
return null;
}
public static Type[] getActualTypeArguments(final ParameterizedType parameterizedType) {
return ReflectionCache.getActualTypeArguments(parameterizedType);
}
@Nullable
public static Class<?> substituteGenericType(final Type genericType, final Type classType) {
if (genericType instanceof TypeVariable) {
final Class<?> aClass = getRawType(classType);
final Type type = resolveVariable((TypeVariable) genericType, aClass);
if (type instanceof Class) {
return (Class) type;
}
if (type instanceof ParameterizedType) {
return (Class<?>) ((ParameterizedType) type).getRawType();
}
if (type instanceof TypeVariable && classType instanceof ParameterizedType) {
final int index = ArrayUtil.find(ReflectionCache.getTypeParameters(aClass), type);
if (index >= 0) {
return getRawType(getActualTypeArguments(((ParameterizedType) classType))[index]);
}
}
} else {
return getRawType(genericType);
}
return null;
}
public static ArrayList<Field> collectFields(Class clazz) {
ArrayList<Field> result = new ArrayList<Field>();
collectFields(clazz, result);
return result;
}
public static Field findField(Class clazz, @Nullable Class type, String name) throws NoSuchFieldException {
final ArrayList<Field> fields = collectFields(clazz);
for (Field each : fields) {
if (name.equals(each.getName()) && (type == null || each.getType().equals(type))) return each;
}
throw new NoSuchFieldException("Class: " + clazz + " name: " + name + " type: " + type);
}
public static Field findAssignableField(Class clazz, Class type, String name) throws NoSuchFieldException {
final ArrayList<Field> fields = collectFields(clazz);
for (Field each : fields) {
if (name.equals(each.getName()) && type.isAssignableFrom(each.getType())) return each;
}
throw new NoSuchFieldException("Class: " + clazz + " name: " + name + " type: " + type);
}
private static void collectFields(final Class clazz, final ArrayList<Field> result) {
final Field[] fields = clazz.getDeclaredFields();
result.addAll(Arrays.asList(fields));
final Class superClass = clazz.getSuperclass();
if (superClass != null) {
collectFields(superClass, result);
}
final Class[] interfaces = clazz.getInterfaces();
for (Class each : interfaces) {
collectFields(each, result);
}
}
public static void resetField(Class clazz, Class type, String name) {
try {
resetField(null, findField(clazz, type, name));
} catch (NoSuchFieldException e) {
LOG.info(e);
}
}
public static void resetField(Object object, Class type, String name) {
try {
resetField(object, findField(object.getClass(), type, name));
} catch (NoSuchFieldException e) {
LOG.info(e);
}
}
public static void resetField(Object object, String name) {
try {
resetField(object, findField(object.getClass(), null, name));
} catch (NoSuchFieldException e) {
LOG.info(e);
}
}
public static void resetField(@Nullable final Object object, final Field field) {
// field.setAccessible(true);
Class<?> type = field.getType();
try {
if (type.isPrimitive()) {
if (boolean.class.equals(type)) {
field.set(object, Boolean.FALSE);
} else if (int.class.equals(type)) {
field.set(object, new Integer(0));
} else if (double.class.equals(type)) {
field.set(object, new Double(0));
} else if (float.class.equals(type)) {
field.set(object, new Float(0));
}
} else {
field.set(object, null);
}
} catch (IllegalAccessException e) {
LOG.info(e);
}
}
@Nullable
public static Method findMethod(Method[] methods, @NonNls @NotNull String name, Class... parameters) {
for (final Method method : methods) {
if (name.equals(method.getName()) && Arrays.equals(parameters, method.getParameterTypes())) return method;
}
return null;
}
@Nullable
public static Method getMethod(@NotNull Class aClass, @NonNls @NotNull String name, Class... parameters) {
return findMethod(ReflectionCache.getMethods(aClass), name, parameters);
}
@Nullable
public static Method getDeclaredMethod(@NotNull Class aClass, @NonNls @NotNull String name, Class... parameters) {
return findMethod(aClass.getDeclaredMethods(), name, parameters);
}
public static Object getField(Class objectClass, Object object, Class type, @NonNls String name) {
try {
final Field field = findAssignableField(objectClass, type, name);
// field.setAccessible(true);
return field.get(object);
} catch (NoSuchFieldException e) {
LOG.debug(e);
return null;
} catch (IllegalAccessException e) {
LOG.debug(e);
return null;
}
}
public static Type resolveVariableInHierarchy(final TypeVariable variable, final Class aClass) {
Type type;
Class current = aClass;
while ((type = resolveVariable(variable, current, false)) == null) {
current = ReflectionCache.getSuperClass(current);
if (current == null) {
return null;
}
}
if (type instanceof TypeVariable) {
return resolveVariableInHierarchy((TypeVariable) type, aClass);
}
return type;
}
@NotNull
public static <T> Constructor<T> getDefaultConstructor(final Class<T> aClass) {
try {
final Constructor<T> constructor = aClass.getConstructor();
// constructor.setAccessible(true);
return constructor;
} catch (NoSuchMethodException e) {
LOG.error("No default constructor in " + aClass, e);
return null;
}
}
@NotNull
public static <T> T createInstance(final Constructor<T> constructor, final Object... args) {
try {
return constructor.newInstance(args);
} catch (InstantiationException e) {
LOG.error(e);
return null;
} catch (IllegalAccessException e) {
LOG.error(e);
return null;
} catch (InvocationTargetException e) {
LOG.error(e);
return null;
}
}
}
+2 -2
View File
@@ -5,9 +5,9 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="1.7" jdkType="JavaSDK" />
<orderEntry type="jdk" jdkName="1.6" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="lib" level="project" />
<orderEntry type="library" name="libs" level="project" />
</component>
</module>
@@ -11,6 +11,7 @@ import com.intellij.psi.impl.PsiFileFactoryImpl;
import com.intellij.testFramework.LightVirtualFile;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetCoreEnvironment;
import org.jetbrains.jet.compiler.CompileEnvironment;
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory;
@@ -20,56 +21,74 @@ import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.plugin.JetLanguage;
import org.jetbrains.k2js.generate.CodeGenerator;
import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.utils.JetTestUtils;
import org.jetbrains.k2js.utils.GenerationUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getNamespaceDescriptor;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.nameForNamespace;
import static org.jetbrains.k2js.utils.JetTestUtils.analyzeNamespace;
/**
* @author Talanov Pavel
*/
public final class K2JSTranslator {
private final JetCoreEnvironment myEnvironment = new JetCoreEnvironment(new Disposable() {
@NotNull
private final JetCoreEnvironment environment = new JetCoreEnvironment(new Disposable() {
@Override
public void dispose() {
}
});
@Nullable
private BindingContext bindingContext = null;
public K2JSTranslator() {
}
public void translateFile(@NotNull String inputFile, @NotNull String outputFile) throws Exception {
JetFile PsiFile = loadPsiFile(inputFile);
includeRtJar();
JsProgram program = generateProgram(PsiFile);
CodeGenerator generator = new CodeGenerator();
generator.generateToFile(program, new File(outputFile));
}
@NotNull
public String translateString(@NotNull String programText) {
JetFile PsiFile = createPsiFile("test", programText);
JsProgram program = generateProgram(PsiFile);
public String translateStringWithCallToMain(@NotNull String programText, @NotNull String argumentsString) {
JetFile file = createPsiFile("test", programText);
String programCode = generateProgramCode(file);
String callToMain = generateCallToMain(file, argumentsString);
return programCode + callToMain;
}
private String generateProgramCode(JetFile psiFile) {
JsProgram program = generateProgram(psiFile);
CodeGenerator generator = new CodeGenerator();
return generator.generateToString(program);
}
@NotNull
private JsProgram generateProgram(@NotNull JetFile psiFile) {
final File rtJar = CompileEnvironment.findRtJar(true);
myEnvironment.addToClasspath(rtJar);
JetNamespace namespace = psiFile.getRootNamespace();
BindingContext bindingContext = JetTestUtils.analyzeNamespace(namespace,
bindingContext = analyzeNamespace(namespace,
JetControlFlowDataTraceFactory.EMPTY);
assert bindingContext != null;
return Translation.generateAst(bindingContext, namespace, myEnvironment.getProject());
return Translation.generateAst(bindingContext, namespace, environment.getProject());
}
private void includeRtJar() {
final File rtJar = CompileEnvironment.findRtJar(true);
environment.addToClasspath(rtJar);
}
@NotNull
@@ -102,7 +121,34 @@ public final class K2JSTranslator {
protected PsiFile createFile(@NonNls String name, String text) {
LightVirtualFile virtualFile = new LightVirtualFile(name, JetLanguage.INSTANCE, text);
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
return ((PsiFileFactoryImpl) PsiFileFactory.getInstance(myEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false);
return ((PsiFileFactoryImpl) PsiFileFactory.getInstance(environment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false);
}
@NotNull
public String generateCallToMain(@NotNull JetFile file, String argumentString) {
String namespaceName = getRootNamespaceName(file);
List<String> arguments = parseString(argumentString);
return GenerationUtils.generateCallToMain(namespaceName, arguments);
}
@NotNull
private List<String> parseString(@NotNull String argumentString) {
List<String> result = new ArrayList<String>();
StringTokenizer stringTokenizer = new StringTokenizer(argumentString);
while (stringTokenizer.hasMoreTokens()) {
result.add(stringTokenizer.nextToken());
}
return result;
}
//TODO: make "anonymous" a constant
@NotNull
private String getRootNamespaceName(@NotNull JetFile psiFile) {
JetNamespace namespace = psiFile.getRootNamespace();
assert bindingContext != null;
return nameForNamespace(getNamespaceDescriptor(bindingContext, namespace));
}
}
@@ -10,10 +10,11 @@ import java.applet.Applet;
public final class K2JSTranslatorApplet extends Applet {
@NotNull
public String translate(@NotNull String code) {
String generatedCode = (new K2JSTranslator()).translateString(code);
public String translate(@NotNull String code, @NotNull String arguments) {
String generatedCode = (new K2JSTranslator()).translateStringWithCallToMain(code, arguments);
System.out.println("GENERATED JAVASCRIPT CODE:\n-----------------------------------\n");
System.out.println(generatedCode);
return generatedCode;
}
}
@@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getOwnDeclarations;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.nameForNamespace;
/**
* @author Talanov Pavel
@@ -123,15 +124,6 @@ public final class DeclarationVisitor extends DeclarationDescriptorVisitor<Void,
return context.innerDeclaration(namespaceScope, namespaceName);
}
@NotNull
private String nameForNamespace(@NotNull NamespaceDescriptor descriptor) {
String name = descriptor.getName();
if (name.equals("")) {
return "Anonymous";
}
return name;
}
private void declareMembers(@NotNull NamespaceDescriptor descriptor, @NotNull DeclarationContext context) {
for (DeclarationDescriptor memberDescriptor :
descriptor.getMemberScope().getAllDescriptors()) {
@@ -121,4 +121,13 @@ public final class DescriptorUtils {
public static boolean isExtensionFunction(@NotNull FunctionDescriptor functionDescriptor) {
return (functionDescriptor.getReceiverParameter().exists());
}
@NotNull
public static String nameForNamespace(@NotNull NamespaceDescriptor descriptor) {
String name = descriptor.getName();
if (name.equals("")) {
return "Anonymous";
}
return name;
}
}
@@ -0,0 +1,25 @@
package org.jetbrains.k2js.utils;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* @author Talanov Pavel
*/
public final class GenerationUtils {
@NotNull
public static String generateCallToMain(@NotNull String namespaceName, @NotNull List<String> arguments) {
String constructArguments = "var args = Kotlin.array(" + arguments.size() + ");\n";
int index = 0;
for (String argument : arguments) {
constructArguments = constructArguments + "args.set(" + index + ", \"" + argument + "\");\n";
index++;
}
String callMain = namespaceName + ".main(args);\n";
return constructArguments + callMain;
}
}
@@ -17,6 +17,6 @@ public final class AppletTest extends TranslationTest {
@Test
public void simpleTest() throws Exception {
System.out.println((new K2JSTranslatorApplet()).translate("fun main(args : Array<String>) {}"));
(new K2JSTranslatorApplet()).translate("fun main(args : Array<String>) {}", " a 3 1 2134");
}
}
@@ -1,6 +1,7 @@
package org.jetbrains.k2js.test;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.k2js.utils.GenerationUtils;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
@@ -36,18 +37,7 @@ public final class RhinoSystemOutputChecker implements RhinoResultChecker {
}
private void runMain(Context context, Scriptable scope) {
context.evaluateString(scope, execMain(), "function call", 0, null);
}
@NotNull
private String execMain() {
String constructArguments = "var args = Kotlin.array(" + arguments.size() + ");\n";
int index = 0;
for (String argument : arguments) {
constructArguments = constructArguments + "args.set(" + index + ", \"" + argument + "\");\n";
index++;
}
String callMain = "Anonymous.main(args);\n";
return constructArguments + callMain;
String callToMain = GenerationUtils.generateCallToMain("Anonymous", arguments);
context.evaluateString(scope, callToMain, "function call", 0, null);
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ function $A(iterable) {
extend(Object, {
extend:extend,
keys:Object.keys || keys,
values:values,
values:values
});
})();