001    /*
002     * Copyright 2008-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2015 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk.unboundidds.extensions;
022    
023    
024    
025    import java.text.ParseException;
026    import java.util.ArrayList;
027    import java.util.Collections;
028    import java.util.Date;
029    import java.util.Iterator;
030    import java.util.LinkedHashMap;
031    import java.util.Map;
032    import java.util.NoSuchElementException;
033    
034    import com.unboundid.asn1.ASN1Element;
035    import com.unboundid.asn1.ASN1OctetString;
036    import com.unboundid.asn1.ASN1Sequence;
037    import com.unboundid.ldap.sdk.Control;
038    import com.unboundid.ldap.sdk.ExtendedResult;
039    import com.unboundid.ldap.sdk.LDAPException;
040    import com.unboundid.ldap.sdk.ResultCode;
041    import com.unboundid.util.NotMutable;
042    import com.unboundid.util.ThreadSafety;
043    import com.unboundid.util.ThreadSafetyLevel;
044    
045    import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
046    import static com.unboundid.util.Debug.*;
047    
048    
049    
050    /**
051     * <BLOCKQUOTE>
052     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
053     *   LDAP SDK for Java.  It is not available for use in applications that
054     *   include only the Standard Edition of the LDAP SDK, and is not supported for
055     *   use in conjunction with non-UnboundID products.
056     * </BLOCKQUOTE>
057     * This class implements a data structure for storing the information from an
058     * extended result for the password policy state extended request as used in the
059     * UnboundID Directory Server.  It is able to decode a generic extended result
060     * to obtain the user DN and operations.  See the documentation in the
061     * {@link PasswordPolicyStateExtendedRequest} class for an example that
062     * demonstrates the use of the password policy state extended operation.
063     */
064    @NotMutable()
065    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
066    public final class PasswordPolicyStateExtendedResult
067           extends ExtendedResult
068    {
069      /**
070       * The serial version UID for this serializable class.
071       */
072      private static final long serialVersionUID = 7140468768443263344L;
073    
074    
075    
076      // A map containing all of the response operations, indexed by operation type.
077      private final Map<Integer,PasswordPolicyStateOperation> operations;
078    
079      // The user DN from the response.
080      private final String userDN;
081    
082    
083    
084      /**
085       * Creates a new password policy state extended result from the provided
086       * extended result.
087       *
088       * @param  extendedResult  The extended result to be decoded as a password
089       *                         policy state extended result.  It must not be
090       *                         {@code null}.
091       *
092       * @throws  LDAPException  If the provided extended result cannot be decoded
093       *                         as a password policy state extended result.
094       */
095      public PasswordPolicyStateExtendedResult(final ExtendedResult extendedResult)
096             throws LDAPException
097      {
098        super(extendedResult);
099    
100        final ASN1OctetString value = extendedResult.getValue();
101        if (value == null)
102        {
103          userDN = null;
104          operations = Collections.emptyMap();
105          return;
106        }
107    
108        final ASN1Element[] elements;
109        try
110        {
111          final ASN1Element valueElement = ASN1Element.decode(value.getValue());
112          elements = ASN1Sequence.decodeAsSequence(valueElement).elements();
113        }
114        catch (Exception e)
115        {
116          debugException(e);
117          throw new LDAPException(ResultCode.DECODING_ERROR,
118                                  ERR_PWP_STATE_RESPONSE_VALUE_NOT_SEQUENCE.get(e),
119                                  e);
120        }
121    
122        if ((elements.length < 1) || (elements.length > 2))
123        {
124          throw new LDAPException(ResultCode.DECODING_ERROR,
125                                  ERR_PWP_STATE_RESPONSE_INVALID_ELEMENT_COUNT.get(
126                                       elements.length));
127        }
128    
129        userDN = ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
130    
131        final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops =
132             new LinkedHashMap<Integer,PasswordPolicyStateOperation>();
133        if (elements.length == 2)
134        {
135          try
136          {
137            final ASN1Element[] opElements =
138                 ASN1Sequence.decodeAsSequence(elements[1]).elements();
139            for (final ASN1Element e : opElements)
140            {
141              final PasswordPolicyStateOperation op =
142                   PasswordPolicyStateOperation.decode(e);
143              ops.put(op.getOperationType(), op);
144            }
145          }
146          catch (Exception e)
147          {
148            debugException(e);
149            throw new LDAPException(ResultCode.DECODING_ERROR,
150                                    ERR_PWP_STATE_RESPONSE_CANNOT_DECODE_OPS.get(e),
151                                    e);
152          }
153        }
154    
155        operations = Collections.unmodifiableMap(ops);
156      }
157    
158    
159    
160      /**
161       * Creates a new password policy state extended result with the provided
162       * information.
163       * @param  messageID          The message ID for the LDAP message that is
164       *                            associated with this LDAP result.
165       * @param  resultCode         The result code from the response.
166       * @param  diagnosticMessage  The diagnostic message from the response, if
167       *                            available.
168       * @param  matchedDN          The matched DN from the response, if available.
169       * @param  referralURLs       The set of referral URLs from the response, if
170       *                            available.
171       * @param  userDN             The user DN from the response.
172       * @param  operations         The set of operations from the response, mapped
173       *                            from operation type to the corresponding
174       *                            operation data.
175       * @param  responseControls   The set of controls from the response, if
176       *                            available.
177       */
178      public PasswordPolicyStateExtendedResult(final int messageID,
179                  final ResultCode resultCode, final String diagnosticMessage,
180                  final String matchedDN, final String[] referralURLs,
181                  final String userDN,
182                  final PasswordPolicyStateOperation[] operations,
183                  final Control[] responseControls)
184      {
185        super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
186              null, encodeValue(userDN, operations), responseControls);
187    
188        this.userDN = userDN;
189    
190        if ((operations == null) || (operations.length == 0))
191        {
192          this.operations = Collections.emptyMap();
193        }
194        else
195        {
196          final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops =
197               new LinkedHashMap<Integer,PasswordPolicyStateOperation>(
198                        operations.length);
199          for (final PasswordPolicyStateOperation o : operations)
200          {
201            ops.put(o.getOperationType(), o);
202          }
203          this.operations = Collections.unmodifiableMap(ops);
204        }
205      }
206    
207    
208    
209      /**
210       * Encodes the provided information into a suitable value for this control.
211       *
212       * @param  userDN             The user DN from the response.
213       * @param  operations         The set of operations from the response, mapped
214       *                            from operation type to the corresponding
215       *                            operation data.
216       *
217       * @return  An ASN.1 octet string containing the appropriately-encoded value
218       *          for this control, or {@code null} if there should not be a value.
219       */
220      private static ASN1OctetString encodeValue(final String userDN,
221           final PasswordPolicyStateOperation[] operations)
222      {
223        if ((userDN == null) && ((operations == null) || (operations.length == 0)))
224        {
225          return null;
226        }
227    
228        final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
229        elements.add(new ASN1OctetString(userDN));
230    
231        if ((operations != null) && (operations.length > 0))
232        {
233          final ASN1Element[] opElements = new ASN1Element[operations.length];
234          for (int i=0; i < operations.length; i++)
235          {
236            opElements[i] = operations[i].encode();
237          }
238    
239          elements.add(new ASN1Sequence(opElements));
240        }
241    
242        return new ASN1OctetString(new ASN1Sequence(elements).encode());
243      }
244    
245    
246    
247    
248      /**
249       * Retrieves the user DN included in the response.
250       *
251       * @return  The user DN included in the response, or {@code null} if the user
252       *          DN is not available (e.g., if this is an error response).
253       */
254      public String getUserDN()
255      {
256        return userDN;
257      }
258    
259    
260    
261      /**
262       * Retrieves the set of password policy operations included in the response.
263       *
264       * @return  The set of password policy operations included in the response.
265       */
266      public Iterable<PasswordPolicyStateOperation> getOperations()
267      {
268        return operations.values();
269      }
270    
271    
272    
273      /**
274       * Retrieves the specified password policy state operation from the response.
275       *
276       * @param  opType  The operation type for the password policy state operation
277       *                 to retrieve.
278       *
279       * @return  The requested password policy state operation, or {@code null} if
280       *          no such operation was included in the response.
281       */
282      public PasswordPolicyStateOperation getOperation(final int opType)
283      {
284        return operations.get(opType);
285      }
286    
287    
288    
289      /**
290       * Retrieves the value for the specified password policy state operation as a
291       * string.
292       *
293       * @param  opType  The operation type for the password policy state operation
294       *                 to retrieve.
295       *
296       * @return  The string value of the requested password policy state operation,
297       *          or {@code null} if the specified operation was not included in the
298       *          response or did not have any values.
299       */
300      public String getStringValue(final int opType)
301      {
302        final PasswordPolicyStateOperation op = operations.get(opType);
303        if (op == null)
304        {
305          return null;
306        }
307    
308        return op.getStringValue();
309      }
310    
311    
312    
313      /**
314       * Retrieves the set of string values for the specified password policy state
315       * operation.
316       *
317       * @param  opType  The operation type for the password policy state operation
318       *                 to retrieve.
319       *
320       * @return  The set of string values for the requested password policy state
321       *          operation, or {@code null} if the specified operation was not
322       *          included in the response.
323       */
324      public String[] getStringValues(final int opType)
325      {
326        final PasswordPolicyStateOperation op = operations.get(opType);
327        if (op == null)
328        {
329          return null;
330        }
331    
332        return op.getStringValues();
333      }
334    
335    
336    
337      /**
338       * Retrieves the value of the specified password policy state operation as a
339       * boolean.
340       *
341       * @param  opType  The operation type for the password policy state operation
342       *                 to retrieve.
343       *
344       * @return  The boolean value of the requested password policy state
345       *          operation.
346       *
347       * @throws  NoSuchElementException  If the specified operation was not
348       *                                  included in the response.
349       *
350       * @throws  IllegalStateException  If the specified password policy state
351       *                                 operation does not have exactly one value,
352       *                                 or if the value cannot be parsed as a
353       *                                 boolean value.
354       */
355      public boolean getBooleanValue(final int opType)
356             throws NoSuchElementException, IllegalStateException
357      {
358        final PasswordPolicyStateOperation op = operations.get(opType);
359        if (op == null)
360        {
361          throw new NoSuchElementException(
362                         ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get());
363        }
364    
365        return op.getBooleanValue();
366      }
367    
368    
369    
370      /**
371       * Retrieves the value of the specified password policy state operation as an
372       * integer.
373       *
374       * @param  opType  The operation type for the password policy state operation
375       *                 to retrieve.
376       *
377       * @return  The integer value of the requested password policy state
378       *          operation.
379       *
380       * @throws  NoSuchElementException  If the specified operation was not
381       *                                  included in the response.
382       *
383       * @throws  IllegalStateException  If the value of the specified password
384       *                                 policy state operation cannot be parsed as
385       *                                 an integer value.
386       */
387      public int getIntValue(final int opType)
388             throws NoSuchElementException, IllegalStateException
389      {
390        final PasswordPolicyStateOperation op = operations.get(opType);
391        if (op == null)
392        {
393          throw new NoSuchElementException(
394                         ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get());
395        }
396    
397        return op.getIntValue();
398      }
399    
400    
401    
402      /**
403       * Retrieves the value for the specified password policy state operation as a
404       * {@code Date} in generalized time format.
405       *
406       * @param  opType  The operation type for the password policy state operation
407       *                 to retrieve.
408       *
409       * @return  The value of the requested password policy state operation as a
410       *          {@code Date}, or {@code null} if the specified operation was not
411       *          included in the response or did not have any values.
412       *
413       * @throws  ParseException  If the value cannot be parsed as a date in
414       *                          generalized time format.
415       */
416      public Date getGeneralizedTimeValue(final int opType)
417             throws ParseException
418      {
419        final PasswordPolicyStateOperation op = operations.get(opType);
420        if (op == null)
421        {
422          return null;
423        }
424    
425        return op.getGeneralizedTimeValue();
426      }
427    
428    
429    
430      /**
431       * Retrieves the set of values for the specified password policy state
432       * operation as {@code Date}s in generalized time format.
433       *
434       * @param  opType  The operation type for the password policy state operation
435       *                 to retrieve.
436       *
437       * @return  The set of values of the requested password policy state operation
438       *          as {@code Date}s.
439       *
440       * @throws  ParseException  If any of the values cannot be parsed as a date in
441       *                          generalized time format.
442       */
443      public Date[] getGeneralizedTimeValues(final int opType)
444             throws ParseException
445      {
446        final PasswordPolicyStateOperation op = operations.get(opType);
447        if (op == null)
448        {
449          return null;
450        }
451    
452        return op.getGeneralizedTimeValues();
453      }
454    
455    
456    
457      /**
458       * {@inheritDoc}
459       */
460      @Override()
461      public String getExtendedResultName()
462      {
463        return INFO_EXTENDED_RESULT_NAME_PW_POLICY_STATE.get();
464      }
465    
466    
467    
468      /**
469       * Appends a string representation of this extended result to the provided
470       * buffer.
471       *
472       * @param  buffer  The buffer to which a string representation of this
473       *                 extended result will be appended.
474       */
475      @Override()
476      public void toString(final StringBuilder buffer)
477      {
478        buffer.append("PasswordPolicyStateExtendedResult(resultCode=");
479        buffer.append(getResultCode());
480    
481        final int messageID = getMessageID();
482        if (messageID >= 0)
483        {
484          buffer.append(", messageID=");
485          buffer.append(messageID);
486        }
487    
488        buffer.append(", userDN='");
489        buffer.append(userDN);
490        buffer.append("', operations={");
491    
492        final Iterator<PasswordPolicyStateOperation> iterator =
493             operations.values().iterator();
494        while (iterator.hasNext())
495        {
496          iterator.next().toString(buffer);
497          if (iterator.hasNext())
498          {
499            buffer.append(", ");
500          }
501        }
502        buffer.append('}');
503    
504        final String diagnosticMessage = getDiagnosticMessage();
505        if (diagnosticMessage != null)
506        {
507          buffer.append(", diagnosticMessage='");
508          buffer.append(diagnosticMessage);
509          buffer.append('\'');
510        }
511    
512        final String matchedDN = getMatchedDN();
513        if (matchedDN != null)
514        {
515          buffer.append(", matchedDN='");
516          buffer.append(matchedDN);
517          buffer.append('\'');
518        }
519    
520        final String[] referralURLs = getReferralURLs();
521        if (referralURLs.length > 0)
522        {
523          buffer.append(", referralURLs={");
524          for (int i=0; i < referralURLs.length; i++)
525          {
526            if (i > 0)
527            {
528              buffer.append(", ");
529            }
530    
531            buffer.append('\'');
532            buffer.append(referralURLs[i]);
533            buffer.append('\'');
534          }
535          buffer.append('}');
536        }
537    
538        final Control[] responseControls = getResponseControls();
539        if (responseControls.length > 0)
540        {
541          buffer.append(", responseControls={");
542          for (int i=0; i < responseControls.length; i++)
543          {
544            if (i > 0)
545            {
546              buffer.append(", ");
547            }
548    
549            buffer.append(responseControls[i]);
550          }
551          buffer.append('}');
552        }
553    
554        buffer.append(')');
555      }
556    }