javafx: simple navigation

This commit is contained in:
anna
2013-01-10 22:07:37 +01:00
parent cab46ebc7f
commit 5373b631c6
15 changed files with 675 additions and 0 deletions
+4
View File
@@ -5,6 +5,7 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/resources" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/testSrc" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
@@ -13,6 +14,9 @@
<orderEntry type="module" module-name="execution-impl" />
<orderEntry type="module" module-name="lang-impl" />
<orderEntry type="module" module-name="java-psi-api" />
<orderEntry type="module" module-name="xml-openapi" />
<orderEntry type="module" module-name="xml" />
<orderEntry type="module" module-name="testFramework-java" scope="TEST" />
</component>
</module>
+5
View File
@@ -5,5 +5,10 @@
<vendor>JetBrains</vendor>
<extensions defaultExtensionNs="com.intellij">
<errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
<fileTypeFactory implementation="org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory"/>
<xml.xmlExtension implementation="org.jetbrains.plugins.javaFX.fxml.JavaFxXmlExtension"/>
<xml.fileNSInfoProvider implementation="org.jetbrains.plugins.javaFX.fxml.JavaFXNamespaceProvider"/>
<metaDataContributor implementation="org.jetbrains.plugins.javaFX.fxml.JavaFXMetaDataContributor"/>
<xml.schemaProvider implementation="org.jetbrains.plugins.javaFX.fxml.JavaFXSchemaHandler"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,17 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.psi.filters.position.NamespaceFilter;
import com.intellij.psi.filters.position.RootTagFilter;
import com.intellij.psi.meta.MetaDataContributor;
import com.intellij.psi.meta.MetaDataRegistrar;
/**
* User: anna
* Date: 1/9/13
*/
public class JavaFXMetaDataContributor implements MetaDataContributor {
@Override
public void contributeMetaData(MetaDataRegistrar registrar) {
MetaDataRegistrar.getInstance().registerMetaData(new RootTagFilter(new NamespaceFilter(JavaFXNamespaceProvider.JAVAFX_NAMESPACE)), JavaFXNSDescriptor.class);
}
}
@@ -0,0 +1,109 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.codeInsight.daemon.Validator;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.util.ArrayUtil;
import com.intellij.xml.XmlElementDescriptor;
import com.intellij.xml.XmlNSDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* User: anna
* Date: 1/9/13
*/
public class JavaFXNSDescriptor implements XmlNSDescriptor, Validator<XmlDocument> {
private XmlFile myFile;
@Nullable
@Override
public XmlElementDescriptor getElementDescriptor(@NotNull XmlTag tag) {
final String name = tag.getName();
if (StringUtil.isCapitalized(name)) {
return new JavaFxClassBackedElementDescriptor(name, tag);
}
else {
XmlTag parentTag = tag.getParentTag();
return new JavaFxClassBackedElementDescriptor(parentTag.getName(), parentTag).getElementDescriptor(tag, parentTag);
}
}
public static List<String> parseImports(XmlFile file) {
List<String> definedImports = new ArrayList<String>();
XmlDocument document = file.getDocument();
if (document != null) {
XmlProlog prolog = document.getProlog();
final Collection<XmlProcessingInstruction>
instructions = new ArrayList<XmlProcessingInstruction>(PsiTreeUtil.findChildrenOfType(prolog, XmlProcessingInstruction.class));
for (Iterator<XmlProcessingInstruction> iterator = instructions.iterator(); iterator.hasNext(); ) {
final XmlProcessingInstruction instruction = iterator.next();
final ASTNode node = instruction.getNode();
ASTNode xmlNameNode = node.findChildByType(XmlTokenType.XML_NAME);
ASTNode importNode = node.findChildByType(XmlTokenType.XML_TAG_CHARACTERS);
if (xmlNameNode == null || !"import".equals(xmlNameNode.getText()) || importNode == null) {
iterator.remove();
} else {
definedImports.add(importNode.getText());
}
}
}
return definedImports;
}
@NotNull
@Override
public XmlElementDescriptor[] getRootElementsDescriptors(@Nullable XmlDocument document) {
//todo
return new XmlElementDescriptor[0];
}
@Nullable
public XmlFile getDescriptorFile() {
return myFile;
}
public boolean isHierarhyEnabled() {
return false;
}
public PsiElement getDeclaration() {
return myFile;
}
@Override
public String getName(PsiElement context) {
return null;
}
@Override
public String getName() {
return null;
}
@Override
public void init(PsiElement element) {
XmlDocument document = (XmlDocument) element;
myFile = ((XmlFile)document.getContainingFile());
}
@Override
public Object[] getDependences() {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
@Override
public void validate(@NotNull XmlDocument context, @NotNull ValidationHost host) {
//todo check that node has correct type
}
}
@@ -0,0 +1,15 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlFileNSInfoProvider;
import org.jetbrains.annotations.NotNull;
public class JavaFXNamespaceProvider implements XmlFileNSInfoProvider {
public static final String JAVAFX_NAMESPACE = "http://javafx.com/fxml";
private static final String[][] NAMESPACES = {{"", JAVAFX_NAMESPACE}};
public String[][] getDefaultNamespaces(@NotNull XmlFile file) {
return JavaFxFileTypeFactory.isFxml(file) ? NAMESPACES : null;
}
}
@@ -0,0 +1,44 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.xml.XmlFile;
import com.intellij.xml.XmlSchemaProvider;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.net.URL;
/**
* User: anna
* Date: 1/10/13
*/
public class JavaFXSchemaHandler extends XmlSchemaProvider {
private static final Logger LOG = Logger.getInstance("#" + JavaFXSchemaHandler.class.getName());
@Override
public boolean isAvailable(final @NotNull XmlFile file) {
return JavaFxFileTypeFactory.isFxml(file);
}
@Nullable
@Override
public XmlFile getSchema(@NotNull @NonNls String url, @Nullable Module module, @NotNull PsiFile baseFile) {
return module != null && JavaFxFileTypeFactory.isFxml(baseFile) ? getReference(module) : null;
}
private static XmlFile getReference(@NotNull Module module) {
final URL resource = JavaFXSchemaHandler.class.getResource("fx.xsd");
final VirtualFile fileByURL = VfsUtil.findFileByURL(resource);
PsiFile psiFile = PsiManager.getInstance(module.getProject()).findFile(fileByURL);
LOG.assertTrue(psiFile != null);
return (XmlFile)psiFile.copy();
}
}
@@ -0,0 +1,165 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import com.intellij.xml.XmlAttributeDescriptor;
import com.intellij.xml.XmlElementDescriptor;
import com.intellij.xml.XmlElementsGroup;
import com.intellij.xml.XmlNSDescriptor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* User: anna
* Date: 1/9/13
*/
public class JavaFxClassBackedElementDescriptor implements XmlElementDescriptor {
private final PsiClass myPsiClass;
private final String myName;
public JavaFxClassBackedElementDescriptor(String name, XmlTag tag) {
myName = name;
myPsiClass = findPsiClass(name, JavaFXNSDescriptor.parseImports((XmlFile)tag.getContainingFile()), tag, tag.getProject());
}
private static PsiClass findPsiClass(String name, List<String> imports, XmlTag tag, Project project) {
PsiClass psiClass = null;
if (imports != null) {
JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
PsiFile file = tag.getContainingFile();
for (String anImport : imports) {
if (StringUtil.endsWith(anImport, "." + name)) {
psiClass = psiFacade.findClass(anImport, file.getResolveScope());
} else if (StringUtil.endsWith(anImport, ".*")) {
psiClass = psiFacade.findClass(StringUtil.trimEnd(anImport, "*") + name, file.getResolveScope());
}
if (psiClass != null) {
return psiClass;
}
}
}
return psiClass;
}
@Override
public String getQualifiedName() {
return myPsiClass != null ? myPsiClass.getQualifiedName() : getName();
}
@Override
public String getDefaultName() {
return getName();
}
@Override
public XmlElementDescriptor[] getElementsDescriptors(XmlTag context) {
if (context != null) {
//todo
XmlElementDescriptor descriptor = context.getDescriptor();
if (descriptor instanceof JavaFxClassBackedElementDescriptor) {
} else if (descriptor instanceof JavaFxListPropertyElementDescriptor) {
}
}
return XmlElementDescriptor.EMPTY_ARRAY;
}
@Nullable
@Override
public XmlElementDescriptor getElementDescriptor(XmlTag childTag, XmlTag contextTag) {
final String name = childTag.getName();
if (StringUtil.isCapitalized(name)) {
return new JavaFxClassBackedElementDescriptor(name, childTag);
}
else {
return myPsiClass != null ? new JavaFxListPropertyElementDescriptor(myPsiClass, name) : null;
}
}
@Override
public XmlAttributeDescriptor[] getAttributesDescriptors(@Nullable XmlTag context) {
if (context != null) {
//todo
final String name = context.getName();
if (Comparing.equal(name, getName()) && myPsiClass != null) {
final PsiField[] fields = myPsiClass.getAllFields();
if (fields.length > 0) {
final XmlAttributeDescriptor[] simpleAttrs = new XmlAttributeDescriptor[fields.length];
for (int i = 0; i < fields.length; i++) {
simpleAttrs[i] = new JavaFxPropertyBackedAttributeDescriptor(fields[i].getName(), myPsiClass);
}
return simpleAttrs;
}
}
}
return XmlAttributeDescriptor.EMPTY;
}
@Nullable
@Override
public XmlAttributeDescriptor getAttributeDescriptor(@NonNls String attributeName, @Nullable XmlTag context) {
return new JavaFxPropertyBackedAttributeDescriptor(attributeName, (PsiClass)getDeclaration());
}
@Nullable
@Override
public XmlAttributeDescriptor getAttributeDescriptor(XmlAttribute attribute) {
return getAttributeDescriptor(attribute.getName(), attribute.getParent());
}
@Override
public XmlNSDescriptor getNSDescriptor() {
return null;
}
@Nullable
@Override
public XmlElementsGroup getTopGroup() {
return null;
}
@Override
public int getContentType() {
return CONTENT_TYPE_UNKNOWN;
}
@Nullable
@Override
public String getDefaultValue() {
return null;
}
@Override
public PsiElement getDeclaration() {
return myPsiClass;
}
@Override
public String getName(PsiElement context) {
return getName();
}
@Override
public String getName() {
return myName;
}
@Override
public void init(PsiElement element) {
}
@Override
public Object[] getDependences() {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
}
@@ -0,0 +1,31 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeConsumer;
import com.intellij.openapi.fileTypes.FileTypeFactory;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
/**
* User: anna
* Date: 1/8/13
*/
public class JavaFxFileTypeFactory extends FileTypeFactory {
public static boolean isFxml(PsiFile file) {
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile != null) {
FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension("fxml");
return virtualFile.getFileType() == fileType;
}
return false;
}
@Override
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
final FileType fileType = consumer.getStandardFileTypeByName("XML");
assert fileType != null;
consumer.consume(fileType, "fxml");
}
}
@@ -0,0 +1,119 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import com.intellij.xml.XmlAttributeDescriptor;
import com.intellij.xml.XmlElementDescriptor;
import com.intellij.xml.XmlElementsGroup;
import com.intellij.xml.XmlNSDescriptor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
/**
* User: anna
* Date: 1/10/13
*/
public class JavaFxListPropertyElementDescriptor implements XmlElementDescriptor {
private final PsiClass myPsiClass;
private final String myName;
public JavaFxListPropertyElementDescriptor(PsiClass psiClass, String name) {
myPsiClass = psiClass;
myName = name;
}
@Override
public String getQualifiedName() {
return getName();
}
@Override
public String getDefaultName() {
return getName();
}
@Override
public XmlElementDescriptor[] getElementsDescriptors(XmlTag context) {
//todo
return XmlElementDescriptor.EMPTY_ARRAY;
}
@Nullable
@Override
public XmlElementDescriptor getElementDescriptor(XmlTag childTag, XmlTag contextTag) {
final String name = childTag.getName();
if (StringUtil.isCapitalized(name)) {
return new JavaFxClassBackedElementDescriptor(name, childTag);
}
else {
return myPsiClass != null ? new JavaFxListPropertyElementDescriptor(myPsiClass, name) : null;
}
}
@Override
public XmlAttributeDescriptor[] getAttributesDescriptors(@Nullable XmlTag context) {
return XmlAttributeDescriptor.EMPTY;
}
@Nullable
@Override
public XmlAttributeDescriptor getAttributeDescriptor(@NonNls String attributeName, @Nullable XmlTag context) {
return null;
}
@Nullable
@Override
public XmlAttributeDescriptor getAttributeDescriptor(XmlAttribute attribute) {
return getAttributeDescriptor(attribute.getName(), attribute.getParent());
}
@Override
public XmlNSDescriptor getNSDescriptor() {
return null;
}
@Nullable
@Override
public XmlElementsGroup getTopGroup() {
return null;
}
@Override
public int getContentType() {
return CONTENT_TYPE_UNKNOWN;
}
@Nullable
@Override
public String getDefaultValue() {
return null;
}
@Override
public PsiElement getDeclaration() {
return myPsiClass.findFieldByName(myName, true);
}
@Override
public String getName(PsiElement context) {
return getName();
}
@Override
public String getName() {
return myName;
}
@Override
public void init(PsiElement element) {}
@Override
public Object[] getDependences() {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
}
@@ -0,0 +1,88 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlElement;
import com.intellij.util.ArrayUtil;
import com.intellij.xml.XmlAttributeDescriptor;
import org.jetbrains.annotations.Nullable;
/**
* User: anna
* Date: 1/10/13
*/
public class JavaFxPropertyBackedAttributeDescriptor implements XmlAttributeDescriptor {
private final String myName;
private final PsiClass myPsiClass;
public JavaFxPropertyBackedAttributeDescriptor(String name, PsiClass psiClass) {
myName = name;
myPsiClass = psiClass;
}
@Override
public boolean isRequired() {
return false;
}
@Override
public boolean isFixed() {
return false;
}
@Override
public boolean hasIdType() {
return false;
}
@Override
public boolean hasIdRefType() {
return false;
}
@Nullable
@Override
public String getDefaultValue() {
return null;
}
@Override
public boolean isEnumerated() {
return false;
}
@Nullable
@Override
public String[] getEnumeratedValues() {
return new String[0];
}
@Nullable
@Override
public String validateValue(XmlElement context, String value) {
return null;
}
@Override
public PsiElement getDeclaration() {
return myPsiClass != null ? myPsiClass.findFieldByName(myName, true) : null;
}
@Override
public String getName(PsiElement context) {
return getName();
}
@Override
public String getName() {
return myName;
}
@Override
public void init(PsiElement element) {}
@Override
public Object[] getDependences() {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
}
@@ -0,0 +1,14 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.lang.ASTNode;
import com.intellij.psi.impl.source.xml.TagNameReference;
/**
* User: anna
* Date: 1/8/13
*/
public class JavaFxTagNameReference extends TagNameReference {
public JavaFxTagNameReference(ASTNode element, boolean startTagFlag) {
super(element, startTagFlag);
}
}
@@ -0,0 +1,25 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.xml.TagNameReference;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.xml.DefaultXmlExtension;
import com.intellij.xml.util.XmlUtil;
import org.jetbrains.annotations.Nullable;
public class JavaFxXmlExtension extends DefaultXmlExtension {
public boolean isAvailable(final PsiFile file) {
return JavaFxFileTypeFactory.isFxml(file);
}
public TagNameReference createTagNameReference(final ASTNode nameElement, final boolean startTagFlag) {
return new JavaFxTagNameReference(nameElement, startTagFlag);
}
@Nullable
@Override
public String[][] getNamespacesFromDocument(XmlDocument parent, boolean declarationsExist) {
return XmlUtil.getDefaultNamespaces(parent);
}
}
@@ -0,0 +1 @@
<x xmlns="http://javafx.com/fxml"/>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" unknownAttr="val" xmlns:fx="http://javafx.com/fxml">
<children>
<AnchorPane id="anchorPane2" prefHeight="300.0" AnchorPane.bottomAnchor="200.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
</children>
</AnchorPane>
</children>
<stylesheets/>
<<error descr="Cannot resolve symbol 'UnknownClass'">UnknownClass</error>/>
<<error descr="Cannot resolve symbol 'unknownField'">unknownField</error>/>
</AnchorPane>
@@ -0,0 +1,25 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
import com.intellij.openapi.application.PluginPathManager;
import org.jetbrains.annotations.NotNull;
/**
* User: anna
* Date: 1/10/13
*/
public class JavaFXHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testLoginForm() throws Exception {
doTest();
}
private void doTest() {
doTest(getTestName(true) + ".fxml", false, false);
}
@NotNull
@Override
protected String getTestDataPath() {
return PluginPathManager.getPluginHomePath("javaFX") + "/testData/highlighting/";
}
}