@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 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));
   }
 }
 /**
  * shorten recursively all attributes that are package dependent of the passed nodes and all its
  * child nodes.
  *
  * @param packageName the manifest package name.
  * @param xmlElement the xml element to process recursively.
  */
 private void extractFcqns(String packageName, XmlElement xmlElement) {
   for (XmlAttribute xmlAttribute : xmlElement.getAttributes()) {
     if (xmlAttribute.getModel() != null && xmlAttribute.getModel().isPackageDependent()) {
       String value = xmlAttribute.getValue();
       if (value != null
           && value.startsWith(packageName)
           && value.charAt(packageName.length()) == '.') {
         xmlAttribute.getXml().setValue(value.substring(packageName.length()));
       }
     }
   }
   for (XmlElement child : xmlElement.getMergeableElements()) {
     extractFcqns(packageName, child);
   }
 }
 @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();
 }
  @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;
  }
  private void visit(
      @NonNull ManifestMerger2.MergeType mergeType,
      @NonNull XmlElement xmlElement,
      @NonNull KeyBasedValueResolver<String> valueProvider,
      @NonNull MergingReport.Builder mergingReportBuilder) {

    for (XmlAttribute xmlAttribute : xmlElement.getAttributes()) {

      StringBuilder resultString = new StringBuilder();
      String inputString = xmlAttribute.getValue();
      Matcher matcher = PATTERN.matcher(inputString);
      if (matcher.matches()) {
        while (matcher.matches()) {
          String placeholderValue = valueProvider.getValue(matcher.group(2));
          // whatever precedes the placeholder key is added back to the string.
          resultString.append(matcher.group(1));
          if (placeholderValue == null) {
            // if this is a library, ignore the failure
            MergingReport.Record.Severity severity =
                mergeType == ManifestMerger2.MergeType.LIBRARY
                    ? MergingReport.Record.Severity.INFO
                    : MergingReport.Record.Severity.ERROR;

            xmlAttribute.addMessage(
                mergingReportBuilder,
                severity,
                String.format(
                    "Attribute %1$s at %2$s requires a placeholder substitution"
                        + " but no value for <%3$s> is provided.",
                    xmlAttribute.getId(), xmlAttribute.printPosition(), matcher.group(2)));
            // we add back the placeholder key, since this is not an error for libraries
            resultString.append("${");
            resultString.append(matcher.group(2));
            resultString.append("}");
          } else {
            // record the attribute set
            mergingReportBuilder
                .getActionRecorder()
                .recordAttributeAction(
                    xmlAttribute,
                    PositionImpl.UNKNOWN,
                    Actions.ActionType.INJECTED,
                    null /* attributeOperationType */);

            // substitute the placeholder key with its value.
            resultString.append(placeholderValue);
          }
          // the new input string is the tail of the previous match, as it may contain
          // more placeholders to substitute.
          inputString = matcher.group(3);
          // reset the pattern matching with that new string to test for more placeholders
          matcher = PATTERN.matcher(inputString);
        }
        // append the last remainder (without placeholders) in the result string.
        resultString.append(inputString);
        xmlAttribute.getXml().setValue(resultString.toString());
      }
    }
    for (XmlElement childElement : xmlElement.getMergeableElements()) {
      visit(mergeType, childElement, valueProvider, mergingReportBuilder);
    }
  }