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