Merge remote branch 'origin/master'

This commit is contained in:
Dmitry Jemerov
2012-01-26 16:19:08 +01:00
149 changed files with 4907 additions and 600 deletions
@@ -16,6 +16,7 @@
package org.intellij.images.util;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.io.UnsyncByteArrayInputStream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -65,7 +66,7 @@ public class ImageInfoReader {
@Nullable
private static Info read(@NotNull final byte[] data) {
final DataInputStream is = new DataInputStream(new ByteArrayInputStream(data));
final DataInputStream is = new DataInputStream(new UnsyncByteArrayInputStream(data));
try {
return readFileData(is);
}
@@ -61,9 +61,16 @@ public class RequestHint {
private boolean myMethodExecuted;
public SmartStepFilter(PsiMethod psiMethod) {
myDeclaringClassName = JVMNameUtil.getJVMQualifiedName(psiMethod.getContainingClass());
myTargetMethodName = psiMethod.isConstructor()? "<init>" : psiMethod.getName();
myTargetMethodSignature = JVMNameUtil.getJVMSignature(psiMethod);
this(JVMNameUtil.getJVMQualifiedName(psiMethod.getContainingClass()),
psiMethod.isConstructor()? "<init>" : psiMethod.getName(),
JVMNameUtil.getJVMSignature(psiMethod));
}
public SmartStepFilter(@NotNull JVMName declaringClassName, @NonNls String targetMethodName,
@NotNull JVMName targetMethodSignature) {
myDeclaringClassName = declaringClassName;
myTargetMethodName = targetMethodName;
myTargetMethodSignature = targetMethodSignature;
}
public String getTargetMethodName() {
@@ -27,6 +27,7 @@ import com.intellij.psi.tree.TokenSet;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import static com.intellij.lang.PsiBuilderUtil.expect;
import static com.intellij.lang.PsiBuilderUtil.nextTokenType;
@@ -86,7 +87,7 @@ public class DeclarationParser {
}
parseClassBodyDeclarations(builderWrapper, isAnnotation);
expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace"));
expectOrError(builder, JavaTokenType.RBRACE, "expected.rbrace");
}
@Nullable
@@ -139,7 +140,7 @@ public class DeclarationParser {
}
if (declarationsAfterEnd) {
expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace"));
expectOrError(builder, JavaTokenType.RBRACE, "expected.rbrace");
}
}
@@ -426,7 +427,7 @@ public class DeclarationParser {
final boolean anno, final boolean constructor) {
parseParameterList(builder);
eatBrackets(builder, constructor, JavaErrorMessages.message("expected.semicolon"));
eatBrackets(builder, constructor, "expected.semicolon");
if (areTypeAnnotationsSupported(builder)) {
final PsiBuilder.Marker receiver = builder.mark();
@@ -489,8 +490,8 @@ public class DeclarationParser {
builder.advanceLexer();
final IElementType delimiter = resources ? JavaTokenType.SEMICOLON : JavaTokenType.COMMA;
final String noDelimiterMsg = JavaErrorMessages.message(resources ? "expected.semicolon" : "expected.comma");
final String noElementMsg = JavaErrorMessages.message(resources ? "expected.resource" : "expected.parameter");
final String noDelimiterMsg = resources ? "expected.semicolon" : "expected.comma";
final String noElementMsg = resources ? "expected.resource" : "expected.parameter";
PsiBuilder.Marker invalidElements = null;
String errorMessage = null;
@@ -548,7 +549,7 @@ public class DeclarationParser {
if (invalidElements == null) {
if (builder.getTokenType() == delimiter) {
error(builder, noElementMsg);
error(builder, JavaErrorMessages.message(noElementMsg));
builder.advanceLexer();
if (noElements && resources) {
noElements = false;
@@ -557,7 +558,7 @@ public class DeclarationParser {
}
else {
invalidElements = builder.mark();
errorMessage = delimiterExpected ? noDelimiterMsg : noElementMsg;
errorMessage = JavaErrorMessages.message(delimiterExpected ? noDelimiterMsg : noElementMsg);
}
}
@@ -613,7 +614,7 @@ public class DeclarationParser {
if (expect(builder, JavaTokenType.IDENTIFIER)) {
if (!resource) {
eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, JavaErrorMessages.message("expected.rparen"));
eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, "expected.rparen");
done(param, JavaElementType.PARAMETER);
return param;
}
@@ -624,7 +625,7 @@ public class DeclarationParser {
return modListInfo.first;
}
if (expectOrError(builder, JavaTokenType.EQ, JavaErrorMessages.message("expected.eq"))) {
if (expectOrError(builder, JavaTokenType.EQ, "expected.eq")) {
if (myExpressionParser.parse(builder) == null) {
error(builder, JavaErrorMessages.message("expected.expression"));
}
@@ -720,7 +721,8 @@ public class DeclarationParser {
return declaration;
}
private static boolean eatBrackets(final PsiBuilder builder, final boolean isError, @Nullable final String error) {
private static boolean eatBrackets(final PsiBuilder builder, final boolean isError,
@Nullable @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String errorKey) {
if (builder.getTokenType() != JavaTokenType.LBRACKET) return true;
final PsiBuilder.Marker marker = isError ? builder.mark() : null;
@@ -735,7 +737,7 @@ public class DeclarationParser {
}
if (marker != null) {
marker.error(error);
marker.error(errorKey != null ? JavaErrorMessages.message(errorKey):null);
}
return result;
@@ -840,9 +842,9 @@ public class DeclarationParser {
pair = builder.mark();
}
final boolean hasName = expectOrError(builder, JavaTokenType.IDENTIFIER, JavaErrorMessages.message("expected.identifier"));
final boolean hasName = expectOrError(builder, JavaTokenType.IDENTIFIER, "expected.identifier");
expectOrError(builder, JavaTokenType.EQ, JavaErrorMessages.message("expected.eq"));
expectOrError(builder, JavaTokenType.EQ, "expected.eq");
parseAnnotationValue(builder);
@@ -404,7 +404,7 @@ public class ExpressionParser {
final PsiBuilder.Marker refExpr = expr.precede();
myReferenceParser.parseReferenceParameterList(builder, false, false);
if (!JavaParserUtil.expectOrError(builder, JavaTokenType.IDENTIFIER, JavaErrorMessages.message("expected.identifier"))) {
if (!JavaParserUtil.expectOrError(builder, JavaTokenType.IDENTIFIER, "expected.identifier")) {
refExpr.done(JavaElementType.REFERENCE_EXPRESSION);
startMarker.drop();
return refExpr;
@@ -726,7 +726,7 @@ public class ExpressionParser {
}
bracketCount++;
if (!JavaParserUtil.expectOrError(builder, JavaTokenType.RBRACKET, JavaErrorMessages.message("expected.rbracket"))) {
if (!JavaParserUtil.expectOrError(builder, JavaTokenType.RBRACKET, "expected.rbracket")) {
newExpr.done(JavaElementType.NEW_EXPRESSION);
return newExpr;
}
@@ -812,7 +812,7 @@ public class ExpressionParser {
}
}
final boolean closed = JavaParserUtil.expectOrError(builder, JavaTokenType.RPARENTH, JavaErrorMessages.message("expected.rparen"));
final boolean closed = JavaParserUtil.expectOrError(builder, JavaTokenType.RPARENTH, "expected.rparen");
list.done(JavaElementType.EXPRESSION_LIST);
if (!closed) {
@@ -39,6 +39,7 @@ import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import java.util.List;
@@ -258,9 +259,10 @@ public class JavaParserUtil {
}
}
public static boolean expectOrError(final PsiBuilder builder, final IElementType expectedType, final String errorMessage) {
public static boolean expectOrError(final PsiBuilder builder, final IElementType expectedType,
@PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String errorMessageKey) {
if (!PsiBuilderUtil.expect(builder, expectedType)) {
error(builder, errorMessage);
error(builder, JavaErrorMessages.message(errorMessageKey));
return false;
}
return true;
@@ -275,7 +277,7 @@ public class JavaParserUtil {
}
public static void semicolon(final PsiBuilder builder) {
expectOrError(builder, JavaTokenType.SEMICOLON, JavaErrorMessages.message("expected.semicolon"));
expectOrError(builder, JavaTokenType.SEMICOLON, "expected.semicolon");
}
public static PsiBuilder braceMatchingBuilder(final PsiBuilder builder) {
@@ -293,7 +293,7 @@ public class ReferenceParser {
if (expect(builder, JavaTokenType.GT)) {
break;
}
else if (!expectOrError(builder, JavaTokenType.COMMA, JavaErrorMessages.message("expected.gt.or.comma"))) {
else if (!expectOrError(builder, JavaTokenType.COMMA, "expected.gt.or.comma")) {
isOk = false;
break;
}
@@ -131,7 +131,7 @@ public class StatementParser {
parseStatements(builder, parseUntilEof ? BraceMode.TILL_LAST : BraceMode.TILL_FIRST);
final boolean greedyBlock = !expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace"));
final boolean greedyBlock = !expectOrError(builder, JavaTokenType.RBRACE, "expected.rbrace");
builder.getTokenType(); // eat spaces
done(codeBlock, JavaElementType.CODE_BLOCK);
@@ -543,7 +543,7 @@ public class StatementParser {
}
}
expectOrError(builder, JavaTokenType.COLON, JavaErrorMessages.message("expected.colon"));
expectOrError(builder, JavaTokenType.COLON, "expected.colon");
done(statement, JavaElementType.SWITCH_LABEL_STATEMENT);
return statement;
@@ -328,6 +328,7 @@ expected.lparen.or.lbracket='(' or '[' expected
expected.array.initializer=Array initializer expected
unexpected.tokens=Unexpected tokens
expected.gt.or.comma='>' or ',' expected.
string.expected=String literal expected
else.without.if='else' without 'if'
catch.without.try='catch' without 'try'
finally.without.try='finally' without 'try'
@@ -1,11 +1,13 @@
package org.jetbrains.jps.incremental;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.ether.dependencyView.Mappings;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.incremental.storage.SourceToOutputMapping;
import sun.util.LocaleServiceProviderPool;
import java.io.File;
import java.io.IOException;
@@ -16,6 +18,8 @@ import java.util.*;
* Date: 9/17/11
*/
public abstract class Builder {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.Builder");
private static final Key<Set<File>> ALL_AFFECTED_FILES_KEY = Key.create("_all_affected_files_");
private static final Key<Set<File>> ALL_COMPILED_FILES_KEY = Key.create("_all_compiled_files_");
@@ -70,6 +74,24 @@ public abstract class Builder {
delta, removedPaths, successfullyCompiled, allCompiledFiles, allAffectedFiles
);
if (LOG.isDebugEnabled()) {
LOG.debug("Differentiate Results:");
LOG.debug(" Compiled Files:");
for (final File c : allCompiledFiles) {
LOG.debug(" " + c.getAbsolutePath());
}
LOG.debug(" Affected Files:");
for (final File c : allAffectedFiles) {
LOG.debug(" " + c.getAbsolutePath());
}
LOG.debug("End Of Differentiate Results.");
}
if (incremental) {
final Set<File> newlyAffectedFiles = new HashSet<File>(allAffectedFiles);
newlyAffectedFiles.removeAll(affectedBeforeDif);
@@ -172,9 +172,9 @@ class DependencyContext {
public Logger<S> getLogger(final com.intellij.openapi.diagnostic.Logger log) {
return new Logger<S>() {
@Override
public void debug(S s) {
public void debug(String comment, S s) {
if (log.isDebugEnabled()) {
log.debug(getValue(s));
log.debug(comment + getValue(s));
}
}
};
@@ -23,5 +23,5 @@ package org.jetbrains.ether.dependencyView;
* To change this template use File | Settings | File Templates.
*/
public interface Logger<T> {
void debug(T t);
void debug(String comment, T t);
}
@@ -42,8 +42,8 @@ public class Mappings {
LOG.debug(s);
}
private void debug(final DependencyContext.S s) {
myDebugS.debug(s);
private void debug(final String comment, final DependencyContext.S s) {
myDebugS.debug(comment, s);
}
private MultiMaplet<DependencyContext.S, DependencyContext.S> myClassToSubclasses;
@@ -534,16 +534,25 @@ public class Mappings {
final Collection<UsageRepr.Usage> affectedUsages,
final Collection<DependencyContext.S> dependants,
final boolean usages) {
debug("Affecting subclasses of class: ", className);
final DependencyContext.S fileName = myClassToSourceFile.get(className);
if (fileName == null) {
debug("No source file detected for class ", className);
debug("End of affectSubclasses");
return;
}
debug("Source file name: ", fileName);
if (usages) {
debug("Class usages affection requested");
final ClassRepr classRepr = reprByName(className);
if (classRepr != null) {
debug("Added class usage for ", classRepr.name);
affectedUsages.add(classRepr.createUsage());
}
}
@@ -730,32 +739,38 @@ public class Mappings {
// Public branch --- hopeless
if ((member.access & Opcodes.ACC_PUBLIC) > 0) {
debug("Switched to non-incremental mode");
debug("Public access, switching to a non-incremental mode");
return false;
}
// Protected branch
if ((member.access & Opcodes.ACC_PROTECTED) > 0) {
debug("Softening non-incremental decision: adding all relevant subclasses for a recompilation");
debug("Protected access, softening non-incremental decision: adding all relevant subclasses for a recompilation");
debug("Root class: ", owner);
final Collection<DependencyContext.S> propagated = self.propagateFieldAccess(isField ? member.name : myContext.get(""), owner);
for (DependencyContext.S className : propagated) {
affectedFiles.add(new File(myContext.getValue(myClassToSourceFile.get(className))));
final String fileName = myContext.getValue(myClassToSourceFile.get(className));
debug("Adding " + fileName);
affectedFiles.add(new File(fileName));
}
}
debug("Softening non-incremental decision: adding all package classes for a recompilation");
final String packageName = ClassRepr.getPackageName(myContext.getValue(isField ? owner : member.name));
debug("Softening non-incremental decision: adding all package classes for a recompilation");
debug("Package name: " + packageName);
// Package-local branch
for (Map.Entry<DependencyContext.S, DependencyContext.S> e : myClassToSourceFile.entrySet()) {
final DependencyContext.S className = e.getKey();
final DependencyContext.S fileName = e.getValue();
if (ClassRepr.getPackageName(myContext.getValue(className)).equals(packageName)) {
affectedFiles.add(new File(myContext.getValue(fileName)));
final String f = myContext.getValue(fileName);
debug("Adding: " + f);
affectedFiles.add(new File(f));
}
}
@@ -767,6 +782,8 @@ public class Mappings {
final Collection<File> filesToCompile,
final Collection<File> compiledFiles,
final Collection<File> affectedFiles) {
debug("Begin of Differentiate:");
delta.compensateRemovedContent(filesToCompile);
final Util u = new Util(delta);
@@ -798,14 +815,12 @@ public class Mappings {
final Difference.Specifier<ClassRepr> classDiff = Difference.make(pastClasses, classes);
debug("Processing changed classes");
debug("Processing changed classes:");
for (Pair<ClassRepr, Difference> changed : classDiff.changed()) {
final ClassRepr it = changed.first;
final ClassRepr.Diff diff = (ClassRepr.Diff)changed.second;
debug("Changed: ");
debug(it.name);
debug("Changed: ", it.name);
final int addedModifiers = diff.addedModifiers();
final int removedModifiers = diff.removedModifiers();
@@ -815,24 +830,34 @@ public class Mappings {
final boolean signatureChanged = (diff.base() & Difference.SIGNATURE) > 0;
if (superClassChanged || interfacesChanged || signatureChanged) {
debug("Superclass changed: " + superClassChanged);
debug("Interfaces changed: " + interfacesChanged);
debug("Signature changed " + signatureChanged);
final boolean extendsChanged = superClassChanged && !diff.extendsAdded();
final boolean interfacesRemoved = interfacesChanged && !diff.interfaces().removed().isEmpty();
debug("Extends changed: " + extendsChanged);
debug("Interfaces removed: " + interfacesRemoved);
u.affectSubclasses(it.name, affectedFiles, affectedUsages, dependants, extendsChanged || interfacesRemoved || signatureChanged);
}
if ((diff.addedModifiers() & Opcodes.ACC_INTERFACE) > 0 || (diff.removedModifiers() & Opcodes.ACC_INTERFACE) > 0) {
debug("Class-to-interface or interface-to-class conversion detected, added class usage to affected usages");
affectedUsages.add(it.createUsage());
}
if (it.isAnnotation() && it.policy == RetentionPolicy.SOURCE) {
debug("Annotation, retention policy = SOURCE => a switch to non-incremental mode requested");
if (!incrementalDecision(it.outerClassName, it, affectedFiles)) {
debug("End of Differentiate, returning false");
return false;
}
}
if ((addedModifiers & Opcodes.ACC_PROTECTED) > 0) {
debug("Introduction of 'protected' modifier detected, adding class usage + inheritance constraint to affected usages");
final UsageRepr.Usage usage = it.createUsage();
affectedUsages.add(usage);
@@ -840,6 +865,7 @@ public class Mappings {
}
if (diff.packageLocalOn()) {
debug("Introduction of 'package local' access detected, adding class usage + package constraint to affected usages");
final UsageRepr.Usage usage = it.createUsage();
affectedUsages.add(usage);
@@ -847,46 +873,52 @@ public class Mappings {
}
if ((addedModifiers & Opcodes.ACC_FINAL) > 0 || (addedModifiers & Opcodes.ACC_PRIVATE) > 0) {
debug("Introduction of 'private' or 'final' modifier(s) detected, adding class usage to affected usages");
affectedUsages.add(it.createUsage());
}
if ((addedModifiers & Opcodes.ACC_ABSTRACT) > 0) {
affectedUsages.add(UsageRepr.createClassNewUsage(myContext, it.name));
}
if ((addedModifiers & Opcodes.ACC_STATIC) > 0 ||
(removedModifiers & Opcodes.ACC_STATIC) > 0 ||
(addedModifiers & Opcodes.ACC_ABSTRACT) > 0) {
if ((addedModifiers & Opcodes.ACC_ABSTRACT) > 0 || (addedModifiers & Opcodes.ACC_STATIC) > 0) {
debug("Introduction of 'abstract' or 'static' modifier(s) detected, adding class new usage to affected usages");
affectedUsages.add(UsageRepr.createClassNewUsage(myContext, it.name));
}
if (it.isAnnotation()) {
debug("Class is annotation, performing annotation-specific analysis");
if (diff.retentionChanged()) {
debug("Retention policy change detected, adding class usage to affected usages");
affectedUsages.add(it.createUsage());
}
else {
final Collection<ElementType> removedtargets = diff.targets().removed();
if (removedtargets.contains(ElementType.LOCAL_VARIABLE)) {
debug("Annotation, removed target contains LOCAL_VARIABLE => a switch to non-incremental mode requested");
debug("Removed target contains LOCAL_VARIABLE => a switch to non-incremental mode requested");
if (!incrementalDecision(it.outerClassName, it, affectedFiles)) {
debug("End of Differentiate, returning false");
return false;
}
}
if (!removedtargets.isEmpty()) {
debug("Removed some annotation targets, adding annotation query");
annotationQuery.add((UsageRepr.AnnotationUsage)UsageRepr
.createAnnotationUsage(myContext, TypeRepr.createClassType(myContext, it.name), null, removedtargets));
}
for (MethodRepr m : diff.methods().added()) {
if (!m.hasValue()) {
debug("Added method with no default value: " + m.name);
debug("Adding class usage to affected usages");
affectedUsages.add(it.createUsage());
}
}
}
debug("End of annotation-specific analysis");
}
debug("Processing added methods: ");
for (MethodRepr m : diff.methods().added()) {
if (it.isAnnotation()) {
continue;
@@ -984,7 +1016,9 @@ public class Mappings {
}
}
}
debug("End of added methods processing");
debug("Processing removed methods:");
for (MethodRepr m : diff.methods().removed()) {
final Collection<Pair<MethodRepr, ClassRepr>> overridenMethods = u.findOverridenMethods(m, it);
final Collection<DependencyContext.S> propagated = u.propagateMethodAccess(m.name, it.name);
@@ -1053,7 +1087,9 @@ public class Mappings {
}
}
}
debug("End of removed methods processing");
debug("Processing changed methods:");
for (Pair<MethodRepr, Difference> mr : diff.methods().changed()) {
final MethodRepr m = mr.first;
final MethodRepr.Diff d = (MethodRepr.Diff)mr.second;
@@ -1129,9 +1165,11 @@ public class Mappings {
}
}
}
debug("End of changed methods processing");
final int mask = Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
debug("Processing added fields");
for (FieldRepr f : diff.fields().added()) {
final boolean fPrivate = (f.access & Opcodes.ACC_PRIVATE) > 0;
final boolean fProtected = (f.access & Opcodes.ACC_PROTECTED) > 0;
@@ -1214,16 +1252,16 @@ public class Mappings {
}
}
}
debug("Processing removed fields");
debug("End of added fields processing");
debug("Processing removed fields:");
for (FieldRepr f : diff.fields().removed()) {
debug("Field ");
debug(f.name);
debug("Field: ", it.name);
if ((f.access & Opcodes.ACC_PRIVATE) == 0 && (f.access & mask) == mask && f.hasValue()) {
debug("Field had value and was (non-private) final static => a switch to non-incremental mode requested");
if (!incrementalDecision(it.name, f, affectedFiles)) {
debug("End of Differentiate, returning false");
return false;
}
}
@@ -1231,20 +1269,20 @@ public class Mappings {
final Collection<DependencyContext.S> propagated = u.propagateFieldAccess(f.name, it.name);
u.affectFieldUsages(f, propagated, f.createUsage(myContext, it.name), affectedUsages, dependants);
}
debug("End of removed fields processing");
debug("Processing changed fields");
debug("Processing changed fields:");
for (Pair<FieldRepr, Difference> f : diff.fields().changed()) {
final Difference d = f.second;
final FieldRepr field = f.first;
debug("Field ");
debug(field.name);
debug("Field: ", it.name);
if ((field.access & Opcodes.ACC_PRIVATE) == 0 && (field.access & mask) == mask) {
if ((d.base() & Difference.ACCESS) > 0 || (d.base() & Difference.VALUE) > 0) {
debug("Inline field changed it's access or value => a switch to non-incremental mode requested");
if (!incrementalDecision(it.name, field, affectedFiles)){
debug("End of Differentiate, returning false");
return false;
}
}
@@ -1293,12 +1331,18 @@ public class Mappings {
}
}
}
debug("End of changed fields processing");
}
debug("End of changed classes processing");
debug("Processing removed classes:");
for (ClassRepr c : classDiff.removed()) {
debug("Adding usages of class ", c.name);
affectedUsages.add(c.createUsage());
}
debug("End of removed classes processing.");
debug("Processing added classes:");
for (ClassRepr c : classDiff.added()) {
final Collection<DependencyContext.S> depClasses = myClassToClassDependency.get(c.name);
@@ -1307,12 +1351,16 @@ public class Mappings {
final DependencyContext.S fName = myClassToSourceFile.get(depClass);
if (fName != null) {
affectedFiles.add(new File(myContext.getValue(fName)));
final String f = myContext.getValue(fName);
debug("Adding dependent file " + f);
affectedFiles.add(new File(f));
}
}
}
}
debug("End of added classes processing.");
debug("Checking dependent files:");
if (dependants != null) {
final Set<DependencyContext.S> dependentFiles = new HashSet<DependencyContext.S>();
@@ -1332,6 +1380,8 @@ public class Mappings {
continue filewise;
}
debug("Dependent file: " + theFile.getAbsolutePath());
final Collection<UsageRepr.Cluster> depClusters = mySourceFileToUsages.get(depFile);
for (UsageRepr.Cluster depCluster : depClusters) {
@@ -1347,6 +1397,7 @@ public class Mappings {
final Util.UsageConstraint constraint = usageConstraints.get(usage);
if (constraint == null) {
debug("Added file with no constraints");
affectedFiles.add(theFile);
continue filewise;
}
@@ -1354,6 +1405,7 @@ public class Mappings {
final Set<DependencyContext.S> residenceClasses = depCluster.getResidence(usage);
for (DependencyContext.S residentName : residenceClasses) {
if (constraint.checkResidence(residentName)) {
debug("Added file with satisfied constraint");
affectedFiles.add(theFile);
continue filewise;
}
@@ -1369,6 +1421,7 @@ public class Mappings {
for (UsageRepr.Usage usage : annotationUsages) {
for (UsageRepr.AnnotationUsage query : annotationQuery) {
if (query.satisfies(usage)) {
debug("Added file due to annotation query");
affectedFiles.add(theFile);
continue filewise;
}
@@ -1381,6 +1434,7 @@ public class Mappings {
}
}
debug("End of Differentiate, returning true");
return true;
}
@@ -51,29 +51,29 @@ public class DefaultWordsScanner implements WordsScanner {
public void processWords(CharSequence fileText, Processor<WordOccurrence> processor) {
myLexer.start(fileText);
WordOccurrence occurence = null; // shared occurence
WordOccurrence occurrence = null; // shared occurrence
while (myLexer.getTokenType() != null) {
final IElementType type = myLexer.getTokenType();
if (myIdentifierTokenSet.contains(type)) {
if (occurence == null) {
occurence = new WordOccurrence(fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE);
if (occurrence == null) {
occurrence = new WordOccurrence(fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE);
}
else {
occurence.init(fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE);
occurrence.init(fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE);
}
if (!processor.process(occurence)) return;
if (!processor.process(occurrence)) return;
}
else if (myCommentTokenSet.contains(type)) {
if (!stripWords(processor, fileText,myLexer.getTokenStart(),myLexer.getTokenEnd(), WordOccurrence.Kind.COMMENTS,occurence, false)) return;
if (!stripWords(processor, fileText,myLexer.getTokenStart(),myLexer.getTokenEnd(), WordOccurrence.Kind.COMMENTS,occurrence, false)) return;
}
else if (myLiteralTokenSet.contains(type)) {
if (!stripWords(processor, fileText, myLexer.getTokenStart(),myLexer.getTokenEnd(),WordOccurrence.Kind.LITERALS,occurence, myMayHaveFileRefsInLiterals)) return;
if (!stripWords(processor, fileText, myLexer.getTokenStart(),myLexer.getTokenEnd(),WordOccurrence.Kind.LITERALS,occurrence, myMayHaveFileRefsInLiterals)) return;
}
else {
// process all word-like characters as words
// Plugin writers may have (Maximka in JavaScript especially) some keyword token types omitted from the identifierTokenSet
if (!stripWords(processor, fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE, occurence, false)) return;
if (!stripWords(processor, fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE, occurrence, false)) return;
}
myLexer.advance();
}
@@ -32,6 +32,8 @@ import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.popup.list.ListPopupImpl;
import com.intellij.util.Processor;
import com.intellij.util.containers.hash.*;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -39,6 +41,7 @@ import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.*;
import java.util.HashMap;
import java.util.List;
/**
@@ -207,7 +210,7 @@ public class GotoRelatedFileAction extends AnAction {
}
}
List<GotoRelatedItem> items = new ArrayList<GotoRelatedItem>();
Set<GotoRelatedItem> items = new HashSet<GotoRelatedItem> ();
for (GotoRelatedProvider provider : Extensions.getExtensions(GotoRelatedProvider.EP_NAME)) {
items.addAll(provider.getItems(contextElement));
@@ -215,7 +218,7 @@ public class GotoRelatedFileAction extends AnAction {
items.addAll(provider.getItems(dataContext));
}
}
return items;
return new ArrayList<GotoRelatedItem>(items);
}
@Override
@@ -539,6 +539,7 @@ public abstract class TodoTreeBuilder extends AbstractTreeBuilder {
return null;
}
Object[] children = getTreeStructure().getChildElements(parent);
Arrays.sort(children, getUi().getNodeDescriptorComparator());
int idx = -1;
for (int i = 0; i < children.length; i++) {
if (obj.equals(children[i])) {
@@ -584,6 +585,7 @@ public abstract class TodoTreeBuilder extends AbstractTreeBuilder {
return null;
}
Object[] children = getTreeStructure().getChildElements(parent);
Arrays.sort(children, getUi().getNodeDescriptorComparator());
int idx = -1;
for (int i = 0; i < children.length; i++) {
if (obj.equals(children[i])) {
@@ -19,7 +19,8 @@
*/
package com.intellij.psi.stubs;
import java.io.ByteArrayInputStream;
import com.intellij.util.io.UnsyncByteArrayInputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@@ -45,7 +46,7 @@ public class SerializedStubTree {
}
public StubElement getStub() {
return SerializationManager.getInstance().deserialize(new ByteArrayInputStream(myBytes));
return SerializationManager.getInstance().deserialize(new UnsyncByteArrayInputStream(myBytes));
}
public boolean equals(final Object that) {
@@ -3570,6 +3570,10 @@ public class AbstractTreeUi {
}
}
public Comparator getNodeDescriptorComparator() {
return myNodeDescriptorComparator;
}
private void disposeNode(DefaultMutableTreeNode node) {
TreeNode parent = node.getParent();
if (parent instanceof DefaultMutableTreeNode) {
@@ -97,6 +97,11 @@ public class Notification {
public void expire() {
NotificationsManager.getNotificationsManager().expire(this);
hideBalloon();
myExpired = true;
}
public void hideBalloon() {
if (myBalloonRef != null) {
final Balloon balloon = myBalloonRef.get();
if (balloon != null) {
@@ -104,7 +109,6 @@ public class Notification {
}
myBalloonRef = null;
}
myExpired = true;
}
public void setBalloon(@Nullable final Balloon balloon) {
@@ -133,6 +133,7 @@ public class EventLog implements Notifications {
mainText += content;
}
mainText = StringUtil.replace(mainText, "&nbsp;", " ");
int nlIndex = eolIndex(mainText);
if (nlIndex >= 0) {
mainText = mainText.substring(0, nlIndex);
@@ -329,6 +330,7 @@ public class EventLog implements Notifications {
group.add(new DumbAwareAction("Mark all as read", "Mark all unread notifications as read", IconLoader.getIcon("/general/reset.png")) {
@Override
public void update(AnActionEvent e) {
if (project.isDisposed()) return;
e.getPresentation().setEnabled(!getProjectComponent(project).myProjectModel.getNotifications().isEmpty());
}
@@ -413,6 +415,8 @@ public class EventLog implements Notifications {
Balloon balloon = NotificationsManagerImpl.createBalloon(myNotification, true, true);
Disposer.register(project, balloon);
balloon.show(target, Balloon.Position.above);
myNotification.hideBalloon();
myNotification.setBalloon(balloon);
}
}
@@ -188,6 +188,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem {
return ioFile;
}
// todo[r.sh] use NIO2 API after migration to JDK 7
// returns -1 if could not get attributes
@MagicConstant(flags = {BA_EXISTS, BA_REGULAR, BA_DIRECTORY, BA_HIDDEN})
private static int getBooleanAttributes(@NotNull File f) {
@@ -196,8 +197,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem {
Object flags = JAVA_IO_FILESYSTEM_GET_BOOLEAN_ATTRIBUTES_METHOD.invoke(JAVA_IO_FILESYSTEM, f);
return ((Integer)flags).intValue();
}
catch (Exception ignored) {
}
catch (Exception ignored) { }
}
return -1;
}
@@ -0,0 +1,30 @@
/*
* Copyright 2000-2012 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.notification;
import com.intellij.testFramework.UsefulTestCase;
/**
* @author peter
*/
class EventLogTest extends UsefulTestCase {
public void testNbsp() {
def entry = EventLog.formatForLog(new Notification("xxx", "Title", "Hello&nbsp;world", NotificationType.ERROR))
assert entry.message == 'Title: Hello world'
}
}
@@ -0,0 +1,580 @@
package com.intellij.openapi.fileEditor;
import com.intellij.codeStyle.CodeStyleFacade;
import com.intellij.codeStyle.DefaultCodeStyleFacade;
import com.intellij.mock.MockCommandProcessor;
import com.intellij.mock.MockEditorFactory;
import com.intellij.mock.MockVirtualFile;
import com.intellij.mock.MockVirtualFileManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl;
import com.intellij.openapi.fileTypes.*;
import com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl;
import com.intellij.openapi.fileTypes.impl.InternalFileTypeFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectLocator;
import com.intellij.openapi.project.impl.DefaultProjectLocator;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileEvent;
import com.intellij.testFramework.MockSchemesManagerFactory;
import com.intellij.testFramework.PlatformLiteFixture;
import com.intellij.util.LocalTimeCounter;
import org.jetbrains.annotations.NotNull;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.Arrays;
public class FileDocumentManagerImplTest extends PlatformLiteFixture {
private MyFileDocumentManagerImpl myDocumentManager;
@Override
protected void setUp() throws Exception {
super.setUp();
initApplication();
registerExtension(FileTypeFactory.FILE_TYPE_FACTORY_EP, new InternalFileTypeFactory());
registerExtensionPoint(FileDocumentSynchronizationVetoer.EP_NAME, FileDocumentSynchronizationVetoer.class);
getApplication().registerService(CommandProcessor.class, new MyMockCommandProcessor());
getApplication().registerService(CodeStyleFacade.class, new DefaultCodeStyleFacade());
getApplication().registerService(ProjectLocator.class, new DefaultProjectLocator());
MockEditorFactory editorFactory = new MockEditorFactory();
getApplication().registerService(EditorFactory.class, editorFactory);
final LanguageFileType[] fileType = {null};
getApplication().addComponent(FileTypeManager.class, new FileTypeManagerImpl(null, new MockSchemesManagerFactory()){
@NotNull
@Override
public FileType getFileTypeByFileName(@NotNull String fileName) {
return fileType[0];
}
@NotNull
@Override
public FileType getFileTypeByFile(@NotNull VirtualFile file) {
return fileType[0];
}
@NotNull
@Override
public FileType getFileTypeByExtension(@NotNull String extension) {
return fileType[0];
}
});
fileType[0] = StdFileTypes.JAVA;
getApplication().getComponent(FileTypeManager.class);
myDocumentManager = new MyFileDocumentManagerImpl();
getApplication().registerService(FileDocumentManager.class, myDocumentManager);
}
public void testGetCachedDocument_Cached() throws Exception {
final Document cachedDocument = myDocumentManager.getCachedDocument(new MockVirtualFile("test.txt"));
assertNull(cachedDocument);
}
public void testGetCachedDocument_NotCached() throws Exception {
final VirtualFile file = newTextFile();
final Document document = myDocumentManager.getDocument(file);
assertSame(myDocumentManager.getCachedDocument(file), document);
}
public void testGetDocument_CreateNew() throws Exception {
final VirtualFile file = newTextFile();
final Document document = myDocumentManager.getDocument(file);
assertNotNull(document);
assertEquals("test", document.getText());
assertEquals(file.getModificationStamp(), document.getModificationStamp());
assertEquals(file.isWritable(), document.isWritable());
}
public void testGetDocument_CreateNew_ReadOnly() throws Exception {
final MockVirtualFile file = newTextFile();
file.setWritable(false);
final Document document = myDocumentManager.getDocument(file);
assertNotNull(document);
assertEquals("test", document.getText());
assertEquals(file.getModificationStamp(), document.getModificationStamp());
assertEquals(file.isWritable(), document.isWritable());
}
public void testGetDocument_ReturnCachedValueTwice() throws Exception {
final VirtualFile file = newTextFile();
final Document document = myDocumentManager.getDocument(file);
assertNotNull(document);
assertEquals("test", document.getText());
final Document document2 = myDocumentManager.getDocument(file);
assertSame(document2, document);
}
public void testGetDocument_CreatesNewAfterGced() throws Exception {
final VirtualFile file = newTextFile();
Document document = myDocumentManager.getDocument(file);
int idCode = System.identityHashCode(document);
//noinspection UnusedAssignment
document = null;
System.gc();
System.gc();
document = myDocumentManager.getDocument(file);
assertTrue(idCode != System.identityHashCode(document));
}
public void testGetUnsavedDocuments_NoDocuments() throws Exception {
final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments();
assertEquals(0, unsavedDocuments.length);
}
public void testGetUnsavedDocuments_CreatedDocument() throws Exception {
final VirtualFile file = newTextFile();
myDocumentManager.getDocument(file);
final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments();
assertEquals(0, unsavedDocuments.length);
}
public void testGetUnsavedDocuments_ModifiedDocument() throws Exception {
final VirtualFile file = newTextFile();
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments();
assertEquals(1, unsavedDocuments.length);
assertSame(document, unsavedDocuments[0]);
assertTrue(Arrays.equals("test".getBytes(), file.contentsToByteArray()));
}
public void testGetUnsavedDocuments_afterSaveAllDocuments() throws Exception {
final VirtualFile file = newTextFile();
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
myDocumentManager.saveAllDocuments();
final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments();
assertEquals(0, unsavedDocuments.length);
}
public void testGetUnsavedDocuments_afterSaveDocuments() throws Exception {
final VirtualFile file = newTextFile();
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
myDocumentManager.saveDocument(document);
final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments();
assertEquals(0, unsavedDocuments.length);
}
private static MockVirtualFile newTextFile() {
return new MockVirtualFile("test.txt", "test");
}
public void testGetUnsavedDocuments_afterSaveDocumentWithProblems() throws Exception {
final VirtualFile file = new MockVirtualFile("test.txt", "test") {
@Override
@NotNull
public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
throw new IOException("");
}
};
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
myDocumentManager.saveDocument(document);
assertNotNull(myDocumentManager.myExceptionOnSave);
final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments();
assertEquals(1, unsavedDocuments.length);
assertSame(document, unsavedDocuments[0]);
assertTrue(Arrays.equals("test".getBytes(), file.contentsToByteArray()));
}
public void testUnsavedDocument_DontGc() throws Exception {
final VirtualFile file = newTextFile();
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
int idCode = System.identityHashCode(document);
//noinspection UnusedAssignment
document = null;
System.gc();
System.gc();
document = myDocumentManager.getDocument(file);
assertEquals(idCode, System.identityHashCode(document));
}
public void testUnsavedDocument_GcAfterSave() throws Exception {
final VirtualFile file = newTextFile();
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
int idCode = System.identityHashCode(document);
//noinspection UnusedAssignment
document = null;
myDocumentManager.saveAllDocuments();
System.gc();
System.gc();
document = myDocumentManager.getDocument(file);
assertTrue(idCode != System.identityHashCode(document));
}
public void testSaveDocument_DocumentWasntChanged() throws Exception {
final VirtualFile file = newTextFile();
final long stamp = file.getModificationStamp();
Document document = myDocumentManager.getDocument(file);
myDocumentManager.saveDocument(document);
assertEquals(stamp, file.getModificationStamp());
}
public void testSaveDocument_DocumentWasChanged() throws Exception {
final VirtualFile file = newTextFile();
final long stamp = file.getModificationStamp();
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
myDocumentManager.saveDocument(document);
assertTrue(stamp != file.getModificationStamp());
assertEquals(document.getModificationStamp(), file.getModificationStamp());
assertTrue(Arrays.equals("xxxtest".getBytes(), file.contentsToByteArray()));
}
public void testSaveAllDocuments_DocumentWasChanged() throws Exception {
final VirtualFile file = newTextFile();
final long stamp = file.getModificationStamp();
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
myDocumentManager.saveAllDocuments();
assertTrue(stamp != file.getModificationStamp());
assertTrue(Arrays.equals("xxxtest".getBytes(), file.contentsToByteArray()));
}
public void testGetFile() throws Exception {
final VirtualFile file = newTextFile();
Document document = myDocumentManager.getDocument(file);
assertSame(file, myDocumentManager.getFile(document));
}
public void testConvertSeparators() throws Exception {
final VirtualFile file = new MockVirtualFile("test.txt", "test\rtest");
Document document = myDocumentManager.getDocument(file);
assertEquals("test\ntest", document.getText());
}
public void testRememberSeparators() throws Exception {
final VirtualFile file = new MockVirtualFile("test.txt", "test\rtest");
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
myDocumentManager.saveAllDocuments();
assertTrue(Arrays.equals("xxxtest\rtest".getBytes(), file.contentsToByteArray()));
}
public void testContentChanged_noDocument() throws Exception {
final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest");
file.setListener(myDocumentManager);
file.setContent(null, "xxx", true);
assertNull(myDocumentManager.getCachedDocument(file));
}
public void testContentChanged_documentPresent() throws Exception {
final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest");
file.setListener(myDocumentManager);
Document document = myDocumentManager.getDocument(file);
file.setContent(null, "xxx", true);
assertEquals("xxx", document.getText());
assertEquals(file.getModificationStamp(), document.getModificationStamp());
}
public void testContentChanged_ignoreEventsFromSelf() throws Exception {
final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest");
file.setListener(myDocumentManager);
Document document = myDocumentManager.getDocument(file);
file.setContent(myDocumentManager, "xxx", true);
assertEquals("test\ntest", document.getText());
}
public void testContentChanged_ignoreEventsFromSelfOnSave() throws Exception {
final VirtualFile file = new MockVirtualFile("test.txt", "test\rtest") {
public Writer getWriter(final Object requestor, final long newModificationStamp) {
final VirtualFile self = this;
return new CharArrayWriter() {
@Override
public void close() {
super.close();
long oldStamp = getModificationStamp();
setModificationStamp(newModificationStamp);
setText(toString());
myDocumentManager.contentsChanged(new VirtualFileEvent(requestor, self, null, oldStamp, getModificationStamp()));
}
};
}
};
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "xxx");
final long stamp = document.getModificationStamp();
myDocumentManager.saveAllDocuments();
assertEquals(stamp, document.getModificationStamp());
}
public void testContentChanged_reloadChangedDocument() throws Exception {
final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest");
file.setListener(myDocumentManager);
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "zzz");
myDocumentManager.myReloadFromDisk = Boolean.TRUE;
try {
file.setContent(null, "xxx", true);
assertEquals("xxx", document.getText());
assertEquals(file.getModificationStamp(), document.getModificationStamp());
assertEquals(0, myDocumentManager.getUnsavedDocuments().length);
}
finally {
myDocumentManager.myReloadFromDisk = null;
}
}
public void testContentChanged_dontreloadChangedDocument() throws Exception {
final MockVirtualFile file = newTextFile();
file.setListener(myDocumentManager);
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "zzz");
myDocumentManager.myReloadFromDisk = Boolean.FALSE;
try {
long oldDocumentStamp = document.getModificationStamp();
file.setContent(null, "xxx", true);
assertEquals("zzztest", document.getText());
assertEquals(oldDocumentStamp, document.getModificationStamp());
}
finally {
myDocumentManager.myReloadFromDisk = null;
}
}
public void testSaveDocument_dontSaveIfModStampEqualsToFile() throws Exception {
final VirtualFile file = new MockVirtualFile("test.txt", "test") {
@NotNull
@Override
public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
fail();
throw new IOException();
}
};
DocumentEx document = (DocumentEx)myDocumentManager.getDocument(file);
document.insertString(0, "zzz");
document.setModificationStamp(file.getModificationStamp());
myDocumentManager.saveDocument(document);
}
// this test requires changes in idea code to support MockFile as local file system file (FileDocumentManager.needsRefresh).
// TODO: think how to test this functionality without hacking production code
public void atestContentChanged_reloadChangedDocumentOnSave() throws Exception {
final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest") {
@Override
public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) {
long oldStamp = getModificationStamp();
setModificationStamp(LocalTimeCounter.currentTime());
myDocumentManager.contentsChanged(new VirtualFileEvent(null, this, null, oldStamp, getModificationStamp()));
}
};
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "zzz");
file.setContent(null, "xxx", false);
myDocumentManager.myReloadFromDisk = Boolean.TRUE;
try {
file.setActualTimeStamp(file.getTimeStamp() + 1);
myDocumentManager.saveAllDocuments();
long fileStamp = file.getModificationStamp();
assertEquals("xxx", document.getText());
assertEquals(file.getModificationStamp(), document.getModificationStamp());
assertEquals(file.getModificationStamp(), fileStamp);
assertEquals(0, myDocumentManager.getUnsavedDocuments().length);
}
finally {
myDocumentManager.myReloadFromDisk = null;
}
}
public void testContentChanged_dontreloadChangedDocumentOnSave() throws Exception {
final MockVirtualFile file = new MockVirtualFile("test.txt", "test") {
@Override
public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) {
long oldStamp = getModificationStamp();
setModificationStamp(LocalTimeCounter.currentTime());
myDocumentManager.contentsChanged(new VirtualFileEvent(null, this, null, oldStamp, getModificationStamp()));
}
};
myDocumentManager.myReloadFromDisk = Boolean.FALSE;
try {
Document document = myDocumentManager.getDocument(file);
document.insertString(0, "zzz");
long documentStamp = document.getModificationStamp();
file.setContent(null, "xxx", false);
file.setActualTimeStamp(file.getTimeStamp() + 1);
myDocumentManager.saveAllDocuments();
assertEquals("zzztest", document.getText());
assertEquals(file.getModificationStamp(), document.getModificationStamp());
assertTrue(Arrays.equals("zzztest".getBytes(), file.contentsToByteArray()));
assertEquals(documentStamp, document.getModificationStamp());
}
finally {
myDocumentManager.myReloadFromDisk = null;
}
}
public void testReplaceDocumentTestWithTheSameTest() throws Exception {
final boolean[] canCallSave = {true};
final VirtualFile file = new MockVirtualFile("test.txt", "test") {
@NotNull
@Override
public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
if (!canCallSave[0]) fail();
return super.getOutputStream(requestor, newModificationStamp, newTimeStamp);
}
};
DocumentEx document = (DocumentEx)myDocumentManager.getDocument(file);
String newText = "test text";
document.replaceString(0, document.getTextLength(), newText);
assertTrue(myDocumentManager.isDocumentUnsaved(document));
myDocumentManager.saveDocument(document);
canCallSave[0] = false;
final long modificationStamp = document.getModificationStamp();
document.replaceString(0, document.getTextLength(), newText);
if (myDocumentManager.isDocumentUnsaved(document)) {
assertTrue(document.getModificationStamp() > modificationStamp);
}
else {
assertEquals(modificationStamp, document.getModificationStamp());
}
}
public void testExternalReplaceWithTheSameText() throws Exception{
final long[] modificationStamp = new long[1];
modificationStamp[0] = 1;
final VirtualFile file = new MockVirtualFile("test.txt", "test") {
@NotNull
@Override
public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
fail();
throw new IOException();
}
@Override
public long getModificationStamp() {
return modificationStamp[0];
}
@Override
public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) {
myDocumentManager.contentsChanged(new VirtualFileEvent(null, this, null, 1, 2));
}
};
DocumentEx document = (DocumentEx)myDocumentManager.getDocument(file);
modificationStamp[0] = 2;
file.refresh(false, false);
assertEquals(2, document.getModificationStamp());
}
public static class MyFileDocumentManagerImpl extends FileDocumentManagerImpl {
public static final FileDocumentManagerListener[] LISTENERS = new FileDocumentManagerListener[0];
public IOException myExceptionOnSave;
public Boolean myReloadFromDisk = null;
public MyFileDocumentManagerImpl() {
super(new MockVirtualFileManager());
}
@Override
protected void reportErrorOnSave(IOException e) {
myExceptionOnSave = e;
}
@Override
protected boolean askReloadFromDisk(VirtualFile file, Document document) {
if (myReloadFromDisk == null){
fail();
return false;
}
else{
return myReloadFromDisk.booleanValue();
}
}
@NotNull
@Override
protected FileDocumentManagerListener[] getListeners() {
return LISTENERS;
}
}
private static class MyMockCommandProcessor extends MockCommandProcessor {
@Override
public void executeCommand(Project project, @NotNull Runnable runnable, String name, Object groupId) {
runnable.run();
}
@Override
public void executeCommand(Project project,
@NotNull Runnable runnable,
String name,
Object groupId,
@NotNull UndoConfirmationPolicy confirmationPolicy,
Document document) {
runnable.run();
}
@Override
public void executeCommand(Project project,
@NotNull Runnable runnable,
String name,
Object groupId,
@NotNull UndoConfirmationPolicy confirmationPolicy) {
runnable.run();
}
@Override
public void executeCommand(@NotNull Runnable runnable, String name, Object groupId) {
runnable.run();
}
}
}
@@ -0,0 +1,210 @@
package com.intellij.openapi.fileEditor;
import com.intellij.mock.Mock;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.testFramework.PlatformTestCase;
import org.jetbrains.annotations.NotNull;
public class IdeDocumentHistoryTest extends PlatformTestCase {
private IdeDocumentHistoryImpl myHistory;
private Mock.MyFileEditor mySelectedEditor;
private Mock.MyVirtualFile mySelectedFile;
private FileEditorState myEditorState;
private FileEditorProvider myProvider;
private MyState myState1;
private MyState myState2;
private MyState myState3;
@Override
protected void setUp() throws Exception {
super.setUp();
myHistory = new IdeDocumentHistoryImpl(getProject(), EditorFactory.getInstance(), new EditorManager(), VirtualFileManager.getInstance(), CommandProcessor.getInstance(), new Mock.MyToolWindowManager()) {
@Override
protected Pair<FileEditor,FileEditorProvider> getSelectedEditor() {
return Pair.create ((FileEditor)mySelectedEditor, myProvider);
}
@Override
protected void executeCommand(Runnable runnable, String name, Object groupId) {
myHistory.onCommandStarted();
runnable.run();
myHistory.onSelectionChanged();
myHistory.onCommandFinished(groupId);
}
};
mySelectedEditor = new Mock.MyFileEditor() {
@Override
@NotNull
public FileEditorState getState(@NotNull FileEditorStateLevel level) {
return myEditorState;
}
@Override
public void setState(@NotNull FileEditorState state) {
myEditorState = state;
}
};
mySelectedFile = new Mock.MyVirtualFile();
myEditorState = new MyState(false, "start");
myProvider = new Mock.MyFileEditorProvider() {
@Override
@NotNull
public String getEditorTypeId() {
return "EditorType";
}
};
}
public void testNoHistoryRecording() throws Throwable {
myHistory.onCommandStarted();
myHistory.onCommandFinished(null);
assertFalse(myHistory.isBackAvailable());
assertFalse(myHistory.isForwardAvailable());
}
public void testNavigationRecording() throws Throwable {
makeNavigationChange(myState2);
assertTrue(myHistory.isBackAvailable());
assertFalse(myHistory.isForwardAvailable());
assertEquals(1, myHistory.getBackPlaces().size());
}
public void testMergingForwardPlaces() throws Throwable {
myEditorState = new MyState(true, "state1");
makeNavigationChange(new MyState(true, "state2"));
assertTrue(myHistory.isBackAvailable());
assertFalse(myHistory.isForwardAvailable());
assertEquals(1, myHistory.getBackPlaces().size());
}
public void testSimpleNavigation() throws Throwable {
pushTwoStates();
assertFalse(myHistory.isForwardAvailable());
assertTrue(myHistory.isBackAvailable());
myHistory.back();
assertTrue(myHistory.isBackAvailable());
assertTrue(myHistory.isForwardAvailable());
assertSame(myState2, myEditorState);
myHistory.back();
assertFalse(myHistory.isBackAvailable());
assertTrue(myHistory.isForwardAvailable());
assertSame(myState1, myEditorState);
myHistory.forward();
assertTrue(myHistory.isBackAvailable());
assertTrue(myHistory.isForwardAvailable());
assertSame(myState2, myEditorState);
myHistory.forward();
assertTrue(myHistory.isBackAvailable());
assertFalse(myHistory.isForwardAvailable());
assertSame(myState3, myEditorState);
}
public void testQueueCutOff() throws Throwable {
pushTwoStates();
myHistory.back();
assertTrue(myHistory.isBackAvailable());
assertTrue(myHistory.isForwardAvailable());
MyState newState = new MyState(false, "newState");
makeNavigationChange(newState);
assertTrue(myHistory.isBackAvailable());
assertFalse(myHistory.isForwardAvailable());
myHistory.back();
assertSame(myState2, myEditorState);
myHistory.back();
assertSame(myState1, myEditorState);
assertFalse(myHistory.isBackAvailable());
}
public void testRemoveInvalid() throws Throwable {
pushTwoStates();
assertTrue(myHistory.isBackAvailable());
mySelectedFile.myValid = false;
myHistory.onFileDeleted();
assertFalse(myHistory.isBackAvailable());
assertFalse(myHistory.isForwardAvailable());
}
private void pushTwoStates() {
myState1 = new MyState(false, "state1");
myState2 = new MyState(false, "state2");
myState3 = new MyState(false, "state3");
myEditorState = myState1;
makeNavigationChange(myState2);
makeNavigationChange(myState3);
}
private void makeNavigationChange(MyState newState) {
myHistory.onCommandStarted();
myHistory.onSelectionChanged();
myHistory.onCommandFinished(null);
myEditorState = newState;
}
private class EditorManager extends Mock.MyFileEditorManager {
@Override
public VirtualFile getFile(@NotNull FileEditor editor) {
return mySelectedFile;
}
@Override
@NotNull
public Pair<FileEditor[],FileEditorProvider[]> openFileWithProviders(@NotNull VirtualFile file,
boolean focusEditor,
boolean searchForSplitter) {
return Pair.create (new FileEditor[] {mySelectedEditor}, new FileEditorProvider[] {myProvider});
}
@Override
public FileEditorProvider getProvider(FileEditor editor) {
return myProvider;
}
}
private static class MyState implements FileEditorState {
private final boolean myCanBeMerged;
private final String myName;
public MyState(boolean canBeMerged, String name) {
myCanBeMerged = canBeMerged;
myName = name;
}
@Override
public boolean canBeMergedWith(FileEditorState otherState, FileEditorStateLevel level) {
return myCanBeMerged;
}
public String toString() {
return myName;
}
}
}
@@ -0,0 +1,37 @@
package com.intellij.openapi.fileEditor;
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
import com.intellij.openapi.util.Comparing;
import com.intellij.testFramework.LightPlatformTestCase;
import com.intellij.testFramework.LightVirtualFile;
public class LoadTextUtilTest extends LightPlatformTestCase {
private static void doTest(String source, String expected, String expectedSeparator) {
final LightVirtualFile vFile = new LightVirtualFile("test.txt");
final CharSequence real = LoadTextUtil.getTextByBinaryPresentation(source.getBytes(), vFile);
assertTrue("content", Comparing.equal(expected, real));
if (expectedSeparator != null) {
assertEquals("detected line separator", expectedSeparator, FileDocumentManager.getInstance().getLineSeparator(vFile, null));
}
}
public void testSimpleLoad() {
doTest("test", "test", null);
}
public void testConvert_SlashR() {
doTest("test\rtest\rtest", "test\ntest\ntest", "\r");
}
public void testConvert_SlashN() {
doTest("test\ntest\ntest", "test\ntest\ntest", "\n");
}
public void testConvert_SlashR_SlashN() {
doTest("test\r\ntest\r\ntest", "test\ntest\ntest", "\r\n");
}
public void testConvertMostCommon() {
doTest("test\r\ntest\r\ntest\ntest", "test\ntest\ntest\ntest", "\r\n");
}
}
@@ -0,0 +1,681 @@
package com.intellij.mock;
import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
import com.intellij.ide.structureView.StructureViewBuilder;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.impl.EditorComposite;
import com.intellij.openapi.fileEditor.impl.EditorWindow;
import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.impl.IdeFocusManagerHeadless;
import com.intellij.util.ArrayUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
//[kirillk] - this class looks to be an overkill but IdeDocumentHistory is highly coupled
// with all of that stuff below, so it's not possible to test it's back/forward capabilities
// w/o making mocks for all of them. perhaps later we will decouple those things
public class Mock {
public static class MyFileEditor extends UserDataHolderBase implements DocumentsEditor {
public Document[] DOCUMENTS;
@Override
public Document[] getDocuments() {
return DOCUMENTS;
}
@Override
@NotNull
public JComponent getComponent() {
throw new UnsupportedOperationException();
}
@Override
public JComponent getPreferredFocusedComponent() {
return null;
}
@Override
@NotNull
public String getName() {
return "";
}
@Override
public void dispose() {
}
@Override
public StructureViewBuilder getStructureViewBuilder() {
return null;
}
@Override
@NotNull
public FileEditorState getState(@NotNull FileEditorStateLevel level) {
return new FileEditorState() {
@Override
public boolean canBeMergedWith(FileEditorState fileEditorState, FileEditorStateLevel fileEditorStateLevel) {
return false;
}
};
}
@Override
public void setState(@NotNull FileEditorState state) {
}
@Override
public boolean isModified() {
return false;
}
@Override
public boolean isValid() {
return false;
}
@Override
public void selectNotify() {
}
@Override
public void deselectNotify() {
}
@Override
public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
}
@Override
public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
}
@Override
public BackgroundEditorHighlighter getBackgroundHighlighter() {
return null;
}
@Override
public FileEditorLocation getCurrentLocation() {
return null;
}
}
public static class MyFileEditorManager extends FileEditorManagerEx {
@Override
public JComponent getComponent() {
return null;
}
@Override
public ActionCallback notifyPublisher(Runnable runnable) {
runnable.run();
return new ActionCallback.Done();
}
@Override
public ActionCallback getReady(@NotNull Object requestor) {
return new ActionCallback.Done();
}
@NotNull
@Override
public Pair<FileEditor[], FileEditorProvider[]> openFileWithProviders(@NotNull VirtualFile file,
boolean focusEditor,
@NotNull EditorWindow window) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public boolean isInsideChange() {
return false;
}
@Override
public boolean hasSplitOrUndockedWindows() {
return false;
}
@Override
public EditorsSplitters getSplittersFor(Component c) {
return null;
}
@Override
public EditorsSplitters getSplitters() {
return null;
}
@Override
public AsyncResult<EditorWindow> getActiveWindow() {
return null;
}
@Override
public void addTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) {
}
@Override
public void removeTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) {
}
@Override
public void addBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) {
}
@Override
public void removeBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) {
}
@Override
public JComponent getPreferredFocusedComponent() {
return null;
}
@Override
@NotNull
public Pair<FileEditor[], FileEditorProvider[]> getEditorsWithProviders(@NotNull VirtualFile file) {
throw new UnsupportedOperationException();
}
public FileEditorProvider getProvider(FileEditor editor) {
return null;
}
@Override
public EditorWindow getCurrentWindow() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void setCurrentWindow(EditorWindow window) {
}
@Override
public VirtualFile getFile(@NotNull FileEditor editor) {
return null;
}
@Override
public void updateFilePresentation(VirtualFile file) {
}
@Override
public void unsplitWindow() {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void unsplitAllWindow() {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
@NotNull
public EditorWindow[] getWindows() {
return new EditorWindow[0]; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
@NotNull
public VirtualFile[] getSiblings(VirtualFile file) {
return new VirtualFile[0];
}
@Override
public void createSplitter(int orientation, @Nullable EditorWindow window) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void changeSplitterOrientation() {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void flipTabs() {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public boolean tabsMode() {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public boolean isInSplitter() {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public boolean hasOpenedFile() {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public VirtualFile getCurrentFile() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public Pair<FileEditor, FileEditorProvider> getSelectedEditorWithProvider(@NotNull VirtualFile file) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public boolean isChanged(@NotNull EditorComposite editor) {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public EditorWindow getNextWindow(@NotNull EditorWindow window) {
return null;
}
@Override
public EditorWindow getPrevWindow(@NotNull EditorWindow window) {
return null;
}
@Override
public void closeAllFiles() {
}
public Editor openTextEditorEnsureNoFocus(@NotNull OpenFileDescriptor descriptor) {
return null;
}
@Override
@NotNull
public Pair<FileEditor[],FileEditorProvider[]> openFileWithProviders(@NotNull VirtualFile file,
boolean focusEditor,
boolean searchForSplitter) {
return Pair.create (new FileEditor[0], new FileEditorProvider [0]);
}
@Override
public void closeFile(@NotNull VirtualFile file) {
}
@Override
public void closeFile(@NotNull VirtualFile file, @NotNull EditorWindow window) {
}
@Override
public Editor openTextEditor(OpenFileDescriptor descriptor, boolean focusEditor) {
return null;
}
@Override
public Editor getSelectedTextEditor() {
return null;
}
@Override
public boolean isFileOpen(@NotNull VirtualFile file) {
return false;
}
@Override
@NotNull
public VirtualFile[] getOpenFiles() {
return new VirtualFile[0];
}
@Override
@NotNull
public VirtualFile[] getSelectedFiles() {
return new VirtualFile[0];
}
@Override
@NotNull
public FileEditor[] getSelectedEditors() {
return new FileEditor[0];
}
@Override
public FileEditor getSelectedEditor(@NotNull VirtualFile file) {
return null;
}
@Override
@NotNull
public FileEditor[] getEditors(@NotNull VirtualFile file) {
return new FileEditor[0];
}
@NotNull
@Override
public FileEditor[] getAllEditors(@NotNull VirtualFile file) {
return new FileEditor[0];
}
@Override
@NotNull
public FileEditor[] getAllEditors() {
return new FileEditor[0];
}
@Override
public void removeEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComoponent) {
}
@Override
public void showEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComoponent) {
}
@Override
public void addFileEditorManagerListener(@NotNull FileEditorManagerListener listener) {
}
@Override
public void addFileEditorManagerListener(@NotNull FileEditorManagerListener listener, Disposable parentDisposable) {
}
@Override
public void removeFileEditorManagerListener(@NotNull FileEditorManagerListener listener) {
}
@Override
@NotNull
public List<FileEditor> openEditor(@NotNull OpenFileDescriptor descriptor, boolean focusEditor) {
return Collections.emptyList();
}
@Override
@NotNull
public Project getProject() {
throw new UnsupportedOperationException();
}
@Override
public void registerExtraEditorDataProvider(@NotNull EditorDataProvider provider, Disposable parentDisposable) {
}
@Override
public int getWindowSplitCount() {
return 0;
}
}
public static class MyVirtualFile extends VirtualFile {
public boolean myValid = true;
@Override
@NotNull
public VirtualFileSystem getFileSystem() {
throw new UnsupportedOperationException();
}
@Override
public String getPath() {
return null;
}
@Override
@NotNull
public String getName() {
throw new UnsupportedOperationException();
}
@Override
public void rename(Object requestor, @NotNull String newName) throws IOException {
}
@Override
public boolean isWritable() {
return false;
}
@Override
public boolean isDirectory() {
return false;
}
@Override
public boolean isValid() {
return myValid;
}
@Override
public VirtualFile getParent() {
return null;
}
@Override
public VirtualFile[] getChildren() {
return new VirtualFile[0];
}
@Override
public VirtualFile createChildDirectory(Object requestor, String name) throws IOException {
return null;
}
@Override
public VirtualFile createChildData(Object requestor, @NotNull String name) throws IOException {
return null;
}
@Override
public void delete(Object requestor) throws IOException {
}
@Override
public void move(Object requestor, @NotNull VirtualFile newParent) throws IOException {
}
@Override
public InputStream getInputStream() throws IOException {
return null;
}
@Override
@NotNull
public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
throw new UnsupportedOperationException();
}
@Override
@NotNull
public byte[] contentsToByteArray() throws IOException {
return ArrayUtil.EMPTY_BYTE_ARRAY;
}
@Override
public long getModificationStamp() {
return 0;
}
@Override
public long getTimeStamp() {
return 0;
}
@Override
public long getLength() {
return 0;
}
@Override
public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) {
}
}
public static class MyToolWindowManager extends ToolWindowManager {
@Override
public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @NotNull ToolWindowAnchor anchor) {
return null;
}
@Override
public ToolWindow registerToolWindow(@NotNull String id,
@NotNull JComponent component,
@NotNull ToolWindowAnchor anchor,
Disposable parentDisposable,
boolean canWorkInDumbMode, boolean canCloseContents) {
return null;
}
@Override
public ToolWindow registerToolWindow(@NotNull String id,
@NotNull JComponent component,
@NotNull ToolWindowAnchor anchor,
Disposable parentDisposable,
boolean canWorkInDumbMode) {
return null;
}
@Override
public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @NotNull ToolWindowAnchor anchor, Disposable parentDisposable) {
return null;
}
@Override
public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor) {
return null;
}
@Override
public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor,
final Disposable parentDisposable, final boolean dumbAware) {
return null;
}
@Override
public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor, final boolean sideTool) {
return null;
}
public JComponent getFocusTargetFor(final JComponent comp) {
return null;
}
@Override
public void unregisterToolWindow(@NotNull String id) {
}
@Override
public void activateEditorComponent() {
}
public ActionCallback requestFocus(final Component c, final boolean forced) {
return new ActionCallback.Done();
}
public ActionCallback requestFocus(final ActiveRunnable command, final boolean forced) {
return new ActionCallback.Done();
}
@Override
public boolean isEditorComponentActive() {
return false;
}
@Override
public String[] getToolWindowIds() {
return ArrayUtil.EMPTY_STRING_ARRAY;
}
@Override
public String getActiveToolWindowId() {
return null;
}
@Override
public ToolWindow getToolWindow(String id) {
return null;
}
@Override
public void invokeLater(Runnable runnable) {
}
@Override
public IdeFocusManager getFocusManager() {
return IdeFocusManagerHeadless.INSTANCE;
}
@Override
public void notifyByBalloon(@NotNull final String toolWindowId, final MessageType type, @NotNull final String text, @Nullable final Icon icon,
@Nullable final HyperlinkListener listener) {
}
@Override
public Balloon getToolWindowBalloon(String id) {
return null;
}
@Override
public void notifyByBalloon(@NotNull final String toolWindowId, @NotNull final MessageType type, @NotNull final String htmlBody) {
}
}
public static class MyFileEditorProvider implements FileEditorProvider {
@Override
public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
return false;
}
@Override
@NotNull
public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
throw new UnsupportedOperationException();
}
@Override
public void disposeEditor(@NotNull FileEditor editor) {
}
@Override
@NotNull
public FileEditorState readState(@NotNull Element sourceElement, @NotNull Project project, @NotNull VirtualFile file) {
throw new UnsupportedOperationException();
}
@Override
public void writeState(@NotNull FileEditorState state, @NotNull Project project, @NotNull Element targetElement) {
}
@Override
@NotNull
public String getEditorTypeId() {
throw new UnsupportedOperationException();
}
@Override
@NotNull
public FileEditorPolicy getPolicy() {
throw new UnsupportedOperationException();
}
}
}
@@ -0,0 +1,104 @@
package com.intellij.mock;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.command.CommandListener;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class MockCommandProcessor extends CommandProcessor {
@Override
public void executeCommand(@NotNull Runnable runnable, String name, Object groupId) {
}
@Override
public void executeCommand(Project project, @NotNull Runnable runnable, String name, Object groupId) {
}
@Override
public void executeCommand(Project project,
@NotNull Runnable runnable,
String name,
Object groupId,
@NotNull UndoConfirmationPolicy confirmationPolicy) {
}
@Override
public void setCurrentCommandName(String name) {
}
@Override
public void setCurrentCommandGroupId(Object groupId) {
}
@Override
public Runnable getCurrentCommand() {
return null;
}
@Override
public String getCurrentCommandName() {
return null;
}
@Override
@Nullable
public Object getCurrentCommandGroupId() {
return null;
}
@Override
public Project getCurrentCommandProject() {
return null;
}
@Override
public void addCommandListener(@NotNull CommandListener listener) {
}
@Override
public void addCommandListener(@NotNull CommandListener listener, @NotNull Disposable parentDisposable) {
}
@Override
public void removeCommandListener(@NotNull CommandListener listener) {
}
@Override
public boolean isUndoTransparentActionInProgress() {
return false;
}
@Override
public void markCurrentCommandAsGlobal(Project project) {
}
@Override
public void runUndoTransparentAction(@NotNull Runnable action) {
}
@Override
public void executeCommand(Project project, @NotNull Runnable command, String name, Object groupId, @NotNull UndoConfirmationPolicy confirmationPolicy,
Document document) {
}
@Override
public void executeCommand(Project project, @NotNull Runnable runnable, @Nls String name, Object groupId, @Nullable Document document) {
}
@Override
public void addAffectedDocuments(Project project, @NotNull Document... docs) {
}
@Override
public void addAffectedFiles(Project project, @NotNull VirtualFile... files) {
}
}
@@ -3,15 +3,14 @@
*/
package com.intellij.mock;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.openapi.vfs.*;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -26,8 +25,8 @@ public class MockVirtualFile extends VirtualFile {
private final boolean myDirectory;
private final List<VirtualFile> myChildren = new SmartList<VirtualFile>();
private String myText;
private FileType myFileType;
private final MockVirtualFileSystem myFileSystem = new MockVirtualFileSystem();
private long myModStamp = LocalTimeCounter.currentTime();
public MockVirtualFile(final String name) {
this(false, name);
@@ -38,6 +37,12 @@ public class MockVirtualFile extends VirtualFile {
myName = name;
}
public MockVirtualFile(String name, String text) {
myName = name;
myText = text;
myDirectory = false;
}
public void setText(final String text) {
myText = text;
}
@@ -73,9 +78,13 @@ public class MockVirtualFile extends VirtualFile {
return prefix + "/" + myName;
}
private boolean myIsWritable = true;
@Override
public boolean isWritable() {
throw new UnsupportedOperationException("Method isWritable is not yet implemented in " + getClass().getName());
return myIsWritable;
}
public void setWritable(boolean b) {
myIsWritable = b;
}
@Override
@@ -96,13 +105,28 @@ public class MockVirtualFile extends VirtualFile {
@Override
public VirtualFile[] getChildren() {
return (VirtualFile[])VfsUtil.toVirtualFileArray(myChildren);
return VfsUtil.toVirtualFileArray(myChildren);
}
@Override
@NotNull
public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
throw new UnsupportedOperationException("Method getOutputStream is not yet implemented in " + getClass().getName());
public OutputStream getOutputStream(Object requestor, final long newModificationStamp, long newTimeStamp) throws IOException {
return new ByteArrayOutputStream() {
@Override
public void close() {
myModStamp = newModificationStamp;
myText = toString();
}
};
}
@Override
public long getModificationStamp() {
return myModStamp;
}
public void setModificationStamp(long modStamp) {
myModStamp = modStamp;
}
@Override
@@ -111,9 +135,11 @@ public class MockVirtualFile extends VirtualFile {
return myText.getBytes();
}
private final long myTimeStamp = System.currentTimeMillis();
@Override
public long getTimeStamp() {
throw new UnsupportedOperationException("Method getTimeStamp is not yet implemented in " + getClass().getName());
return myTimeStamp;
}
@Override
@@ -123,7 +149,16 @@ public class MockVirtualFile extends VirtualFile {
@Override
public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) {
throw new UnsupportedOperationException("Method refresh is not yet implemented in " + getClass().getName());
}
private long myActualTimeStamp = myTimeStamp;
public void setActualTimeStamp(long actualTimeStamp) {
myActualTimeStamp = actualTimeStamp;
}
public long getActualTimeStamp() {
return myActualTimeStamp;
}
@Override
@@ -131,6 +166,19 @@ public class MockVirtualFile extends VirtualFile {
throw new UnsupportedOperationException("Method getInputStream is not yet implemented in " + getClass().getName());
}
private VirtualFileListener myListener = null;
public void setListener(VirtualFileListener listener) {
myListener = listener;
}
public void setContent(Object requestor, String content, boolean fireEvent) {
long oldStamp = myModStamp;
myText = content;
if (fireEvent) {
myModStamp = LocalTimeCounter.currentTime();
myListener.contentsChanged(new VirtualFileEvent(requestor, this, null, oldStamp, myModStamp));
}
}
}
@@ -145,10 +145,10 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro
if (resource == null) {
resource = PlatformTestCase.class.getClassLoader().getResource("idea/IdeaApplicationInfo.xml");
if (resource == null) {
System.setProperty("idea.platform.prefix", "PlatformLangXml");
setPlatformPrefix("PlatformLangXml");
}
else {
System.setProperty("idea.platform.prefix", "Idea");
setPlatformPrefix("Idea");
}
}
}
@@ -742,11 +742,15 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro
isUltimate = false;
}
if (!isUltimate) {
System.setProperty("idea.platform.prefix", prefix);
setPlatformPrefix(prefix);
}
}
}
public static void setPlatformPrefix(String prefix) {
System.setProperty("idea.platform.prefix", prefix);
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface WrapInCommand {}
@@ -21,6 +21,7 @@ import com.intellij.util.containers.FactoryMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.Locale;
import java.util.ResourceBundle;
@@ -43,6 +44,7 @@ import java.util.ResourceBundle;
* @since 8/1/11 2:37 PM
*/
public abstract class AbstractBundle {
private Reference<ResourceBundle> myBundle;
@NonNls private final String myPathToBundle;
protected AbstractBundle(@NonNls @NotNull String pathToBundle) {
@@ -54,7 +56,13 @@ public abstract class AbstractBundle {
}
private ResourceBundle getBundle() {
return getResourceBundle(myPathToBundle, getClass().getClassLoader());
ResourceBundle bundle = null;
if (myBundle != null) bundle = myBundle.get();
if (bundle == null) {
bundle = getResourceBundle(myPathToBundle, getClass().getClassLoader());
myBundle = new SoftReference<ResourceBundle>(bundle);
}
return bundle;
}
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
@@ -15,17 +15,16 @@
*/
package com.intellij.openapi.util.io;
import com.intellij.util.io.UnsyncByteArrayInputStream;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayInputStream;
public class BufferExposingByteArrayInputStream extends ByteArrayInputStream {
public class BufferExposingByteArrayInputStream extends UnsyncByteArrayInputStream {
public BufferExposingByteArrayInputStream(@NotNull byte[] bytes) {
super(bytes);
}
@NotNull
public byte[] getInternalBuffer() {
return buf;
return myBuffer;
}
}
@@ -15,13 +15,11 @@
*/
package com.intellij.openapi.util.io;
import com.intellij.util.io.UnsyncByteArrayOutputStream;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayOutputStream;
public class BufferExposingByteArrayOutputStream extends ByteArrayOutputStream {
public BufferExposingByteArrayOutputStream() {
}
public class BufferExposingByteArrayOutputStream extends UnsyncByteArrayOutputStream {
public BufferExposingByteArrayOutputStream() {}
public BufferExposingByteArrayOutputStream(int size) {
super(size);
@@ -29,14 +27,14 @@ public class BufferExposingByteArrayOutputStream extends ByteArrayOutputStream {
@NotNull
public byte[] getInternalBuffer() {
return buf;
return myBuffer;
}
// moves back the written bytes pointer by {@link #size}, to "unwrite" last {@link #size} bytes
public int backOff(int size) {
assert size >= 0 : size;
count -= size;
assert count >= 0 : count;
return count;
myCount -= size;
assert myCount >= 0 : myCount;
return myCount;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -73,9 +73,9 @@ public class FileSystemUtil {
private FileSystemUtil() { }
public static boolean isSymLink(@NotNull final File file) {
public static boolean isSymLink(@NotNull final String path) {
try {
return ourMediator != null && file.exists() && ourMediator.isSymLink(file.getAbsolutePath());
return ourMediator != null && ourMediator.isSymLink(path);
}
catch (Exception e) {
LOG.warn(e);
@@ -83,15 +83,15 @@ public class FileSystemUtil {
}
}
public static boolean isSymLink(@NotNull final String path) {
return isSymLink(new File(path));
public static boolean isSymLink(@NotNull final File file) {
return isSymLink(file.getAbsolutePath());
}
@Nullable
public static String resolveSymLink(@NotNull final File file) {
if (file.exists() && ourMediator != null) {
public static String resolveSymLink(@NotNull final String path) {
if (ourMediator != null) {
try {
final String realPath = ourMediator.resolveSymLink(file.getAbsolutePath());
final String realPath = ourMediator.resolveSymLink(path);
if (realPath != null && new File(realPath).exists()) {
return realPath;
}
@@ -104,18 +104,14 @@ public class FileSystemUtil {
}
@Nullable
public static String resolveSymLink(@NotNull final String path) {
return resolveSymLink(new File(path));
public static String resolveSymLink(@NotNull final File file) {
return resolveSymLink(file.getAbsolutePath());
}
public static int getPermissions(@NotNull final String path) {
return getPermissions(new File(path));
}
public static int getPermissions(@NotNull final File file) {
if (file.exists() && SystemInfo.isUnix && ourMediator != null) {
if (SystemInfo.isUnix && ourMediator != null) {
try {
return ourMediator.getPermissions(file.getAbsolutePath());
return ourMediator.getPermissions(path);
}
catch (Exception e) {
LOG.warn(e);
@@ -124,14 +120,14 @@ public class FileSystemUtil {
return -1;
}
public static void setPermissions(@NotNull final String path, final int permissions) {
setPermissions(new File(path), permissions);
public static int getPermissions(@NotNull final File file) {
return getPermissions(file.getAbsolutePath());
}
public static void setPermissions(@NotNull final File file, final int permissions) {
if (file.exists() && SystemInfo.isUnix && ourMediator != null) {
public static void setPermissions(@NotNull final String path, final int permissions) {
if (SystemInfo.isUnix && ourMediator != null) {
try {
ourMediator.setPermissions(file.getAbsolutePath(), permissions);
ourMediator.setPermissions(path, permissions);
}
catch (Exception e) {
LOG.warn(e);
@@ -139,6 +135,10 @@ public class FileSystemUtil {
}
}
public static void setPermissions(@NotNull final File file, final int permissions) {
setPermissions(file.getAbsolutePath(), permissions);
}
private interface Mediator {
boolean isSymLink(@NotNull String path) throws Exception;
@@ -156,20 +156,28 @@ public class FileSystemUtil {
private final Method myGetPath;
private final Method myIsSymbolicLink;
private final Object myLinkOptions;
private final Object myNoFollowLinkOptions;
private final Method myGetAttribute;
private final Method mySetAttribute;
private Jdk7MediatorImpl() throws Exception {
myDefaultFileSystem = Class.forName("java.nio.file.FileSystems").getMethod("getDefault").invoke(null);
myGetPath = Class.forName("java.nio.file.FileSystem").getMethod("getPath", String.class, String[].class);
myGetPath.setAccessible(true);
final Class<?> pathClass = Class.forName("java.nio.file.Path");
myIsSymbolicLink = Class.forName("java.nio.file.Files").getMethod("isSymbolicLink", pathClass);
myIsSymbolicLink.setAccessible(true);
myLinkOptions = Array.newInstance(Class.forName("java.nio.file.LinkOption"), 0);
final Class<?> linkOptClass = myLinkOptions.getClass();
myGetAttribute = Class.forName("java.nio.file.Files").getMethod("getAttribute", pathClass, String.class, linkOptClass);
mySetAttribute = Class.forName("java.nio.file.Files").getMethod("setAttribute", pathClass, String.class, Object.class, linkOptClass);
final Class<?> linkOptClass = Class.forName("java.nio.file.LinkOption");
myLinkOptions = Array.newInstance(linkOptClass, 0);
myNoFollowLinkOptions = Array.newInstance(linkOptClass, 1);
Array.set(myNoFollowLinkOptions, 0, linkOptClass.getField("NOFOLLOW_LINKS").get(null));
final Class<?> linkOptArrClass = myLinkOptions.getClass();
myGetAttribute = Class.forName("java.nio.file.Files").getMethod("getAttribute", pathClass, String.class, linkOptArrClass);
mySetAttribute = Class.forName("java.nio.file.Files").getMethod("setAttribute", pathClass, String.class, Object.class, linkOptArrClass);
}
@Override
@@ -189,7 +197,7 @@ public class FileSystemUtil {
@Override
public int getPermissions(@NotNull final String path) throws Exception {
final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY);
final Object attribute = myGetAttribute.invoke(null, pathObj, POSIX_PERMISSIONS_ATTR, myLinkOptions);
final Object attribute = myGetAttribute.invoke(null, pathObj, POSIX_PERMISSIONS_ATTR, myNoFollowLinkOptions);
return decodePermissions(attribute);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -678,9 +678,10 @@ public class FileUtil {
}
if (SystemInfo.isUnix && fromFile.canExecute()) {
final int permissions = FileSystemUtil.getPermissions(fromFile);
if (permissions != -1) {
FileSystemUtil.setPermissions(toFile, permissions);
final int oldPermissions = FileSystemUtil.getPermissions(fromFile);
final int newPermissions = FileSystemUtil.getPermissions(toFile);
if (oldPermissions != -1 && newPermissions != -1) {
FileSystemUtil.setPermissions(toFile, (oldPermissions | newPermissions));
}
}
}
@@ -2,7 +2,6 @@ package com.intellij.util.io;
import com.intellij.openapi.util.io.FileUtil;
import gnu.trove.TIntIntHashMap;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
@@ -130,10 +129,11 @@ class IntToIntBtree {
return pageStart;
}
public @Nullable Integer get(int key) {
public boolean get(int key, int[] result) {
if (hasCachedMappings) {
if (myCachedMappings.containsKey(key)) {
return myCachedMappings.get(key);
result[0] = myCachedMappings.get(key);
return true;
}
}
@@ -141,8 +141,9 @@ class IntToIntBtree {
currentIndexNode.setAddress(root.address);
int index = currentIndexNode.locate(key, false);
if (index < 0) return null;
return currentIndexNode.addressAt(index);
if (index < 0) return false;
result[0] = currentIndexNode.addressAt(index);
return true;
}
public void put(int key, int value) {
@@ -186,9 +186,11 @@ public class PersistentBTreeEnumerator<Data> extends PersistentEnumeratorBase<Da
out:
for(IntToIntBtree.BtreeIndexNodeView page:leafPages) {
for(int key:page.exportKeys()) {
Integer record = btree.get(key);
boolean hasMapping = btree.get(key, myResultBuf);
p.setCurrentKey(key);
assert record != null;
assert hasMapping;
int record = myResultBuf[0];
if (record > 0) {
if (!p.process(record)) return false;
} else {
@@ -259,22 +261,23 @@ public class PersistentBTreeEnumerator<Data> extends PersistentEnumeratorBase<Da
return super.getValue(keyId, processingKey);
}
private final int[] myResultBuf = new int[1];
protected synchronized int enumerateImpl(final Data value, final boolean onlyCheckForExisting, boolean saveNewValue) throws IOException {
synchronized (ourLock) {
if (IntToIntBtree.doDump) System.out.println(value);
final int valueHC = myDataDescriptor.getHashCode(value);
final Integer keyValue = btree.get(valueHC);
if (keyValue == null && onlyCheckForExisting) {
final boolean hasMapping = btree.get(valueHC, myResultBuf);
if (!hasMapping && onlyCheckForExisting) {
return NULL_ID;
}
int indexNodeValueAddress = keyValue != null ? keyValue:0;
int indexNodeValueAddress = hasMapping ? myResultBuf[0]:0;
int collisionAddress = NULL_ID;
Data existingData = null;
if (!myInlineKeysNoMapping) {
indexNodeValueAddress = keyValue != null ? keyValue:0;
collisionAddress = NULL_ID;
if (indexNodeValueAddress > 0) {
@@ -309,7 +312,7 @@ public class PersistentBTreeEnumerator<Data> extends PersistentEnumeratorBase<Da
if (onlyCheckForExisting) return NULL_ID;
} else {
if (keyValue != null) {
if (hasMapping) {
if(!saveNewValue) return indexNodeValueAddress;
existingData = value;
}
@@ -69,25 +69,12 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
super(new BufferExposingByteArrayOutputStream());
}
public int getBufferSize() {
return ((ByteArrayOutputStream)out).size();
private void reset() {
((UnsyncByteArrayOutputStream)out).reset();
}
public void writeTo(OutputStream stream) throws IOException {
((ByteArrayOutputStream)out).writeTo(stream);
}
public void reset() {
((ByteArrayOutputStream)out).reset();
}
public byte[] toByteArray() {
return ((ByteArrayOutputStream)out).toByteArray();
}
public ByteSequence getInternalBuffer() {
final BufferExposingByteArrayOutputStream _out = (BufferExposingByteArrayOutputStream)out;
return new ByteSequence(_out.getInternalBuffer(), 0, _out.size());
private BufferExposingByteArrayOutputStream getInternalBuffer() {
return (BufferExposingByteArrayOutputStream)out;
}
}
@@ -110,16 +97,14 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
protected void onDropFromCache(final Key key, final AppendStream value) {
synchronized (PersistentEnumerator.ourLock) {
try {
final ByteSequence bytes = value.getInternalBuffer();
final BufferExposingByteArrayOutputStream bytes = value.getInternalBuffer();
final int id = enumerate(key);
HeaderRecord oldHeaderRecord = readValueId(id);
long oldHeaderRecord = readValueId(id);
HeaderRecord headerRecord = new HeaderRecord(
myValueStorage.appendBytes(bytes, oldHeaderRecord.address)
);
long headerRecord = myValueStorage.appendBytes(bytes.getInternalBuffer(), 0, bytes.size(), oldHeaderRecord);
updateValueId(id, headerRecord, oldHeaderRecord, key, 0);
if (oldHeaderRecord == HeaderRecord.EMPTY) {
if (oldHeaderRecord == NULL_ADDR) {
myLiveAndGarbageKeysCounter += LIVE_KEY_MASK;
}
@@ -266,18 +251,18 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
final AppendStream record = new AppendStream();
myValueExternalizer.save(record, value);
final ByteSequence bytes = record.getInternalBuffer();
final BufferExposingByteArrayOutputStream bytes = record.getInternalBuffer();
final int id = enumerate(key);
HeaderRecord oldheader = readValueId(id);
if (oldheader != HeaderRecord.EMPTY) {
long oldheader = readValueId(id);
if (oldheader != NULL_ADDR) {
myLiveAndGarbageKeysCounter++;
}
else {
myLiveAndGarbageKeysCounter += LIVE_KEY_MASK;
}
HeaderRecord header = new HeaderRecord(myValueStorage.appendBytes(bytes, 0));
long header = myValueStorage.appendBytes(bytes.getInternalBuffer(), 0, bytes.size(), 0);
updateValueId(id, header, oldheader, key, 0);
}
@@ -330,41 +315,39 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
myAppendCache.clear();
return myEnumerator.processAllDataObject(processor, new PersistentEnumerator.DataFilter() {
public boolean accept(final int id) {
return readValueId(id).address != NULL_ADDR;
return readValueId(id) != NULL_ADDR;
}
});
}
}
public final Value get(Key key) throws IOException {
public final @Nullable Value get(Key key) throws IOException {
synchronized (myEnumerator) {
return doGet(key);
}
}
protected Value doGet(Key key) throws IOException {
protected @Nullable Value doGet(Key key) throws IOException {
synchronized (PersistentEnumerator.ourLock) {
myAppendCache.remove(key);
final int id = tryEnumerate(key);
if (id == PersistentEnumerator.NULL_ID) {
return null;
}
final HeaderRecord oldHeader = readValueId(id);
if (oldHeader.address == PersistentEnumerator.NULL_ID) {
final long oldHeader = readValueId(id);
if (oldHeader == PersistentEnumerator.NULL_ID) {
return null;
}
Pair<Long, byte[]> readResult = myValueStorage.readBytes(oldHeader.address);
if (readResult.first != null && readResult.first != oldHeader.address) {
Pair<Long, byte[]> readResult = myValueStorage.readBytes(oldHeader);
if (readResult.first != null && readResult.first != oldHeader) {
myEnumerator.markDirty(true);
updateValueId(id, new HeaderRecord(readResult.first), oldHeader, key, 0);
if (oldHeader != HeaderRecord.EMPTY) {
myLiveAndGarbageKeysCounter++;
}
updateValueId(id, readResult.first, oldHeader, key, 0);
myLiveAndGarbageKeysCounter++;
}
final DataInputStream input = new DataInputStream(new ByteArrayInputStream(readResult.second));
final DataInputStream input = new DataInputStream(new UnsyncByteArrayInputStream(readResult.second));
try {
return myValueExternalizer.read(input);
}
@@ -387,7 +370,7 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
if (id == PersistentEnumerator.NULL_ID) {
return false;
}
return readValueId(id).address != NULL_ADDR;
return readValueId(id) != NULL_ADDR;
}
}
@@ -406,12 +389,12 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
}
myEnumerator.markDirty(true);
final HeaderRecord record = readValueId(id);
if (record != HeaderRecord.EMPTY) {
final long record = readValueId(id);
if (record != NULL_ADDR) {
myLiveAndGarbageKeysCounter++;
}
updateValueId(id, HeaderRecord.EMPTY, record, key, 0);
updateValueId(id, NULL_ADDR, record, key, 0);
}
}
@@ -473,10 +456,10 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
traverseAllRecords(new PersistentEnumerator.RecordsProcessor() {
public boolean process(final int keyId) throws IOException {
final HeaderRecord record = readValueId(keyId);
if (record.address != NULL_ADDR) {
Pair<Long, byte[]> readResult = myValueStorage.readBytes(record.address);
HeaderRecord value = new HeaderRecord(newStorage.appendBytes(new ByteSequence(readResult.second), 0));
final long record = readValueId(keyId);
if (record != NULL_ADDR) {
Pair<Long, byte[]> readResult = myValueStorage.readBytes(record);
long value = newStorage.appendBytes(new ByteSequence(readResult.second), 0);
updateValueId(keyId, value, record, null, getCurrentKey());
myLiveAndGarbageKeysCounter += LIVE_KEY_MASK;
}
@@ -496,10 +479,10 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
}
}
private HeaderRecord readValueId(final int keyId) {
private long readValueId(final int keyId) {
long address = myEnumerator.myStorage.getInt(keyId + myParentValueRefOffset);
if (address == 0 || address == -POSITIVE_VALUE_SHIFT) {
return HeaderRecord.EMPTY;
return NULL_ADDR;
}
if (address < 0) {
@@ -509,7 +492,7 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
address = ((address << 32) + value) & ~USED_LONG_VALUE_MASK;
}
return new HeaderRecord(address);
return address;
}
private int smallKeys;
@@ -517,19 +500,19 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
private int transformedKeys;
private int requests;
private int updateValueId(int keyId, HeaderRecord value, HeaderRecord oldValue, @Nullable Key key, int processingKey) throws IOException {
final boolean newKey = oldValue == null || oldValue.address == NULL_ADDR;
private int updateValueId(int keyId, long value, long oldValue, @Nullable Key key, int processingKey) throws IOException {
final boolean newKey = oldValue == NULL_ADDR;
if (newKey) ++requests;
boolean defaultSizeInfo = true;
if (myCanReEnumerate) {
if (canUseIntAddressForNewRecord(value.address)) {
if (canUseIntAddressForNewRecord(value)) {
defaultSizeInfo = false;
myEnumerator.myStorage.putInt(keyId + myParentValueRefOffset, -(int)(value.address + POSITIVE_VALUE_SHIFT));
myEnumerator.myStorage.putInt(keyId + myParentValueRefOffset, -(int)(value + POSITIVE_VALUE_SHIFT));
if (newKey) ++smallKeys;
} else {
if (newKey && myWatermarkId == 0) myWatermarkId = keyId;
if (keyId < myWatermarkId && (oldValue == null || canUseIntAddressForNewRecord(oldValue.address))) {
if (keyId < myWatermarkId && (oldValue == NULL_ADDR || canUseIntAddressForNewRecord(oldValue))) {
// keyId is result of enumerate, if we do reenumerate then it is no longer accessible unless somebody cached it
myIntAddressForNewRecord = false;
keyId = myEnumerator.reenumerate(key == null ? myEnumerator.getValue(keyId, processingKey) : key);
@@ -539,7 +522,7 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
}
if (defaultSizeInfo) {
myEnumerator.myStorage.putLong(keyId + myParentValueRefOffset, value.address | USED_LONG_VALUE_MASK);
myEnumerator.myStorage.putLong(keyId + myParentValueRefOffset, value | USED_LONG_VALUE_MASK);
if (newKey) ++largeKeys;
}
@@ -548,21 +531,11 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
",@"+getBaseFile().getPath());
}
if (doHardConsistencyChecks) {
HeaderRecord checkRecord = readValueId(keyId);
if (checkRecord.address != value.address) {
assert false:value.address;
long checkRecord = readValueId(keyId);
if (checkRecord != value) {
assert false:value;
}
}
return keyId;
}
private static class HeaderRecord {
final long address;
HeaderRecord(long address) {
this.address = address;
}
static final HeaderRecord EMPTY = new HeaderRecord(NULL_ADDR);
}
}
@@ -79,10 +79,13 @@ public class PersistentHashMapValueStorage {
private static final int INT_LENGTH_LONG_ADDRESS = 4 + 8;
public long appendBytes(ByteSequence data, long prevChunkAddress) throws IOException {
return appendBytes(data.getBytes(), data.getOffset(), data.getLength(), prevChunkAddress);
}
public long appendBytes(byte[] data, int offset, int dataLength, long prevChunkAddress) throws IOException {
assert !myCompactionMode;
long result = mySize;
final CacheValue<DataOutputStream> appender = ourAppendersCache.get(myPath);
int dataLength = data.getLength();
int serviceFieldsSizeIncrease;
try {
@@ -102,7 +105,7 @@ public class PersistentHashMapValueStorage {
dataOutputStream.writeLong(prevChunkAddress);
serviceFieldsSizeIncrease = INT_LENGTH_LONG_ADDRESS;
}
dataOutputStream.write(data.getBytes(), data.getOffset(), dataLength);
dataOutputStream.write(data, offset, dataLength);
if (requests % IOStatistics.KEYS_FACTOR == 0 && IOStatistics.DEBUG) {
IOStatistics.dump("Small writes:"+smallWritesCount +", bytes:"+smallWrites + ", largeWrites:"+largeWritesCount
+ ", bytes:"+largeWrites+", total:"+requests + "@"+myFile.getPath());
@@ -0,0 +1,69 @@
package com.intellij.util.io;
import java.io.InputStream;
public class UnsyncByteArrayInputStream extends InputStream {
protected byte[] myBuffer;
private int myPosition;
private int myCount;
private int myMarkedPosition;
public UnsyncByteArrayInputStream(byte buf[]) {
this.myBuffer = buf;
this.myPosition = 0;
this.myCount = buf.length;
}
public int read() {
return (myPosition < myCount) ? (myBuffer[myPosition++] & 0xff) : -1;
}
public int read(byte b[], int off, int len) {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (myPosition >= myCount) {
return -1;
}
if (myPosition + len > myCount) {
len = myCount - myPosition;
}
if (len <= 0) {
return 0;
}
System.arraycopy(myBuffer, myPosition, b, off, len);
myPosition += len;
return len;
}
public long skip(long n) {
if (myPosition + n > myCount) {
n = myCount - myPosition;
}
if (n < 0) {
return 0;
}
myPosition += n;
return n;
}
public int available() {
return myCount - myPosition;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public void mark(int readlimit) {
myMarkedPosition = myPosition;
}
public void reset() {
myPosition = myMarkedPosition;
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2000-2012 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.io;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
public class UnsyncByteArrayOutputStream extends OutputStream {
protected byte[] myBuffer;
protected int myCount;
public UnsyncByteArrayOutputStream() {
this(32);
}
public UnsyncByteArrayOutputStream(int size) {
myBuffer = new byte[size];
}
public void write(int b) {
int newcount = myCount + 1;
if (newcount > myBuffer.length) {
myBuffer = Arrays.copyOf(myBuffer, Math.max(myBuffer.length << 1, newcount));
}
myBuffer[myCount] = (byte)b;
myCount = newcount;
}
public void write(byte b[], int off, int len) {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
int newcount = myCount + len;
if (newcount > myBuffer.length) {
myBuffer = Arrays.copyOf(myBuffer, Math.max(myBuffer.length << 1, newcount));
}
System.arraycopy(b, off, myBuffer, myCount, len);
myCount = newcount;
}
public void writeTo(OutputStream out) throws IOException {
out.write(myBuffer, 0, myCount);
}
public void reset() {
myCount = 0;
}
public byte[] toByteArray() {
return Arrays.copyOf(myBuffer, myCount);
}
public int size() {
return myCount;
}
public String toString() {
return new String(myBuffer, 0, myCount);
}
}
@@ -28,6 +28,7 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.io.PagePool;
import com.intellij.util.io.RecordDataOutput;
import com.intellij.util.io.UnsyncByteArrayInputStream;
import org.jetbrains.annotations.NonNls;
import java.io.*;
@@ -228,7 +229,7 @@ public abstract class AbstractStorage implements Disposable, Forceable {
public DataInputStream readStream(int record) throws IOException {
final byte[] bytes = readBytes(record);
return new DataInputStream(new ByteArrayInputStream(bytes));
return new DataInputStream(new UnsyncByteArrayInputStream(bytes));
}
protected byte[] readBytes(int record) throws IOException {
@@ -223,7 +223,7 @@ public abstract class VcsVFSListener implements Disposable {
getSingleFileDeletePromptTemplate(), myRemoveOption);
}
protected void beforeContentsChange(VirtualFile file) {
protected void beforeContentsChange(VirtualFileEvent event, VirtualFile file) {
}
private void addFileToMove(final VirtualFile file, final String newParentPath, final String newName) {
@@ -378,10 +378,8 @@ public abstract class VcsVFSListener implements Disposable {
@Override
public void beforeContentsChange(VirtualFileEvent event) {
if (!isEventIgnored(event, false)) {
assert !event.getFile().isDirectory();
VcsVFSListener.this.beforeContentsChange(event.getFile());
}
assert !event.getFile().isDirectory();
VcsVFSListener.this.beforeContentsChange(event, event.getFile());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2010 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,7 @@ import com.siyeh.ig.psiutils.TypeUtils;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import org.jetbrains.annotations.NotNull;
public class StringBufferReplaceableByStringInspection
extends BaseInspection {
public class StringBufferReplaceableByStringInspection extends BaseInspection {
@Override
@NotNull
@@ -46,21 +45,17 @@ public class StringBufferReplaceableByStringInspection
return new StringBufferReplaceableByStringBuilderVisitor();
}
private static class StringBufferReplaceableByStringBuilderVisitor
extends BaseInspectionVisitor {
private static class StringBufferReplaceableByStringBuilderVisitor extends BaseInspectionVisitor {
@Override
public void visitLocalVariable(@NotNull PsiLocalVariable variable) {
super.visitLocalVariable(variable);
final PsiCodeBlock codeBlock =
PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
if (codeBlock == null) {
return;
}
final PsiType type = variable.getType();
if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUFFER,
type) &&
if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUFFER, type) &&
!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUILDER, type)) {
return;
}
@@ -80,8 +75,7 @@ public class StringBufferReplaceableByStringInspection
if (VariableAccessUtils.variableIsReturned(variable, codeBlock)) {
return;
}
if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable,
codeBlock)) {
if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, codeBlock)) {
return;
}
if (variableIsModified(variable, codeBlock)) {
@@ -90,16 +84,13 @@ public class StringBufferReplaceableByStringInspection
registerVariableError(variable);
}
public static boolean variableIsModified(PsiVariable variable,
PsiElement context) {
final VariableIsModifiedVisitor visitor =
new VariableIsModifiedVisitor(variable);
public static boolean variableIsModified(PsiVariable variable, PsiElement context) {
final VariableIsModifiedVisitor visitor = new VariableIsModifiedVisitor(variable);
context.accept(visitor);
return visitor.isModified();
}
private static boolean isNewStringBufferOrStringBuilder(
PsiExpression expression) {
private static boolean isNewStringBufferOrStringBuilder(PsiExpression expression) {
if (expression == null) {
return false;
}
@@ -107,16 +98,12 @@ public class StringBufferReplaceableByStringInspection
return true;
}
else if (expression instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression =
(PsiMethodCallExpression)expression;
if (!VariableIsModifiedVisitor.isStringBufferUpdate(
methodCallExpression)) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
if (!VariableIsModifiedVisitor.isStringBufferUpdate(methodCallExpression)) {
return false;
}
final PsiReferenceExpression methodExpression =
methodCallExpression.getMethodExpression();
final PsiExpression qualifier =
methodExpression.getQualifierExpression();
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
final PsiExpression qualifier = methodExpression.getQualifierExpression();
return isNewStringBufferOrStringBuilder(qualifier);
}
return false;
@@ -0,0 +1,11 @@
package com.siyeh.igtest.performance.constant_string_buffer_may_be_string;
public class StringBufferReplaceableByString {
StringBuilder foo() {
StringBuilder builder = new StringBuilder("asdfasdfasdf");
StringBuffer buffer = new StringBuffer("test");
StringBuilder result = new StringBuilder("return");
return result;
}
}
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>StringBufferReplaceableByString.java</file>
<line>6</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Constant StringBuffer may be String</problem_class>
<description>Constant StringBuffer &lt;code&gt;builder&lt;/code&gt; may be declared as String #loc</description>
</problem>
<problem>
<file>StringBufferReplaceableByString.java</file>
<line>7</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Constant StringBuffer may be String</problem_class>
<description>Constant StringBuffer &lt;code&gt;buffer&lt;/code&gt; may be declared as String #loc</description>
</problem>
</problems>
@@ -0,0 +1,11 @@
package com.siyeh.ig.performance;
import com.siyeh.ig.IGInspectionTestCase;
public class StringBufferReplaceableByStringInspectionTest extends IGInspectionTestCase {
public void test() throws Exception {
doTest("com/siyeh/igtest/performance/constant_string_buffer_may_be_string",
new StringBufferReplaceableByStringInspection());
}
}
@@ -1,6 +1,6 @@
command.0.execution.failed.with.exit.code.1=Command \"{0}\" execution failed with exit code {1}
aidl.filetype.description=Android IDL files
add.string.resource.intention.text=Add a string resource
add.string.resource.intention.text=Extract string resource
resource.name=Resource name
intention.family=Android
package.not.found.error=Package is not specified in the manifest file
@@ -8,8 +8,8 @@ cannot.resolve.flag.error=Cannot resolve flag
cannot.resolve.format.error=Cannot resolve format
create.resource.quickfix.name=Create resource {0} in {1}
quick.fixes.family=Android Quick Fixes
not.resource.file.error="File {0} is not resource file"
check.resource.dir.error=Check if resource directory exists
not.resource.file.error=File {0} is not resource file
check.resource.dir.error=Cannot find resource directory for module {0}
choose.device.dialog.title=Choose Device
replace.avd.question=AVD {0} already exists. Replace?
create.avd.dialog.title=Create Android Virtual Device
@@ -147,6 +147,7 @@ create.run.configuration.title=Create Android Run Configuration
file.must.be.under.module.error=Cannot compute relative path for {0}
android.extract.package.choose.dest.apk=Choose destination APK file
android.cannot.create.file.error=Cannot create file {0}
android.cannot.create.dir.error=Cannot create directory {0}
android.manifest.not.found.error=AndroidManifest.xml doesn't exist or has incorrect root tag
android.apt.settings.title=AAPT Compiler
android.aidl.settings.title=AIDL Compiler
@@ -305,4 +306,20 @@ android.facet.settings.custom.debug.keystore.label=C&ustom debug keystore\:
android.export.package.run.proguard.label=Run &ProGuard
android.export.package.proguad.config.label=C&onfig file path\:
android.extract.package.specify.proguard.cfg.path.error=Please specify ProGuard config file path
android.inspections.lint.name=Android Lint Validation
android.inspections.lint.name=Android Lint Validation
android.lint.quickfixes.family=Android Lint Quick Fixes
android.lint.inspections.add.content.description=Add 'contentDescription' attribute
android.lint.inspections.scroll.view.children=ScrollView element can have only one child
android.lint.inspections.deprecated=Deprecated XML elements
android.lint.inspections.missing.prefix=Missing android XML namespace
android.lint.inspections.adapter.view.children=AdapterView element cannot have children
android.lint.inspections.add.android.prefix=Add android prefix
android.lint.inspections.replace.with.zero.dp=Replace size attribute with 0dp
android.lint.inspections.set.baseline.attribute=Set 'baselineAligned' attribute
android.lint.inspections.remove.attribute=Remove attribute
android.lint.inspections.convert.to.dp=Convert to \\"dp\\"...
android.lint.inspections.set.to.wrap.content=Replace size attribute with 'wrap_content'
android.lint.inspections.add.permission.attribute=Add 'permission' attribute
android.lint.inspections.add.input.type.attribute=Add 'inputType' attribute
android.lint.inspections.remove.unnecessary.view=Remove unnecessary view
android.lint.inspections.replace.with.suggested.characters=Replace with suggested characters
@@ -3,7 +3,7 @@
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="5" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="407" height="170"/>
<xy x="20" y="20" width="417" height="188"/>
</constraints>
<properties/>
<border type="none"/>
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.android.actions.CreateXmlResourceDialog">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="5" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="82472" binding="myDeviceConfigurationWrapper" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
<component id="43fab" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="83b48"/>
<text value="&amp;Name:"/>
</properties>
</component>
<component id="83b48" class="javax.swing.JTextField" binding="myNameField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="c0cba" class="javax.swing.JComboBox" binding="myModuleCombo">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="61abf" class="com.intellij.ui.components.JBLabel" binding="myModuleLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="c0cba"/>
<text value="&amp;Module:"/>
</properties>
</component>
<component id="d6c07" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="832da"/>
<text value="&amp;Directory name:"/>
</properties>
</component>
<component id="832da" class="javax.swing.JTextField" binding="myDirectoryNameField">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="da401" class="com.intellij.ui.components.JBLabel" binding="myErrorLabel">
<constraints>
<grid row="5" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="21872" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="185f7"/>
<text value="&amp;File name:"/>
</properties>
</component>
<component id="185f7" class="javax.swing.JTextField" binding="myFileNameField">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
</children>
</grid>
</form>
@@ -0,0 +1,264 @@
package org.jetbrains.android.actions;
import com.android.AndroidConstants;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.intellij.CommonBundle;
import com.intellij.ide.ui.ListCellRendererWrapper;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.containers.HashSet;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.resources.Resources;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.uipreview.DeviceConfiguratorPanel;
import org.jetbrains.android.uipreview.InvalidOptionValueException;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
/**
* @author Eugene.Kudelevsky
*/
public class CreateXmlResourceDialog extends DialogWrapper {
private JPanel myPanel;
private JPanel myDeviceConfigurationWrapper;
private JTextField myNameField;
private JComboBox myModuleCombo;
private JBLabel myModuleLabel;
private JTextField myDirectoryNameField;
private JBLabel myErrorLabel;
private JTextField myFileNameField;
private final DeviceConfiguratorPanel myDeviceConfiguratorPanel;
private final Module myModule;
private final ResourceType myResourceType;
public CreateXmlResourceDialog(@NotNull Module module, @NotNull ResourceType resourceType) {
super(module.getProject());
myResourceType = resourceType;
final Set<Module> modulesSet = new HashSet<Module>();
modulesSet.add(module);
for (AndroidFacet depFacet : AndroidUtils.getAllAndroidDependencies(module, true)) {
modulesSet.add(depFacet.getModule());
}
assert modulesSet.size() > 0;
if (modulesSet.size() == 1) {
myModule = module;
myModuleLabel.setVisible(false);
myModuleCombo.setVisible(false);
}
else {
myModule = null;
final Module[] modules = modulesSet.toArray(new Module[modulesSet.size()]);
Arrays.sort(modules, new Comparator<Module>() {
@Override
public int compare(Module m1, Module m2) {
return m1.getName().compareTo(m2.getName());
}
});
myModuleCombo.setModel(new DefaultComboBoxModel(modules));
myModuleCombo.setSelectedItem(module);
myModuleCombo.setRenderer(new ListCellRendererWrapper<Module>(myModuleCombo.getRenderer()) {
@Override
public void customize(JList list, Module module, int index, boolean selected, boolean hasFocus) {
setText(module.getName());
setIcon(ModuleType.get(module).getNodeIcon(false));
}
});
}
myDeviceConfiguratorPanel = new DeviceConfiguratorPanel(null) {
@Override
public void applyEditors() {
try {
doApplyEditors();
final FolderConfiguration config = myDeviceConfiguratorPanel.getConfiguration();
myErrorLabel.setText("");
myDirectoryNameField.setText(config.getFolderName(ResourceFolderType.VALUES));
}
catch (InvalidOptionValueException e) {
myErrorLabel.setText("<html><body><font color=\"red\">" + e.getMessage() + "</font></body></html>");
myDirectoryNameField.setText(AndroidConstants.FD_RES_VALUES);
}
}
};
myDeviceConfigurationWrapper.add(myDeviceConfiguratorPanel, BorderLayout.CENTER);
final String defaultResFileName = ResourceManager.getDefaultResourceFileName(resourceType.getName());
if (defaultResFileName != null) {
myFileNameField.setText(defaultResFileName);
}
myDirectoryNameField.setText(AndroidConstants.FD_RES_VALUES);
myDeviceConfiguratorPanel.updateAll();
init();
}
@Override
protected ValidationInfo doValidate() {
final String resourceName = getResourceName();
final Module selectedModule = getModule();
final String directoryName = getDirectoryName();
final String fileName = getFileName();
if (resourceName.length() == 0) {
return new ValidationInfo("specify resource name", myNameField);
}
else if (!AndroidResourceUtil.isCorrectAndroidResourceName(resourceName)) {
return new ValidationInfo(resourceName + " is not correct resource name", myNameField);
}
else if (fileName.length() == 0) {
return new ValidationInfo("specify file name", myFileNameField);
}
else if (selectedModule == null) {
return new ValidationInfo("specify module", myModuleCombo);
}
else if (!ResourceFolderType.VALUES.getName().equals(
ResourceManager.getResourceTypeByDirName(directoryName))) {
return new ValidationInfo("directory name is not appropriate for value resources");
}
final ValidationInfo info = checkIfResourceAlreadyExists(selectedModule, resourceName, myResourceType, directoryName, fileName);
if (info != null) {
return info;
}
try {
myDeviceConfiguratorPanel.doApplyEditors();
}
catch (InvalidOptionValueException e) {
return new ValidationInfo("fix errors in configuration editor");
}
return null;
}
@Nullable
private static ValidationInfo checkIfResourceAlreadyExists(@NotNull Module selectedModule,
@NotNull String resourceName,
@NotNull ResourceType resourceType,
@NotNull String directoryName,
@NotNull String fileName) {
if (resourceName.length() == 0 ||
directoryName.length() == 0 ||
fileName.length() == 0) {
return null;
}
final VirtualFile resourceDir = AndroidRootUtil.getResourceDir(selectedModule);
if (resourceDir == null) {
return null;
}
final VirtualFile resourceSubdir = resourceDir.findChild(directoryName);
if (resourceSubdir == null) {
return null;
}
final VirtualFile resFile = resourceSubdir.findChild(fileName);
if (resFile == null) {
return null;
}
if (resFile.getFileType() != StdFileTypes.XML) {
return new ValidationInfo("File " + FileUtil.toSystemDependentName(resFile.getPath()) + " is not XML file");
}
final Resources resources = AndroidUtils.loadDomElement(selectedModule, resFile, Resources.class);
if (resources == null) {
return new ValidationInfo(AndroidBundle.message("not.resource.file.error", FileUtil.toSystemDependentName(resFile.getPath())));
}
for (ResourceElement element : ResourceManager.getValueResources(resourceType.getName(), resources)) {
if (resourceName.equals(element.getName().getValue())) {
return new ValidationInfo("resource '" + resourceName + "' already exists in " + FileUtil.toSystemDependentName(
resFile.getPath()));
}
}
return null;
}
@Override
public JComponent getPreferredFocusedComponent() {
return myNameField;
}
@Override
protected void doOKAction() {
final String resourceName = getResourceName();
final String fileName = getFileName();
final String dirName = getDirectoryName();
final Module module = getModule();
if (resourceName.length() == 0) {
Messages.showErrorDialog(myPanel, "Resource name is not specified", CommonBundle.getErrorTitle());
}
else if (!AndroidResourceUtil.isCorrectAndroidResourceName(resourceName)) {
Messages.showErrorDialog(myPanel, resourceName + " is not correct resource name", CommonBundle.getErrorTitle());
}
else if (fileName.length() == 0) {
Messages.showErrorDialog(myPanel, "File name is not specified", CommonBundle.getErrorTitle());
}
else if (dirName.length() == 0) {
Messages.showErrorDialog(myPanel, "Directory name is not specified", CommonBundle.getErrorTitle());
}
else if (module == null) {
Messages.showErrorDialog(myPanel, "Module is not specified", CommonBundle.getErrorTitle());
}
else {
super.doOKAction();
}
}
@NotNull
public String getResourceName() {
return myNameField.getText().trim();
}
@NotNull
public String getDirectoryName() {
return myDirectoryNameField.getText().trim();
}
@NotNull
public String getFileName() {
return myFileNameField.getText().trim();
}
@Nullable
public Module getModule() {
return myModule != null ? myModule : (Module)myModuleCombo.getSelectedItem();
}
@Override
protected JComponent createCenterPanel() {
return myPanel;
}
}
@@ -165,7 +165,12 @@ public class ResourceReferenceConverter extends ResolvingConverter<ResourceValue
}
private Set<String> getResourceTypes(ConvertContext context) {
ResourceType resourceType = context.getInvocationElement().getAnnotation(ResourceType.class);
return getResourceTypes(context.getInvocationElement());
}
@NotNull
public Set<String> getResourceTypes(@NotNull DomElement element) {
ResourceType resourceType = element.getAnnotation(ResourceType.class);
Set<String> types = new HashSet<String>(myResourceTypes);
if (resourceType != null) {
String s = resourceType.value();
@@ -0,0 +1,72 @@
package org.jetbrains.android.inspections.lint;
import com.android.sdklib.SdkConstants;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.xml.XmlExtension;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
/**
* @author Eugene.Kudelevsky
*/
class AddMissingPrefixQuickFix implements AndroidLintQuickFix {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.AddMissingPrefixQuickFix");
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class, false);
if (attribute == null) {
return;
}
final XmlTag tag = attribute.getParent();
if (tag == null) {
LOG.debug("tag is null");
return;
}
String androidNsPrefix = tag.getPrefixByNamespace(SdkConstants.NS_RESOURCES);
if (androidNsPrefix == null) {
final PsiFile file = tag.getContainingFile();
final XmlExtension extension = XmlExtension.getExtension(file);
if (extension == null) {
LOG.debug("Cannot get XmlExtension for file + " + file);
return;
}
if (!(file instanceof XmlFile)) {
LOG.debug(file + " is not XmlFile");
return;
}
final XmlFile xmlFile = (XmlFile)file;
final String defaultPrefix = "android";
extension.insertNamespaceDeclaration(xmlFile, null, Collections.singleton(SdkConstants.NS_RESOURCES), defaultPrefix, null);
androidNsPrefix = defaultPrefix;
}
attribute.setName(androidNsPrefix + ':' + attribute.getLocalName());
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
return PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class, false) != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.add.android.prefix");
}
}
@@ -0,0 +1,47 @@
package org.jetbrains.android.inspections.lint;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.android.intentions.AndroidAddStringResourceAction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidAddStringResourceQuickFix extends AndroidAddStringResourceAction {
private final PsiElement myStartElement;
public AndroidAddStringResourceQuickFix(@NotNull PsiElement startElement) {
myStartElement = startElement;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return getAttributeValue(myStartElement) != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
invokeIntention(project, editor, file, null);
}
public void invokeIntention(Project project, Editor editor, PsiFile file, String resName) {
final XmlAttributeValue attributeValue = getAttributeValue(myStartElement);
if (attributeValue != null) {
doInvoke(project, editor, file, resName, attributeValue);
}
}
@Nullable
private static XmlAttributeValue getAttributeValue(@NotNull PsiElement element) {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class);
return attribute != null ? attribute.getValueElement() : null;
}
}
@@ -7,11 +7,11 @@ import com.android.tools.lint.client.api.Lint;
import com.android.tools.lint.detector.api.Issue;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.ex.CustomEditInspectionToolsSettingsAction;
import com.intellij.codeInspection.ex.DisableInspectionToolAction;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.ex.EditInspectionToolsSettingsAction;
import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.ExternalAnnotator;
@@ -22,7 +22,10 @@ import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiElement;
@@ -30,6 +33,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -62,7 +66,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
}
final FileType fileType = file.getFileType();
if (fileType == StdFileTypes.XML) {
if (facet.getLocalResourceManager().getFileResourceType(file) == null &&
AndroidRootUtil.getManifestFile(module) != vFile) {
@@ -95,7 +99,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
return state.getIssues();
}
}, client);
lint.analyze(Collections.singletonList(new File(state.getMainFile().getPath())), null);
}
finally {
@@ -126,10 +130,10 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
if (!enabled) {
continue;
}
result.add(issue);
result.add(issue);
}
return result;
}
}
@Override
public void apply(@NotNull PsiFile file, State state, @NotNull AnnotationHolder holder) {
@@ -141,31 +145,41 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
final Issue issue = problemData.getIssue();
final String message = problemData.getMessage();
final TextRange range = problemData.getTextRange();
if (range.getStartOffset() == range.getEndOffset()) {
continue;
}
final Pair<AndroidLintInspectionBase, HighlightDisplayLevel> pair = getHighlighLevelAndInspection(issue, file);
final Pair<AndroidLintInspectionBase, HighlightDisplayLevel> pair = AndroidLintUtil.getHighlighLevelAndInspection(issue, file);
if (pair == null) {
continue;
}
final AndroidLintInspectionBase inspection = pair.getFirst();
final HighlightDisplayLevel displayLevel = pair.getSecond();
final Annotation annotation = createAnnotation(holder, message, range, displayLevel);
if (inspection != null) {
final HighlightDisplayKey key = HighlightDisplayKey.find(inspection.getShortName());
if (key != null) {
annotation.registerFix(new MyDisableInspectionFix(key));
annotation.registerFix(new CustomEditInspectionToolsSettingsAction(key, new Computable<String>() {
@Override
public String compute() {
return "Edit '" + inspection.getDisplayName() + "' inspection settings";
final PsiElement startElement = file.findElementAt(range.getStartOffset());
final PsiElement endElement = file.findElementAt(range.getEndOffset() - 1);
if (startElement != null && endElement != null) {
for (AndroidLintQuickFix fix : inspection.getQuickFixes(message)) {
if (fix.isApplicable(startElement, endElement, false)) {
annotation.registerFix(new MyFixingIntention(fix, startElement, endElement));
}
}
}));
for (IntentionAction intention : inspection.getIntentions(startElement, endElement)) {
annotation.registerFix(intention);
}
}
annotation.registerFix(new MyDisableInspectionFix(key));
annotation.registerFix(new MyEditInspectionToolsSettingsAction(key, inspection));
}
}
}
@@ -189,36 +203,6 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
}
}
@Nullable
private static Pair<AndroidLintInspectionBase, HighlightDisplayLevel> getHighlighLevelAndInspection(@NotNull Issue issue,
@NotNull PsiElement context) {
final String inspectionShortName = AndroidLintInspectionBase.getInspectionShortNameByIssue(issue);
if (inspectionShortName == null) {
return null;
}
final HighlightDisplayKey key = HighlightDisplayKey.find(inspectionShortName);
if (key == null) {
return null;
}
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(context.getProject()).getInspectionProfile();
if (!profile.isToolEnabled(key, context)) {
return null;
}
final InspectionToolWrapper toolWrapper =
(InspectionToolWrapper)profile.getInspectionTool(inspectionShortName, context);
if (toolWrapper == null) {
return null;
}
final AndroidLintInspectionBase inspection = (AndroidLintInspectionBase)toolWrapper.getTool();
final HighlightDisplayLevel errorLevel = profile.getErrorLevel(key, context);
return new Pair<AndroidLintInspectionBase, HighlightDisplayLevel>(inspection,
errorLevel != null ? errorLevel : HighlightDisplayLevel.WARNING);
}
private static class MyDisableInspectionFix implements IntentionAction, Iconable {
private final DisableInspectionToolAction myDisableInspectionToolAction;
@@ -258,4 +242,90 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
return myDisableInspectionToolAction.getIcon(flags);
}
}
public static class MyFixingIntention implements IntentionAction, HighPriorityAction {
private final AndroidLintQuickFix myQuickFix;
private final PsiElement myStartElement;
private final PsiElement myEndElement;
public MyFixingIntention(@NotNull AndroidLintQuickFix quickFix, @NotNull PsiElement startElement, @NotNull PsiElement endElement) {
myQuickFix = quickFix;
myStartElement = startElement;
myEndElement = endElement;
}
@NotNull
@Override
public String getText() {
return myQuickFix.getName();
}
@NotNull
@Override
public String getFamilyName() {
return AndroidBundle.message("android.lint.quickfixes.family");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
myQuickFix.apply(myStartElement, myEndElement, editor);
}
@Override
public boolean startInWriteAction() {
return true;
}
@NotNull
public AndroidLintQuickFix getQuickFix() {
return myQuickFix;
}
}
private static class MyEditInspectionToolsSettingsAction implements IntentionAction, Iconable {
private final EditInspectionToolsSettingsAction myEditInspectionToolsSettingsAction;
private final AndroidLintInspectionBase myInspection;
private MyEditInspectionToolsSettingsAction(@NotNull HighlightDisplayKey key, @NotNull final AndroidLintInspectionBase inspection) {
myEditInspectionToolsSettingsAction = new EditInspectionToolsSettingsAction(key);
myInspection = inspection;
}
@NotNull
@Override
public String getText() {
return "Edit '" + myInspection.getDisplayName() + "' inspection settings";
}
@NotNull
@Override
public String getFamilyName() {
return myEditInspectionToolsSettingsAction.getFamilyName();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
myEditInspectionToolsSettingsAction.invoke(project, editor, file);
}
@Override
public boolean startInWriteAction() {
return myEditInspectionToolsSettingsAction.startInWriteAction();
}
@Override
public Icon getIcon(@IconFlags int flags) {
return myEditInspectionToolsSettingsAction.getIcon(flags);
}
}
}
@@ -1,10 +1,7 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.PositionXmlParser;
import com.android.tools.lint.client.api.IDomParser;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.client.api.Lint;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.client.api.*;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
@@ -83,7 +80,7 @@ class AndroidLintGlobalInspectionContext implements GlobalInspectionContextExten
final File[] ioContentRoots = toIoFiles(allContentRoots);
final AnalysisScope scope = context.getRefManager().getScope();
final LintClient client = new MyLintClient(project, problemMap, scope);
final LintClient client = new MyLintClient(project, problemMap, scope, issues);
final Lint lint = new Lint(new IssueRegistry() {
@Override
public List<Issue> getIssues() {
@@ -128,13 +125,21 @@ class AndroidLintGlobalInspectionContext implements GlobalInspectionContextExten
private final Project myProject;
private final Map<Issue, Map<File, List<ProblemData>>> myProblemMap;
private final AnalysisScope myScope;
private final Collection<Issue> myIssues;
private MyLintClient(@NotNull Project project,
@NotNull Map<Issue, Map<File, List<ProblemData>>> problemMap,
@NotNull AnalysisScope scope) {
@NotNull AnalysisScope scope,
@NotNull Collection<Issue> issues) {
myProject = project;
myProblemMap = problemMap;
myScope = scope;
myIssues = issues;
}
@Override
public Configuration getConfiguration(com.android.tools.lint.detector.api.Project project) {
return new IntellijLintConfiguration(myIssues);
}
@Override
@@ -248,6 +253,9 @@ class AndroidLintGlobalInspectionContext implements GlobalInspectionContextExten
@Override
public List<File> getJavaSourceFolders(com.android.tools.lint.detector.api.Project project) {
final Module module = findModuleForLintProject(myProject, project);
if (module == null) {
return Collections.emptyList();
}
final VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(false);
final List<File> result = new ArrayList<File>(sourceRoots.length);
@@ -5,11 +5,13 @@ import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Severity;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
@@ -34,12 +36,12 @@ import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
public abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.AndroidLintInspectionBase");
private static final Map<Issue, String> ourIssue2InspectionShortName = new HashMap<Issue, String>();
private final Issue myIssue;
protected final Issue myIssue;
private final String[] myGroupPath;
private final String myDisplayName;
@@ -58,6 +60,29 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
addIssue(issue, getShortName());
}
@NotNull
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return AndroidLintQuickFix.EMPTY_ARRAY;
}
@NotNull
protected IntentionAction[] getIntentions(@NotNull PsiElement startElement, @NotNull PsiElement endElement) {
return IntentionAction.EMPTY_ARRAY;
}
@NotNull
private LocalQuickFix[] getLocalQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) {
final AndroidLintQuickFix[] fixes = getQuickFixes(message);
final LocalQuickFix[] result = new LocalQuickFix[fixes.length];
for (int i = 0; i < fixes.length; i++) {
if (fixes[i].isApplicable(startElement, endElement, true)) {
result[i] = new MyLocalQuickFix(fixes[i]);
}
}
return result;
}
@Override
public void runInspection(AnalysisScope scope,
final InspectionManager manager,
@@ -81,7 +106,7 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
for (final Map.Entry<File, List<ProblemData>> entry : file2ProblemList.entrySet()) {
final File file = entry.getKey();
final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file);
if (vFile == null) {
continue;
}
@@ -104,9 +129,9 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
}
@NotNull
private static ProblemDescriptor[] computeProblemDescriptors(@NotNull PsiFile psiFile,
@NotNull InspectionManager manager,
@NotNull List<ProblemData> problems) {
private ProblemDescriptor[] computeProblemDescriptors(@NotNull PsiFile psiFile,
@NotNull InspectionManager manager,
@NotNull List<ProblemData> problems) {
final List<ProblemDescriptor> result = new ArrayList<ProblemDescriptor>();
for (ProblemData problemData : problems) {
@@ -128,9 +153,9 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
}
}
}
if (f != null) {
result.add(manager.createProblemDescriptor(f, message, false, LocalQuickFix.EMPTY_ARRAY,
result.add(manager.createProblemDescriptor(f, message, false, getLocalQuickFixes(f, f, message),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING));
}
}
@@ -140,7 +165,8 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
if (startElement != null && endElement != null) {
result.add(manager.createProblemDescriptor(startElement, endElement, message + "#loc",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false));
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false,
getLocalQuickFixes(startElement, endElement, message)));
}
}
}
@@ -218,7 +244,7 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
@Override
public boolean isEnabledByDefault() {
return true;
return myIssue.isEnabledByDefault();
}
@NotNull
@@ -254,4 +280,29 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool {
return null;
}
}
static class MyLocalQuickFix implements LocalQuickFix {
private final AndroidLintQuickFix myLintQuickFix;
MyLocalQuickFix(@NotNull AndroidLintQuickFix lintQuickFix) {
myLintQuickFix = lintQuickFix;
}
@NotNull
@Override
public String getName() {
return myLintQuickFix.getName();
}
@NotNull
@Override
public String getFamilyName() {
return AndroidBundle.message("android.lint.quickfixes.family");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
myLintQuickFix.apply(descriptor.getStartElement(), descriptor.getEndElement(), null);
}
}
}
@@ -1,7 +1,13 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.checks.*;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintConstants;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.InspectionToolProvider;
import com.intellij.psi.PsiElement;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene.Kudelevsky
@@ -9,11 +15,11 @@ import com.intellij.codeInspection.InspectionToolProvider;
public class AndroidLintInspectionToolProvider implements InspectionToolProvider {
@Override
public Class[] getInspectionClasses() {
return new Class[] {
return new Class[]{
AndroidLintContentDescriptionInspection.class,
AndroidLintAdapterViewChildrenInspection.class,
AndroidLintScrollViewCountInspection.class,
AndroidLintDeprecatedInspection.class,
//AndroidLintDeprecatedInspection.class,
AndroidLintMissingPrefixInspection.class,
AndroidLintDuplicateIdsInspection.class,
AndroidLintGridLayoutInspection.class,
@@ -169,29 +175,44 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintContentDescriptionInspection() {
super("Missing content description", AccessibilityDetector.ISSUE);
}
@NotNull
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.add.content.description"),
LintConstants.ATTR_CONTENT_DESCRIPTION, null)
};
}
}
public static class AndroidLintAdapterViewChildrenInspection extends AndroidLintInspectionBase {
public AndroidLintAdapterViewChildrenInspection() {
super("AdapterView element cannot have children", ChildCountDetector.ADAPTERVIEW_ISSUE);
super(AndroidBundle.message("android.lint.inspections.adapter.view.children"), ChildCountDetector.ADAPTERVIEW_ISSUE);
}
}
public static class AndroidLintScrollViewCountInspection extends AndroidLintInspectionBase {
public AndroidLintScrollViewCountInspection() {
super("ScrollView element can have only one child", ChildCountDetector.SCROLLVIEW_ISSUE);
super(AndroidBundle.message("android.lint.inspections.scroll.view.children"), ChildCountDetector.SCROLLVIEW_ISSUE);
}
}
public static class AndroidLintDeprecatedInspection extends AndroidLintInspectionBase {
// it seems we don't need it because we have our own 'deprecated api' inspection
/*public static class AndroidLintDeprecatedInspection extends AndroidLintInspectionBase {
public AndroidLintDeprecatedInspection() {
super("Deprecated XML elements", DeprecationDetector.ISSUE);
super(AndroidBundle.message("android.lint.inspections.deprecated"), DeprecationDetector.ISSUE);
}
}
}*/
public static class AndroidLintMissingPrefixInspection extends AndroidLintInspectionBase {
public AndroidLintMissingPrefixInspection() {
super("Missing android XML namespace", DetectMissingPrefix.MISSING_NAMESPACE);
super(AndroidBundle.message("android.lint.inspections.missing.prefix"), DetectMissingPrefix.MISSING_NAMESPACE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new AddMissingPrefixQuickFix()};
}
}
@@ -211,12 +232,26 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintHardcodedTextInspection() {
super("Hardcoded text", HardcodedValuesDetector.ISSUE);
}
@NotNull
@Override
protected IntentionAction[] getIntentions(@NotNull final PsiElement startElement, @NotNull PsiElement endElement) {
return new IntentionAction[]{new AndroidAddStringResourceQuickFix(startElement)};
}
}
public static class AndroidLintInefficientWeightInspection extends AndroidLintInspectionBase {
public AndroidLintInefficientWeightInspection() {
super("Inefficient layout weight", InefficientWeightDetector.INEFFICIENT_WEIGHT);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new InefficientWeightQuickFix()
};
}
}
public static class AndroidLintNestedWeightsInspection extends AndroidLintInspectionBase {
@@ -229,6 +264,15 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintDisableBaselineAlignmentInspection() {
super("Missing baselineAligned attribute", InefficientWeightDetector.BASELINE_WEIGHTS);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.set.baseline.attribute"),
LintConstants.ATTR_BASELINE_ALIGNED, "false")
};
}
}
public static class AndroidLintManifestOrderInspection extends AndroidLintInspectionBase {
@@ -253,6 +297,12 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintObsoleteLayoutParamInspection() {
super("Obsolete layout params", ObsoleteLayoutParamsDetector.ISSUE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new RemoveAttributeQuickFix()};
}
}
public static class AndroidLintProguardInspection extends AndroidLintInspectionBase {
@@ -265,18 +315,39 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintPxUsageInspection() {
super("Using 'px' dimension", PxUsageDetector.ISSUE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new ConvertToDpQuickFix()};
}
}
public static class AndroidLintScrollViewSizeInspection extends AndroidLintInspectionBase {
public AndroidLintScrollViewSizeInspection() {
super("ScrollView size validation", ScrollViewChildDetector.ISSUE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new SetScrollViewSizeQuickFix()};
}
}
public static class AndroidLintExportedServiceInspection extends AndroidLintInspectionBase {
public AndroidLintExportedServiceInspection() {
super("Exported service does not require permission", SecurityDetector.EXPORTED_SERVICE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.add.permission.attribute"),
LintConstants.ATTR_PERMISSION, null)
};
}
}
public static class AndroidLintGrantAllUrisInspection extends AndroidLintInspectionBase {
@@ -295,6 +366,15 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintTextFieldsInspection() {
super("Text field missing inputType or hint settings", TextFieldDetector.ISSUE);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{
new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.add.input.type.attribute"),
LintConstants.ATTR_INPUT_TYPE, null)
};
}
}
public static class AndroidLintTooManyViewsInspection extends AndroidLintInspectionBase {
@@ -309,31 +389,31 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
}
}
public static class AndroidLintTypographyDashesInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyDashesInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyDashesInspection() {
super("Hyphen can be replaced by dash", TypographyDetector.DASHES);
}
}
public static class AndroidLintTypographyQuotesInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyQuotesInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyQuotesInspection() {
super("Straight quotes can be replaced by curvy quotes", TypographyDetector.QUOTES);
}
}
public static class AndroidLintTypographyFractionsInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyFractionsInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyFractionsInspection() {
super("Fraction string can be replaced with fraction character", TypographyDetector.FRACTIONS);
}
}
public static class AndroidLintTypographyEllipsisInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyEllipsisInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyEllipsisInspection() {
super("Ellipsis string can be replaced with ellipsis character", TypographyDetector.ELLIPSIS);
}
}
public static class AndroidLintTypographyOtherInspection extends AndroidLintInspectionBase {
public static class AndroidLintTypographyOtherInspection extends AndroidLintTypographyInspectionBase {
public AndroidLintTypographyOtherInspection() {
super("Other typographical problems", TypographyDetector.OTHER);
}
@@ -355,5 +435,23 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider
public AndroidLintUselessLeafInspection() {
super("Useless leaf layout", UselessViewDetector.USELESS_LEAF);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[]{new RemoveUselessViewQuickFix(myIssue)};
}
}
private static class AndroidLintTypographyInspectionBase extends AndroidLintInspectionBase {
public AndroidLintTypographyInspectionBase(String displayName, Issue issue) {
super(displayName, issue);
}
@NotNull
@Override
protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) {
return new AndroidLintQuickFix[] {new TypographyQuickFix(myIssue, message)};
}
}
}
@@ -0,0 +1,20 @@
package org.jetbrains.android.inspections.lint;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
interface AndroidLintQuickFix {
AndroidLintQuickFix[] EMPTY_ARRAY = new AndroidLintQuickFix[0];
void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor);
boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode);
@NotNull
String getName();
}
@@ -0,0 +1,57 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.detector.api.Issue;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.openapi.util.Pair;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class AndroidLintUtil {
@NonNls static final String ATTR_VALUE_VERTICAL = "vertical";
@NonNls static final String ATTR_VALUE_WRAP_CONTENT = "wrap_content";
@NonNls static final String ATTR_LAYOUT_HEIGHT = "layout_height";
@NonNls static final String ATTR_LAYOUT_WIDTH = "layout_width";
@NonNls static final String ATTR_ORIENTATION = "orientation";
private AndroidLintUtil() {
}
@Nullable
static Pair<AndroidLintInspectionBase, HighlightDisplayLevel> getHighlighLevelAndInspection(@NotNull Issue issue,
@NotNull PsiElement context) {
final String inspectionShortName = AndroidLintInspectionBase.getInspectionShortNameByIssue(issue);
if (inspectionShortName == null) {
return null;
}
final HighlightDisplayKey key = HighlightDisplayKey.find(inspectionShortName);
if (key == null) {
return null;
}
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(context.getProject()).getInspectionProfile();
if (!profile.isToolEnabled(key, context)) {
return null;
}
final InspectionToolWrapper toolWrapper =
(InspectionToolWrapper)profile.getInspectionTool(inspectionShortName, context);
if (toolWrapper == null) {
return null;
}
final AndroidLintInspectionBase inspection = (AndroidLintInspectionBase)toolWrapper.getTool();
final HighlightDisplayLevel errorLevel = profile.getErrorLevel(key, context);
return new Pair<AndroidLintInspectionBase, HighlightDisplayLevel>(inspection,
errorLevel != null ? errorLevel : HighlightDisplayLevel.WARNING);
}
}
@@ -0,0 +1,127 @@
package org.jetbrains.android.inspections.lint;
import com.android.resources.Density;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Eugene.Kudelevsky
*/
class ConvertToDpQuickFix implements AndroidLintQuickFix {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.ConvertToDpQuickFix");
private static final Pattern PX_ATTR_VALUE_PATTERN = Pattern.compile("(\\d+)px");
private static int ourPrevDpi = Density.DEFAULT_DENSITY;
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
if (editor == null) {
return;
}
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
final List<Density> densities = new ArrayList<Density>();
for (Density density : Density.values()) {
if (density.getDpiValue() > 0) {
densities.add(density);
}
}
final String[] densityPresentableNames = new String[densities.size()];
String defaultValue = null;
String initialValue = null;
for (int i = 0; i < densities.size(); i++) {
final Density density = densities.get(i);
densityPresentableNames[i] = getLabelForDensity(density);
final int dpi = density.getDpiValue();
if (dpi == 0) {
continue;
}
if (dpi == ourPrevDpi) {
initialValue = densityPresentableNames[i];
}
else if (dpi == Density.DEFAULT_DENSITY) {
defaultValue = densityPresentableNames[i];
}
}
if (initialValue == null) {
initialValue = defaultValue;
}
if (initialValue == null) {
return;
}
final int dpi;
if (ApplicationManager.getApplication().isUnitTestMode()) {
dpi = Density.DEFAULT_DENSITY;
}
else {
final int selectedIndex = Messages
.showChooseDialog("What is the screen density the current px value works with?", "Choose density", densityPresentableNames,
initialValue, null);
if (selectedIndex < 0) {
return;
}
dpi = densities.get(selectedIndex).getDpiValue();
}
//noinspection AssignmentToStaticFieldFromInstanceMethod
ourPrevDpi = dpi;
for (XmlAttribute attribute : tag.getAttributes()) {
final String value = attribute.getValue();
if (value.endsWith("px")) {
final Matcher matcher = PX_ATTR_VALUE_PATTERN.matcher(value);
if (matcher.matches()) {
final String numberString = matcher.group(1);
try {
final int px = Integer.parseInt(numberString);
final int dp = px * 160 / dpi;
attribute.setValue(Integer.toString(dp) + "dp");
}
catch (NumberFormatException nufe) {
LOG.error(nufe);
}
}
}
}
}
@NotNull
private static String getLabelForDensity(@NotNull Density density) {
return String.format("%1$s (%2$d)", density.getShortDisplayValue(), density.getDpiValue());
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
return !inBatchMode && PsiTreeUtil.getParentOfType(startElement, XmlTag.class) != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.convert.to.dp");
}
}
@@ -0,0 +1,200 @@
package org.jetbrains.android.inspections.lint;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.*;
/**
* @author Eugene.Kudelevsky
*/
class FakeNode implements Node {
private final String myText;
FakeNode(@NotNull String text) {
myText = text;
}
@Override
public String getNodeName() {
throw new UnsupportedOperationException();
}
@Override
public String getNodeValue() throws DOMException {
return myText;
}
@Override
public void setNodeValue(String nodeValue) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public short getNodeType() {
throw new UnsupportedOperationException();
}
@Override
public Node getParentNode() {
throw new UnsupportedOperationException();
}
@Override
public NodeList getChildNodes() {
throw new UnsupportedOperationException();
}
@Override
public Node getFirstChild() {
throw new UnsupportedOperationException();
}
@Override
public Node getLastChild() {
throw new UnsupportedOperationException();
}
@Override
public Node getPreviousSibling() {
throw new UnsupportedOperationException();
}
@Override
public Node getNextSibling() {
throw new UnsupportedOperationException();
}
@Override
public NamedNodeMap getAttributes() {
throw new UnsupportedOperationException();
}
@Override
public Document getOwnerDocument() {
throw new UnsupportedOperationException();
}
@Override
public Node insertBefore(Node newChild, Node refChild) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public Node removeChild(Node oldChild) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public Node appendChild(Node newChild) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public boolean hasChildNodes() {
throw new UnsupportedOperationException();
}
@Override
public Node cloneNode(boolean deep) {
throw new UnsupportedOperationException();
}
@Override
public void normalize() {
throw new UnsupportedOperationException();
}
@Override
public boolean isSupported(String feature, String version) {
throw new UnsupportedOperationException();
}
@Override
public String getNamespaceURI() {
throw new UnsupportedOperationException();
}
@Override
public String getPrefix() {
throw new UnsupportedOperationException();
}
@Override
public void setPrefix(String prefix) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public String getLocalName() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasAttributes() {
throw new UnsupportedOperationException();
}
@Override
public String getBaseURI() {
throw new UnsupportedOperationException();
}
@Override
public short compareDocumentPosition(Node other) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public String getTextContent() throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public void setTextContent(String textContent) throws DOMException {
throw new UnsupportedOperationException();
}
@Override
public boolean isSameNode(Node other) {
throw new UnsupportedOperationException();
}
@Override
public String lookupPrefix(String namespaceURI) {
throw new UnsupportedOperationException();
}
@Override
public boolean isDefaultNamespace(String namespaceURI) {
throw new UnsupportedOperationException();
}
@Override
public String lookupNamespaceURI(String prefix) {
throw new UnsupportedOperationException();
}
@Override
public boolean isEqualNode(Node arg) {
throw new UnsupportedOperationException();
}
@Override
public Object getFeature(String feature, String version) {
throw new UnsupportedOperationException();
}
@Override
public Object setUserData(String key, Object data, UserDataHandler handler) {
throw new UnsupportedOperationException();
}
@Override
public Object getUserData(String key) {
throw new UnsupportedOperationException();
}
}
@@ -0,0 +1,54 @@
package org.jetbrains.android.inspections.lint;
import com.android.sdklib.SdkConstants;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class InefficientWeightQuickFix implements AndroidLintQuickFix {
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return;
}
final XmlTag parentTag = tag.getParentTag();
if (parentTag == null) {
return;
}
String attrName;
if (AndroidLintUtil.ATTR_VALUE_VERTICAL
.equals(parentTag.getAttributeValue(AndroidLintUtil.ATTR_ORIENTATION, SdkConstants.NS_RESOURCES))) {
attrName = AndroidLintUtil.ATTR_LAYOUT_HEIGHT;
}
else {
attrName = AndroidLintUtil.ATTR_LAYOUT_WIDTH;
}
tag.setAttribute(attrName, SdkConstants.NS_RESOURCES, "0dp");
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return false;
}
return tag.getParentTag() != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.replace.with.zero.dp");
}
}
@@ -1,6 +1,7 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.PositionXmlParser;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.client.api.IDomParser;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Context;
@@ -43,6 +44,11 @@ class IntellijLintClient extends LintClient implements Disposable {
myState = state;
}
@Override
public Configuration getConfiguration(com.android.tools.lint.detector.api.Project project) {
return new IntellijLintConfiguration(myState.getIssues());
}
@Override
public void report(Context context, Issue issue, Location location, String message, Object data) {
if (location != null) {
@@ -0,0 +1,43 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Severity;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Set;
/**
* @author Eugene.Kudelevsky
*/
class IntellijLintConfiguration extends Configuration {
private final Set<Issue> myIssueSet;
IntellijLintConfiguration(@NotNull Collection<Issue> issueSet) {
myIssueSet = new HashSet<Issue>(issueSet);
}
@Override
public void ignore(Context context, Issue issue, Location location, String message, Object data) {
throw new UnsupportedOperationException();
}
@Override
public void setSeverity(Issue issue, Severity severity) {
throw new UnsupportedOperationException();
}
@Override
public boolean isEnabled(Issue issue) {
return myIssueSet.contains(issue);
}
@Override
public Severity getSeverity(Issue issue) {
return Severity.WARNING;
}
}
@@ -0,0 +1,33 @@
package org.jetbrains.android.inspections.lint;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class RemoveAttributeQuickFix implements AndroidLintQuickFix {
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class);
if (attribute != null) {
attribute.getParent().setAttribute(attribute.getName(), null);
}
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
return PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class) != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.remove.attribute");
}
}
@@ -0,0 +1,58 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.checks.UselessViewDetector;
import com.android.tools.lint.detector.api.Issue;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public class RemoveUselessViewQuickFix implements AndroidLintQuickFix {
private final Issue myIssue;
public RemoveUselessViewQuickFix(@NotNull Issue issue) {
myIssue = issue;
}
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return;
}
final XmlTag parentTag = tag.getParentTag();
if (parentTag == null) {
return;
}
if (myIssue.getId().equals(UselessViewDetector.USELESS_LEAF.getId())) {
tag.delete();
}
else {
assert false;
// todo: implement
}
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return false;
}
return tag.getParentTag() != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.remove.unnecessary.view");
}
}
@@ -0,0 +1,74 @@
package org.jetbrains.android.inspections.lint;
import com.android.sdklib.SdkConstants;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class SetAttributeQuickFix implements AndroidLintQuickFix {
private final String myName;
private final String myAttributeName;
private final String myValue;
SetAttributeQuickFix(@NotNull String name, @NotNull String attributeName, @Nullable String value) {
super();
myName = name;
myAttributeName = attributeName;
myValue = value;
}
@NotNull
@Override
public String getName() {
return myName;
}
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class, false);
if (tag == null) {
return;
}
final XmlAttribute attribute = tag.setAttribute(myAttributeName, SdkConstants.NS_RESOURCES, "");
if (attribute != null) {
if (myValue != null) {
attribute.setValue(myValue);
}
if (editor != null) {
final XmlAttributeValue valueElement = attribute.getValueElement();
final TextRange valueTextRange = attribute.getValueTextRange();
if (valueElement != null && valueTextRange != null) {
final int valueElementStart = valueElement.getTextRange().getStartOffset();
editor.getCaretModel().moveToOffset(valueElementStart + valueTextRange.getStartOffset());
if (valueTextRange.getStartOffset() < valueTextRange.getEndOffset()) {
editor.getSelectionModel().setSelection(valueElementStart + valueTextRange.getStartOffset(),
valueElementStart + valueTextRange.getEndOffset());
}
}
}
}
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class, false);
if (tag == null) {
return false;
}
return tag.getAttribute(myAttributeName, SdkConstants.NS_RESOURCES) == null;
}
}
@@ -0,0 +1,50 @@
package org.jetbrains.android.inspections.lint;
import com.android.sdklib.SdkConstants;
import com.android.tools.lint.detector.api.LintConstants;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
class SetScrollViewSizeQuickFix implements AndroidLintQuickFix {
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return;
}
final XmlTag parentTag = tag.getParentTag();
if (parentTag == null) {
return;
}
final boolean isHorizontal = LintConstants.HORIZONTAL_SCROLL_VIEW.equals(parentTag.getName());
final String attributeName = isHorizontal
? AndroidLintUtil.ATTR_LAYOUT_WIDTH
: AndroidLintUtil.ATTR_LAYOUT_HEIGHT;
tag.setAttribute(attributeName, SdkConstants.NS_RESOURCES, AndroidLintUtil.ATTR_VALUE_WRAP_CONTENT);
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return false;
}
return tag.getParentTag() != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.set.to.wrap.content");
}
}
@@ -0,0 +1,67 @@
package org.jetbrains.android.inspections.lint;
import com.android.tools.lint.checks.TypographyDetector;
import com.android.tools.lint.detector.api.Issue;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlText;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
class TypographyQuickFix implements AndroidLintQuickFix {
private final Issue myIssue;
private final String myMessage;
public TypographyQuickFix(@NotNull Issue issue, @NotNull String message) {
myIssue = issue;
myMessage = message;
}
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) {
final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class);
if (tag == null) {
return;
}
for (PsiElement child : tag.getChildren()) {
if (child instanceof XmlText) {
final XmlText xmlText = (XmlText)child;
final String value = xmlText.getValue();
if (value != null) {
final List<TypographyDetector.ReplaceEdit> edits = TypographyDetector.getEdits(myIssue.getId(), myMessage, new FakeNode(value));
final StringBuilder builder = new StringBuilder(value);
for (TypographyDetector.ReplaceEdit edit : edits) {
builder.replace(edit.offset, edit.offset + edit.length, edit.replaceWith);
}
final String newValue = builder.toString();
if (!newValue.equals(value)) {
xmlText.setValue(newValue);
}
}
}
}
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) {
return PsiTreeUtil.getParentOfType(startElement, XmlTag.class) != null;
}
@NotNull
@Override
public String getName() {
return AndroidBundle.message("android.lint.inspections.replace.with.suggested.characters");
}
}
@@ -16,42 +16,57 @@
package org.jetbrains.android.intentions;
import com.android.resources.ResourceType;
import com.intellij.CommonBundle;
import com.intellij.codeInsight.intention.AbstractIntentionAction;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.template.*;
import com.intellij.codeInsight.template.impl.*;
import com.intellij.codeInsight.template.macro.VariableOfTypeMacro;
import com.intellij.ide.actions.CreateElementActionBase;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.InputValidatorEx;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.xml.Converter;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xml.GenericAttributeValue;
import org.jetbrains.android.AndroidFileTemplateProvider;
import org.jetbrains.android.actions.CreateXmlResourceDialog;
import org.jetbrains.android.dom.converters.ResourceReferenceConverter;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.dom.resources.Resources;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.LinkedHashSet;
import java.util.Set;
import static com.intellij.openapi.ui.Messages.showInputDialog;
import static org.jetbrains.android.util.AndroidUtils.VIEW_CLASS_NAME;
/**
@@ -61,7 +76,9 @@ import static org.jetbrains.android.util.AndroidUtils.VIEW_CLASS_NAME;
* Time: 5:02:31 PM
* To change this template use File | Settings | File Templates.
*/
public class AndroidAddStringResourceAction extends AbstractIntentionAction {
public class AndroidAddStringResourceAction extends AbstractIntentionAction implements HighPriorityAction {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.intentions.AndroidAddStringResourceAction");
private static final String CONTEXT = AndroidUtils.ANDROID_PACKAGE + ".content.Context";
private static final String RESOURCES = AndroidUtils.ANDROID_PACKAGE + ".content.res.Resources";
@@ -94,6 +111,31 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction {
return (String)value;
}
}
else if (file instanceof XmlFile && element instanceof XmlAttributeValue) {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class);
if (attribute != null) {
final GenericAttributeValue domAttribute = DomManager.getDomManager(element.getProject()).getDomElement(attribute);
if (domAttribute != null) {
final Converter converter = domAttribute.getConverter();
if (converter instanceof ResourceReferenceConverter) {
final ResourceValue value = (ResourceValue)domAttribute.getValue();
if (value != null && !value.isReference()) {
final Set<String> types = ((ResourceReferenceConverter)converter).getResourceTypes(domAttribute);
for (String type : types) {
if (ResourceType.STRING.getName().equals(type)) {
return ((XmlAttributeValue)element).getValue();
}
}
}
}
}
}
}
return null;
}
@@ -124,50 +166,70 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction {
}
static void doInvoke(Project project, Editor editor, PsiFile file, @Nullable String resName) {
AndroidFacet facet = AndroidFacet.getInstance(file);
assert facet != null;
PsiElement element = getPsiElement(file, editor);
final PsiElement element = getPsiElement(file, editor);
assert element != null;
doInvoke(project, editor, file, resName, element);
}
protected static void doInvoke(Project project, Editor editor, PsiFile file, @Nullable String resName, PsiElement element) {
String value = getStringLiteralValue(element, file);
assert value != null;
final AndroidFacet facet = AndroidFacet.getInstance(file);
assert facet != null;
value = value.replace("'", "\\'").replace("\"", "\\\"");
String aPackage = getPackage(facet);
final String aPackage = getPackage(facet);
if (aPackage == null) {
Messages.showErrorDialog(project, AndroidBundle.message("package.not.found.error"), CommonBundle.getErrorTitle());
return;
}
if (resName == null) {
resName =
showInputDialog(project, AndroidBundle.message("resource.name"), AndroidBundle.message("add.string.resource.intention.text"),
Messages.getQuestionIcon(), "", new InputValidatorEx() {
public String getErrorText(String inputString) {
if (inputString == null || inputString.length() == 0) return null;
String[] ids = inputString.split(".");
for (String id : ids) {
if (!StringUtil.isJavaIdentifier(id)) {
return AndroidBundle.message("android.identifier.expected", id);
}
}
return null;
}
final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(facet.getModule(), ResourceType.STRING);
dialog.setTitle("Extract String Resource");
dialog.show();
public boolean checkInput(String inputString) {
return inputString != null && AndroidResourceUtil.isCorrectAndroidResourceName(inputString);
}
if (!dialog.isOK()) {
return;
}
public boolean canClose(String inputString) {
return checkInput(inputString);
}
});
final Module module = dialog.getModule();
if (module == null) {
return;
}
final ResourceElement createdElement =
doCreate(module, dialog.getResourceName(), ResourceType.STRING, dialog.getFileName(), dialog.getDirectoryName(), value);
if (createdElement != null) {
resName = createdElement.getName().getValue();
}
}
if (resName == null) return;
LocalResourceManager manager = facet.getLocalResourceManager();
String resType = "string";
ResourceElement resElement = manager.addValueResource(resType, resName, value);
assert resElement != null;
createJavaResourceReference(project, editor, file, element, aPackage, resName, resType);
else {
assert ApplicationManager.getApplication().isUnitTestMode();
doCreate(facet.getModule(), resName, ResourceType.STRING, "strings.xml", "values", value);
}
if (resName == null) {
return;
}
if (file instanceof PsiJavaFile) {
createJavaResourceReference(project, editor, file, element, aPackage, resName, ResourceType.STRING.getName());
}
else {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class);
if (attribute != null) {
attribute.setValue(ResourceValue.referenceTo('@', null, ResourceType.STRING.getName(), resName).toString());
}
}
PsiDocumentManager.getInstance(project).commitAllDocuments();
UndoUtil.markPsiFileForUndo(file);
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
ApplicationManager.getApplication().saveAll();
@@ -188,14 +250,25 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction {
assert methodName != null;
final TemplateImpl template;
final boolean inStaticContext = RefactoringUtil.isInStaticContext(element, null);
if (extendsContext && !inStaticContext) {
template = new TemplateImpl("", "$resources$." + methodName + "(" + field + ")", "");
MacroCallNode node = new MacroCallNode(new MyVarOfTypeExpression("getResources()"));
node.addParameter(new ConstantNode(RESOURCES));
template.addVariable("resources", node, new ConstantNode(""), true);
if (ResourceType.STRING.getName().equals(resType)) {
template = new TemplateImpl("", methodName + '(' + field + ')', "");
}
else {
template = new TemplateImpl("", "$resources$." + methodName + "(" + field + ")", "");
MacroCallNode node = new MacroCallNode(new MyVarOfTypeExpression("getResources()"));
node.addParameter(new ConstantNode(RESOURCES));
template.addVariable("resources", node, new ConstantNode(""), true);
}
}
else {
template = new TemplateImpl("", "$context$.getResources()." + methodName + "(" + field + ")", "");
if (ResourceType.STRING.getName().equals(resType)) {
template = new TemplateImpl("", "$context$." + methodName + "(" + field + ")", "");
}
else {
template = new TemplateImpl("", "$context$.getResources()." + methodName + "(" + field + ")", "");
}
final boolean extendsView = getContainingInheritorOf(element, VIEW_CLASS_NAME) != null;
MacroCallNode node =
new MacroCallNode(extendsView && !inStaticContext ? new MyVarOfTypeExpression("getContext()") : new VariableOfTypeMacro());
@@ -242,6 +315,104 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction {
return true;
}
@Nullable
private static ResourceElement doCreate(@NotNull Module module,
@NotNull String resourceName,
@NotNull ResourceType resourceType,
@NotNull String fileName,
@NotNull String dirName,
@NotNull String value) {
final Project project = module.getProject();
final AndroidFacet facet = AndroidFacet.getInstance(module);
assert facet != null;
try {
return addValueResource(facet, resourceName, resourceType, fileName, dirName, value);
}
catch (Exception e) {
final String message = CreateElementActionBase.filterMessage(e.getMessage());
if (message == null || message.length() == 0) {
LOG.error(e);
}
else {
LOG.info(e);
reportError(project, message);
}
return null;
}
}
@Nullable
public static ResourceElement addValueResource(@NotNull AndroidFacet facet,
@NotNull String resourceName,
@NotNull ResourceType resourceType,
@NotNull String fileName,
@NotNull String dirName,
@NotNull String value) throws Exception {
final VirtualFile resFile = findOrCreateResourceFile(facet, fileName, dirName);
if (resFile == null) {
return null;
}
final Resources resources = AndroidUtils.loadDomElement(facet.getModule(), resFile, Resources.class);
if (resources == null) {
reportError(facet.getModule().getProject(), AndroidBundle.message("not.resource.file.error", fileName));
return null;
}
final ResourceElement element = AndroidResourceUtil.addValueResource(resourceType.getName(), resources);
element.getName().setValue(resourceName);
if (value.length() > 0) {
element.setStringValue(value);
}
return element;
}
@Nullable
private static VirtualFile findOrCreateResourceFile(@NotNull AndroidFacet facet,
@NotNull final String fileName,
@NotNull String dirName) throws Exception {
final Module module = facet.getModule();
final Project project = module.getProject();
final VirtualFile resDir = facet.getLocalResourceManager().getResourceDir();
if (resDir == null) {
reportError(project, AndroidBundle.message("check.resource.dir.error", module.getName()));
return null;
}
final VirtualFile dir = AndroidUtils.createChildDirectoryIfNotExist(project, resDir, dirName);
final String dirPath = FileUtil.toSystemDependentName(resDir.getPath() + '/' + dirName);
if (dir == null) {
reportError(project, AndroidBundle.message("android.cannot.create.dir.error", dirPath));
return null;
}
final VirtualFile file = dir.findChild(fileName);
if (file != null) {
return file;
}
AndroidFileTemplateProvider
.createFromTemplate(project, dir, AndroidFileTemplateProvider.VALUE_RESOURCE_FILE_TEMPLATE, fileName);
final VirtualFile result = dir.findChild(fileName);
if (result == null) {
reportError(project, AndroidBundle.message("android.cannot.create.file.error", dirPath + File.separatorChar + fileName));
}
return result;
}
private static void reportError(@NotNull Project project, @NotNull String message) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new IncorrectOperationException(message);
}
else {
Messages.showErrorDialog(project, message, CommonBundle.getErrorTitle());
}
}
private static class MyVarOfTypeExpression extends VariableOfTypeMacro {
private final String myDefaultValue;
@@ -208,7 +208,8 @@ public class LocalResourceManager extends ResourceManager {
private VirtualFile findOrCreateResourceFile(@NotNull final String fileName) {
VirtualFile dir = getResourceDir();
if (dir == null) {
Messages.showErrorDialog(myModule.getProject(), AndroidBundle.message("check.resource.dir.error"), CommonBundle.getErrorTitle());
Messages.showErrorDialog(myModule.getProject(), AndroidBundle.message("check.resource.dir.error", myModule.getName()),
CommonBundle.getErrorTitle());
return null;
}
final VirtualFile valuesDir = findOrCreateChildDir(dir, AndroidConstants.FD_RES_VALUES);
@@ -240,7 +241,8 @@ public class LocalResourceManager extends ResourceManager {
VirtualFile resDir = getResourceDir();
Project project = myModule.getProject();
if (resDir == null) {
Messages.showErrorDialog(project, AndroidBundle.message("check.resource.dir.error"), CommonBundle.getErrorTitle());
Messages
.showErrorDialog(project, AndroidBundle.message("check.resource.dir.error", myModule.getName()), CommonBundle.getErrorTitle());
return null;
}
PsiElement[] createdElements = CreateResourceFileAction.createResourceFile(project, resDir, resType, fileOrResourceName);
@@ -269,7 +271,7 @@ public class LocalResourceManager extends ResourceManager {
CommonBundle.getErrorTitle());
return null;
}
ResourceElement element = addValueResource(type, resources);
ResourceElement element = AndroidResourceUtil.addValueResource(type, resources);
element.getName().setValue(name);
if (value != null) {
element.setStringValue(value);
@@ -277,41 +279,6 @@ public class LocalResourceManager extends ResourceManager {
return element;
}
@NotNull
private static ResourceElement addValueResource(@NotNull final String type, @NotNull final Resources resources) {
if (type.equals("string")) {
return resources.addString();
}
else if (type.equals("dimen")) {
return resources.addDimen();
}
else if (type.equals("color")) {
return resources.addColor();
}
else if (type.equals("drawable")) {
return resources.addDrawable();
}
else if (type.equals("style")) {
return resources.addStyle();
}
else if (type.equals("array")) {
// todo: choose among string-array, integer-array and array
return resources.addStringArray();
}
else if (type.equals("integer")) {
return resources.addInteger();
}
else if (type.equals("bool")) {
return resources.addBool();
}
else if (type.equals("id")) {
Item item = resources.addItem();
item.getType().setValue("id");
return item;
}
throw new IllegalArgumentException("Incorrect resource type");
}
@Nullable
private VirtualFile findOrCreateChildDir(@NotNull final VirtualFile dir, @NotNull final String name) {
try {
@@ -252,7 +252,7 @@ public abstract class DeviceConfiguratorPanel extends JPanel {
applyEditors();
}
protected void doApplyEditors() throws InvalidOptionValueException {
public void doApplyEditors() throws InvalidOptionValueException {
try {
final FolderConfiguration newConfig = new FolderConfiguration();
@@ -35,9 +35,7 @@ import com.intellij.util.containers.HashSet;
import com.intellij.util.indexing.FileBasedIndex;
import org.jetbrains.android.AndroidIdIndex;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.dom.resources.Attr;
import org.jetbrains.android.dom.resources.DeclareStyleable;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.resources.*;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
import org.jetbrains.android.resourceManagers.ResourceManager;
@@ -402,4 +400,39 @@ public class AndroidResourceUtil {
List<PsiElement> targets = findResourcesByField(manager, resField);
return PsiUtilBase.toPsiElementArray(targets);
}
@NotNull
public static ResourceElement addValueResource(@NotNull final String type, @NotNull final Resources resources) {
if (type.equals("string")) {
return resources.addString();
}
else if (type.equals("dimen")) {
return resources.addDimen();
}
else if (type.equals("color")) {
return resources.addColor();
}
else if (type.equals("drawable")) {
return resources.addDrawable();
}
else if (type.equals("style")) {
return resources.addStyle();
}
else if (type.equals("array")) {
// todo: choose among string-array, integer-array and array
return resources.addStringArray();
}
else if (type.equals("integer")) {
return resources.addInteger();
}
else if (type.equals("bool")) {
return resources.addBool();
}
else if (type.equals("id")) {
Item item = resources.addItem();
item.getType().setValue("id");
return item;
}
throw new IllegalArgumentException("Incorrect resource type");
}
}
@@ -6,6 +6,6 @@ import p1.p2.R;
public class Class extends View {
public static void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -6,6 +6,6 @@ import p1.p2.R;
public class Class extends View {
public void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -6,6 +6,6 @@ import p1.p2.R;
public class Class extends View {
public void f() {
String s = getContext().getResources().getString(R.string.hello);
String s = getContext().getString(R.string.hello);
}
}
@@ -4,7 +4,7 @@ import android.content.Context;
import android.content.res.Resources;
public class Class extends Context {
public void f(Resources resources) {
public void f(Context resources) {
String s = "h<caret>ello";
}
}
@@ -5,7 +5,7 @@ import android.content.res.Resources;
import p1.p2.R;
public class Class extends Context {
public void f(Resources resources) {
String s = resources.getString(R.string.hello);
public void f(Context resources) {
String s = getString(R.string.hello);
}
}
@@ -4,7 +4,7 @@ import android.content.Context;
import android.content.res.Resources;
public class Class extends Context {
public void f(Resources resourc<caret>es) {
public void f(Context resourc<caret>es) {
String s = "hello";
}
}
@@ -5,6 +5,6 @@ import p1.p2.R;
public class Class {
public void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -4,6 +4,6 @@ import p1.p2.R;
public class Class {
public void f() {
String s = .getResources().getString(R.string.hello);
String s = .getString(R.string.hello);
}
}
@@ -4,6 +4,6 @@ import android.content.Context;
public class Class {
public static void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -5,6 +5,6 @@ import p1.p2.R;
public class Class extends Context {
public void f() {
String s = getResources().getString(R.string.hello);
String s = getString(R.string.hello);
}
}
@@ -5,6 +5,6 @@ import p1.p2.R;
public class Class extends Context {
public void f(Context context) {
String s = r.getString(R.string.hello);
String s = getString(R.string.hello);
}
}
@@ -4,6 +4,6 @@ import android.content.Context;
public class Class extends Context {
public static void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -5,6 +5,6 @@ import p1.p2.R;
public class Class extends Context {
public static void f() {
String s = c.getResources().getString(R.string.hello);
String s = c.getString(R.string.hello);
}
}
@@ -4,7 +4,7 @@ import android.content.res.Resources;
import android.content.Context;
public class Class extends Context {
public static void f(Resources resources) {
public static void f(Context resources) {
String s = "h<caret>ello";
}
}
@@ -4,7 +4,7 @@ import android.content.res.Resources;
import android.content.Context;
public class Class extends Context {
public static void f(Resources resources) {
String s = .getResources().getString(R.string.hello);
public static void f(Context resources) {
String s = resources.getString(R.string.hello);
}
}
@@ -4,7 +4,7 @@ import android.content.res.Resources;
import android.content.Context;
public class Class extends Context {
public void f(Resources resources) {
public void f(Context context) {
String s = "h<caret>ello";
}
}
@@ -4,7 +4,7 @@ import android.content.res.Resources;
import android.content.Context;
public class Class extends Context {
public void f(Resources resources) {
String s = getResources().getString(R.string.hello);
public void f(Context context) {
String s = getString(R.string.hello);
}
}
@@ -4,6 +4,6 @@ import android.content.Context;
public class ClassEscape {
public static void f(Context context) {
String s = context.getResources().getString(R.string.hello);
String s = context.getString(R.string.hello);
}
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="he<caret>llo"
/>
</LinearLayout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wra<caret>p_content"
android:text="hello"
/>
</LinearLayout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application android:label="@string/a1<caret>0" >
</application>
</manifest>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application android:label="hell<caret>o" >
</application>
</manifest>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application android:label="@string/hello" >
</application>
</manifest>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application>
<activity android:name="MyAc<caret>tivity">
</activity>
</application>
</manifest>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<warning><ListView android:layout_width="wrap_content" android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</ListView></warning>
</LinearLayout>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<warning><LinearLayout<caret> xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<AbsoluteLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="10"/>
</LinearLayout></warning>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:baselineAligned="<selection>false</selection>">
<AbsoluteLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="10"/>
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show More