public PsiFile resolve() { final PsiFile referencedFile = getReferencedFile(); if (referencedFile != null) { return referencedFile; } if (myPsiFile != null) { final PsiElement psiElement = myPsiFile.findElementAt(myLinkInfo.offset); if (psiElement != null) { final PsiElement parent = psiElement.getParent(); if (parent instanceof XmlTag) { final XmlAttribute attribute = ((XmlTag) parent).getAttribute(HREF_ATTR); if (attribute != null) { final XmlAttributeValue value = attribute.getValueElement(); if (value != null) { final PsiReference[] references = value.getReferences(); for (PsiReference reference : references) { final PsiElement element = reference.resolve(); if (element instanceof PsiFile) { return (PsiFile) element; } } } } } } } return null; }
private void checkDuplicateAttribute(XmlTag tag, final XmlAttribute attribute) { if (skipValidation(tag)) { return; } final XmlAttribute[] attributes = tag.getAttributes(); final PsiFile containingFile = tag.getContainingFile(); final XmlExtension extension = containingFile instanceof XmlFile ? XmlExtension.getExtension(containingFile) : XmlExtension.DEFAULT_EXTENSION; for (XmlAttribute tagAttribute : attributes) { ProgressManager.checkCanceled(); if (attribute != tagAttribute && Comparing.strEqual(attribute.getName(), tagAttribute.getName())) { final String localName = attribute.getLocalName(); if (extension.canBeDuplicated(tagAttribute)) continue; // multiple import attributes are allowed in jsp directive HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo( getTagProblemInfoType(tag), XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild( SourceTreeToPsiMap.psiElementToTree(attribute)), XmlErrorMessages.message("duplicate.attribute", localName)); addToResults(highlightInfo); IntentionAction intentionAction = new RemoveAttributeIntentionFix(localName, attribute); QuickFixAction.registerQuickFixAction(highlightInfo, intentionAction); } } }
@NotNull public XPathType getExpectedType(XPathExpression expr) { final XmlTag tag = PsiTreeUtil.getContextOfType(expr, XmlTag.class, true); if (tag != null && XsltSupport.isXsltTag(tag)) { final XsltElement element = XsltElementFactory.getInstance().wrapElement(tag, XsltElement.class); if (element instanceof XsltVariable) { return ((XsltVariable) element).getType(); } else { final XmlAttribute attr = PsiTreeUtil.getContextOfType(expr, XmlAttribute.class, true); if (attr != null) { if (element instanceof XsltWithParam) { final XmlAttribute nameAttr = tag.getAttribute("name", null); if (nameAttr != null) { final XmlAttributeValue valueElement = nameAttr.getValueElement(); if (valueElement != null) { final PsiReference[] references = valueElement.getReferences(); for (PsiReference reference : references) { final PsiElement psiElement = reference.resolve(); if (psiElement instanceof XsltVariable) { return ((XsltVariable) psiElement).getType(); } } } } } else { final String name = attr.getName(); return getTypeForTag(tag, name); } } } } return XPathType.UNKNOWN; }
@Override public void visitXmlAttributeValue(XmlAttributeValue value) { final PsiElement parent = value.getParent(); if (!(parent instanceof XmlAttribute)) { checkReferences(value); return; } XmlAttribute attribute = (XmlAttribute) parent; XmlTag tag = attribute.getParent(); XmlElementDescriptor elementDescriptor = tag.getDescriptor(); XmlAttributeDescriptor attributeDescriptor = elementDescriptor != null ? elementDescriptor.getAttributeDescriptor(attribute) : null; if (attributeDescriptor != null && !skipValidation(value)) { String error = attributeDescriptor.validateValue(value, attribute.getValue()); if (error != null) { addToResults(HighlightInfo.createHighlightInfo(getTagProblemInfoType(tag), value, error)); return; } } checkReferences(value); }
@Override public void visitXmlAttribute(XmlAttribute attribute) { final XmlAttributeDescriptor descriptor = attribute.getDescriptor(); if (descriptor instanceof JavaFxStaticSetterAttributeDescriptor) { final PsiElement declaration = descriptor.getDeclaration(); if (declaration instanceof PsiMember) { appendClassName(((PsiMember) declaration).getContainingClass()); } } else if (descriptor instanceof JavaFxRootTagDescriptor.RootTagTypeAttributeDescriptor) { appendClassName(JavaFxPsiUtil.findPsiClass(attribute.getValue(), attribute)); } }
private static boolean isHrefScripted(final XmlTag tag) { final XmlAttribute attribute = tag.getAttribute(HREF_ATTR); if (attribute != null) { final XmlAttributeValue value = attribute.getValueElement(); if (value != null) { if (PsiTreeUtil.getChildOfType(value, OuterLanguageElement.class) != null) { return true; } } } return false; }
@Nullable private static String getAttributeValue(final XmlTag tag, final String attrName) { final XmlAttribute attribute = tag.getAttribute(attrName); if (attribute != null) { final XmlAttributeValue value = attribute.getValueElement(); if (value != null) { if (PsiTreeUtil.getChildOfType(value, OuterLanguageElement.class) == null) { return value.getValue(); } } } return null; }
@Nullable public XmlAttribute getAttribute(String qname) { if (qname == null) return null; final XmlAttribute[] attributes = getAttributes(); final boolean caseSensitive = isCaseSensitive(); for (final XmlAttribute attribute : attributes) { final LeafElement attrNameElement = (LeafElement) XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(attribute.getNode()); if (attrNameElement != null && (caseSensitive && Comparing.equal(attrNameElement.getChars(), qname) || !caseSensitive && Comparing.equal(attrNameElement.getChars(), qname, false))) { return attribute; } } return null; }
public XmlAttribute setAttribute(String qname, String value) throws IncorrectOperationException { final XmlAttribute attribute = getAttribute(qname); if (attribute != null) { if (value == null) { deleteChildInternal(attribute.getNode()); return null; } attribute.setValue(value); return attribute; } else if (value == null) { return null; } else { PsiElement xmlAttribute = add(XmlElementFactory.getInstance(getProject()).createXmlAttribute(qname, value)); while (!(xmlAttribute instanceof XmlAttribute)) xmlAttribute = xmlAttribute.getNextSibling(); return (XmlAttribute) xmlAttribute; } }
@Nullable private BidirectionalMap<String, String> computeNamespaceMap(PsiElement parent) { BidirectionalMap<String, String> map = null; if (hasNamespaceDeclarations()) { map = new BidirectionalMap<String, String>(); final XmlAttribute[] attributes = getAttributes(); for (final XmlAttribute attribute : attributes) { if (attribute.isNamespaceDeclaration()) { final String name = attribute.getName(); int splitIndex = name.indexOf(':'); final String value = getRealNs(attribute.getValue()); if (value != null) { if (splitIndex < 0) { map.put("", value); } else { map.put(XmlUtil.findLocalNameByQualifiedName(name), value); } } } } } if (parent instanceof XmlDocument) { final XmlExtension extension = XmlExtension.getExtensionByElement(parent); if (extension != null) { final String[][] defaultNamespace = extension.getNamespacesFromDocument((XmlDocument) parent, map != null); if (defaultNamespace != null) { if (map == null) { map = new BidirectionalMap<String, String>(); } for (final String[] prefix2ns : defaultNamespace) { map.put(prefix2ns[0], getRealNs(prefix2ns[1])); } } } } return map; }
@NotNull private Map<String, CachedValue<XmlNSDescriptor>> computeNsDescriptorMap() { Map<String, CachedValue<XmlNSDescriptor>> map = null; // XSD aware attributes processing final String noNamespaceDeclaration = getAttributeValue("noNamespaceSchemaLocation", XmlUtil.XML_SCHEMA_INSTANCE_URI); final String schemaLocationDeclaration = getAttributeValue("schemaLocation", XmlUtil.XML_SCHEMA_INSTANCE_URI); if (noNamespaceDeclaration != null) { map = initializeSchema(XmlUtil.EMPTY_URI, null, noNamespaceDeclaration, map); } if (schemaLocationDeclaration != null) { final StringTokenizer tokenizer = new StringTokenizer(schemaLocationDeclaration); while (tokenizer.hasMoreTokens()) { final String uri = tokenizer.nextToken(); if (tokenizer.hasMoreTokens()) { map = initializeSchema(uri, null, tokenizer.nextToken(), map); } } } // namespace attributes processing (XSD declaration via ExternalResourceManager) if (hasNamespaceDeclarations()) { for (final XmlAttribute attribute : getAttributes()) { if (attribute.isNamespaceDeclaration()) { String ns = attribute.getValue(); if (ns == null) ns = XmlUtil.EMPTY_URI; ns = getRealNs(ns); if (map == null || !map.containsKey(ns)) { map = initializeSchema(ns, getNSVersion(ns, this), getNsLocation(ns), map); } } } } return map == null ? Collections.<String, CachedValue<XmlNSDescriptor>>emptyMap() : map; }
public PsiFile[] getRelatedFiles(final XPathFile file) { final XmlAttribute attribute = PsiTreeUtil.getContextOfType(file, XmlAttribute.class, false); assert attribute != null; final PsiFile psiFile = attribute.getContainingFile(); assert psiFile != null; final List<PsiFile> files = new ArrayList<PsiFile>(); psiFile.accept( new XmlRecursiveElementVisitor() { @Override public void visitXmlAttribute(XmlAttribute attribute) { final PsiFile[] _files = XsltSupport.getFiles(attribute); for (PsiFile _file : _files) { if (_file != file) files.add(_file); } } }); return PsiUtilCore.toPsiFileArray(files); }
@NotNull public Map<String, String> getLocalNamespaceDeclarations() { Map<String, String> namespaces = new THashMap<String, String>(); for (final XmlAttribute attribute : getAttributes()) { if (!attribute.isNamespaceDeclaration() || attribute.getValue() == null) continue; // xmlns -> "", xmlns:a -> a final String localName = attribute.getLocalName(); namespaces.put(localName.equals(attribute.getName()) ? "" : localName, attribute.getValue()); } return namespaces; }
@Override public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { if (!FileModificationService.getInstance().preparePsiElementsForWrite(element)) return; final XmlAttribute attr = (XmlAttribute) element.getParent(); final String name = attr.getName(); final XmlAttributeDescriptor descriptor = attr.getDescriptor(); LOG.assertTrue(descriptor != null); String value = attr.getValue(); final PsiElement declaration = descriptor.getDeclaration(); if (declaration instanceof PsiField) { final PsiType fieldType = ((PsiField) declaration).getType(); final PsiType itemType = JavaGenericsUtil.getCollectionItemType(fieldType, declaration.getResolveScope()); if (itemType != null) { final String typeNode = itemType.getPresentableText(); JavaFxPsiUtil.insertImportWhenNeeded( (XmlFile) attr.getContainingFile(), typeNode, itemType.getCanonicalText()); final String[] vals = value.split(","); value = StringUtil.join( vals, new Function<String, String>() { @Override public String fun(String s) { return "<" + typeNode + " " + FxmlConstants.FX_VALUE + "=\"" + s.trim() + "\"/>"; } }, "\n"); } } final XmlTag childTag = XmlElementFactory.getInstance(project) .createTagFromText("<" + name + ">" + value + "</" + name + ">"); attr.getParent().add(childTag); attr.delete(); }
@Override public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) { final PsiFile containingFile = element.getContainingFile(); if (!JavaFxFileTypeFactory.isFxml(containingFile)) return; if (element instanceof XmlAttributeValue) { final String value = ((XmlAttributeValue) element).getValue(); if (!JavaFxPsiUtil.isExpressionBinding(value) && !JavaFxPsiUtil.isIncorrectExpressionBinding(value)) { final PsiReference[] references = element.getReferences(); for (PsiReference reference : references) { if (reference instanceof JavaFxColorReference) { attachColorIcon(element, holder, StringUtil.unquoteString(element.getText())); continue; } final PsiElement resolve = reference.resolve(); if (resolve instanceof PsiMember) { if (!JavaFxPsiUtil.isVisibleInFxml((PsiMember) resolve)) { final String symbolPresentation = "'" + SymbolPresentationUtil.getSymbolPresentableText(resolve) + "'"; final Annotation annotation = holder.createErrorAnnotation( element, symbolPresentation + (resolve instanceof PsiClass ? " should be public" : " should be public or annotated with @FXML")); if (!(resolve instanceof PsiClass)) { annotation.registerUniversalFix( new AddAnnotationFix( JavaFxCommonNames.JAVAFX_FXML_ANNOTATION, (PsiMember) resolve, ArrayUtil.EMPTY_STRING_ARRAY), null, null); } } } } } } else if (element instanceof XmlAttribute) { final XmlAttribute attribute = (XmlAttribute) element; final String attributeName = attribute.getName(); if (!FxmlConstants.FX_BUILT_IN_ATTRIBUTES.contains(attributeName) && !attribute.isNamespaceDeclaration() && JavaFxPsiUtil.isReadOnly(attributeName, attribute.getParent())) { holder.createErrorAnnotation( element.getNavigationElement(), "Property '" + attributeName + "' is read-only"); } if (FxmlConstants.SOURCE.equals(attributeName)) { final XmlAttributeValue valueElement = attribute.getValueElement(); if (valueElement != null) { final XmlTag xmlTag = attribute.getParent(); if (xmlTag != null) { final XmlTag referencedTag = JavaFxBuiltInTagDescriptor.getReferencedTag(xmlTag); if (referencedTag != null) { if (referencedTag.getTextOffset() > xmlTag.getTextOffset()) { holder.createErrorAnnotation( valueElement.getValueTextRange(), valueElement.getValue() + " not found"); } else if (xmlTag.getParentTag() == referencedTag.getParentTag()) { final Annotation annotation = holder.createErrorAnnotation( valueElement.getValueTextRange(), "Duplicate child added"); annotation.registerFix( new JavaFxWrapWithDefineIntention(referencedTag, valueElement.getValue())); } } } } } } else if (element instanceof XmlTag) { if (FxmlConstants.FX_SCRIPT.equals(((XmlTag) element).getName())) { final XmlTagValue tagValue = ((XmlTag) element).getValue(); if (!StringUtil.isEmptyOrSpaces(tagValue.getText())) { final List<String> langs = JavaFxPsiUtil.parseInjectedLanguages((XmlFile) element.getContainingFile()); if (langs.isEmpty()) { final ASTNode openTag = element.getNode().findChildByType(XmlTokenType.XML_NAME); final Annotation annotation = holder.createErrorAnnotation( openTag != null ? openTag.getPsi() : element, "Page language not specified."); annotation.registerFix(new JavaFxInjectPageLanguageIntention()); } } } } }
private void checkAttribute(XmlAttribute attribute) { XmlTag tag = attribute.getParent(); final String name = attribute.getName(); PsiElement prevLeaf = PsiTreeUtil.prevLeaf(attribute); if (!(prevLeaf instanceof PsiWhiteSpace)) { TextRange textRange = attribute.getTextRange(); addToResults( HighlightInfo.createHighlightInfo( tag instanceof HtmlTag ? HighlightInfoType.WARNING : HighlightInfoType.ERROR, textRange.getStartOffset(), textRange.getStartOffset(), XmlErrorMessages.message("attribute.should.be.preceded.with.space"))); } if (attribute.isNamespaceDeclaration()) { checkReferences(attribute.getValueElement()); return; } final String namespace = attribute.getNamespace(); if (XmlUtil.XML_SCHEMA_INSTANCE_URI.equals(namespace)) { checkReferences(attribute.getValueElement()); return; } XmlElementDescriptor elementDescriptor = tag.getDescriptor(); if (elementDescriptor == null || elementDescriptor instanceof AnyXmlElementDescriptor || ourDoJaxpTesting) { return; } XmlAttributeDescriptor attributeDescriptor = elementDescriptor.getAttributeDescriptor(attribute); if (attributeDescriptor == null) { if (!XmlUtil.attributeFromTemplateFramework(name, tag)) { final String localizedMessage = XmlErrorMessages.message("attribute.is.not.allowed.here", name); final HighlightInfo highlightInfo = reportAttributeProblem(tag, name, attribute, localizedMessage); if (highlightInfo != null) { final XmlFile xmlFile = (XmlFile) tag.getContainingFile(); if (xmlFile != null) { XmlExtension.getExtension(xmlFile).createAddAttributeFix(attribute, highlightInfo); } } } } else { checkDuplicateAttribute(tag, attribute); if (tag instanceof HtmlTag && attribute.getValueElement() == null && !HtmlUtil.isSingleHtmlAttribute(name)) { final String localizedMessage = XmlErrorMessages.message("empty.attribute.is.not.allowed", name); reportAttributeProblem(tag, name, attribute, localizedMessage); } // we skip resolve of attribute references since there is separate check when taking attribute // descriptors PsiReference[] attrRefs = attribute.getReferences(); doCheckRefs(attribute, attrRefs, attribute.getNamespacePrefix().length() > 0 ? 2 : 1); } }