@Override
 public void endElement(String uri, String localName, String qName) throws SAXException {
   addElementErrors();
   FxNode node = nodeStack.pop();
   i(node).endsAt(contentLocator.getEndOffset()).endContent(contentLocator.getElementOffset());
   if (node instanceof PropertySetter) {
     PropertySetter s = (PropertySetter) node;
     if (s.isImplicit()) {
       // actually the outer element ends
       node = nodeStack.pop();
       // copy the offset information
       i(node).endsAt(contentLocator.getEndOffset()).endContent(contentLocator.getElementOffset());
     }
   }
   String tn = node.getSourceName();
   if (!tn.equals(localName)) {
     throw new IllegalStateException();
   }
   // special hack for parent nodes, which are implicit property setters:
   FxNode parentNode = nodeStack.peek();
   if (parentNode instanceof PropertySetter) {
     PropertySetter ps = (PropertySetter) parentNode;
     if (ps.isImplicit() && ps.getContent() == null) {
       i(ps).endsAt(contentLocator.getEndOffset()).endContent(contentLocator.getEndOffset());
     }
   }
   if (!nodeStack.isEmpty() && nodeStack.peek().getKind() == Kind.Instance) {
     current = (FxInstance) nodeStack.peek();
   } else {
     current = null;
   }
 }
  @Override
  public void characterSequence(CharSequence seq) {

    addElementErrors();

    int length = seq.length();
    FxNode node = nodeStack.peek();
    FxNode addedNode = null;

    switch (node.getKind()) {
      case Event:
        addedNode = handleEventContent(seq);
        break;
      case Instance:
        addedNode = handleInstanceContent(seq);
        break;
      case Property:
        addedNode = handlePropertyContent(seq);
        break;

      default:
        addError(
            new ErrorMark(
                contentLocator.getElementOffset(),
                length,
                "unexpected-characters",
                ERR_unexpectedCharacters()));
    }
    if (addedNode != null) {
      i(addedNode).endsAt(contentLocator.getEndOffset());
    }
  }
  @NbBundle.Messages({
    "# {0} - NS prefix",
    "ERR_undeclaredElementPrefix=XML namespace prefix ''{0}'' is undeclared"
  })
  @Override
  public void startElement(String uri, String localName, String qName, Attributes atts)
      throws SAXException {
    this.tagName = localName;

    FxNode newElement;

    start = contentLocator.getElementOffset();
    end = contentLocator.getEndOffset();

    addElementErrors();

    if (uri == null && !qName.equals(localName)) {
      // undeclared prefix
      int prefColon = qName.indexOf(':');
      String prefix = qName.substring(0, prefColon);
      addError("undeclared-prefix", ERR_undeclaredElementPrefix(prefix));
      newElement = accessor.createErrorElement(localName);
    } else if ("".equals(localName)) {
      newElement = accessor.createErrorElement(localName);
    } else if (FXML_FX_NAMESPACE.equals(uri)) {
      newElement = handleFxmlElement(localName, atts);
    } else {
      // non-fx namespace, should be either an instance, or a property or an event
      String eventName = FxXmlSymbols.getEventHandlerName(localName);
      if (rootComponent == null || FxXmlSymbols.isClassTagName(localName)) {
        newElement = handleClassTag(localName, atts);
      } else if (eventName != null) {
        newElement = handleEventHandlerTag(eventName);
      } else {
        newElement = handlePropertyTag(localName, atts);
      }
    }
    if (newElement == null) {
      throw new IllegalStateException();
    }
    initElement(newElement);

    FxNode newNode = newElement;

    // if not broken attempt to attach the Element to a parent
    if (!newElement.isBroken()) {
      if (newElement instanceof FxObjectBase) {
        newNode = attachInstance((FxObjectBase) newElement);
      } else if (newElement instanceof PropertyValue) {
        newNode = attachProperty((PropertyValue) newElement);
      }
    }
    attachChildNode(newNode);

    // process attributes, iff it is an instance. Attribute processing needs the node pushed
    // on the stack, so it is delayed after attachChildNode
    if (newNode.getKind() == Kind.Instance) {
      processInstanceAttributes(atts);
    }
  }
 private void pushInstance(FxNode instance) {
   nodeStack.push(instance);
   if (instance.getKind() == Kind.Instance) {
     current = (FxInstance) instance;
   } else {
     current = null;
   }
 }
 @NbBundle.Messages({
   "# {0} - parent tag local name",
   "ERR_doesNotAcceptProperty=The parent element {0} does not accept properties"
 })
 private FxNode attachProperty(PropertyValue p) {
   // FIXME - if 'current' is null,
   if (current == null) {
     FxNode node = nodeStack.peek();
     addError(
         new ErrorMark(
             start,
             end - start,
             "parent-not-accept-property",
             ERR_doesNotAcceptProperty(node.getSourceName()),
             node));
     accessor.makeBroken(p);
   }
   return p;
 }
 private void attachChildNode(FxNode node) {
   FxNode top = nodeStack.peek();
   i(top).addChild(node);
   //        if (!node.isBroken() && (node.getKind() != FxNode.Kind.Element)) {
   accessor.attach(node, fxModel);
   if (!node.isBroken()) {
     accessor.addChild(top, node);
   }
   if (i(node).isElement()) {
     pushInstance(node);
   }
 }
  /**
   * 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;
  }