001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2008-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2015 ForgeRock AS.
016 */
017package org.opends.server.admin;
018
019import java.text.NumberFormat;
020import java.util.EnumSet;
021import java.util.Set;
022import java.util.TreeSet;
023
024import org.forgerock.i18n.LocalizableMessage;
025import org.forgerock.i18n.LocalizableMessageBuilder;
026import org.forgerock.opendj.config.DurationUnit;
027import org.forgerock.opendj.config.SizeUnit;
028import org.forgerock.util.Utils;
029
030/**
031 * A property definition visitor which can be used to generate syntax
032 * usage information.
033 */
034public final class PropertyDefinitionUsageBuilder {
035
036  /**
037   * Underlying implementation.
038   */
039  private class MyPropertyDefinitionVisitor extends
040      PropertyDefinitionVisitor<LocalizableMessage, Void> {
041
042    /**
043     * Flag indicating whether detailed syntax information will be
044     * generated.
045     */
046    private final boolean isDetailed;
047
048    /** The formatter to use for numeric values. */
049    private final NumberFormat numberFormat;
050
051
052
053    /** Private constructor. */
054    private MyPropertyDefinitionVisitor(boolean isDetailed) {
055      this.isDetailed = isDetailed;
056
057      this.numberFormat = NumberFormat.getNumberInstance();
058      this.numberFormat.setGroupingUsed(true);
059      this.numberFormat.setMaximumFractionDigits(2);
060    }
061
062
063
064    /** {@inheritDoc} */
065    @Override
066    public <C extends ConfigurationClient, S extends Configuration>
067    LocalizableMessage visitAggregation(AggregationPropertyDefinition<C, S> d, Void p) {
068      return LocalizableMessage.raw("NAME");
069    }
070
071
072
073    /** {@inheritDoc} */
074    @Override
075    public LocalizableMessage visitAttributeType(AttributeTypePropertyDefinition d,
076        Void p) {
077      return LocalizableMessage.raw("OID");
078    }
079
080    /** {@inheritDoc} */
081    @Override
082    public LocalizableMessage visitACI(ACIPropertyDefinition d, Void p) {
083      return LocalizableMessage.raw("ACI");
084    }
085
086    /** {@inheritDoc} */
087    @Override
088    public LocalizableMessage visitBoolean(BooleanPropertyDefinition d, Void p) {
089      if (isDetailed) {
090        return LocalizableMessage.raw("false | true");
091      } else {
092        return LocalizableMessage.raw("BOOLEAN");
093      }
094    }
095
096
097
098    /** {@inheritDoc} */
099    @Override
100    public LocalizableMessage visitClass(ClassPropertyDefinition d, Void p) {
101      if (isDetailed && !d.getInstanceOfInterface().isEmpty()) {
102        return LocalizableMessage.raw("CLASS <= " + d.getInstanceOfInterface().get(0));
103      } else {
104        return LocalizableMessage.raw("CLASS");
105      }
106    }
107
108
109
110    /** {@inheritDoc} */
111    @Override
112    public LocalizableMessage visitDN(DNPropertyDefinition d, Void p) {
113      if (isDetailed && d.getBaseDN() != null) {
114        return LocalizableMessage.raw("DN <= " + d.getBaseDN());
115      } else {
116        return LocalizableMessage.raw("DN");
117      }
118    }
119
120
121
122    /** {@inheritDoc} */
123    @Override
124    public LocalizableMessage visitDuration(DurationPropertyDefinition d, Void p) {
125      LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
126      DurationUnit unit = d.getBaseUnit();
127
128      if (isDetailed && d.getLowerLimit() > 0) {
129        builder.append(DurationUnit.toString(d.getLowerLimit()));
130        builder.append(" <= ");
131      }
132
133      builder.append("DURATION (");
134      builder.append(unit.getShortName());
135      builder.append(")");
136
137      if (isDetailed) {
138        if (d.getUpperLimit() != null) {
139          builder.append(" <= ");
140          builder.append(DurationUnit.toString(d.getUpperLimit()));
141        }
142
143        if (d.isAllowUnlimited()) {
144          builder.append(" | unlimited");
145        }
146      }
147
148      return builder.toMessage();
149    }
150
151
152
153    /** {@inheritDoc} */
154    @Override
155    public <E extends Enum<E>> LocalizableMessage visitEnum(EnumPropertyDefinition<E> d,
156        Void p) {
157      if (!isDetailed) {
158        // Use the last word in the property name.
159        String name = d.getName();
160        int i = name.lastIndexOf('-');
161        if (i == -1 || i == (name.length() - 1)) {
162          return LocalizableMessage.raw(name.toUpperCase());
163        } else {
164          return LocalizableMessage.raw(name.substring(i + 1).toUpperCase());
165        }
166      } else {
167        Set<String> values = new TreeSet<>();
168        for (Object value : EnumSet.allOf(d.getEnumClass())) {
169          values.add(value.toString().trim().toLowerCase());
170        }
171        return LocalizableMessage.raw(Utils.joinAsString(" | ", values));
172      }
173    }
174
175
176
177    /** {@inheritDoc} */
178    @Override
179    public LocalizableMessage visitInteger(IntegerPropertyDefinition d, Void p) {
180      LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
181
182      if (isDetailed) {
183        builder.append(String.valueOf(d.getLowerLimit()));
184        builder.append(" <= ");
185      }
186
187      builder.append("INTEGER");
188
189      if (isDetailed) {
190        if (d.getUpperLimit() != null) {
191          builder.append(" <= ");
192          builder.append(String.valueOf(d.getUpperLimit()));
193        } else if (d.isAllowUnlimited()) {
194          builder.append(" | unlimited");
195        }
196      }
197
198      return builder.toMessage();
199    }
200
201
202
203    /** {@inheritDoc} */
204    @Override
205    public LocalizableMessage visitIPAddress(IPAddressPropertyDefinition d, Void p) {
206      return LocalizableMessage.raw("HOST_NAME");
207    }
208
209
210
211    /** {@inheritDoc} */
212    @Override
213    public LocalizableMessage visitIPAddressMask(IPAddressMaskPropertyDefinition d,
214        Void p) {
215      return LocalizableMessage.raw("IP_ADDRESS_MASK");
216    }
217
218
219
220    /** {@inheritDoc} */
221    @Override
222    public LocalizableMessage visitSize(SizePropertyDefinition d, Void p) {
223      LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
224
225      if (isDetailed && d.getLowerLimit() > 0) {
226        SizeUnit unit = SizeUnit.getBestFitUnitExact(d.getLowerLimit());
227        builder.append(numberFormat.format(unit.fromBytes(d.getLowerLimit())));
228        builder.append(' ');
229        builder.append(unit.getShortName());
230        builder.append(" <= ");
231      }
232
233      builder.append("SIZE");
234
235      if (isDetailed) {
236        if (d.getUpperLimit() != null) {
237          long upperLimit = d.getUpperLimit();
238          SizeUnit unit = SizeUnit.getBestFitUnitExact(upperLimit);
239
240          // Quite often an upper limit is some power of 2 minus 1. In those
241          // cases lets use a "less than" relation rather than a "less than
242          // or equal to" relation. This will result in a much more readable
243          // quantity.
244          if (unit == SizeUnit.BYTES && upperLimit < Long.MAX_VALUE) {
245            unit = SizeUnit.getBestFitUnitExact(upperLimit + 1);
246            if (unit != SizeUnit.BYTES) {
247              upperLimit += 1;
248              builder.append(" < ");
249            } else {
250              builder.append(" <= ");
251            }
252          } else {
253            builder.append(" <= ");
254          }
255
256          builder.append(numberFormat.format(unit.fromBytes(upperLimit)));
257          builder.append(' ');
258          builder.append(unit.getShortName());
259        }
260
261        if (d.isAllowUnlimited()) {
262          builder.append(" | unlimited");
263        }
264      }
265
266      return builder.toMessage();
267    }
268
269
270
271    /** {@inheritDoc} */
272    @Override
273    public LocalizableMessage visitString(StringPropertyDefinition d, Void p) {
274      if (d.getPattern() != null) {
275        if (isDetailed) {
276          LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
277          builder.append(d.getPatternUsage());
278          builder.append(" - ");
279          final LocalizableMessage synopsis = d.getPatternSynopsis();
280          if (synopsis != null)
281          {
282            builder.append(synopsis);
283          }
284          return builder.toMessage();
285        } else {
286          return LocalizableMessage.raw(d.getPatternUsage());
287        }
288      } else {
289        return LocalizableMessage.raw("STRING");
290      }
291    }
292
293
294
295    /** {@inheritDoc} */
296    @Override
297    public <T> LocalizableMessage visitUnknown(PropertyDefinition<T> d, Void p)
298        throws PropertyException {
299      return LocalizableMessage.raw("?");
300    }
301  }
302
303  /** Underlying implementation. */
304  private final MyPropertyDefinitionVisitor pimpl;
305
306
307
308  /**
309   * Creates a new property usage builder.
310   *
311   * @param isDetailed
312   *          Indicates whether or not the generated usage should
313   *          contain detailed information such as constraints.
314   */
315  public PropertyDefinitionUsageBuilder(boolean isDetailed) {
316    this.pimpl = new MyPropertyDefinitionVisitor(isDetailed);
317  }
318
319
320
321  /**
322   * Generates the usage information for the provided property
323   * definition.
324   *
325   * @param pd
326   *          The property definitions.
327   * @return Returns the usage information for the provided property
328   *         definition.
329   */
330  public LocalizableMessage getUsage(PropertyDefinition<?> pd) {
331    return pd.accept(pimpl, null);
332  }
333}