@NbBundle.Messages({"# {0} - tag name", "ERR_invalidPropertyName=Invalid property name: {0}"})
  private FxNode handlePropertyTag(String propName, Attributes atts) {
    PropertyValue pv;

    int errorAttrs = 0;
    for (int i = 0; i < atts.getLength(); i++) {
      String uri = atts.getURI(i);
      if (uri != null) {
        String qn = atts.getQName(i);
        errorAttrs++;
        addAttributeError(
            qn, "property-namespaced-attribute", ERR_propertyElementNamespacedAttribute(qn), qn);
      }
    }

    int stProp = FxXmlSymbols.findStaticProperty(propName);
    switch (stProp) {
      case -1:
        // simple property
        if (!Utilities.isJavaIdentifier(propName)) {
          addError(
              new ErrorMark(
                  start,
                  end,
                  "invalid-property-name",
                  ERR_invalidPropertyName(propName),
                  propName));
        }
        if (errorAttrs == atts.getLength()) {
          pv = handleSimpleProperty(propName, atts);
        } else {
          pv = handleMapProperty(propName, atts);
        }
        break;

      case -2:
        // broken name, but must create a node
        pv = accessor.makeBroken(accessor.createProperty(propName, false));
        // do not add the property to the parent, it's broken beyond repair
        addError(
            new ErrorMark(
                start, end, "invalid-property-name", ERR_invalidPropertyName(propName), propName));
        break;

      default:
        // static property, just ignore for now
        pv =
            handleStaticProperty(
                propName.substring(0, stProp), propName.substring(stProp + 1), atts);
        break;
    }

    return pv;
  }
  /**
   * Processes instance (non-static) property. As per examples in Guides, instance property element
   * must NOT have any attributes; otherwise it corresponds to an readonly Map element, and the
   * property must be of the Map type.
   *
   * @param propName
   * @param atts As
   */
  @NbBundle.Messages({
    "# {0} - attribute name",
    "ERR_propertyElementNamespacedAttribute=Property elements may not contain attributes with namespace: {0}"
  })
  private PropertyValue handleSimpleProperty(String propName, Attributes atts) {
    PropertyValue p;

    // no relevant attributes to use, real simple property then
    p = accessor.createProperty(propName, false);

    return p;
  }
  private FxNode handleInstanceContent(CharSequence seq) {
    // find among properties as setter, which is marked as implicit. If there's none, create one.
    PropertySetter defaultSetter = null;

    for (PropertyValue p : current.getProperties()) {
      if (p instanceof PropertySetter) {
        PropertySetter ps = (PropertySetter) p;
        if (ps.isImplicit()) {
          defaultSetter = ps;
        }
      }
    }

    if (defaultSetter == null) {
      defaultSetter = accessor.createProperty(null, true);
      i(defaultSetter).startAt(contentLocator.getElementOffset());
      attachProperty(defaultSetter);
      attachChildNode(defaultSetter);
    }
    accessor.addContent(defaultSetter, seq);
    return defaultSetter;
  }
  /**
   * Checks that the instance is allowed in this context. May even create e.g. default property
   * setter etc. Will return true, if the instance can be attached to the parent.
   */
  @NbBundle.Messages({
    "# {0} - tag name",
    "ERR_moreRootElements=Duplicate root element: {0}",
    "ERR_instanceInMapProperty=Cannot add instances directly to readonly Map",
    "# {0} - parent tag name",
    "ERR_parentNotSupportInstance=Instances cannot be added to the parent {0}"
  })
  private FxNode attachInstance(FxObjectBase instance) {
    String localName = instance.getSourceName();
    int off = contentLocator.getElementOffset() + 1;

    // check the parent, whether it is appropriate to host such a node:
    FxNode parent = nodeStack.peek();

    if (parent.getKind() == Kind.Instance) {
      // pretend we have a default property
      PropertySetter s = accessor.createProperty(null, true);
      i(s).startAt(contentLocator.getElementOffset());
      attachChildNode(s);
      parent = s;
    }

    if (parent.getKind() == Kind.Source) {
      FxObjectBase old = rootComponent;
      if (old != null) {
        addError(
            new ErrorMark(
                off,
                contentLocator.getEndOffset() - off,
                "duplicate-root",
                ERR_moreRootElements(localName),
                localName));
        accessor.makeBroken(instance);
      } else if (!(instance instanceof FxInstance)) {
        // FIXME - report error that fx:reference is not accepted on root element
        throw new UnsupportedOperationException();
      } else {
        rootComponent = (FxInstance) instance;
      }
    } else if (parent.getKind() == Kind.Property) {
      if (parent instanceof MapProperty) {
        addError(
            new ErrorMark(
                off,
                contentLocator.getEndOffset() - off,
                "instance-in-map-property",
                ERR_instanceInMapProperty(),
                localName));
        accessor.makeBroken(instance);
      }
    } else if (parent.getKind() == Kind.Element
        && parent.getSourceName().equals(FxXmlSymbols.FX_DEFINITIONS)
        && (instance instanceof FxNewInstance)) {
      instanceDefinitions.add((FxNewInstance) instance);
    } else {
      if (parent.getKind() != Kind.Error) {
        addError(
            new ErrorMark(
                off,
                contentLocator.getEndOffset() - off,
                "parent-not-support-instance",
                ERR_parentNotSupportInstance(parent.getSourceName())));
        accessor.makeBroken(instance);
      }
    }
    return instance;
  }
  @NbBundle.Messages({
    "# {0} - attribute name",
    "ERR_lowercasePropertyName=Invalid property name: {0}. Property name, or the last component of a static property name must start with lowercase.",
    "# {0} - attribute name",
    "ERR_invalidReservedPropertyName=Unknown name in FXML reserved namespace: {0}",
    "# {0} - attribute qname",
    "# {1} - tag name",
    "ERR_unsupportedAttribute=Unsupported attribute {0} on {1}"
  })
  private void processInstanceAttributes(Attributes atts) {
    for (int i = 0; i < atts.getLength(); i++) {
      String uri = atts.getURI(i);
      String name = atts.getLocalName(i);
      String qname = atts.getQName(i);

      PropertySetter ps = null;

      FxNode node;

      if (qname.startsWith("xmlns")) { // NOI18N
        // FIXME - xmlns attributes will be represented as FxNodes :-/
        continue;
      }

      if (FXML_FX_NAMESPACE.equals(uri)) {
        if (!(FX_ID.equals(name)
            || FX_CONTROLLER.equals(name)
            || FX_VALUE.equals(name)
            || FX_FACTORY.contains(name))) {
          addAttributeError(
              qname,
              "error-unsupported-attribute",
              ERR_unsupportedAttribute(qname, tagName),
              qname,
              tagName);
        }
        continue;
      }

      if (current instanceof FxInstanceCopy) {
        if (FxXmlSymbols.FX_ATTR_REFERENCE_SOURCE.equals(name) && uri == null) {
          // ignore source in fx:copy
          continue;
        }
      }

      // if the name begins with "on", it's an event handler.
      if (name.startsWith(EVENT_HANDLER_PREFIX) && name.length() > EVENT_HANDLER_PREFIX_LEN) {
        String en =
            Character.toLowerCase(name.charAt(EVENT_HANDLER_PREFIX_LEN))
                + name.substring(EVENT_HANDLER_PREFIX_LEN + 1);
        node = processEventHandlerAttribute(en, atts.getValue(i));
        // special hack for fx:copy or fx:reference
      } else {
        // FIXME - error detection for static property
        int stProp = FxXmlSymbols.findStaticProperty(name);
        if (stProp == -2) {
          // report error, not a well formed property name.
          addAttributeError(qname, "invalid-property-name", ERR_lowercasePropertyName(name), name);
          node = accessor.makeBroken(accessor.createProperty(name, false));
        } else if (stProp == -1) {
          // this is a normal property
          node = ps = accessor.createProperty(name, false);
        } else {
          // it is a static property
          node =
              ps =
                  accessor.createStaticProperty(
                      name.substring(stProp + 1), name.substring(0, stProp));
        }
        if (ps != null) {
          accessor.addContent(ps, atts.getValue(i));
          node = ps;
        }
      }
      initAttribute(node, qname);
      attachProperty(ps);
      attachChildNode(node);
    }
  }