/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.jackrabbit.api.observation;

import static java.util.Arrays.copyOf;

import org.osgi.annotation.versioning.ConsumerType;

/**
 * A storage object for event filter configuration.
 * <p>
 * The parameters of the filter can then be set by chaining the set methods,
 * since each method returns the same <code>EventFilter</code> with the indicated parameter set.
 * <p>
 * Once the filter is configured, it and an {@link javax.jcr.observation.EventListener} object are
 * passed to
 * {@link org.apache.jackrabbit.api.observation.JackrabbitObservationManager#addEventListener(javax.jcr.observation.EventListener, JackrabbitEventFilter)}.
 * <p>
 * The filter restricts which events are sent to the <code>EventListener</code> according to the
 * following parameters. Note that the term <i>associated parent node</i> of an event means the
 * parent node of the item at (or formerly at) the path returned by
 * {@link javax.jcr.observation.Event#getPath}.
 * <ul>
 * <li>
 * <code>eventTypes</code>:
 * A bitwise <code>OR</code> of the event types to be listened to. See
 * {@link javax.jcr.observation.Event} for details.
 * <li>
 * <code>absPath</code>, <code>absPaths</code>, <code>excludedPaths</code>,
 * <code>isDeep</code>: Only events whose associated parent node is at one
 * of the paths in <code>absPath</code> or <code>absPaths</code> (or within
 * its subgraph, if <code>isDeep</code> is <code>true</code>) will be received
 * except if the associated parent node is at one of the paths in
 * <code>excludedPaths</code> or its subgraph.
 * It is permissible to register a listener for a path where no node currently
 * exists.
 * <li>
 * <code>uuid</code>:
 * Only events whose associated parent node has one of
 * the identifiers in this list will be received. If his parameter is
 * <code>null</code> then no identifier-related restriction is placed on
 * events received. Note that specifying an empty array instead of
 * <code>null</code> would result in no nodes being listened to. The term
 * "UUID" is used for compatibility with JCR 1.0.
 * <li>
 * <code>nodeTypeName</code>:
 * Only events whose associated parent node has
 * one of the node types (or a subtype of one of the node types) in this
 * list will be received. If his parameter is <code>null</code> then no node
 * type-related restriction is placed on events received. Note that
 * specifying an empty array instead of <code>null</code> would result in no
 * nodes types being listened to.
 * <li>
 * <code>noLocal</code>: if <code>true</code>, then events
 * generated by the session through which the listener was registered are
 * ignored. Otherwise, they are not ignored.
 * <li>
 * <code>noExternal</code>: if <code>true</code>, then events
 * from external cluster nodes are ignored. Otherwise, they are not ignored.
 * <li>
 * <code>noInternal</code>: if <code>true</code>, then events
 * from this cluster node are ignored. Otherwise, they are not ignored.
 * </ul>
 * The restrictions are "ANDed" together. In other words, for a particular node to be "listened to" it
 * must meet all the restrictions.
 *
 */
@ConsumerType
public class JackrabbitEventFilter {  // TODO extends EventFilter once JCR 2.1 is out
    private int eventTypes;
    private String absPath;
    private boolean isDeep;
    private String[] identifiers;
    private String[] nodeTypeNames;
    private boolean noLocal;
    private String[] absPaths = new String[]{};
    private String[] excludedPaths = new String[]{};
    private boolean noExternal;
    private boolean noInternal;

    /**
     * Sets the <code>eventTypes</code> parameter of the filter.
     * If left unset, this parameter defaults to <code>0</code>.
     *
     * @param eventTypes an <code>int</code>.
     * @return This EventFilter object with the <code>eventTypes</code> parameter set.
     */
    public JackrabbitEventFilter setEventTypes(int eventTypes) {
        this.eventTypes = eventTypes;
        return this;
    }

    /**
     * Returns the <code>eventTypes</code> parameter of the filter.
     *
     * @return an <code>int</code>.
     */
    public int getEventTypes() {
        return eventTypes;
    }

    /**
     * Sets the <code>absPath</code> parameter of the filter.
     * If left unset, this parameter defaults to <code>null</code>.
     *
     * @param absPath an absolute path <code>String</code>.
     * @return This EventFilter object with the <code>absPath</code> parameter set.
     */
    public JackrabbitEventFilter setAbsPath(String absPath) {
        this.absPath = absPath;
        return this;
    }

    /**
     * Returns the <code>absPath</code> parameter of the filter.
     *
     * @return a <code>String</code>.
     */
    public String getAbsPath() {
        return absPath;
    }

    /**
     * Sets the <code>isDeep</code> parameter of the filter.
     * If left unset, this parameter defaults to <code>false</code>.
     *
     * @param isDeep a <code>boolean</code>.
     * @return This EventFilter object with the <code>isDeep</code> parameter set.
     */
    public JackrabbitEventFilter setIsDeep(boolean isDeep) {
        this.isDeep = isDeep;
        return this;
    }

    /**
     * Returns the <code>isDeep</code> parameter of the filter.
     *
     * @return a <code>boolean</code>.
     */
    public boolean getIsDeep() {
        return isDeep;
    }

    /**
     * Sets the <code>identifiers</code> parameter of the filter.
     * If left unset, this parameter defaults to <code>null</code>.
     *
     * @param identifiers a <code>String</code> array.
     * @return This EventFilter object with the <code>identifiers</code> parameter set.
     */
    public JackrabbitEventFilter setIdentifiers(String[] identifiers) {
        this.identifiers = copyOf(identifiers, identifiers.length);
        return null;
    }

    /**
     * Returns the <code>uuids</code> parameter of the filter.
     *
     * @return a <code>String</code> array.
     */
    public String[] getIdentifiers() {
        return identifiers == null ? null : copyOf(identifiers, identifiers.length);
    }

    /**
     * Sets the <code>nodeTypeNames</code> parameter of the filter.
     * If left unset, this parameter defaults to <code>null</code>.
     *
     * @param nodeTypeNames a <code>String</code> array.
     * @return This EventFilter object with the <code>nodeTypes</code> parameter set.
     */
    public JackrabbitEventFilter setNodeTypes(String[] nodeTypeNames) {
        this.nodeTypeNames = copyOf(nodeTypeNames, nodeTypeNames.length);
        return this;
    }

    /**
     * Returns the <code>nodeTypeName</code> parameter of the filter.
     *
     * @return a <code>String</code> array.
     */
    public String[] getNodeTypes() {
        return nodeTypeNames == null ? null : copyOf(nodeTypeNames, nodeTypeNames.length);
    }

    /**
     * Sets the <code>noLocal</code> parameter of the filter.
     * If left unset, this parameter defaults to <code>false</code>.
     *
     * @param noLocal a <code>boolean</code>.
     * @return This EventFilter object with the <code>noLocal</code> parameter set.
     */
    public JackrabbitEventFilter setNoLocal(boolean noLocal) {
        this.noLocal = noLocal;
        return this;
    }

    /**
     * Returns the <code>noLocal</code> parameter of the filter.
     *
     * @return a <code>boolean</code>.
     */
    public boolean getNoLocal() {
        return noLocal;
    }

    /**
     * Sets the <code>absPaths</code> parameter of the filter.
     * If left unset, this parameter defaults to an empty array.
     *
     * @param absPaths an absolute path <code>String</code> array.
     * @return This EventFilter object with the <code>absPaths</code> parameter set.
     */
    public JackrabbitEventFilter setAdditionalPaths(String... absPaths) {
        this.absPaths = copyOf(absPaths, absPaths.length);
        return this;
    }

    /**
     * Returns the <code>absPaths</code> parameter of the filter.
     *
     * @return a <code>String</code> array.
     */
    public String[] getAdditionalPaths() {
        return copyOf(absPaths, absPaths.length);
    }

    /**
     * Sets the <code>excludedPaths</code> parameter of the filter.
     * If left unset, this parameter defaults to an empty array.
     *
     * @param excludedPaths an absolute path <code>String</code> array.
     * @return This EventFilter object with the <code>excludedPaths</code> parameter set.
     */
    public JackrabbitEventFilter setExcludedPaths(String... excludedPaths) {
        this.excludedPaths = copyOf(excludedPaths, excludedPaths.length);
        return this;
    }

    /**
     * Returns the <code>excludedPaths</code> parameter of the filter.
     *
     * @return a <code>String</code> array.
     */
    public String[] getExcludedPaths() {
        return copyOf(excludedPaths, excludedPaths.length);
    }

    /**
     * Sets the <code>noExternal</code> parameter of the filter.
     * If left unset, this parameter defaults to <code>false</code>.
     *
     * @param noExternal a <code>boolean</code>.
     * @return This EventFilter object with the <code>noExternal</code> parameter set.
     */
    public JackrabbitEventFilter setNoExternal(boolean noExternal) {
        this.noExternal = noExternal;
        return this;
    }

    /**
     * Returns the <code>noExternal</code> parameter of the filter.
     *
     * @return a <code>boolean</code>.
     */
    public boolean getNoExternal() {
        return noExternal;
    }

    /**
     * Sets the <code>noInternal</code> parameter of the filter.
     * If left unset, this parameter defaults to <code>false</code>.
     *
     * @param noInternal a <code>boolean</code>.
     * @return This EventFilter object with the <code>noExternal</code> parameter set.
     */
    public JackrabbitEventFilter setNoInternal(boolean noInternal) {
        this.noInternal = noInternal;
        return this;
    }

    /**
     * Returns the <code>noInternal</code> parameter of the filter.
     *
     * @return a <code>boolean</code>.
     */
    public boolean getNoInternal() {
        return noInternal;
    }

}