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