public static String findUrl(PsiFile file, int offset, String uri) { final PsiElement currentElement = file.findElementAt(offset); final XmlAttribute attribute = PsiTreeUtil.getParentOfType(currentElement, XmlAttribute.class); if (attribute != null) { final XmlTag tag = PsiTreeUtil.getParentOfType(currentElement, XmlTag.class); if (tag != null) { final String prefix = tag.getPrefixByNamespace(XmlUtil.XML_SCHEMA_INSTANCE_URI); if (prefix != null) { final String attrValue = tag.getAttributeValue(XmlUtil.SCHEMA_LOCATION_ATT, XmlUtil.XML_SCHEMA_INSTANCE_URI); if (attrValue != null) { final StringTokenizer tokenizer = new StringTokenizer(attrValue); while (tokenizer.hasMoreElements()) { if (uri.equals(tokenizer.nextToken())) { if (!tokenizer.hasMoreElements()) return uri; final String url = tokenizer.nextToken(); return url.startsWith(HTTP_PROTOCOL) ? url : uri; } if (!tokenizer.hasMoreElements()) return uri; tokenizer.nextToken(); // skip file location } } } } } return uri; }
public String[] knownNamespaces() { final PsiElement parentElement = getParent(); BidirectionalMap<String, String> map = initNamespaceMaps(parentElement); Set<String> known = Collections.emptySet(); if (map != null) { known = new HashSet<String>(map.values()); } if (parentElement instanceof XmlTag) { if (known.isEmpty()) return ((XmlTag) parentElement).knownNamespaces(); ContainerUtil.addAll(known, ((XmlTag) parentElement).knownNamespaces()); } else { XmlExtension xmlExtension = XmlExtension.getExtensionByElement(this); if (xmlExtension != null) { final XmlFile xmlFile = xmlExtension.getContainingFile(this); if (xmlFile != null) { final XmlTag rootTag = xmlFile.getRootTag(); if (rootTag != null && rootTag != this) { if (known.isEmpty()) return rootTag.knownNamespaces(); ContainerUtil.addAll(known, rootTag.knownNamespaces()); } } } } return ArrayUtil.toStringArray(known); }
@Nullable @NonNls public String getSubTagText(@NonNls String qname) { final XmlTag tag = findFirstSubTag(qname); if (tag == null) return null; return tag.getValue().getText(); }
public XmlNSDescriptor getNSDescriptor(final String namespace, boolean strict) { final XmlTag parentTag = getParentTag(); if (parentTag == null && namespace.equals(XmlUtil.XHTML_URI)) { final XmlNSDescriptor descriptor = getDtdDescriptor(XmlUtil.getContainingFile(this)); if (descriptor != null) { return descriptor; } } Map<String, CachedValue<XmlNSDescriptor>> map = initNSDescriptorsMap(); final CachedValue<XmlNSDescriptor> descriptor = map.get(namespace); if (descriptor != null) { final XmlNSDescriptor value = descriptor.getValue(); if (value != null) { return value; } } if (parentTag == null) { final XmlDocument parentOfType = PsiTreeUtil.getParentOfType(this, XmlDocument.class); if (parentOfType == null) { return null; } return parentOfType.getDefaultNSDescriptor(namespace, strict); } return parentTag.getNSDescriptor(namespace, strict); }
private static XmlTag createTag( @NotNull XmlTag contextTag, @NotNull XmlElementDescriptor descriptor) { String namespace = getNamespace(descriptor); XmlTag tag = contextTag.createChildTag(descriptor.getName(), namespace, null, false); PsiElement lastChild = tag.getLastChild(); assert lastChild != null; lastChild.delete(); // remove XML_EMPTY_ELEMENT_END return tag; }
@Override protected boolean isValidForFile( @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { if (!(file instanceof XmlFile)) return false; XmlTag contextTag = getContextTag(editor, file); return contextTag != null && isInsideTagBody(contextTag, editor) && contextTag.getDescriptor() != null; }
@NotNull public XmlTag[] findSubTags(final String name, final String namespace) { final XmlTag[] subTags = getSubTags(); final List<XmlTag> result = new ArrayList<XmlTag>(); for (final XmlTag subTag : subTags) { if (namespace == null) { if (name.equals(subTag.getName())) result.add(subTag); } else if (name.equals(subTag.getLocalName()) && namespace.equals(subTag.getNamespace())) { result.add(subTag); } } return ContainerUtil.toArray(result, new XmlTag[result.size()]); }
@Nullable protected XmlElementDescriptor computeElementDescriptor() { for (XmlElementDescriptorProvider provider : Extensions.getExtensions(XmlElementDescriptorProvider.EP_NAME)) { XmlElementDescriptor elementDescriptor = provider.getDescriptor(this); if (elementDescriptor != null) { return elementDescriptor; } } final String namespace = getNamespace(); if (XmlUtil.EMPTY_URI.equals(namespace)) { // nonqualified items final XmlTag parent = getParentTag(); if (parent != null) { final XmlElementDescriptor descriptor = parent.getDescriptor(); if (descriptor != null) { XmlElementDescriptor fromParent = descriptor.getElementDescriptor(this, parent); if (fromParent != null && !(fromParent instanceof AnyXmlElementDescriptor)) { return fromParent; } } } } XmlElementDescriptor elementDescriptor = null; final XmlNSDescriptor nsDescriptor = getNSDescriptor(namespace, false); LOG.debug( "Descriptor for namespace " + namespace + " is " + (nsDescriptor != null ? nsDescriptor.getClass().getCanonicalName() : "NULL")); if (nsDescriptor != null) { if (!DumbService.getInstance(getProject()).isDumb() || DumbService.isDumbAware(nsDescriptor)) { elementDescriptor = nsDescriptor.getElementDescriptor(this); } } if (elementDescriptor == null) { return XmlUtil.findXmlDescriptorByType(this); } return elementDescriptor; }
public static void generateTag(@NotNull XmlTag newTag, Editor editor) { generateRaw(newTag); final XmlTag restored = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(newTag); if (restored == null) { LOG.error("Could not restore tag: " + newTag.getText()); } TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(restored); replaceElements(restored, builder); builder.run(editor, false); }
private static void replaceElements(XmlTag tag, TemplateBuilder builder) { for (XmlAttribute attribute : tag.getAttributes()) { XmlAttributeValue value = attribute.getValueElement(); if (value != null) { builder.replaceElement(value, TextRange.from(1, 0), new MacroCallNode(new CompleteMacro())); } } if ("<".equals(tag.getText())) { builder.replaceElement( tag, TextRange.from(1, 0), new MacroCallNode(new CompleteSmartMacro())); } else if (tag.getSubTags().length == 0) { int i = tag.getText().indexOf("></"); if (i > 0) { builder.replaceElement( tag, TextRange.from(i + 1, 0), new MacroCallNode(new CompleteMacro())); } } for (XmlTag subTag : tag.getSubTags()) { replaceElements(subTag, builder); } }
private static void generateRaw(final @NotNull XmlTag newTag) { XmlElementDescriptor selected = newTag.getDescriptor(); if (selected == null) return; switch (selected.getContentType()) { case XmlElementDescriptor.CONTENT_TYPE_EMPTY: newTag.collapseIfEmpty(); ASTNode node = newTag.getNode(); assert node != null; ASTNode elementEnd = node.findChildByType(XmlTokenType.XML_EMPTY_ELEMENT_END); if (elementEnd == null) { LeafElement emptyTagEnd = Factory.createSingleLeafElement( XmlTokenType.XML_EMPTY_ELEMENT_END, "/>", 0, 2, null, newTag.getManager()); node.addChild(emptyTagEnd); } break; case XmlElementDescriptor.CONTENT_TYPE_MIXED: newTag.getValue().setText(""); } for (XmlAttributeDescriptor descriptor : selected.getAttributesDescriptors(newTag)) { if (descriptor.isRequired()) { newTag.setAttribute(descriptor.getName(), ""); } } List<XmlElementDescriptor> tags = getRequiredSubTags(selected); for (XmlElementDescriptor descriptor : tags) { if (descriptor == null) { XmlTag tag = XmlElementFactory.getInstance(newTag.getProject()) .createTagFromText("<", newTag.getLanguage()); newTag.addSubTag(tag, false); } else { XmlTag subTag = newTag.addSubTag(createTag(newTag, descriptor), false); generateRaw(subTag); } } }
@Nullable private static XmlTag getAnchor( @NotNull XmlTag contextTag, Editor editor, XmlElementDescriptor selected) { XmlContentDFA contentDFA = XmlContentDFA.getContentDFA(contextTag); int offset = editor.getCaretModel().getOffset(); if (contentDFA == null) { return null; } XmlTag anchor = null; boolean previousPositionIsPossible = true; for (XmlTag subTag : contextTag.getSubTags()) { if (contentDFA.getPossibleElements().contains(selected)) { if (subTag.getTextOffset() > offset) { break; } anchor = subTag; previousPositionIsPossible = true; } else { previousPositionIsPossible = false; } contentDFA.transition(subTag); } return previousPositionIsPossible ? null : anchor; }
@Override public void invoke( @NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) { if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return; try { final XmlTag contextTag = getContextTag(editor, file); if (contextTag == null) { throw new CommonRefactoringUtil.RefactoringErrorHintException( "Caret should be positioned inside a tag"); } XmlElementDescriptor currentTagDescriptor = contextTag.getDescriptor(); assert currentTagDescriptor != null; final XmlElementDescriptor[] descriptors = currentTagDescriptor.getElementsDescriptors(contextTag); Arrays.sort( descriptors, new Comparator<XmlElementDescriptor>() { @Override public int compare(XmlElementDescriptor o1, XmlElementDescriptor o2) { return o1.getName().compareTo(o2.getName()); } }); final JBList list = new JBList(descriptors); list.setCellRenderer(new MyListCellRenderer()); Runnable runnable = new Runnable() { @Override public void run() { final XmlElementDescriptor selected = (XmlElementDescriptor) list.getSelectedValue(); new WriteCommandAction.Simple(project, "Generate XML Tag", file) { @Override protected void run() { if (selected == null) return; XmlTag newTag = createTag(contextTag, selected); PsiElement anchor = getAnchor(contextTag, editor, selected); if (anchor == null) { // insert it in the cursor position int offset = editor.getCaretModel().getOffset(); Document document = editor.getDocument(); document.insertString(offset, newTag.getText()); PsiDocumentManager.getInstance(project).commitDocument(document); newTag = PsiTreeUtil.getParentOfType( file.findElementAt(offset + 1), XmlTag.class, false); } else { newTag = (XmlTag) contextTag.addAfter(newTag, anchor); } if (newTag != null) { generateTag(newTag, editor); } } }.execute(); } }; if (ApplicationManager.getApplication().isUnitTestMode()) { XmlElementDescriptor descriptor = ContainerUtil.find( descriptors, new Condition<XmlElementDescriptor>() { @Override public boolean value(XmlElementDescriptor xmlElementDescriptor) { return xmlElementDescriptor.getName().equals(TEST_THREAD_LOCAL.get()); } }); list.setSelectedValue(descriptor, false); runnable.run(); } else { JBPopupFactory.getInstance() .createListPopupBuilder(list) .setTitle("Choose Tag Name") .setItemChoosenCallback(runnable) .setFilteringEnabled( new Function<Object, String>() { @Override public String fun(Object o) { return ((XmlElementDescriptor) o).getName(); } }) .createPopup() .showInBestPositionFor(editor); } } catch (CommonRefactoringUtil.RefactoringErrorHintException e) { HintManager.getInstance().showErrorHint(editor, e.getMessage()); } }
private static boolean isInsideTagBody(XmlTag contextTag, @NotNull Editor editor) { return contextTag.getValue().getTextRange().contains(editor.getCaretModel().getOffset()); }
@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()); } } } } }