blob: cda85d0ab1ad04be6bcd0fe24e040b767a8f76c2 [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.android.dom.converters;
import com.android.tools.idea.gradle.IdeaAndroidProject;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.PackageReferenceSet;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.PsiPackageReference;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.util.xml.*;
import org.jetbrains.android.AndroidApplicationPackageRenameProcessor;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidPackageConverter extends Converter<String> implements CustomReferenceConverter<String> {
@Override
public String fromString(@Nullable @NonNls String s, ConvertContext context) {
return s;
}
@Override
public String toString(@Nullable String s, ConvertContext context) {
return s;
}
@NotNull
@Override
public PsiReference[] createReferences(GenericDomValue<String> value, PsiElement element, ConvertContext context) {
final String s = value.getStringValue();
return s != null
? new MyPackageReferenceSet(s, element).getPsiReferences()
: PsiReference.EMPTY_ARRAY;
}
private static class MyPackageReferenceSet extends PackageReferenceSet {
final int myStartInElement;
public MyPackageReferenceSet(String s, PsiElement element) {
this(s, element, ElementManipulators.getOffsetInElement(element));
}
public MyPackageReferenceSet(String str, PsiElement element, int startInElement) {
super(str, element, startInElement);
myStartInElement = startInElement;
}
@NotNull
@Override
protected PsiPackageReference createReference(TextRange range, int index) {
// If the Gradle model specifies an application id, which does not rely on
// the package in any way, then the package attribute in the manifest should
// be taken to be a normal package reference, and should participate in normal
// package rename refactoring
AndroidFacet facet = AndroidFacet.getInstance(getElement());
if (facet != null) {
IdeaAndroidProject androidModel = facet.getAndroidModel();
if (androidModel != null && androidModel.overridesManifestPackage()) {
return new PsiPackageReference(this, range, index);
}
}
return new MyPsiPackageReference(this, range, index);
}
}
private static class MyPsiPackageReference extends PsiPackageReference {
private final MyPackageReferenceSet myReferenceSet;
private final TextRange myTextRange;
public MyPsiPackageReference(MyPackageReferenceSet referenceSet, TextRange textRange, int index) {
super(referenceSet, textRange, index);
myReferenceSet = referenceSet;
myTextRange = textRange;
}
@NotNull
@Override
public ResolveResult[] multiResolve(boolean incompleteCode) {
return PsiElementResolveResult.createResults(myElement);
}
@Override
public boolean isReferenceTo(PsiElement element) {
final ResolveResult[] results = doMultiResolve();
for (ResolveResult result : results) {
if (getElement().getManager().areElementsEquivalent(result.getElement(), element)) {
return true;
}
}
return false;
}
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
return myElement;
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
// here we only detect package movement and update relative names of components in manifest, don't change the current element
if (!(element instanceof PsiPackage) || !(myElement instanceof XmlAttributeValue)) {
throw new IncorrectOperationException("Cannot bind to " + element);
}
final String newPackageName = ((PsiPackage)element).getQualifiedName();
final String basePackage = ((XmlAttributeValue)myElement).getValue();
final String oldPackageName = myElement.getText().substring(myReferenceSet.myStartInElement, myTextRange.getEndOffset());
final PsiFile file = myElement.getContainingFile();
if (basePackage.length() > 0 && file instanceof XmlFile) {
AndroidApplicationPackageRenameProcessor.processAllAttributesToUpdate(
(XmlFile)file, basePackage, oldPackageName, newPackageName, new Processor<Pair<GenericAttributeValue, String>>() {
@Override
public boolean process(Pair<GenericAttributeValue, String> pair) {
pair.getFirst().setStringValue(pair.getSecond());
return true;
}
});
}
return myElement;
}
}
}