diff --git a/plugins/android/src/org/jetbrains/android/dom/AndroidDomExtender.java b/plugins/android/src/org/jetbrains/android/dom/AndroidDomExtender.java index 6e0d3f04cbb9..b20daac5e010 100644 --- a/plugins/android/src/org/jetbrains/android/dom/AndroidDomExtender.java +++ b/plugins/android/src/org/jetbrains/android/dom/AndroidDomExtender.java @@ -38,6 +38,7 @@ import org.jetbrains.android.dom.attrs.AttributeFormat; import org.jetbrains.android.dom.attrs.StyleableDefinition; import org.jetbrains.android.dom.converters.CompositeConverter; import org.jetbrains.android.dom.converters.ResourceReferenceConverter; +import org.jetbrains.android.dom.layout.Fragment; import org.jetbrains.android.dom.layout.Include; import org.jetbrains.android.dom.layout.LayoutElement; import org.jetbrains.android.dom.layout.LayoutViewElement; @@ -375,7 +376,8 @@ public class AndroidDomExtender extends DomExtender { private static final MyAttributeProcessor ourLayoutAttrsProcessor = new MyAttributeProcessor() { @Override public void process(@NotNull XmlName attrName, @NotNull DomExtension extension, @NotNull DomElement element) { - if (element instanceof LayoutViewElement && SdkConstants.NS_RESOURCES.equals(attrName.getNamespaceKey())) { + if ((element instanceof LayoutViewElement || element instanceof Fragment) && + SdkConstants.NS_RESOURCES.equals(attrName.getNamespaceKey())) { XmlElement xmlElement = element.getXmlElement(); XmlTag tag = xmlElement instanceof XmlTag ? (XmlTag)xmlElement : null; String tagName = tag != null ? tag.getName() : null; @@ -422,15 +424,21 @@ public class AndroidDomExtender extends DomExtender { } return; } - String tagName = tag.getName(); - if (!tagName.equals("view")) { - PsiClass c = map.get(tagName); - registerAttributesForClassAndSuperclasses(facet, element, c, registrar, ourLayoutAttrsProcessor); + else if (element instanceof Fragment) { + registerAttributes(facet, element, new String[]{"Fragment"}, registrar, ourLayoutAttrsProcessor); } else { - String[] styleableNames = getClassNames(map.values()); - registerAttributes(facet, element, styleableNames, registrar, ourLayoutAttrsProcessor); + String tagName = tag.getName(); + if (!tagName.equals("view")) { + PsiClass c = map.get(tagName); + registerAttributesForClassAndSuperclasses(facet, element, c, registrar, ourLayoutAttrsProcessor); + } + else { + String[] styleableNames = getClassNames(map.values()); + registerAttributes(facet, element, styleableNames, registrar, ourLayoutAttrsProcessor); + } } + registerLayoutAttributes(facet, element, tag, registrar, ourLayoutAttrsProcessor); for (String viewClassName : map.keySet()) { diff --git a/plugins/android/src/org/jetbrains/android/dom/converters/PackageClassConverter.java b/plugins/android/src/org/jetbrains/android/dom/converters/PackageClassConverter.java index 1455df54150b..0e036fd3d4e6 100644 --- a/plugins/android/src/org/jetbrains/android/dom/converters/PackageClassConverter.java +++ b/plugins/android/src/org/jetbrains/android/dom/converters/PackageClassConverter.java @@ -66,28 +66,28 @@ public class PackageClassConverter extends ResolvingConverter implemen if (s == null) return null; DomElement domElement = context.getInvocationElement(); Manifest manifest = domElement.getParentOfType(Manifest.class, true); - if (manifest != null) { - s = s.replace('$', '.'); - String packageName = manifest.getPackage().getValue(); - String className; + s = s.replace('$', '.'); + String packageName = manifest != null ? manifest.getPackage().getValue() : null; + String className = null; + + if (packageName != null) { if (s.startsWith(".")) { className = packageName + s; } else { className = packageName + "." + s; } - JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getPsiManager().getProject()); - final Module module = context.getModule(); - GlobalSearchScope scope = module != null - ? GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module) - : context.getInvocationElement().getResolveScope(); - PsiClass psiClass = facade.findClass(className, scope); - if (psiClass == null) { - psiClass = facade.findClass(s, scope); - } - return psiClass; } - return null; + JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getPsiManager().getProject()); + final Module module = context.getModule(); + GlobalSearchScope scope = module != null + ? GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module) + : context.getInvocationElement().getResolveScope(); + PsiClass psiClass = className != null ? facade.findClass(className, scope) : null; + if (psiClass == null) { + psiClass = facade.findClass(s, scope); + } + return psiClass; } @NotNull diff --git a/plugins/android/src/org/jetbrains/android/dom/layout/Fragment.java b/plugins/android/src/org/jetbrains/android/dom/layout/Fragment.java new file mode 100644 index 000000000000..4f4ca53af2f2 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/dom/layout/Fragment.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.android.dom.layout; + +import com.intellij.psi.PsiClass; +import com.intellij.util.xml.Attribute; +import com.intellij.util.xml.Convert; +import com.intellij.util.xml.ExtendClass; +import com.intellij.util.xml.GenericAttributeValue; +import org.jetbrains.android.dom.AndroidAttributeValue; +import org.jetbrains.android.dom.converters.PackageClassConverter; + +/** + * @author Eugene.Kudelevsky + */ +public interface Fragment extends LayoutElement { + @Attribute("name") + @Convert(PackageClassConverter.class) + @ExtendClass("android.app.Fragment") + AndroidAttributeValue getFragmentName(); + + @Attribute("class") + @Convert(PackageClassConverter.class) + @ExtendClass("android.app.Fragment") + GenericAttributeValue getFragmentClass(); +} diff --git a/plugins/android/src/org/jetbrains/android/dom/layout/LayoutViewElement.java b/plugins/android/src/org/jetbrains/android/dom/layout/LayoutViewElement.java index c1873197c47e..ac810990b7c1 100644 --- a/plugins/android/src/org/jetbrains/android/dom/layout/LayoutViewElement.java +++ b/plugins/android/src/org/jetbrains/android/dom/layout/LayoutViewElement.java @@ -46,4 +46,6 @@ public interface LayoutViewElement extends LayoutElement { GenericAttributeValue getViewClass(); List getIncludes(); + + List getFragments(); } diff --git a/plugins/android/testData/dom/layout/Fragment.java b/plugins/android/testData/dom/layout/Fragment.java new file mode 100644 index 000000000000..4be42783eda2 --- /dev/null +++ b/plugins/android/testData/dom/layout/Fragment.java @@ -0,0 +1,3 @@ +package android.app; + +public class Fragment {} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/MyFragmentActivity.java b/plugins/android/testData/dom/layout/MyFragmentActivity.java new file mode 100644 index 000000000000..01c27ea6f61a --- /dev/null +++ b/plugins/android/testData/dom/layout/MyFragmentActivity.java @@ -0,0 +1,6 @@ +package p1.p2; + +public class MyFragmentActivity extends android.app.Activity { + public static class MyFragment extends android.app.Fragment { + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/fragmentCompletion1.xml b/plugins/android/testData/dom/layout/fragmentCompletion1.xml new file mode 100644 index 000000000000..d85e062d528b --- /dev/null +++ b/plugins/android/testData/dom/layout/fragmentCompletion1.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/fragmentCompletion1_after.xml b/plugins/android/testData/dom/layout/fragmentCompletion1_after.xml new file mode 100644 index 000000000000..49fb40f862e7 --- /dev/null +++ b/plugins/android/testData/dom/layout/fragmentCompletion1_after.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/fragmentCompletion2.xml b/plugins/android/testData/dom/layout/fragmentCompletion2.xml new file mode 100644 index 000000000000..3d608d927e6b --- /dev/null +++ b/plugins/android/testData/dom/layout/fragmentCompletion2.xml @@ -0,0 +1,7 @@ + + + /> + + \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/fragmentCompletion2_after.xml b/plugins/android/testData/dom/layout/fragmentCompletion2_after.xml new file mode 100644 index 000000000000..448cd4bd5c4a --- /dev/null +++ b/plugins/android/testData/dom/layout/fragmentCompletion2_after.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/fragmentCompletion3.xml b/plugins/android/testData/dom/layout/fragmentCompletion3.xml new file mode 100644 index 000000000000..195838f3dce9 --- /dev/null +++ b/plugins/android/testData/dom/layout/fragmentCompletion3.xml @@ -0,0 +1,7 @@ + + + /> + + \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/fragmentCompletion3_after.xml b/plugins/android/testData/dom/layout/fragmentCompletion3_after.xml new file mode 100644 index 000000000000..bf8af317d788 --- /dev/null +++ b/plugins/android/testData/dom/layout/fragmentCompletion3_after.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/fragmentHighlighting.xml b/plugins/android/testData/dom/layout/fragmentHighlighting.xml new file mode 100644 index 000000000000..6cac7a50be61 --- /dev/null +++ b/plugins/android/testData/dom/layout/fragmentHighlighting.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + <fragment class="p1.p2.MyFragmentActivity$MyFragment"/> + + \ No newline at end of file diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java index c6fe3894580a..3ae359731c6b 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java @@ -22,6 +22,10 @@ public class AndroidLayoutDomTest extends AndroidDomTest { public void setUp() throws Exception { super.setUp(); myFixture.copyFileToProject(SdkConstants.FN_ANDROID_MANIFEST_XML, SdkConstants.FN_ANDROID_MANIFEST_XML); + + // copy mock fragment, because it is not included to old android.jar + // todo: create normal mock Android sdk + copyFileToProject("Fragment.java", "src/android/app/Fragment.java"); } @Override @@ -206,6 +210,24 @@ public class AndroidLayoutDomTest extends AndroidDomTest { doTestHighlighting("merge.xml"); } + public void testFragmentHighlighting() throws Throwable { + copyFileToProject("MyFragmentActivity.java", "src/p1/p2/MyFragmentActivity.java"); + doTestHighlighting(getTestName(true) + ".xml"); + } + + public void testFragmentCompletion1() throws Throwable { + copyFileToProject("MyFragmentActivity.java", "src/p1/p2/MyFragmentActivity.java"); + toTestCompletion(getTestName(true) + ".xml", getTestName(true) + "_after.xml"); + } + + public void testFragmentCompletion2() throws Throwable { + toTestCompletion(getTestName(true) + ".xml", getTestName(true) + "_after.xml"); + } + + public void testFragmentCompletion3() throws Throwable { + toTestCompletion(getTestName(true) + ".xml", getTestName(true) + "_after.xml"); + } + /*public void testCustomAttrsPerformance() throws Throwable { myFixture.copyFileToProject("dom/resources/bigfile.xml", "res/values/bigfile.xml"); myFixture.copyFileToProject("dom/resources/bigattrs.xml", "res/values/bigattrs.xml");