/*
 * 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.sling.api.resource.observation;

import java.util.Set;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

import org.osgi.annotation.versioning.ConsumerType;

/**
 * A resource change event is immutable.
 *
 * A change event can either be local or external. Local changes happened
 * on the same instance, while external changes happened on a different
 * instance.
 *
 * Resource listeners only receive external changes if they mark themselves
 * as a {@link ExternalResourceChangeListener}.
 *
 * For all events (local and external), the path and the type of change is
 * set.
 *
 * Resource provider events are always local events and only provide the path.
 *
 * Local events for resources provide the names of the properties that
 * have been added, removed or changed. This information might be missing
 * for external events.
 *
 * @since 1.0.0 (Sling API Bundle 2.11.0)
 */
@ConsumerType
public class ResourceChange {

    /**
     * The type of the change
     */
    public enum ChangeType {
        ADDED,            // the resource has been added
        REMOVED,          // the resource has been removed
        CHANGED,          // the resource has been changed
        PROVIDER_ADDED,   // a provider has been added
        PROVIDER_REMOVED  // a provider has been removed
    }

    /** The resource path. */
    private final String path;

    /** The resource change. */
    private final ChangeType changeType;

    /** Flag whether the change is external. */
    private final boolean isExternal;

    /** Optional set of added property names. */
    private final Set<String> addedPropertyNames;

    /** Optional set of changed property names. */
    private final Set<String> changedPropertyNames;

    /** Optional set of removed property names. */
    private final Set<String> removedPropertyNames;

    /**
     * Create a new change object
     *
     * @param changeType The change type
     * @param path The resource path
     * @param isExternal {code true} if the change happened on another node
     * @since 1.2.0 (Sling API Bundle 2.15.0)
     */
    public ResourceChange(final @Nonnull ChangeType changeType,
            final @Nonnull String path,
            final boolean isExternal) {
        this.path = path;
        this.changeType = changeType;
        this.isExternal = isExternal;
        this.addedPropertyNames = null;
        this.changedPropertyNames = null;
        this.removedPropertyNames = null;
    }

    /**
     * Create a new change object
     *
     * @param changeType The change type
     * @param path The resource path
     * @param isExternal {code true} if the change happened on another node
     * @param addedPropertyNames set of added property names, if provided must be immutable
     * @param changedPropertyNames set of added property names, if provided must be immutable
     * @param removedPropertyNames set of added property names, if provided must be immutable
     * @deprecated The sets of property names are not supported anymore.
     */
    @Deprecated
    public ResourceChange(final @Nonnull ChangeType changeType,
            final @Nonnull String path,
            final boolean isExternal,
            final Set<String> addedPropertyNames,
            final Set<String> changedPropertyNames,
            final Set<String> removedPropertyNames) {
        this.path = path;
        this.changeType = changeType;
        this.isExternal = isExternal;
        this.addedPropertyNames = addedPropertyNames;
        this.changedPropertyNames = changedPropertyNames;
        this.removedPropertyNames = removedPropertyNames;
    }

    /**
     * Get the resource path.
     * @return The path to the resource.
     */
    public @Nonnull String getPath() {
        return this.path;
    }

    /**
     * Get the user id of the user initiating the change
     * @return The user id or {@code null} if it's not available.
     */
    public @CheckForNull String getUserId() {
        return null;
    }

    /**
     * Is this an external event?
     * @return {@code true} if the event is external.
     */
    public boolean isExternal() {
        return this.isExternal;
    }

   /**
     * Get the type of change
     * @return The type of change
     */
    public @Nonnull ChangeType getType() {
        return this.changeType;
    }

    /**
     * Optional information about changed properties.
     * The application code can not rely on getting the correct set of changed
     * properties. A resource provider implementation is free to not support
     * this. Therefore if this method returns {@code null} it does not mean
     * that there are no changed properties. However if an empty set is
     * returned, it can safely be assumed that there are none. Therefore
     * returning {code null} is the equivalent of "don't know".
     * @return The set of changed property names. For external events or
     *         resource provider events {@code null} is returned.
     * @deprecated As there is no guarantee that this information is contained in the change
     *             event, this should not be used anymore.
     */
    @Deprecated
    public @CheckForNull Set<String> getChangedPropertyNames() {
        return this.changedPropertyNames;
    }

    /**
     * Optional information about added properties.
     * The application code can not rely on getting the correct set of added
     * properties. A resource provider implementation is free to not support
     * this. Therefore if this method returns {@code null} it does not mean
     * that there are no added properties. However if an empty set is
     * returned, it can safely be assumed that there are none. Therefore
     * returning {code null} is the equivalent of "don't know".
     * @return The set of changed property names. For external events or
     *         resource provider events {@code null} is returned.
     * @deprecated As there is no guarantee that this information is contained in the change
     *             event, this should not be used anymore.
     */
    @Deprecated
    public @CheckForNull Set<String> getAddedPropertyNames() {
        return this.addedPropertyNames;
    }

    /**
     * Optional information about removed properties.
     * The application code can not rely on getting the correct set of removed
     * properties. A resource provider implementation is free to not support
     * this. Therefore if this method returns {@code null} it does not mean
     * that there are no removed properties. However if an empty set is
     * returned, it can safely be assumed that there are none. Therefore
     * returning {code null} is the equivalent of "don't know".
     * @return The set of changed property names. For external events or
     *         resource provider events {@code null} is returned.
     * @deprecated As there is no guarantee that this information is contained in the change
     *             event, this should not be used anymore.
     */
    @Deprecated
    public @CheckForNull Set<String> getRemovedPropertyNames() {
        return this.removedPropertyNames;
    }
}

@@ -7954,11 +7954,369 @@
 s;%0A    %7D
+%0A%0A    @Override%0A    public String toString() %7B%0A        StringBuilder b = new StringBuilder();%0A        b.append(%22ResourceChange%5Btype=%22)%0A          .append(this.getType())%0A          .append(%22, path=%22)%0A          .append(this.getPath())%0A          .append(%22, external=%22)%0A          .append(this.isExternal)%0A          .append(%22%5D%22);%0A        return b.toString();%0A    %7D
 %0A%7D%0A

Add toString to ResourceChange