/**
   * Fires the appropriate SAX events on the content handler to build the XML elements for the
   * property.
   *
   * @param property the property to be exported
   * @param contentHandler the SAX content handler for which SAX events will be invoked as the XML
   *     document is created.
   * @param skipBinary if <code>true</code>, indicates that binary properties should not be exported
   * @throws SAXException if an exception occurs during generation of the XML document
   * @throws RepositoryException if an exception occurs accessing the content repository
   */
  private void emitProperty(Property property, ContentHandler contentHandler, boolean skipBinary)
      throws RepositoryException, SAXException {
    assert property instanceof AbstractJcrProperty
        : "Illegal attempt to use " + getClass().getName() + " on non-ModeShape property";

    AbstractJcrProperty prop = (AbstractJcrProperty) property;

    // first set the property sv:name attribute
    AttributesImpl propAtts = new AttributesImpl();
    propAtts.addAttribute(
        JcrSvLexicon.NAME.getNamespaceUri(),
        JcrSvLexicon.NAME.getLocalName(),
        getPrefixedName(JcrSvLexicon.NAME),
        PropertyType.nameFromValue(PropertyType.STRING),
        prop.getName());

    // and it's sv:type attribute
    propAtts.addAttribute(
        JcrSvLexicon.TYPE.getNamespaceUri(),
        JcrSvLexicon.TYPE.getLocalName(),
        getPrefixedName(JcrSvLexicon.TYPE),
        PropertyType.nameFromValue(PropertyType.STRING),
        PropertyType.nameFromValue(prop.getType()));

    // and it's sv:multiple attribute
    if (prop.isMultiple()) {
      propAtts.addAttribute(
          JcrSvLexicon.TYPE.getNamespaceUri(),
          JcrSvLexicon.TYPE.getLocalName(),
          getPrefixedName(JcrSvLexicon.MULTIPLE),
          PropertyType.nameFromValue(PropertyType.BOOLEAN),
          Boolean.TRUE.toString());
    }

    // output the sv:property element
    startElement(contentHandler, JcrSvLexicon.PROPERTY, propAtts);

    boolean modified = property.isModified();

    // then output a sv:value element for each of its values
    if (prop instanceof JcrMultiValueProperty) {
      Value[] values = prop.getValues();
      for (int i = 0; i < values.length; i++) {

        emitValue(values[i], contentHandler, property.getType(), skipBinary, modified);
      }
    } else {
      emitValue(property.getValue(), contentHandler, property.getType(), skipBinary, modified);
    }

    // end the sv:property element
    endElement(contentHandler, JcrSvLexicon.PROPERTY);
  }
  /**
   * Fires the appropriate SAX events on the content handler to build the XML elements for the
   * property.
   *
   * @param propertyName the name of the property to be exported
   * @param propertyType the type of the property to be exported
   * @param value the value of the single-valued property to be exported
   * @param contentHandler the SAX content handler for which SAX events will be invoked as the XML
   *     document is created.
   * @param skipBinary if <code>true</code>, indicates that binary properties should not be exported
   * @throws SAXException if an exception occurs during generation of the XML document
   * @throws RepositoryException if an exception occurs accessing the content repository
   */
  private void emitProperty(
      Name propertyName,
      int propertyType,
      Object value,
      ContentHandler contentHandler,
      boolean skipBinary)
      throws RepositoryException, SAXException {
    ValueFactory<String> strings =
        session.getExecutionContext().getValueFactories().getStringFactory();

    // first set the property sv:name attribute
    AttributesImpl propAtts = new AttributesImpl();
    propAtts.addAttribute(
        JcrSvLexicon.NAME.getNamespaceUri(),
        JcrSvLexicon.NAME.getLocalName(),
        getPrefixedName(JcrSvLexicon.NAME),
        PropertyType.nameFromValue(PropertyType.STRING),
        strings.create(propertyName));

    // and it's sv:type attribute
    propAtts.addAttribute(
        JcrSvLexicon.TYPE.getNamespaceUri(),
        JcrSvLexicon.TYPE.getLocalName(),
        getPrefixedName(JcrSvLexicon.TYPE),
        PropertyType.nameFromValue(PropertyType.STRING),
        PropertyType.nameFromValue(propertyType));

    // output the sv:property element
    startElement(contentHandler, JcrSvLexicon.PROPERTY, propAtts);

    // then output a sv:value element for each of its values
    emitValue(strings.create(value), contentHandler);

    // end the sv:property element
    endElement(contentHandler, JcrSvLexicon.PROPERTY);
  }
  /**
   * Exports <code>node</code> (or the subtree rooted at <code>node</code>) into an XML document by
   * invoking SAX events on <code>contentHandler</code>.
   *
   * @param node the node which should be exported. If <code>noRecursion</code> was set to <code>
   *     false</code> in the constructor, the entire subtree rooted at <code>node</code> will be
   *     exported.
   * @param contentHandler the SAX content handler for which SAX events will be invoked as the XML
   *     document is created.
   * @param skipBinary if <code>true</code>, indicates that binary properties should not be exported
   * @param noRecurse if<code>true</code>, indicates that only the given node should be exported,
   *     otherwise a recursive export and not any of its child nodes.
   * @param isRoot true if the supplied node is the root node (supplied as an efficiency)
   * @throws SAXException if an exception occurs during generation of the XML document
   * @throws RepositoryException if an exception occurs accessing the content repository
   */
  protected void exportNode(
      Node node,
      ContentHandler contentHandler,
      boolean skipBinary,
      boolean noRecurse,
      boolean isRoot)
      throws RepositoryException, SAXException {

    // start the sv:node element for this JCR node
    AttributesImpl atts = new AttributesImpl();
    String nodeName = node.getName();
    if (isRoot && node.getDepth() == 0) {
      // This is the root node ...
      nodeName = "jcr:root";
    }
    atts.addAttribute(
        JcrSvLexicon.NAME.getNamespaceUri(),
        JcrSvLexicon.NAME.getLocalName(),
        getPrefixedName(JcrSvLexicon.NAME),
        PropertyType.nameFromValue(PropertyType.STRING),
        nodeName);

    startElement(contentHandler, JcrSvLexicon.NODE, atts);

    if (node instanceof JcrSharedNode) {
      // This is a shared node, and per Section 14.7 of the JCR 2.0 specification, they have to be
      // written out
      // in a special way ...

      // jcr:primaryType = nt:share ...
      emitProperty(
          JcrLexicon.PRIMARY_TYPE,
          PropertyType.NAME,
          JcrNtLexicon.SHARE,
          contentHandler,
          skipBinary);

      // jcr:uuid = UUID of shared node ...
      emitProperty(
          JcrLexicon.UUID, PropertyType.STRING, node.getIdentifier(), contentHandler, skipBinary);
    } else {

      // Output any special properties first (see Javadoc for SPECIAL_PROPERTY_NAMES for more
      // context)
      for (Name specialPropertyName : SPECIAL_PROPERTY_NAMES) {
        Property specialProperty = ((AbstractJcrNode) node).getProperty(specialPropertyName);

        if (specialProperty != null) {
          emitProperty(specialProperty, contentHandler, skipBinary);
        }
      }

      PropertyIterator properties = node.getProperties();
      while (properties.hasNext()) {
        exportProperty(properties.nextProperty(), contentHandler, skipBinary);
      }

      if (!noRecurse) {
        NodeIterator nodes = node.getNodes();
        while (nodes.hasNext()) {
          exportNode(nodes.nextNode(), contentHandler, skipBinary, noRecurse, false);
        }
      }
    }

    endElement(contentHandler, JcrSvLexicon.NODE);
  }