001    /*
002     * Copyright 2010-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2010-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.util.args;
022    
023    
024    
025    import java.util.Collections;
026    import java.util.HashMap;
027    import java.util.Map;
028    import java.util.concurrent.atomic.AtomicReference;
029    
030    import com.unboundid.ldap.sdk.SearchScope;
031    import com.unboundid.util.Mutable;
032    import com.unboundid.util.StaticUtils;
033    import com.unboundid.util.ThreadSafety;
034    import com.unboundid.util.ThreadSafetyLevel;
035    
036    import static com.unboundid.util.args.ArgsMessages.*;
037    
038    
039    
040    /**
041     * This class defines an argument that is intended to hold one search scope
042     * values.  Scope arguments must take values, and those arguments must represent
043     * valid search scopes.  Supported scope values include:
044     * <UL>
045     *   <LI>baseObject scope -- base, baseObject, base-object, 0</LI>
046     *   <LI>singleLevel scope -- one, singleLevel, single-level, oneLevel,
047     *       one-level, 1</LI>
048     *   <LI>wholeSubtree scope -- sub, subtree, wholeSubtree, whole-subtree, 2</LI>
049     *   <LI>subordinateSubtree scope -- subord, subordinate, subordinateSubtree,
050     *       subordinate-subtree, 3</LI>
051     * </UL>
052     */
053    @Mutable()
054    @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
055    public final class ScopeArgument
056           extends Argument
057    {
058      /**
059       * A map of value strings to the corresponding search scopes.
060       */
061      private static final Map<String,SearchScope> SCOPE_STRINGS;
062    
063      static
064      {
065        final HashMap<String,SearchScope> scopeMap =
066             new HashMap<String,SearchScope>(20);
067    
068        scopeMap.put("base", SearchScope.BASE);
069        scopeMap.put("baseobject", SearchScope.BASE);
070        scopeMap.put("base-object", SearchScope.BASE);
071        scopeMap.put("0", SearchScope.BASE);
072    
073        scopeMap.put("one", SearchScope.ONE);
074        scopeMap.put("singlelevel", SearchScope.ONE);
075        scopeMap.put("single-level", SearchScope.ONE);
076        scopeMap.put("onelevel", SearchScope.ONE);
077        scopeMap.put("one-level", SearchScope.ONE);
078        scopeMap.put("1", SearchScope.ONE);
079    
080        scopeMap.put("sub", SearchScope.SUB);
081        scopeMap.put("subtree", SearchScope.SUB);
082        scopeMap.put("wholesubtree", SearchScope.SUB);
083        scopeMap.put("whole-subtree", SearchScope.SUB);
084        scopeMap.put("2", SearchScope.SUB);
085    
086        scopeMap.put("subord", SearchScope.SUBORDINATE_SUBTREE);
087        scopeMap.put("subordinate", SearchScope.SUBORDINATE_SUBTREE);
088        scopeMap.put("subordinatesubtree", SearchScope.SUBORDINATE_SUBTREE);
089        scopeMap.put("subordinate-subtree", SearchScope.SUBORDINATE_SUBTREE);
090        scopeMap.put("3", SearchScope.SUBORDINATE_SUBTREE);
091    
092        SCOPE_STRINGS = Collections.unmodifiableMap(scopeMap);
093      }
094    
095    
096    
097      /**
098       * The serial version UID for this serializable class.
099       */
100      private static final long serialVersionUID = 5962857448814911423L;
101    
102    
103    
104      // The value assigned to this argument.
105      private final AtomicReference<SearchScope> value;
106    
107      // The default value for this argument.
108      private final SearchScope defaultValue;
109    
110    
111    
112      /**
113       * Creates a new search scope argument with the provided information.  It will
114       * not have a default value.
115       *
116       * @param  shortIdentifier   The short identifier for this argument.  It may
117       *                           not be {@code null} if the long identifier is
118       *                           {@code null}.
119       * @param  longIdentifier    The long identifier for this argument.  It may
120       *                           not be {@code null} if the short identifier is
121       *                           {@code null}.
122       * @param  isRequired        Indicates whether this argument is required to
123       *                           be provided.
124       * @param  valuePlaceholder  A placeholder to display in usage information to
125       *                           indicate that a value must be provided.  It must
126       *                           not be {@code null}.
127       * @param  description       A human-readable description for this argument.
128       *                           It must not be {@code null}.
129       *
130       * @throws  ArgumentException  If there is a problem with the definition of
131       *                             this argument.
132       */
133      public ScopeArgument(final Character shortIdentifier,
134                           final String longIdentifier, final boolean isRequired,
135                           final String valuePlaceholder, final String description)
136             throws ArgumentException
137      {
138        this(shortIdentifier, longIdentifier, isRequired,  valuePlaceholder,
139             description, null);
140      }
141    
142    
143    
144    
145    
146    
147      /**
148       * Creates a new search scope argument with the provided information.
149       *
150       * @param  shortIdentifier   The short identifier for this argument.  It may
151       *                           not be {@code null} if the long identifier is
152       *                           {@code null}.
153       * @param  longIdentifier    The long identifier for this argument.  It may
154       *                           not be {@code null} if the short identifier is
155       *                           {@code null}.
156       * @param  isRequired        Indicates whether this argument is required to
157       *                           be provided.
158       * @param  valuePlaceholder  A placeholder to display in usage information to
159       *                           indicate that a value must be provided.  It must
160       *                           not be {@code null}.
161       * @param  description       A human-readable description for this argument.
162       *                           It must not be {@code null}.
163       * @param  defaultValue      The default value to use for this argument if no
164       *                           values were provided.  It may be {@code null} if
165       *                           there should be no default values.
166       *
167       * @throws  ArgumentException  If there is a problem with the definition of
168       *                             this argument.
169       */
170      public ScopeArgument(final Character shortIdentifier,
171                           final String longIdentifier, final boolean isRequired,
172                           final String valuePlaceholder, final String description,
173                           final SearchScope defaultValue)
174             throws ArgumentException
175      {
176        super(shortIdentifier, longIdentifier, isRequired,  1, valuePlaceholder,
177             description);
178    
179        if (valuePlaceholder == null)
180        {
181          throw new ArgumentException(ERR_ARG_MUST_TAKE_VALUE.get(
182                                           getIdentifierString()));
183        }
184    
185        this.defaultValue = defaultValue;
186    
187        value = new AtomicReference<SearchScope>();
188      }
189    
190    
191    
192      /**
193       * Creates a new scope argument that is a "clean" copy of the provided
194       * source argument.
195       *
196       * @param  source  The source argument to use for this argument.
197       */
198      private ScopeArgument(final ScopeArgument source)
199      {
200        super(source);
201    
202        defaultValue = source.defaultValue;
203        value        = new AtomicReference<SearchScope>();
204      }
205    
206    
207    
208      /**
209       * Retrieves the default value for this argument, which will be used if no
210       * value was provided.
211       *
212       * @return  The default value for this argument, or {@code null} if there is
213       *          no default value.
214       */
215      public SearchScope getDefaultValue()
216      {
217        return defaultValue;
218      }
219    
220    
221    
222      /**
223       * {@inheritDoc}
224       */
225      @Override()
226      protected void addValue(final String valueString)
227                throws ArgumentException
228      {
229        final SearchScope scope =
230             SCOPE_STRINGS.get(StaticUtils.toLowerCase(valueString));
231        if (scope == null)
232        {
233          throw new ArgumentException(ERR_SCOPE_VALUE_NOT_VALID.get(valueString,
234               getIdentifierString()));
235        }
236    
237        if (! value.compareAndSet(null, scope))
238        {
239          throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
240                                           getIdentifierString()));
241        }
242      }
243    
244    
245    
246      /**
247       * Retrieves the value for this argument, or the default value if none was
248       * provided.
249       *
250       * @return  The value for this argument, or the default value if none was
251       *          provided, or {@code null} if there is no value and no default
252       *          value.
253       */
254      public SearchScope getValue()
255      {
256        final SearchScope s = value.get();
257        if (s == null)
258        {
259          return defaultValue;
260        }
261        else
262        {
263          return s;
264        }
265      }
266    
267    
268    
269      /**
270       * {@inheritDoc}
271       */
272      @Override()
273      protected boolean hasDefaultValue()
274      {
275        return (defaultValue != null);
276      }
277    
278    
279    
280      /**
281       * {@inheritDoc}
282       */
283      @Override()
284      public String getDataTypeName()
285      {
286        return INFO_SCOPE_TYPE_NAME.get();
287      }
288    
289    
290    
291      /**
292       * {@inheritDoc}
293       */
294      @Override()
295      public String getValueConstraints()
296      {
297        return INFO_SCOPE_CONSTRAINTS.get();
298      }
299    
300    
301    
302      /**
303       * {@inheritDoc}
304       */
305      @Override()
306      public ScopeArgument getCleanCopy()
307      {
308        return new ScopeArgument(this);
309      }
310    
311    
312    
313      /**
314       * {@inheritDoc}
315       */
316      @Override()
317      public void toString(final StringBuilder buffer)
318      {
319        buffer.append("ScopeArgument(");
320        appendBasicToStringInfo(buffer);
321    
322        if (defaultValue != null)
323        {
324          buffer.append(", defaultValue='");
325          switch (defaultValue.intValue())
326          {
327            case SearchScope.BASE_INT_VALUE:
328              buffer.append("base");
329              break;
330            case SearchScope.ONE_INT_VALUE:
331              buffer.append("one");
332              break;
333            case SearchScope.SUB_INT_VALUE:
334              buffer.append("sub");
335              break;
336            case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE:
337              buffer.append("subordinate");
338              break;
339            default:
340              buffer.append(defaultValue.intValue());
341              break;
342          }
343          buffer.append('\'');
344        }
345    
346        buffer.append(')');
347      }
348    }