public void startDocument(XMLLocator locator, String encoding, NamespaceContext namespaceContext, Augmentations augs) throws XNIException {
        fNamespaceContext = namespaceContext;
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.startDocument(locator, encoding, namespaceContext, augs);
        }
    }

/*
 * reserved comment block
 * DO NOT REMOVE OR ALTER!
 */
package com.sun.org.apache.xerces.internal.impl;

import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
import com.sun.org.apache.xerces.internal.util.SymbolTable;
import com.sun.org.apache.xerces.internal.util.XMLSymbols;
import com.sun.org.apache.xerces.internal.xni.Augmentations;
import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
import com.sun.org.apache.xerces.internal.xni.XMLLocator;
import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
import com.sun.org.apache.xerces.internal.xni.XMLString;
import com.sun.org.apache.xerces.internal.xni.XNIException;
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;

/**
 * This class performs namespace binding on the startElement and endElement
 * method calls and passes all other methods through to the registered
 * document handler. This class can be configured to only pass the
 * start and end prefix mappings (start/endPrefixMapping).
 *

 * This component requires the following features and properties from the
 * component manager that uses it:
 *

 *
http://example.com

 *
http://apache.org/xml/properties/internal/symbol-table

 *
http://apache.org/xml/properties/internal/error-reporter

 *

 *
 * @xerces.internal
 *
 * @author Andy Clark, IBM
 */
public class XMLNamespaceBinder implements XMLComponent, XMLDocumentFilter {

    // Constants
    // feature identifiers
    /**
     * Feature identifier: namespaces.
     */
    protected static final String NAMESPACES = Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;

    // property identifiers
    /**
     * Property identifier: symbol table.
     */
    protected static final String SYMBOL_TABLE = Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;

    /**
     * Property identifier: error reporter.
     */
    protected static final String ERROR_REPORTER = Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;

    // recognized features and properties
    /**
     * Recognized features.
     */
    private static final String[] RECOGNIZED_FEATURES = { NAMESPACES };

    private static final Boolean[] FEATURE_DEFAULTS = { null };

    /**
     * Recognized properties.
     */
    private static final String[] RECOGNIZED_PROPERTIES = { SYMBOL_TABLE, ERROR_REPORTER };

    private static final Object[] PROPERTY_DEFAULTS = { null, null };

    // Data
    // features
    protected boolean fNamespaces;

    // properties
    /**
     * Symbol table.
     */
    protected SymbolTable fSymbolTable;

    /**
     * Error reporter.
     */
    protected XMLErrorReporter fErrorReporter;

    // handlers
    /**
     * Document handler.
     */
    protected XMLDocumentHandler fDocumentHandler;

    protected XMLDocumentSource fDocumentSource;

    // settings
    /**
     * Only pass start and end prefix mapping events.
     */
    protected boolean fOnlyPassPrefixMappingEvents;

    // shared context
    /**
     * Namespace context.
     */
    private NamespaceContext fNamespaceContext;

    // temp vars
    /**
     * Attribute QName.
     */
    private QName fAttributeQName = new QName();

    // Constructors
    public XMLNamespaceBinder() {
    }

    // Public methods
    // settings
    /**
     * Sets whether the namespace binder only passes the prefix mapping
     * events to the registered document handler or passes all document
     * events.
     *
     * @param onlyPassPrefixMappingEvents True to pass only the prefix
     *                                    mapping events; false to pass
     *                                    all events.
     */
    public void setOnlyPassPrefixMappingEvents(boolean onlyPassPrefixMappingEvents) {
        fOnlyPassPrefixMappingEvents = onlyPassPrefixMappingEvents;
    }

    /**
     * Returns true if the namespace binder only passes the prefix mapping
     * events to the registered document handler; false if the namespace
     * binder passes all document events.
     */
    public boolean getOnlyPassPrefixMappingEvents() {
        return fOnlyPassPrefixMappingEvents;
    }

    // XMLComponent methods
    /**
     * Resets the component. The component can query the component manager
     * about any features and properties that affect the operation of the
     * component.
     *
     * @param componentManager The component manager.
     *
     * @throws SAXException Thrown by component on initialization error.
     *                      For example, if a feature or property is
     *                      required for the operation of the component, the
     *                      component manager may throw a
     *                      SAXNotRecognizedException or a
     *                      SAXNotSupportedException.
     */
    public void reset(XMLComponentManager componentManager) throws XNIException {
        // features
        try {
            fNamespaces = componentManager.getFeature(NAMESPACES);
        } catch (XMLConfigurationException e) {
            fNamespaces = true;
        }
        // Xerces properties
        fSymbolTable = (SymbolTable) componentManager.getProperty(SYMBOL_TABLE);
        fErrorReporter = (XMLErrorReporter) componentManager.getProperty(ERROR_REPORTER);
    }

    /**
     * Returns a list of feature identifiers that are recognized by
     * this component. This method may return null if no features
     * are recognized by this component.
     */
    public String[] getRecognizedFeatures() {
        return (String[]) (RECOGNIZED_FEATURES.clone());
    }

    /**
     * Sets the state of a feature. This method is called by the component
     * manager any time after reset when a feature changes state.
     *

     * Note: Components should silently ignore features
     * that do not affect the operation of the component.
     *
     * @param featureId The feature identifier.
     * @param state     The state of the feature.
     *
     * @throws SAXNotRecognizedException The component should not throw
     *                                   this exception.
     * @throws SAXNotSupportedException The component should not throw
     *                                  this exception.
     */
    public void setFeature(String featureId, boolean state) throws XMLConfigurationException {
    }

    /**
     * Returns a list of property identifiers that are recognized by
     * this component. This method may return null if no properties
     * are recognized by this component.
     */
    public String[] getRecognizedProperties() {
        return (String[]) (RECOGNIZED_PROPERTIES.clone());
    }

    /**
     * Sets the value of a property during parsing.
     *
     * @param propertyId
     * @param value
     */
    public void setProperty(String propertyId, Object value) throws XMLConfigurationException {
        // Xerces properties
        if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
            final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
            if (suffixLength == Constants.SYMBOL_TABLE_PROPERTY.length() && propertyId.endsWith(Constants.SYMBOL_TABLE_PROPERTY)) {
                fSymbolTable = (SymbolTable) value;
            } else if (suffixLength == Constants.ERROR_REPORTER_PROPERTY.length() && propertyId.endsWith(Constants.ERROR_REPORTER_PROPERTY)) {
                fErrorReporter = (XMLErrorReporter) value;
            }
            return;
        }
    }

    /**
     * Returns the default state for a feature, or null if this
     * component does not want to report a default value for this
     * feature.
     *
     * @param featureId The feature identifier.
     *
     * @since Xerces 2.2.0
     */
    public Boolean getFeatureDefault(String featureId) {
        for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
            if (RECOGNIZED_FEATURES[i].equals(featureId)) {
                return FEATURE_DEFAULTS[i];
            }
        }
        return null;
    }

    /**
     * Returns the default state for a property, or null if this
     * component does not want to report a default value for this
     * property.
     *
     * @param propertyId The property identifier.
     *
     * @since Xerces 2.2.0
     */
    public Object getPropertyDefault(String propertyId) {
        for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
            if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
                return PROPERTY_DEFAULTS[i];
            }
        }
        return null;
    }

    // XMLDocumentSource methods
    /**
     * Sets the document handler to receive information about the document.
     */
    public void setDocumentHandler(XMLDocumentHandler documentHandler) {
        fDocumentHandler = documentHandler;
    }

    /**
     * Returns the document handler
     */
    public XMLDocumentHandler getDocumentHandler() {
        return fDocumentHandler;
    }

    // XMLDocumentHandler methods
    /**
     * Sets the document source
     */
    public void setDocumentSource(XMLDocumentSource source) {
        fDocumentSource = source;
    }

    // setDocumentSource
    /**
     * Returns the document source
     */
    public XMLDocumentSource getDocumentSource() {
        return fDocumentSource;
    }

    // getDocumentSource
    /**
     * This method notifies the start of a general entity.
     *

     * Note: This method is not called for entity references
     * appearing as part of attribute values.
     *
     * @param name     The name of the general entity.
     * @param identifier The resource identifier.
     * @param encoding The auto-detected IANA encoding name of the entity
     *                 stream. This value will be null in those situations
     *                 where the entity encoding is not auto-detected (e.g.
     *                 internal entities or a document entity that is
     *                 parsed from a java.io.Reader).
     * @param augs     Additional information that may include infoset augmentations
     *
     * @exception XNIException Thrown by handler to signal an error.
     */
    public void startGeneralEntity(String name, XMLResourceIdentifier identifier, String encoding, Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs);
        }
    }

    /**
     * Notifies of the presence of a TextDecl line in an entity. If present,
     * this method will be called immediately following the startEntity call.
     *

     * Note: This method will never be called for the
     * document entity; it is only called for external general entities
     * referenced in document content.
     *

     * Note: This method is not called for entity references
     * appearing as part of attribute values.
     *
     * @param version  The XML version, or null if not specified.
     * @param encoding The IANA encoding name of the entity.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void textDecl(String version, String encoding, Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.textDecl(version, encoding, augs);
        }
    }

    /**
     * The start of the document.
     *
     * @param locator  The system identifier of the entity if the entity
     *                 is external, null otherwise.
     * @param encoding The auto-detected IANA encoding name of the entity
     *                 stream. This value will be null in those situations
     *                 where the entity encoding is not auto-detected (e.g.
     *                 internal entities or a document entity that is
     *                 parsed from a java.io.Reader).
     * @param namespaceContext
     *                 The namespace context in effect at the
     *                 start of this document.
     *                 This object represents the current context.
     *                 Implementors of this class are responsible
     *                 for copying the namespace bindings from the
     *                 the current context (and its parent contexts)
     *                 if that information is important.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */

/**
     * Notifies of the presence of an XMLDecl line in the document. If
     * present, this method will be called immediately following the
     * startDocument call.
     *
     * @param version    The XML version.
     * @param encoding   The IANA encoding name of the document, or null if
     *                   not specified.
     * @param standalone The standalone value, or null if not specified.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void xmlDecl(String version, String encoding, String standalone, Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
        }
    }

    /**
     * Notifies of the presence of the DOCTYPE line in the document.
     *
     * @param rootElement The name of the root element.
     * @param publicId    The public identifier if an external DTD or null
     *                    if the external DTD is specified using SYSTEM.
     * @param systemId    The system identifier if an external DTD, null
     *                    otherwise.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void doctypeDecl(String rootElement, String publicId, String systemId, Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
        }
    }

    /**
     * A comment.
     *
     * @param text The text in the comment.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by application to signal an error.
     */
    public void comment(XMLString text, Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.comment(text, augs);
        }
    }

    /**
     * A processing instruction. Processing instructions consist of a
     * target name and, optionally, text data. The data is only meaningful
     * to the application.
     *

     * Typically, a processing instruction's data will contain a series
     * of pseudo-attributes. These pseudo-attributes follow the form of
     * element attributes but are not parsed or presented
     * to the application as anything other than text. The application is
     * responsible for parsing the data.
     *
     * @param target The target.
     * @param data   The data or null if none specified.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void processingInstruction(String target, XMLString data, Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.processingInstruction(target, data, augs);
        }
    }

    /**
     * Binds the namespaces. This method will handle calling the
     * document handler to start the prefix mappings.
     *

     * Note: This method makes use of the
     * fAttributeQName variable. Any contents of the variable will
     * be destroyed. Caller should copy the values out of this
     * temporary variable before calling this method.
     *
     * @param element    The name of the element.
     * @param attributes The element attributes.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException {
        if (fNamespaces) {
            handleStartElement(element, attributes, augs, false);
        } else if (fDocumentHandler != null) {
            fDocumentHandler.startElement(element, attributes, augs);
        }
    }

    /**
     * An empty element.
     *
     * @param element    The name of the element.
     * @param attributes The element attributes.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException {
        if (fNamespaces) {
            handleStartElement(element, attributes, augs, true);
            handleEndElement(element, augs, true);
        } else if (fDocumentHandler != null) {
            fDocumentHandler.emptyElement(element, attributes, augs);
        }
    }

    /**
     * Character content.
     *
     * @param text The content.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void characters(XMLString text, Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.characters(text, augs);
        }
    }

    /**
     * Ignorable whitespace. For this method to be called, the document
     * source must have some way of determining that the text containing
     * only whitespace characters should be considered ignorable. For
     * example, the validator can determine if a length of whitespace
     * characters in the document are ignorable based on the element
     * content model.
     *
     * @param text The ignorable whitespace.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.ignorableWhitespace(text, augs);
        }
    }

    /**
     * The end of an element.
     *
     * @param element The name of the element.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endElement(QName element, Augmentations augs) throws XNIException {
        if (fNamespaces) {
            handleEndElement(element, augs, false);
        } else if (fDocumentHandler != null) {
            fDocumentHandler.endElement(element, augs);
        }
    }

    /**
     * The start of a CDATA section.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void startCDATA(Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.startCDATA(augs);
        }
    }

    /**
     * The end of a CDATA section.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endCDATA(Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.endCDATA(augs);
        }
    }

    /**
     * The end of the document.
     * @param augs     Additional information that may include infoset augmentations
     *
     * @throws XNIException Thrown by handler to signal an error.
     */
    public void endDocument(Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.endDocument(augs);
        }
    }

    /**
     * This method notifies the end of a general entity.
     *

     * Note: This method is not called for entity references
     * appearing as part of attribute values.
     *
     * @param name   The name of the entity.
     * @param augs   Additional information that may include infoset augmentations
     *
     * @exception XNIException
     *                   Thrown by handler to signal an error.
     */
    public void endGeneralEntity(String name, Augmentations augs) throws XNIException {
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            fDocumentHandler.endGeneralEntity(name, augs);
        }
    }

    // Protected methods
    /**
     * Handles start element.
     */
    protected void handleStartElement(QName element, XMLAttributes attributes, Augmentations augs, boolean isEmpty) throws XNIException {
        // add new namespace context
        fNamespaceContext.pushContext();
        if (element.prefix == XMLSymbols.PREFIX_XMLNS) {
            fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, "ElementXMLNSPrefix", new Object[] { element.rawname }, XMLErrorReporter.SEVERITY_FATAL_ERROR);
        }
        // search for new namespace bindings
        int length = attributes.getLength();
        for (int i = 0; i < length; i++) {
            String localpart = attributes.getLocalName(i);
            String prefix = attributes.getPrefix(i);
            // it's a namespace declaration. but prefix:xmlns="..." isn't.
            if (prefix == XMLSymbols.PREFIX_XMLNS || prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS) {
                // get the internalized value of this attribute
                String uri = fSymbolTable.addSymbol(attributes.getValue(i));
                // 1. "xmlns" can't be bound to any namespace
                if (prefix == XMLSymbols.PREFIX_XMLNS && localpart == XMLSymbols.PREFIX_XMLNS) {
                    fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, "CantBindXMLNS", new Object[] { attributes.getQName(i) }, XMLErrorReporter.SEVERITY_FATAL_ERROR);
                }
                // 2. the namespace for "xmlns" can't be bound to any prefix
                if (uri == NamespaceContext.XMLNS_URI) {
                    fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, "CantBindXMLNS", new Object[] { attributes.getQName(i) }, XMLErrorReporter.SEVERITY_FATAL_ERROR);
                }
                // 3. "xml" can't be bound to any other namespace than it's own
                if (localpart == XMLSymbols.PREFIX_XML) {
                    if (uri != NamespaceContext.XML_URI) {
                        fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, "CantBindXML", new Object[] { attributes.getQName(i) }, XMLErrorReporter.SEVERITY_FATAL_ERROR);
                    }
                } else // 4. the namespace for "xml" can't be bound to any other prefix
                {
                    if (uri == NamespaceContext.XML_URI) {
                        fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, "CantBindXML", new Object[] { attributes.getQName(i) }, XMLErrorReporter.SEVERITY_FATAL_ERROR);
                    }
                }
                prefix = localpart != XMLSymbols.PREFIX_XMLNS ? localpart : XMLSymbols.EMPTY_STRING;
                // We should only report an error if there is a prefix,
                // that is, the local part is not "xmlns". -SG
                // Since this is an error condition in XML 1.0,
                // and should be relatively uncommon in XML 1.1,
                // making this test into a method call to reuse code
                // should be acceptable.  - NG
                if (prefixBoundToNullURI(uri, localpart)) {
                    fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, "EmptyPrefixedAttName", new Object[] { attributes.getQName(i) }, XMLErrorReporter.SEVERITY_FATAL_ERROR);
                    continue;
                }
                // declare prefix in context
                fNamespaceContext.declarePrefix(prefix, uri.length() != 0 ? uri : null);
            }
        }
        // bind the element
        String prefix = element.prefix != null ? element.prefix : XMLSymbols.EMPTY_STRING;
        element.uri = fNamespaceContext.getURI(prefix);
        if (element.prefix == null && element.uri != null) {
            element.prefix = XMLSymbols.EMPTY_STRING;
        }
        if (element.prefix != null && element.uri == null) {
            fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, "ElementPrefixUnbound", new Object[] { element.prefix, element.rawname }, XMLErrorReporter.SEVERITY_FATAL_ERROR);
        }
        // bind the attributes
        for (int i = 0; i < length; i++) {
            attributes.getName(i, fAttributeQName);
            String aprefix = fAttributeQName.prefix != null ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
            String arawname = fAttributeQName.rawname;
            if (arawname == XMLSymbols.PREFIX_XMLNS) {
                fAttributeQName.uri = fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS);
                attributes.setName(i, fAttributeQName);
            } else if (aprefix != XMLSymbols.EMPTY_STRING) {
                fAttributeQName.uri = fNamespaceContext.getURI(aprefix);
                if (fAttributeQName.uri == null) {
                    fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, "AttributePrefixUnbound", new Object[] { element.rawname, arawname, aprefix }, XMLErrorReporter.SEVERITY_FATAL_ERROR);
                }
                attributes.setName(i, fAttributeQName);
            }
        }
        // verify that duplicate attributes don't exist
        int attrCount = attributes.getLength();
        for (int i = 0; i < attrCount - 1; i++) {
            String auri = attributes.getURI(i);
            if (auri == null || auri == NamespaceContext.XMLNS_URI) {
                continue;
            }
            String alocalpart = attributes.getLocalName(i);
            for (int j = i + 1; j < attrCount; j++) {
                String blocalpart = attributes.getLocalName(j);
                String buri = attributes.getURI(j);
                if (alocalpart == blocalpart && auri == buri) {
                    fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, "AttributeNSNotUnique", new Object[] { element.rawname, alocalpart, auri }, XMLErrorReporter.SEVERITY_FATAL_ERROR);
                }
            }
        }
        // call handler
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            if (isEmpty) {
                fDocumentHandler.emptyElement(element, attributes, augs);
            } else {
                fDocumentHandler.startElement(element, attributes, augs);
            }
        }
    }

    /**
     * Handles end element.
     */
    protected void handleEndElement(QName element, Augmentations augs, boolean isEmpty) throws XNIException {
        // bind element
        String eprefix = element.prefix != null ? element.prefix : XMLSymbols.EMPTY_STRING;
        element.uri = fNamespaceContext.getURI(eprefix);
        if (element.uri != null) {
            element.prefix = eprefix;
        }
        // call handlers
        if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
            if (!isEmpty) {
                fDocumentHandler.endElement(element, augs);
            }
        }
        // pop context
        fNamespaceContext.popContext();
    }

    // returns true iff the given prefix is bound to "" *and*
    // this is disallowed by the version of XML namespaces in use.
    protected boolean prefixBoundToNullURI(String uri, String localpart) {
        return (uri == XMLSymbols.EMPTY_STRING && localpart != XMLSymbols.PREFIX_XMLNS);
    }
}


void onBlur(String value) {
        m_codeValue = value;
        fireEvent(new FieldEvents.BlurEvent(CmsCodeMirror.this));
    }

/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 *
 */
package org.opencms.ui.components.codemirror;

import org.opencms.i18n.CmsMessages;
import org.opencms.json.JSONObject;
import org.opencms.ui.components.Messages;
import org.opencms.workplace.CmsWorkplace;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import com.vaadin.annotations.JavaScript;
import com.vaadin.event.FieldEvents;
import com.vaadin.shared.ui.JavaScriptComponentState;
import com.vaadin.ui.AbstractJavaScriptComponent;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.JavaScriptFunction;
import com.vaadin.ui.UI;
import com.vaadin.v7.data.Property;
import elemental.json.JsonArray;

/**
 * Code mirror input component.

 */
@JavaScript({ "codemirror-connector.js" })
public class CmsCodeMirror extends AbstractJavaScriptComponent implements Property<String>, Property.ValueChangeNotifier {

    /**
     * The available editor languages.
     */
    public enum CodeMirrorLanguage {

        CSS("css", new String[] { "css" }),
        HTML("text/html", new String[] { "html", "htm", "xhtml" }),
        JAVA("text/x-java", new String[] { "java" }),
        JAVASCRIPT("javascript", new String[] { "js" }),
        JSP("application/x-jsp", new String[] { "jsp" }),
        XML("application/xml", new String[] { "xml", "xsd" });

        /**
         * The language name.
         */
        private final String m_languageName;

        /**
         * The supported file types.
         */
        private Set<String> m_supportedFileTypes;

        /**
         * Constructor.

         *
         * @param languageName the language name
         * @param fileTypes the file types suported by this language
         */
        private CodeMirrorLanguage(String languageName, String[] fileTypes) {
            m_languageName = languageName;
            m_supportedFileTypes = new HashSet<String>(Arrays.asList(fileTypes));
        }

        /**
         * Returns the language name.

         *
         * @return the language name
         */
        public String getLanguageName() {
            return m_languageName;
        }

        /**
         * Returns whether the given file ending is a supported file type.

         *
         * @param fileNameSuffix the file name suffix
         *
         * @return true in case the file type is supported
         */
        public boolean isSupportedFileType(String fileNameSuffix) {
            return m_supportedFileTypes.contains(fileNameSuffix);
        }

        /**
         * @see java.lang.Enum#toString()
         */
        @Override
        public String toString() {
            return m_languageName;
        }
    }

    /**
     * The editor state.

     */
    public static class CodeMirrorState extends JavaScriptComponentState {

        /**
         * The serial version id.
         */
        private static final long serialVersionUID = 1L;

        /**
         * The close brackets flag.
         */
        public boolean m_closeBrackets = true;

        /**
         * The content value.
         */
        public String m_contentValue = "//please start to code";

        /**
         * The required CSS stylesheet URIs.
         */
        public String[] m_cssURIs;

        /**
         * The search and replace enabled flag.
         */
        public boolean m_enableSearchReplace;

        /**
         * The undo redo enabled flag.
         */
        public boolean m_enableUndoRedo;

        /**
         * The font size.
         */
        public String m_fontSize = "14px";

        /**
         * The height.
         */
        public String m_height = "600";

        /**
         * The highlighting flag.
         */
        public boolean m_highlighting = true;

        /**
         * The editor instance id.
         */
        public long m_id;

        /**
         * The line wrapping flag.
         */
        public boolean m_lineWrapping = true;

        /**
         * The language mode.
         */
        public String m_mode = CodeMirrorLanguage.JAVASCRIPT.toString();

        /**
         * The required JavaScript resource URIs.
         */
        public String[] m_scriptURIs;

        /**
         * The short cut descriptions HTML.
         */
        public String m_shortcutsMessage;

        /**
         * The tab visibility flag.
         */
        public boolean m_tabsVisible = true;

        /**
         * The theme.
         */
        public String m_theme = CodeMirrorTheme.ECLIPSE.toString();

        /**
         * The width.
         */
        public String m_width = "600";

        /**
         * The code mirror localization.
         */
        public String m_messages;
    }

    /**
     * The available editor themes.
     */
    public enum CodeMirrorTheme {

        AMBIANCE("ambiance"),
        BASE16_DARK("base16-dark"),
        BASE16_LIGHT("base16-light"),
        BLACKBOARD("blackboard"),
        COBALT("cobalt"),
        DAY_3024("3024-day"),
        DEFAULT("default"),
        ECLIPSE("eclipse"),
        ELEGANT("elegant"),
        ERLANG_DARK("erlang-dark"),
        ERLANG_LIGHT("erlang-light"),
        LESSER_DARK("lesser-dark"),
        MBO("mbo"),
        MDN_LIKE("mdn-like"),
        MIDNIGHT("midnight"),
        MONOKAI("monokai"),
        NEAT("neat"),
        NIGHT("night"),
        NIGHT_3024("3024-night"),
        PARAISO_DARK("paraiso-dark"),
        PARAISO_LIGHT("paraiso-light"),
        PASTEL_ON_DARK("paster-on-dark"),
        RUBYBLUE("rubyblue"),
        SOLARIZED_DARK("solarized dark"),
        SOLARIZED_LIGHT("solarized light"),
        THE_MATRIX("the-matrix"),
        TOMORROW_NIGHT_EIGHTIES("tomorrow-night-eighties"),
        TWILIGHT("twilight"),
        VIBRANT_INK("vibrant-ink"),
        XQ_DARK("xq-dark"),
        XQ_LIGHT("xq-light");

        /**
         * The theme name.
         */
        private final String m_themeName;

        /**
         * Constructor.

         *
         * @param themeName the theme name
         */
        private CodeMirrorTheme(String themeName) {
            this.m_themeName = themeName;
        }

        /**
         * Returns the theme name.

         *
         * @return the theme name
         */
        public String getThemeName() {
            return m_themeName;
        }

        /**
         * @see java.lang.Enum#toString()
         */
        @Override
        public String toString() {
            return m_themeName;
        }
    }

    /**
     * The code mirror change event.

     */
    public static class ValueChangeEvent extends Component.Event implements Property.ValueChangeEvent {

        /**
         * The serial version id.
         */
        private static final long serialVersionUID = 1L;

        /**
         * Constructs a new event object with the specified source field object.
         *
         * @param eventSource the field that caused the event.
         */
        public ValueChangeEvent(CmsCodeMirror eventSource) {
            super(eventSource);
        }

        /**
         * Gets the Property which triggered the event.
         *
         * @return the Source Property of the event.
         */
        @Override
        public Property getProperty() {
            return (Property<?>) getSource();
        }
    }

    /**
     * The blur method.
     */
    private static final Method BLUR_METHOD;

    /**
     * The required CSS stylesheet URIs.
     */
    private static String[] CSS_URIS;

    /**
     * The HTML id prefix.
     */
    private static final String HTML_ID_PREFIX = "cm-addon-";

    /**
     * The required JavaScript resource URIs.
     */
    private static String[] JAVASCRIPT_URIS;

    /**
     * The code mirror component count, used as HTML ids.
     */
    private static long m_componentCount;

    /**
     * The serial version id.
     */
    private static final long serialVersionUID = -4921119175861329688L;

    /**
     * Them value change method.
     */
    private static final Method VALUE_CHANGE_METHOD;

    static {
        try {
            VALUE_CHANGE_METHOD = Property.ValueChangeListener.class.getDeclaredMethod("valueChange", new Class[] { Property.ValueChangeEvent.class });
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException("Internal error finding methods in ValueChangeListener");
        }
        try {
            BLUR_METHOD = FieldEvents.BlurListener.class.getDeclaredMethod("blur", new Class[] { FieldEvents.BlurEvent.class });
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException("Internal error finding methods in ValueChangeListener");
        }
    }

    /**
     * The current content value.
     */
    private String m_codeValue;

    /**
     * The component id.
     */
    private final long m_componentId;

    public CmsCodeMirror() {
        m_componentId = m_componentCount;
        m_componentCount++;
        m_codeValue = "";
        getState().m_id = m_componentId;
        getState().m_contentValue = m_codeValue;
        CmsMessages messages = Messages.get().getBundle(UI.getCurrent().getLocale());
        getState().m_shortcutsMessage = getShortcutMessages(messages);
        getState().m_messages = getLocalizationMessages(messages);
        if (CSS_URIS == null) {
            CSS_URIS = new String[] { CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/lib/codemirror.css"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/theme/eclipse.css"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/dialog/dialog.css"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/show-hint.css") };
        }
        getState().m_cssURIs = CSS_URIS;
        if (JAVASCRIPT_URIS == null) {
            JAVASCRIPT_URIS = new String[] { CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/lib/codemirror.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/dialog/dialog.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/search/searchcursor.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/js/search.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/edit/closebrackets.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/edit/closetag.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/edit/matchbrackets.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/show-hint.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/html-hint.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/javascript-hint.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/xml-hint.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/fold/foldcode.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/fold/brace-fold.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/fold/xml-fold.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/comment/comment.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/selection/active-line.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/css/css.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/xml/xml.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/clike/clike.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/javascript/javascript.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/css/css.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/htmlmixed/htmlmixed.js"), CmsWorkplace.getStaticResourceUri("/editors/codemirror/js/htmlembedded_modified.js") };
        }
        getState().m_scriptURIs = JAVASCRIPT_URIS;
        addFunction("onBlur", new JavaScriptFunction() {

            private static final long serialVersionUID = 1L;

            @Override
            public void call(JsonArray arguments) {
                onBlur(arguments.getString(0));
            }
        });
        addFunction("onChange", new JavaScriptFunction() {

            private static final long serialVersionUID = 1L;

            @Override
            public void call(JsonArray arguments) {
                onChange(arguments.getString(0));
            }
        });
        addStyleName("o-codemirror");
    }

    /**
     * Adds a blur listener.

     *
     * @param listener the blur listener
     */
    public void addBlurListener(FieldEvents.BlurListener listener) {
        addListener(FieldEvents.BlurEvent.class, listener, BLUR_METHOD);
        markAsDirty();
    }

    /**
     * @see com.vaadin.v7.data.Property.ValueChangeNotifier#addListener(com.vaadin.v7.data.Property.ValueChangeListener)
     * @deprecated use {@link #addValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)}
     */
    @Deprecated
    public void addListener(com.vaadin.v7.data.Property.ValueChangeListener listener) {
        addValueChangeListener(listener);
    }

    /**
     * @see com.vaadin.v7.data.Property.ValueChangeNotifier#addValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)
     */
    public void addValueChangeListener(Property.ValueChangeListener listener) {
        addListener(Property.ValueChangeEvent.class, listener, VALUE_CHANGE_METHOD);
        // ensure "automatic immediate handling" works
        markAsDirty();
    }

    /**
     * Returns the font size.

     *
     * @return the font size
     */
    public String getFontSize() {
        return getState().m_fontSize;
    }

    /**
     * @see com.vaadin.v7.data.Property#getType()
     */
    public Class<? extends String> getType() {
        return String.class;
    }

    /**
     * Returns the current editor content value.

     *
     * @return the editor content value
     */
    public String getValue() {
        return m_codeValue;
    }

    /**
     * Returns whether auto close brackets is active.

     *
     * @return true if auto close brackets is active
     */
    public boolean isCloseBrackets() {
        return getState().m_closeBrackets;
    }

    /**
     * Returns whether highlighting is active.

     *
     * @return true if highlighting is active
     */
    public boolean isHighlighting() {
        return getState().m_highlighting;
    }

    /**
     * Returns whether line wrapping is active.

     *
     * @return true if line wrapping is active
     */
    public boolean isLineWrapping() {
        return getState().m_lineWrapping;
    }

    /**
     * @see com.vaadin.ui.AbstractComponent#isReadOnly()
     */
    @Override
    public boolean isReadOnly() {
        return super.isReadOnly();
    }

    /**
     * Returns whether tabs are visible.

     *
     * @return true if tabs are visible
     */
    public boolean isTabsVisible() {
        return getState().m_tabsVisible;
    }

    /**
     * Registers the given buttons as the search and replace buttons.

     *
     * @param search the search button
     * @param replace the replace button
     */
    public void registerSearchReplace(Button search, Button replace) {
        if (getState().m_enableSearchReplace) {
            throw new RuntimeException("Search/replace already registered.");
        }
        search.setId(HTML_ID_PREFIX + m_componentId + "-search");
        replace.setId(HTML_ID_PREFIX + m_componentId + "-replace");
        getState().m_enableSearchReplace = true;
        markAsDirty();
    }

    /**
     * Registers the given buttons as undo redo buttons.

     *
     * @param undo the undo button
     * @param redo the redo button
     */
    public void registerUndoRedo(Button undo, Button redo) {
        if (getState().m_enableUndoRedo) {
            throw new RuntimeException("Undo/redo already registered.");
        }
        undo.setId(HTML_ID_PREFIX + m_componentId + "-undo");
        redo.setId(HTML_ID_PREFIX + m_componentId + "-redo");
        getState().m_enableUndoRedo = true;
        markAsDirty();
    }

    /**
     * Removes the given blur listener.

     *
     * @param listener the listener to remove
     */
    public void removeBlurListener(FieldEvents.BlurNotifier listener) {
        removeListener(FieldEvents.BlurEvent.class, listener, BLUR_METHOD);
        // ensure "automatic immediate handling" works
        markAsDirty();
    }

    /**
     * @see com.vaadin.v7.data.Property.ValueChangeNotifier#removeListener(com.vaadin.v7.data.Property.ValueChangeListener)
     * @deprecated use {@link #removeValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)}
     */
    @Deprecated
    public void removeListener(com.vaadin.v7.data.Property.ValueChangeListener listener) {
        removeValueChangeListener(listener);
    }

    /**
     * @see com.vaadin.v7.data.Property.ValueChangeNotifier#removeValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)
     */
    public void removeValueChangeListener(Property.ValueChangeListener listener) {
        removeListener(Property.ValueChangeEvent.class, listener, VALUE_CHANGE_METHOD);
        markAsDirty();
    }

    /**
     * Sets the auto close brackets feature enabled.

     *
     * @param closeBrackets true to auto close brackets
     */
    public void setCloseBrackets(boolean closeBrackets) {
        getState().m_closeBrackets = closeBrackets;
        markAsDirty();
    }

    /**
     * Sets the editor font size.

     *
     * @param fontSize the editor font size
     */
    public void setFontSize(String fontSize) {
        getState().m_fontSize = fontSize;
        markAsDirty();
    }

    /**
     * @see com.vaadin.ui.AbstractComponent#setHeight(float, com.vaadin.server.Sizeable.Unit)
     */
    @Override
    public void setHeight(float height, Unit unit) {
        super.setHeight(height, unit);
        getState().m_height = height + unit.getSymbol();
        markAsDirty();
    }

    /**
     * @see com.vaadin.ui.AbstractComponent#setHeight(java.lang.String)
     */
    @Override
    public void setHeight(String height) {
        super.setHeight(height);
        getState().m_height = height;
        markAsDirty();
    }

    /**
     * Sets the language highlighting enabled.

     *
     * @param highlighting true to highlight
     */
    public void setHighlighting(boolean highlighting) {
        getState().m_highlighting = highlighting;
        markAsDirty();
    }

    /**
     * Sets the editor language.

     *
     * @param codeMirrorLanguage the editor language
     */
    public void setLanguage(CodeMirrorLanguage codeMirrorLanguage) {
        getState().m_mode = codeMirrorLanguage.getLanguageName();
        markAsDirty();
    }

    /**
     * Sets line wrapping enabled.

     *
     * @param lineWrapping true to wrap lines
     */
    public void setLineWrapping(boolean lineWrapping) {
        getState().m_lineWrapping = lineWrapping;
        markAsDirty();
    }

    /**
     * @see com.vaadin.ui.AbstractComponent#setReadOnly(boolean)
     */
    @Override
    public void setReadOnly(boolean readOnly) {
        super.setReadOnly(readOnly);
    }

    /**
     * Sets tab characters visible.

     *
     * @param tabsVisible true to show tab characters
     */
    public void setTabsVisible(boolean tabsVisible) {
        getState().m_tabsVisible = tabsVisible;
        markAsDirty();
    }

    /**
     * Sets the editor theme.

     *
     * @param codeMirrorTheme the editor theme
     */
    public void setTheme(CodeMirrorTheme codeMirrorTheme) {
        getState().m_theme = codeMirrorTheme.getThemeName();
        markAsDirty();
    }

    /**
     * Sets the editor content value.

     *
     * @param value the content value
     */
    public void setValue(String value) {
        m_codeValue = value;
        getState().m_contentValue = value;
        markAsDirty();
    }

    /**
     * @see com.vaadin.ui.AbstractComponent#setWidth(float, com.vaadin.server.Sizeable.Unit)
     */
    @Override
    public void setWidth(float width, Unit unit) {
        super.setWidth(width, unit);
        getState().m_width = width + unit.getSymbol();
        markAsDirty();
    }

    /**
     * @see com.vaadin.ui.AbstractComponent#setWidth(java.lang.String)
     */
    @Override
    public void setWidth(String width) {
        super.setWidth(width);
        getState().m_width = width;
        markAsDirty();
    }

    /**
     * @see com.vaadin.ui.AbstractJavaScriptComponent#getState()
     */
    @Override
    protected CodeMirrorState getState() {
        return (CodeMirrorState) super.getState();
    }

    /**
     * Called on blur.

     *
     * @param value the content value
     */

/**
     * Called on blur.

     *
     * @param value the content value
     */
    void onChange(String value) {
        boolean changed = !m_codeValue.equals(value);
        m_codeValue = value;
        if (changed) {
            fireEvent(new ValueChangeEvent(this));
        }
    }

    /**
     * Returns the code mirror localization JSON.

     *
     * @param messages the message bundle to use
     *
     * @return the localization
     */
    private String getLocalizationMessages(CmsMessages messages) {
        JSONObject result = new JSONObject();
        try {
            result.put("search", messages.key(Messages.GUI_CODEMIRROR_LANG_SEARCH_0));
            result.put("hint", messages.key(Messages.GUI_CODEMIRROR_LANG_HINT_0));
            result.put("replace", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_0));
            result.put("replacewith", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_WITH_0));
            result.put("replaceconfirm", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_CONFIRM_0));
            result.put("replaceyes", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_YES_0));
            result.put("replaceno", messages.key(Messages.GUI_CODEMIRROR_LANG_RELACE_NO_0));
            result.put("replacestop", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_STOP_0));
            result.put("fontsize", messages.key(Messages.GUI_CODEMIRROR_LANG_FONT_SIZE_0));
        } catch (org.opencms.json.JSONException e) {
            // should never happen
        }
        return result.toString();
    }

    /**
     * Returns the localized short cut messages HTML.

     *
     * @param messages the message bundle to use
     *
     * @return the HTML
     */
    private String getShortcutMessages(CmsMessages messages) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_SEARCH_0));
        buffer.append(" ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_SEARCH_HELP_0));
        buffer.append(" ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_FIND_NEXT_0));
        buffer.append(" ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_FIND_NEXT_HELP_0));
        buffer.append(" ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_FIND_PREVIOUS_0));
        buffer.append(" ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_FIND_PREVIOUS_HELP_0));
        buffer.append(" ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_REPLACE_0));
        buffer.append(" ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_REPLACE_HELP_0));
        buffer.append(" ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_REPLACE_ALL_0));
        buffer.append(" ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_REPLACE_ALL_HELP_0)).append("");
        return buffer.toString();
    }
}


private static <T> Set<T> fillSet(Set<T> baseSet, Set<T> fillSet, Predicate<T> predicate) {
        Set<T> result = new HashSet<>(baseSet);
        for (T element : fillSet) {
            if (predicate.test(element)) {
                result.add(element);
            }
        }
        return result;
    }

package org.apache.kafka.clients;

import org.apache.kafka.common.Cluster;
import org.apache.kafka.common.ClusterResource;
import org.apache.kafka.common.Node;
import org.apache.kafka.common.PartitionInfo;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.Uuid;
import org.apache.kafka.common.requests.MetadataResponse;
import org.apache.kafka.common.requests.MetadataResponse.PartitionMetadata;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * An internal mutable cache of nodes, topics, and partitions in the Kafka cluster. This keeps an up-to-date Cluster
 * instance which is optimized for read access.
 */
public class MetadataCache {

    private final String clusterId;

    private final Map<Integer, Node> nodes;

    private final Set<String> unauthorizedTopics;

    private final Set<String> invalidTopics;

    private final Set<String> internalTopics;

    private final Node controller;

    private final Map<TopicPartition, PartitionMetadata> metadataByPartition;

    private final Map<String, Uuid> topicIds;

    private final Map<Uuid, String> topicNames;

    private Cluster clusterInstance;

    MetadataCache(String clusterId, Map<Integer, Node> nodes, Collection<PartitionMetadata> partitions, Set<String> unauthorizedTopics, Set<String> invalidTopics, Set<String> internalTopics, Node controller, Map<String, Uuid> topicIds) {
        this(clusterId, nodes, partitions, unauthorizedTopics, invalidTopics, internalTopics, controller, topicIds, null);
    }

    private MetadataCache(String clusterId, Map<Integer, Node> nodes, Collection<PartitionMetadata> partitions, Set<String> unauthorizedTopics, Set<String> invalidTopics, Set<String> internalTopics, Node controller, Map<String, Uuid> topicIds, Cluster clusterInstance) {
        this.clusterId = clusterId;
        this.nodes = nodes;
        this.unauthorizedTopics = unauthorizedTopics;
        this.invalidTopics = invalidTopics;
        this.internalTopics = internalTopics;
        this.controller = controller;
        this.topicIds = topicIds;
        this.topicNames = new HashMap<>(topicIds.size());
        for (Map.Entry<String, Uuid> entry : topicIds.entrySet()) {
            this.topicNames.put(entry.getValue(), entry.getKey());
        }
        this.metadataByPartition = new HashMap<>(partitions.size());
        for (PartitionMetadata p : partitions) {
            this.metadataByPartition.put(p.topicPartition, p);
        }
        if (clusterInstance == null) {
            computeClusterView();
        } else {
            this.clusterInstance = clusterInstance;
        }
    }

    Optional<PartitionMetadata> partitionMetadata(TopicPartition topicPartition) {
        return Optional.ofNullable(metadataByPartition.get(topicPartition));
    }

    Map<String, Uuid> topicIds() {
        return topicIds;
    }

    Map<Uuid, String> topicNames() {
        return topicNames;
    }

    Optional<Node> nodeById(int id) {
        return Optional.ofNullable(nodes.get(id));
    }

    Cluster cluster() {
        if (clusterInstance == null) {
            throw new IllegalStateException("Cached Cluster instance should not be null, but was.");
        } else {
            return clusterInstance;
        }
    }

    ClusterResource clusterResource() {
        return new ClusterResource(clusterId);
    }

    /**
     * Merges the metadata cache's contents with the provided metadata, returning a new metadata cache. The provided
     * metadata is presumed to be more recent than the cache's metadata, and therefore all overlapping metadata will
     * be overridden.
     *
     * @param newClusterId the new cluster Id
     * @param newNodes the new set of nodes
     * @param addPartitions partitions to add
     * @param addUnauthorizedTopics unauthorized topics to add
     * @param addInternalTopics internal topics to add
     * @param newController the new controller node
     * @param topicIds the mapping from topic name to topic ID from the MetadataResponse
     * @param retainTopic returns whether a topic's metadata should be retained
     * @return the merged metadata cache
     */
    MetadataCache mergeWith(String newClusterId, Map<Integer, Node> newNodes, Collection<PartitionMetadata> addPartitions, Set<String> addUnauthorizedTopics, Set<String> addInvalidTopics, Set<String> addInternalTopics, Node newController, Map<String, Uuid> topicIds, BiPredicate<String, Boolean> retainTopic) {
        Predicate<String> shouldRetainTopic = topic -> retainTopic.test(topic, internalTopics.contains(topic));
        Map<TopicPartition, PartitionMetadata> newMetadataByPartition = new HashMap<>(addPartitions.size());
        Map<String, Uuid> newTopicIds = new HashMap<>(topicIds.size());
        // We want the most recent topic ID. We add the old one here for retained topics and then update with newest information in the MetadataResponse
        // we add if a new topic ID is added or remove if the request did not support topic IDs for this topic.
        for (Map.Entry<String, Uuid> entry : this.topicIds.entrySet()) {
            if (shouldRetainTopic.test(entry.getKey())) {
                newTopicIds.put(entry.getKey(), entry.getValue());
            }
        }
        for (PartitionMetadata partition : addPartitions) {
            newMetadataByPartition.put(partition.topicPartition, partition);
            Uuid id = topicIds.get(partition.topic());
            if (id != null)
                newTopicIds.put(partition.topic(), id);
            else
                // Remove if the latest metadata does not have a topic ID
                newTopicIds.remove(partition.topic());
        }
        for (Map.Entry<TopicPartition, PartitionMetadata> entry : metadataByPartition.entrySet()) {
            if (shouldRetainTopic.test(entry.getKey().topic())) {
                newMetadataByPartition.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        Set<String> newUnauthorizedTopics = fillSet(addUnauthorizedTopics, unauthorizedTopics, shouldRetainTopic);
        Set<String> newInvalidTopics = fillSet(addInvalidTopics, invalidTopics, shouldRetainTopic);
        Set<String> newInternalTopics = fillSet(addInternalTopics, internalTopics, shouldRetainTopic);
        return new MetadataCache(newClusterId, newNodes, newMetadataByPartition.values(), newUnauthorizedTopics, newInvalidTopics, newInternalTopics, newController, newTopicIds);
    }

    /**
     * Copies {@code baseSet} and adds all non-existent elements in {@code fillSet} such that {@code predicate} is true.
     * In other words, all elements of {@code baseSet} will be contained in the result, with additional non-overlapping
     * elements in {@code fillSet} where the predicate is true.
     *
     * @param baseSet the base elements for the resulting set
     * @param fillSet elements to be filled into the resulting set
     * @param predicate tested against the fill set to determine whether elements should be added to the base set
     */

    private void computeClusterView() {
        List<PartitionInfo> partitionInfos = metadataByPartition.values().stream().map(metadata -> MetadataResponse.toPartitionInfo(metadata, nodes)).collect(Collectors.toList());
        this.clusterInstance = new Cluster(clusterId, nodes.values(), partitionInfos, unauthorizedTopics, invalidTopics, internalTopics, controller, topicIds);
    }

    static MetadataCache bootstrap(List<InetSocketAddress> addresses) {
        Map<Integer, Node> nodes = new HashMap<>();
        int nodeId = -1;
        for (InetSocketAddress address : addresses) {
            nodes.put(nodeId, new Node(nodeId, address.getHostString(), address.getPort()));
            nodeId--;
        }
        return new MetadataCache(null, nodes, Collections.emptyList(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), null, Collections.emptyMap(), Cluster.bootstrap(addresses));
    }

    static MetadataCache empty() {
        return new MetadataCache(null, Collections.emptyMap(), Collections.emptyList(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), null, Collections.emptyMap(), Cluster.empty());
    }

    @Override
    public String toString() {
        return "MetadataCache{" + "clusterId='" + clusterId + '\'' + ", nodes=" + nodes + ", partitions=" + metadataByPartition.values() + ", controller=" + controller + '}';
    }
}


public Consistency getConsistency() {
        return consistency;
    }

package org.ehcache.clustered.client.config;

import org.ehcache.clustered.client.internal.store.ClusteredStore;
import org.ehcache.clustered.common.Consistency;
import org.ehcache.spi.service.ServiceConfiguration;

/**
 * {@link ServiceConfiguration} for the {@link ClusteredStore}.
 */
public class ClusteredStoreConfiguration implements ServiceConfiguration<ClusteredStore.Provider, Consistency> {

    private final Consistency consistency;

    /**
     * Creates a new configuration with consistency set to {@link Consistency#EVENTUAL EVENTUAL}.
     */
    public ClusteredStoreConfiguration() {
        this(Consistency.EVENTUAL);
    }

    /**
     * Creates a new configuration with the provided {@link Consistency}.
     *
     * @param consistency the {@code Consistency}
     */
    public ClusteredStoreConfiguration(Consistency consistency) {
        this.consistency = consistency;
    }

    @Override
    public Class<ClusteredStore.Provider> getServiceType() {
        return ClusteredStore.Provider.class;
    }

    /**
     * Returns the {@link Consistency} for this configuration instance.
     *
     * @return the {@code Consistency}
     */

    @Override
    public Consistency derive() {
        return getConsistency();
    }

    @Override
    public ClusteredStoreConfiguration build(Consistency representation) {
        return new ClusteredStoreConfiguration(representation);
    }
}


public boolean isConflicting(ISchedulingRule rule) {
        return rule == this;
    }

/**
 * Aptana Studio
 * Any modifications to this file must keep this entire header intact.
 */
package com.aptana.ui;

import org.eclipse.core.runtime.jobs.ISchedulingRule;

/**
 * @author Max Stepanov
 */
public final class PopupSchedulingRule implements ISchedulingRule {

    public static final PopupSchedulingRule INSTANCE = new PopupSchedulingRule();

    private PopupSchedulingRule() {
    }

    /* (non-Javadoc)
	 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
	 */
    public boolean contains(ISchedulingRule rule) {
        return rule == this;
    }

    /* (non-Javadoc)
	 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
	 */


private void deleteSlot(int i) {
        for (int j = i + 1; j < keys.length; j += 1) {
            keys[j - 1] = keys[j];
            priKeys[j - 1] = priKeys[j];
            values[j - 1] = values[j];
        }
        int last = keys.length - 1;
        keys[last] = null;
        priKeys[last] = null;
        values[last] = null;
        if (nextIndex > i) {
            nextIndex -= 1;
        }
        dataIndex = -1;
    }

/*-
 * See the file LICENSE for redistribution information.
 *
 *
 */
package com.sleepycat.collections;

import java.util.ListIterator;
import java.util.NoSuchElementException;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.db.DatabaseEntry;
import com.sleepycat.db.DatabaseException;
import com.sleepycat.db.OperationStatus;
import com.sleepycat.util.keyrange.KeyRange;

/**
 * An iterator that does not need closing because a cursor is not kept open
 * across method calls.  A cursor is opened to read a block of records at a
 * time and then closed before the method returns.
 *
 * @author Mark Hayes
 */
class BlockIterator<E> extends BaseIterator<E> {

    private StoredCollection<E> coll;

    private boolean writeAllowed;

    /**
     * Slots for a block of record keys and values.  The priKeys array is only
     * used for secondary databases; otherwise it is set to the keys array.
     */
    private byte[][] keys;

    private byte[][] priKeys;

    private byte[][] values;

    /**
     * The slot index of the record that would be returned by next().
     * nextIndex is always greater or equal to zero.  If the next record is not
     * available, then nextIndex is equal to keys.length or keys[nextIndex] is
     * null.
     *
     * If the block is empty, then either the iterator is uninitialized or the
     * key range is empty.  Either way, nextIndex will be the array length and
     * all array values will be null.  This is the initial state set by the
     * constructor.  If remove() is used to delete all records in the key
     * range, it will restore the iterator to this initial state.  The block
     * must never be allowed to become empty when the key range is non-empty,
     * since then the iterator's position would be lost.  [#15858]
     */
    private int nextIndex;

    /**
     * The slot index of the record last returned by next() or previous(), or
     * the record inserted by add().  dataIndex is -1 if the data record is not
     * available.  If greater or equal to zero, the slot at dataIndex is always
     * non-null.
     */
    private int dataIndex;

    /**
     * The iterator data last returned by next() or previous().  This value is
     * set to null if dataIndex is -1, or if the state of the iterator is such
     * that set() or remove() cannot be called.  For example, after add() this
     * field is set to null, even though the dataIndex is still valid.
     */
    private E dataObject;

    /**
     * Creates an iterator.
     */
    BlockIterator(StoredCollection<E> coll, boolean writeAllowed, int blockSize) {
        this.coll = coll;
        this.writeAllowed = writeAllowed;
        keys = new byte[blockSize][];
        priKeys = coll.isSecondary() ? (new byte[blockSize][]) : keys;
        values = new byte[blockSize][];
        nextIndex = blockSize;
        dataIndex = -1;
        dataObject = null;
    }

    private BlockIterator(BlockIterator<E> o) {
        coll = o.coll;
        writeAllowed = o.writeAllowed;
        keys = copyArray(o.keys);
        priKeys = coll.isSecondary() ? copyArray(o.priKeys) : keys;
        values = copyArray(o.values);
        nextIndex = o.nextIndex;
        dataIndex = o.dataIndex;
        dataObject = o.dataObject;
    }

    /**
     * Copies an array of byte arrays.
     */
    private byte[][] copyArray(byte[][] a) {
        byte[][] b = new byte[a.length][];
        for (int i = 0; i < b.length; i += 1) {
            if (a[i] != null) {
                b[i] = KeyRange.copyBytes(a[i]);
            }
        }
        return b;
    }

    /**
     * Returns whether the element at nextIndex is available.
     */
    private boolean isNextAvailable() {
        return (nextIndex < keys.length) && (keys[nextIndex] != null);
    }

    /**
     * Returns whether the element at nextIndex-1 is available.
     */
    private boolean isPrevAvailable() {
        return (nextIndex > 0) && (keys[nextIndex - 1] != null);
    }

    /**
     * Returns the record number at the given slot position.
     */
    private int getRecordNumber(int i) {
        if (coll.view.btreeRecNumDb) {
            DataCursor cursor = null;
            try {
                cursor = new DataCursor(coll.view, false);
                if (!moveCursor(i, cursor)) {
                    throw new IllegalStateException();
                }
                return cursor.getCurrentRecordNumber();
            } catch (Exception e) {
                throw StoredContainer.convertException(e);
            } finally {
                coll.closeCursor(cursor);
            }
        } else {
            DatabaseEntry entry = new DatabaseEntry(keys[i]);
            return DbCompat.getRecordNumber(entry);
        }
    }

    /**
     * Sets dataObject to the iterator data for the element at dataIndex.
     */
    private void makeDataObject() {
        int i = dataIndex;
        DatabaseEntry keyEntry = new DatabaseEntry(keys[i]);
        DatabaseEntry priKeyEntry = (keys != priKeys) ? (new DatabaseEntry(priKeys[i])) : keyEntry;
        DatabaseEntry valuesEntry = new DatabaseEntry(values[i]);
        dataObject = coll.makeIteratorData(this, keyEntry, priKeyEntry, valuesEntry);
    }

    /**
     * Sets all slots to null.
     */
    private void clearSlots() {
        for (int i = 0; i < keys.length; i += 1) {
            keys[i] = null;
            priKeys[i] = null;
            values[i] = null;
        }
    }

    /**
     * Sets a given slot using the data in the given cursor.
     */
    private void setSlot(int i, DataCursor cursor) {
        keys[i] = KeyRange.getByteArray(cursor.getKeyThang());
        if (keys != priKeys) {
            priKeys[i] = KeyRange.getByteArray(cursor.getPrimaryKeyThang());
        }
        values[i] = KeyRange.getByteArray(cursor.getValueThang());
    }

    /**
     * Inserts an added record at a given slot position and shifts other slots
     * accordingly.  Also adjusts nextIndex and sets dataIndex to -1.
     */
    private void insertSlot(int i, DataCursor cursor) {
        if (i < keys.length) {
            for (int j = keys.length - 1; j > i; j -= 1) {
                /* Shift right. */
                keys[j] = keys[j - 1];
                priKeys[j] = priKeys[j - 1];
                values[j] = values[j - 1];
                /* Bump key in recno-renumber database. */
                if (coll.view.recNumRenumber && keys[j] != null) {
                    bumpRecordNumber(j);
                }
            }
            nextIndex += 1;
        } else {
            if (i != keys.length) {
                throw DbCompat.unexpectedState();
            }
            i -= 1;
            for (int j = 0; j < i; j += 1) {
                /* Shift left. */
                keys[j] = keys[j + 1];
                priKeys[j] = priKeys[j + 1];
                values[j] = values[j + 1];
            }
        }
        setSlot(i, cursor);
        dataIndex = -1;
    }

    /**
     * Increments the record number key at the given slot.
     */
    private void bumpRecordNumber(int i) {
        DatabaseEntry entry = new DatabaseEntry(keys[i]);
        DbCompat.setRecordNumber(entry, DbCompat.getRecordNumber(entry) + 1);
        keys[i] = entry.getData();
    }

    /**
     * Deletes the given slot, adjusts nextIndex and sets dataIndex to -1.
     */

    /**
     * Moves the cursor to the key/data at the given slot, and returns false
     * if the reposition (search) fails.
     */
    private boolean moveCursor(int i, DataCursor cursor) throws DatabaseException {
        return cursor.repositionExact(keys[i], priKeys[i], values[i], false);
    }

    public boolean hasNext() {
        if (isNextAvailable()) {
            return true;
        }
        DataCursor cursor = null;
        try {
            cursor = new DataCursor(coll.view, writeAllowed);
            int prev = nextIndex - 1;
            boolean found = false;
            if (keys[prev] == null) {
                /* Get the first record for an uninitialized iterator. */
                OperationStatus status = cursor.getFirst(false);
                if (status == OperationStatus.SUCCESS) {
                    found = true;
                    nextIndex = 0;
                }
            } else {
                /* Reposition to the last known key/data pair. */
                int repos = cursor.repositionRange(keys[prev], priKeys[prev], values[prev], false);
                if (repos == DataCursor.REPOS_EXACT) {
                    /*
                     * The last known key/data pair was found and will now be
                     * in slot zero.
                     */
                    found = true;
                    nextIndex = 1;
                    /* The data object is now in slot zero or not available. */
                    if (dataIndex == prev) {
                        dataIndex = 0;
                    } else {
                        dataIndex = -1;
                        dataObject = null;
                    }
                } else if (repos == DataCursor.REPOS_NEXT) {
                    /*
                     * The last known key/data pair was not found, but the
                     * following record was found and it will be in slot zero.
                     */
                    found = true;
                    nextIndex = 0;
                    /* The data object is no longer available. */
                    dataIndex = -1;
                    dataObject = null;
                } else {
                    if (repos != DataCursor.REPOS_EOF) {
                        throw DbCompat.unexpectedState();
                    }
                }
            }
            if (found) {
                /* Clear all slots first in case an exception occurs below. */
                clearSlots();
                /* Attempt to fill all slots with records. */
                int i = 0;
                boolean done = false;
                while (!done) {
                    setSlot(i, cursor);
                    i += 1;
                    if (i < keys.length) {
                        OperationStatus status = coll.iterateDuplicates() ? cursor.getNext(false) : cursor.getNextNoDup(false);
                        if (status != OperationStatus.SUCCESS) {
                            done = true;
                        }
                    } else {
                        done = true;
                    }
                }
            }
            /*
             * If REPOS_EXACT was returned above, make sure we retrieved
             * the following record.
             */
            return isNextAvailable();
        } catch (Exception e) {
            throw StoredContainer.convertException(e);
        } finally {
            coll.closeCursor(cursor);
        }
    }

    public boolean hasPrevious() {
        if (isPrevAvailable()) {
            return true;
        }
        if (!isNextAvailable()) {
            return false;
        }
        DataCursor cursor = null;
        try {
            cursor = new DataCursor(coll.view, writeAllowed);
            int last = keys.length - 1;
            int next = nextIndex;
            boolean found = false;
            /* Reposition to the last known key/data pair. */
            int repos = cursor.repositionRange(keys[next], priKeys[next], values[next], false);
            if (repos == DataCursor.REPOS_EXACT || repos == DataCursor.REPOS_NEXT) {
                /*
                 * The last known key/data pair, or the record following it,
                 * was found and will now be in the last slot.
                 */
                found = true;
                nextIndex = last;
                /* The data object is now in the last slot or not available. */
                if (dataIndex == next && repos == DataCursor.REPOS_EXACT) {
                    dataIndex = last;
                } else {
                    dataIndex = -1;
                    dataObject = null;
                }
            } else {
                if (repos != DataCursor.REPOS_EOF) {
                    throw DbCompat.unexpectedState();
                }
            }
            if (found) {
                /* Clear all slots first in case an exception occurs below. */
                clearSlots();
                /* Attempt to fill all slots with records. */
                int i = last;
                boolean done = false;
                while (!done) {
                    setSlot(i, cursor);
                    i -= 1;
                    if (i >= 0) {
                        OperationStatus status = coll.iterateDuplicates() ? cursor.getPrev(false) : cursor.getPrevNoDup(false);
                        if (status != OperationStatus.SUCCESS) {
                            done = true;
                        }
                    } else {
                        done = true;
                    }
                }
            }
            /*
             * Make sure we retrieved the preceding record after the reposition
             * above.
             */
            return isPrevAvailable();
        } catch (Exception e) {
            throw StoredContainer.convertException(e);
        } finally {
            coll.closeCursor(cursor);
        }
    }

    public E next() {
        if (hasNext()) {
            dataIndex = nextIndex;
            nextIndex += 1;
            makeDataObject();
            return dataObject;
        } else {
            throw new NoSuchElementException();
        }
    }

    public E previous() {
        if (hasPrevious()) {
            nextIndex -= 1;
            dataIndex = nextIndex;
            makeDataObject();
            return dataObject;
        } else {
            throw new NoSuchElementException();
        }
    }

    public int nextIndex() {
        if (!coll.view.recNumAccess) {
            throw new UnsupportedOperationException("Record number access not supported");
        }
        return hasNext() ? (getRecordNumber(nextIndex) - coll.getIndexOffset()) : Integer.MAX_VALUE;
    }

    public int previousIndex() {
        if (!coll.view.recNumAccess) {
            throw new UnsupportedOperationException("Record number access not supported");
        }
        return hasPrevious() ? (getRecordNumber(nextIndex - 1) - coll.getIndexOffset()) : (-1);
    }

    public void set(E value) {
        if (dataObject == null) {
            throw new IllegalStateException();
        }
        if (!coll.hasValues()) {
            throw new UnsupportedOperationException();
        }
        DataCursor cursor = null;
        boolean doAutoCommit = coll.beginAutoCommit();
        try {
            cursor = new DataCursor(coll.view, writeAllowed);
            if (moveCursor(dataIndex, cursor)) {
                cursor.putCurrent(value);
                setSlot(dataIndex, cursor);
                coll.closeCursor(cursor);
                coll.commitAutoCommit(doAutoCommit);
            } else {
                throw new IllegalStateException();
            }
        } catch (Exception e) {
            coll.closeCursor(cursor);
            throw coll.handleException(e, doAutoCommit);
        }
    }

    public void remove() {
        if (dataObject == null) {
            throw new IllegalStateException();
        }
        DataCursor cursor = null;
        boolean doAutoCommit = coll.beginAutoCommit();
        try {
            cursor = new DataCursor(coll.view, writeAllowed);
            if (moveCursor(dataIndex, cursor)) {
                cursor.delete();
                deleteSlot(dataIndex);
                dataObject = null;
                /*
                 * Repopulate the block after removing all records, using the
                 * cursor position of the deleted record as a starting point.
                 * First try moving forward, since the user was moving forward.
                 * (It is possible to delete all records in the block only by
                 * moving forward, i.e, when nextIndex is greater than
                 * dataIndex.)
                 */
                if (nextIndex == 0 && keys[0] == null) {
                    OperationStatus status;
                    for (int i = 0; i < keys.length; i += 1) {
                        status = coll.iterateDuplicates() ? cursor.getNext(false) : cursor.getNextNoDup(false);
                        if (status == OperationStatus.SUCCESS) {
                            setSlot(i, cursor);
                        } else {
                            break;
                        }
                    }
                    /*
                     * If no records are found past the cursor position, try
                     * moving backward.  If no records are found before the
                     * cursor position, leave nextIndex set to keys.length,
                     * which is the same as the initial iterator state and is
                     * appropriate for an empty key range.
                     */
                    if (keys[0] == null) {
                        nextIndex = keys.length;
                        for (int i = nextIndex - 1; i >= 0; i -= 1) {
                            status = coll.iterateDuplicates() ? cursor.getPrev(false) : cursor.getPrevNoDup(false);
                            if (status == OperationStatus.SUCCESS) {
                                setSlot(i, cursor);
                            } else {
                                break;
                            }
                        }
                    }
                }
                coll.closeCursor(cursor);
                coll.commitAutoCommit(doAutoCommit);
            } else {
                throw new IllegalStateException();
            }
        } catch (Exception e) {
            coll.closeCursor(cursor);
            throw coll.handleException(e, doAutoCommit);
        }
    }

    public void add(E value) {
        /*
         * The checkIterAddAllowed method ensures that one of the following two
         * conditions holds and throws UnsupportedOperationException otherwise:
         * 1- This is a list iterator for a recno-renumber database.
         * 2- This is a collection iterator for a duplicates database.
         */
        coll.checkIterAddAllowed();
        OperationStatus status = OperationStatus.SUCCESS;
        DataCursor cursor = null;
        boolean doAutoCommit = coll.beginAutoCommit();
        try {
            if (coll.view.keysRenumbered || !coll.areDuplicatesOrdered()) {
                /*
                 * This is a recno-renumber database or a btree/hash database
                 * with unordered duplicates.
                 */
                boolean hasPrev = hasPrevious();
                if (!hasPrev && !hasNext()) {
                    /* The collection is empty. */
                    if (coll.view.keysRenumbered) {
                        /* Append to an empty recno-renumber database. */
                        status = coll.view.append(value, null, null);
                    } else if (coll.view.dupsAllowed && coll.view.range.isSingleKey()) {
                        /*
                         * When inserting a duplicate into a single-key range,
                         * the main key is fixed, so we can always insert into
                         * an empty collection.
                         */
                        cursor = new DataCursor(coll.view, writeAllowed);
                        cursor.useRangeKey();
                        status = cursor.putNoDupData(null, value, null, true);
                        coll.closeCursor(cursor);
                        cursor = null;
                    } else {
                        throw new IllegalStateException("Collection is empty, cannot add() duplicate");
                    }
                    /*
                     * Move past the record just inserted (the cursor should be
                     * closed above to prevent multiple open cursors in certain
                     * DB core modes).
                     */
                    if (status == OperationStatus.SUCCESS) {
                        next();
                        dataIndex = nextIndex - 1;
                    }
                } else {
                    /*
                     * The collection is non-empty.  If hasPrev is true then
                     * the element at (nextIndex - 1) is available; otherwise
                     * the element at nextIndex is available.
                     */
                    cursor = new DataCursor(coll.view, writeAllowed);
                    int insertIndex = hasPrev ? (nextIndex - 1) : nextIndex;
                    if (!moveCursor(insertIndex, cursor)) {
                        throw new IllegalStateException();
                    }
                    /*
                     * For a recno-renumber database or a database with
                     * unsorted duplicates, insert before the iterator 'next'
                     * position, or after the 'prev' position.  Then adjust
                     * the slots to account for the inserted record.
                     */
                    status = hasPrev ? cursor.putAfter(value) : cursor.putBefore(value);
                    if (status == OperationStatus.SUCCESS) {
                        insertSlot(nextIndex, cursor);
                    }
                }
            } else {
                /* This is a btree/hash database with ordered duplicates. */
                cursor = new DataCursor(coll.view, writeAllowed);
                if (coll.view.range.isSingleKey()) {
                    /*
                     * When inserting a duplicate into a single-key range,
                     * the main key is fixed.
                     */
                    cursor.useRangeKey();
                } else {
                    /*
                     * When inserting into a multi-key range, the main key
                     * is the last dataIndex accessed by next(), previous()
                     * or add().
                     */
                    if (dataIndex < 0 || !moveCursor(dataIndex, cursor)) {
                        throw new IllegalStateException();
                    }
                }
                /*
                 * For a hash/btree with duplicates, insert the duplicate,
                 * put the new record in slot zero, and set the next index
                 * to slot one (past the new record).
                 */
                status = cursor.putNoDupData(null, value, null, true);
                if (status == OperationStatus.SUCCESS) {
                    clearSlots();
                    setSlot(0, cursor);
                    dataIndex = 0;
                    nextIndex = 1;
                }
            }
            if (status == OperationStatus.KEYEXIST) {
                throw new IllegalArgumentException("Duplicate value");
            } else if (status != OperationStatus.SUCCESS) {
                DbCompat.unexpectedState("Could not insert: " + status);
            }
            /* Prevent subsequent set() or remove() call. */
            dataObject = null;
            coll.closeCursor(cursor);
            coll.commitAutoCommit(doAutoCommit);
        } catch (Exception e) {
            /* Catch RuntimeExceptions too. */
            coll.closeCursor(cursor);
            throw coll.handleException(e, doAutoCommit);
        }
    }

    // --- begin BaseIterator methods ---
    final ListIterator<E> dup() {
        return new BlockIterator<E>(this);
    }

    final boolean isCurrentData(Object currentData) {
        return (dataObject == currentData);
    }

    final boolean moveToIndex(int index) {
        DataCursor cursor = null;
        try {
            cursor = new DataCursor(coll.view, writeAllowed);
            OperationStatus status = cursor.getSearchKey(Integer.valueOf(index), null, false);
            boolean retVal;
            if (status == OperationStatus.SUCCESS) {
                clearSlots();
                setSlot(0, cursor);
                nextIndex = 0;
                retVal = true;
            } else {
                retVal = false;
            }
            return retVal;
        } catch (Exception e) {
            throw StoredContainer.convertException(e);
        } finally {
            coll.closeCursor(cursor);
        }
    }
    // --- end BaseIterator methods ---
}