001/*
002 * Copyright 2019-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2019-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2019-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util;
037
038
039
040import java.io.Serializable;
041import java.util.Collections;
042import java.util.SortedMap;
043import java.util.TreeMap;
044
045import com.unboundid.ldap.sdk.DN;
046import com.unboundid.ldap.sdk.LDAPResult;
047import com.unboundid.ldap.sdk.SearchResult;
048
049
050
051/**
052 * This class provides a data structure with information about the results of
053 * a subtree delete attempt.
054 *
055 * @see  SubtreeDeleter
056 */
057@NotMutable()
058@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
059public final class SubtreeDeleterResult
060       implements Serializable
061{
062  /**
063   * The serial version UID for this serializable class.
064   */
065  private static final long serialVersionUID = -4801520019525316763L;
066
067
068
069  // Indicates whether the target subtree is inaccessible.
070  private final boolean subtreeInaccessible;
071
072  // An error that occurred during an attempt to make the target subtree
073  // inaccessible.
074  @Nullable private final LDAPResult setSubtreeAccessibilityError;
075
076  // The number of entries that were successfully deleted.
077  private final long entriesDeleted;
078
079  // An error that occurred during search processing that prevented identifying
080  // all of the entries in the target subtree.
081  @Nullable private final SearchResult searchError;
082
083  // A map that contains the DNs of the entries that could not be deleted,
084  // associated with a result indicating the reason for the delete failure.
085  // It will be sorted in descending order
086  @NotNull private final TreeMap<DN,LDAPResult> deleteErrors;
087
088
089
090  /**
091   * Creates a new subtree deleter result with the provided information.
092   *
093   * @param  setSubtreeAccessibilityError
094   *              An {@code LDAPResult} object with information about an error
095   *              that occurred while trying to make the target subtree
096   *              inaccessible, or while trying to remove that accessibility
097   *              restriction after all other processing completed successfully
098   *              (and the two cases can be differentiated using the value of
099   *              the {@code subtreeInaccessible} argument).  This may be
100   *              {@code null} if no attempt was made to alter the accessibility
101   *              of the target subtree, or if its accessibility was
102   *              successfully altered.
103   * @param  subtreeInaccessible
104   *              Indicates whether the target subtree was left inaccessible
105   *              after processing completed.  If the subtree was made
106   *              inaccessible, it will be left in an inaccessible state if any
107   *              error occurs during search or delete processing.  The
108   *              accessibility restriction will be removed if all processing
109   *              completes successfully.
110   * @param  searchError
111   *              A search result with information about an error that occurred
112   *              during search processing that prevented identifying all of the
113   *              entries in the target subtree.  It may be {@code null} if
114   *              there was no error during search processing.
115   * @param  entriesDeleted
116   *              The number of entries that were successfully deleted.
117   * @param  deleteErrors
118   *              A map that contains the DNs of entries that could not be
119   *              deleted, associated with a result indicating the reason for
120   *              the delete failure.  It must not be {@code null} but may be
121   *              empty.
122   */
123  SubtreeDeleterResult(@Nullable final LDAPResult setSubtreeAccessibilityError,
124                       final boolean subtreeInaccessible,
125                       @Nullable final SearchResult searchError,
126                       final long entriesDeleted,
127                       @NotNull final TreeMap<DN,LDAPResult> deleteErrors)
128  {
129    this.setSubtreeAccessibilityError = setSubtreeAccessibilityError;
130    this.subtreeInaccessible = subtreeInaccessible;
131    this.searchError = searchError;
132    this.entriesDeleted = entriesDeleted;
133    this.deleteErrors = deleteErrors;
134  }
135
136
137
138  /**
139   * Indicates whether the {@link SubtreeDeleter} processing was completely
140   * successful.
141   *
142   * @return  {@code true} if the subtree deleter processing was completely
143   *          successful, or {@code false} if not.
144   */
145  public boolean completelySuccessful()
146  {
147    return ((setSubtreeAccessibilityError == null) &&
148         (! subtreeInaccessible) &&
149         (searchError == null) &&
150         deleteErrors.isEmpty());
151  }
152
153
154
155  /**
156   * Retrieves an {@code LDAPResult} that provides information about an error
157   * that occurred while trying to make the target subtree inaccessible before
158   * subtree delete processing, or if an error occurred while trying to remove
159   * the subtree accessibility restriction after all other processing had
160   * completed successfully.  This may be {@code null} if no attempts was made
161   * to alter the subtree accessibility, or if no error occurred during
162   * processing.
163   * <BR><BR>
164   * If the return value is non-{@code null} and {@link #subtreeInaccessible}
165   * returns {@code false}, then the error occurred while attempting to make the
166   * target subtree inaccessible.  If the return value is non-{@code null} and
167   * {@code isSubtreeInaccessible} returns {@code true}, then the error occurred
168   * while attempting to remove the subtree accessibility restriction.
169   *
170   * @return  An {@code LDAPResult} that provides information about an error
171   *          that occurred while attempting to alter the accessibility of the
172   *          target subtree, or {@code null} if no such error occurred.
173   */
174  @Nullable()
175  public LDAPResult getSetSubtreeAccessibilityError()
176  {
177    return setSubtreeAccessibilityError;
178  }
179
180
181
182  /**
183   * Indicates whether the target subtree was left in an inaccessible state
184   * after some error occurred during subtree delete processing.
185   *
186   * @return  {@code true} if the subtree was set inaccessible at the start of
187   *          subtree delete processing and remains inaccessible after an error
188   *          occurred during processing, or {@code false} if the subtree
189   *          accessibility was not altered or if the accessibility restriction
190   *          was removed after all processing completed successfully.
191   */
192  public boolean subtreeInaccessible()
193  {
194    return subtreeInaccessible;
195  }
196
197
198
199  /**
200   * Retrieves a search result with information about an error that occurred
201   * during search processing that prevented identifying all of the entries in
202   * the target subtree.
203   *
204   * @return  A search result with information about an error that occurred
205   *          during search processing that prevented identifying all of the
206   *          entries in the target subtree, or {@code null} if no error
207   *          occurred during search processing.
208   */
209  @Nullable()
210  public SearchResult getSearchError()
211  {
212    return searchError;
213  }
214
215
216
217  /**
218   * Retrieves the number of entries that were successfully deleted.
219   *
220   * @return  The number of entries that were successfully deleted.
221   */
222  public long getEntriesDeleted()
223  {
224    return entriesDeleted;
225  }
226
227
228
229  /**
230   * Retrieves an unmodifiable sorted map of the DNs of entries that could not
231   * be successfully deleted, each of which is associated with an
232   * {@code LDAPResult} indicating the reason for the delete failure.  The map
233   * will be ordered in ascending order using the comparator provided by the
234   * {@code DN} class (that is, with ancestor entries before their descendants).
235   *
236   * @return  An unmodifiable sorted map of the DNs of the entries that could
237   *          not be deleted, each of which is associated with an
238   *          {@code LDAPResult} indicating the reason for the delete failure.
239   */
240  @NotNull()
241  public SortedMap<DN,LDAPResult> getDeleteErrors()
242  {
243    return Collections.unmodifiableSortedMap(deleteErrors);
244  }
245
246
247
248  /**
249   * Retrieves an unmodifiable sorted map of the DNs of entries that could not
250   * be successfully deleted, each of which is associated with an
251   * {@code LDAPResult} indicating the reason for the delete failure.  The map
252   * will be ordered in descending order using the comparator provided by the
253   * {@code DN} class (that is, with descendant entries before their ancestors).
254   *
255   * @return  An unmodifiable sorted map of the DNs of the entries that could
256   *          not be deleted, each of which is associated with an
257   *          {@code LDAPResult} indicating the reason for the delete failure.
258   */
259  @NotNull()
260  public SortedMap<DN,LDAPResult> getDeleteErrorsDescendingMap()
261  {
262    return Collections.unmodifiableSortedMap(deleteErrors.descendingMap());
263  }
264
265
266
267  /**
268   * Retrieves the delete errors as a {@code TreeMap}.
269   *
270   * @return  Retrieves the delete errors as a {@code TreeMap}.
271   */
272  @NotNull()
273  TreeMap<DN,LDAPResult> getDeleteErrorsTreeMap()
274  {
275    return deleteErrors;
276  }
277
278
279
280  /**
281   * Retrieves a string representation of this subtree deleter result.
282   *
283   * @return  A string representation of this subtree deleter result.
284   */
285  @Override()
286  @NotNull()
287  public String toString()
288  {
289    final StringBuilder buffer = new StringBuilder();
290    toString(buffer);
291    return buffer.toString();
292  }
293
294
295
296  /**
297   * Appends a string representation of this subtree deleter result to the
298   * provided buffer.
299   *
300   * @param  buffer  The buffer to which the string representation should be
301   *                 appended.
302   */
303  public void toString(@NotNull final StringBuilder buffer)
304  {
305    buffer.append("SubtreeDeleterResult=(completelySuccessful=");
306    buffer.append(completelySuccessful());
307
308    if (setSubtreeAccessibilityError != null)
309    {
310      buffer.append(", setSubtreeAccessibilityError=");
311      setSubtreeAccessibilityError.toString(buffer);
312    }
313
314    if (subtreeInaccessible)
315    {
316      buffer.append(", subtreeInaccessible=true");
317    }
318
319    if (searchError != null)
320    {
321      buffer.append(", searchError=");
322      searchError.toString(buffer);
323    }
324
325    buffer.append(", entriesDeleted=");
326    buffer.append(entriesDeleted);
327
328    if (! deleteErrors.isEmpty())
329    {
330      buffer.append(", deleteErrors=");
331      buffer.append(deleteErrors);
332    }
333  }
334}