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.forgerock.opendj.config;
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.util.Utils;
037
038/**
039 * A property definition visitor which can be used to generate syntax usage
040 * information.
041 */
042public final class PropertyDefinitionUsageBuilder {
043
044    /**
045     * Underlying implementation.
046     */
047    private static final class MyPropertyDefinitionVisitor extends
048            PropertyDefinitionVisitor<LocalizableMessage, Void> {
049        /**
050         * Flag indicating whether detailed syntax information will be
051         * generated.
052         */
053        private final boolean isDetailed;
054
055        /** The formatter to use for numeric values. */
056        private final NumberFormat numberFormat;
057
058        /** Private constructor. */
059        private MyPropertyDefinitionVisitor(boolean isDetailed) {
060            this.isDetailed = isDetailed;
061
062            this.numberFormat = NumberFormat.getNumberInstance();
063            this.numberFormat.setGroupingUsed(true);
064            this.numberFormat.setMaximumFractionDigits(2);
065        }
066
067        /** {@inheritDoc} */
068        @Override
069        public <C extends ConfigurationClient, S extends Configuration> LocalizableMessage visitAggregation(
070            AggregationPropertyDefinition<C, S> d, Void p) {
071            return LocalizableMessage.raw("NAME");
072        }
073
074        /** {@inheritDoc} */
075        @Override
076        public LocalizableMessage visitAttributeType(AttributeTypePropertyDefinition d, 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        /** {@inheritDoc} */
097        @Override
098        public LocalizableMessage visitClass(ClassPropertyDefinition d, Void p) {
099            if (isDetailed && !d.getInstanceOfInterface().isEmpty()) {
100                return LocalizableMessage.raw("CLASS <= " + d.getInstanceOfInterface().get(0));
101            } else {
102                return LocalizableMessage.raw("CLASS");
103            }
104        }
105
106        /** {@inheritDoc} */
107        @Override
108        public LocalizableMessage visitDN(DNPropertyDefinition d, Void p) {
109            if (isDetailed && d.getBaseDN() != null) {
110                return LocalizableMessage.raw("DN <= " + d.getBaseDN());
111            } else {
112                return LocalizableMessage.raw("DN");
113            }
114        }
115
116        /** {@inheritDoc} */
117        @Override
118        public LocalizableMessage visitDuration(DurationPropertyDefinition d, Void p) {
119            LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
120            DurationUnit unit = d.getBaseUnit();
121
122            if (isDetailed && d.getLowerLimit() > 0) {
123                builder.append(DurationUnit.toString(d.getLowerLimit()));
124                builder.append(" <= ");
125            }
126
127            builder.append("DURATION (");
128            builder.append(unit.getShortName());
129            builder.append(")");
130
131            if (isDetailed) {
132                if (d.getUpperLimit() != null) {
133                    builder.append(" <= ");
134                    builder.append(DurationUnit.toString(d.getUpperLimit()));
135                }
136
137                if (d.isAllowUnlimited()) {
138                    builder.append(" | unlimited");
139                }
140            }
141
142            return builder.toMessage();
143        }
144
145        /** {@inheritDoc} */
146        @Override
147        public <E extends Enum<E>> LocalizableMessage visitEnum(EnumPropertyDefinition<E> d, Void p) {
148            if (!isDetailed) {
149                // Use the last word in the property name.
150                String name = d.getName();
151                int i = name.lastIndexOf('-');
152                if (i == -1 || i == (name.length() - 1)) {
153                    return LocalizableMessage.raw(name.toUpperCase());
154                } else {
155                    return LocalizableMessage.raw(name.substring(i + 1).toUpperCase());
156                }
157            } else {
158                Set<String> values = new TreeSet<>();
159                for (Object value : EnumSet.allOf(d.getEnumClass())) {
160                    values.add(value.toString().trim().toLowerCase());
161                }
162                return LocalizableMessage.raw(Utils.joinAsString(" | ", values));
163            }
164        }
165
166        /** {@inheritDoc} */
167        @Override
168        public LocalizableMessage visitInteger(IntegerPropertyDefinition d, Void p) {
169            LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
170
171            if (isDetailed) {
172                builder.append(String.valueOf(d.getLowerLimit()));
173                builder.append(" <= ");
174            }
175
176            builder.append("INTEGER");
177
178            if (isDetailed) {
179                if (d.getUpperLimit() != null) {
180                    builder.append(" <= ");
181                    builder.append(String.valueOf(d.getUpperLimit()));
182                } else if (d.isAllowUnlimited()) {
183                    builder.append(" | unlimited");
184                }
185            }
186
187            return builder.toMessage();
188        }
189
190        /** {@inheritDoc} */
191        @Override
192        public LocalizableMessage visitIPAddress(IPAddressPropertyDefinition d, Void p) {
193            return LocalizableMessage.raw("HOST_NAME");
194        }
195
196        /** {@inheritDoc} */
197        @Override
198        public LocalizableMessage visitIPAddressMask(IPAddressMaskPropertyDefinition d, Void p) {
199            return LocalizableMessage.raw("IP_ADDRESS_MASK");
200        }
201
202        /** {@inheritDoc} */
203        @Override
204        public LocalizableMessage visitSize(SizePropertyDefinition d, Void p) {
205            LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
206
207            if (isDetailed && d.getLowerLimit() > 0) {
208                SizeUnit unit = SizeUnit.getBestFitUnitExact(d.getLowerLimit());
209                builder.append(numberFormat.format(unit.fromBytes(d.getLowerLimit())));
210                builder.append(' ');
211                builder.append(unit.getShortName());
212                builder.append(" <= ");
213            }
214
215            builder.append("SIZE");
216
217            if (isDetailed) {
218                if (d.getUpperLimit() != null) {
219                    long upperLimit = d.getUpperLimit();
220                    SizeUnit unit = SizeUnit.getBestFitUnitExact(upperLimit);
221
222                    // Quite often an upper limit is some power of 2 minus 1. In
223                    // those
224                    // cases lets use a "less than" relation rather than a "less
225                    // than
226                    // or equal to" relation. This will result in a much more
227                    // readable
228                    // quantity.
229                    if (unit == SizeUnit.BYTES && upperLimit < Long.MAX_VALUE) {
230                        unit = SizeUnit.getBestFitUnitExact(upperLimit + 1);
231                        if (unit != SizeUnit.BYTES) {
232                            upperLimit += 1;
233                            builder.append(" < ");
234                        } else {
235                            builder.append(" <= ");
236                        }
237                    } else {
238                        builder.append(" <= ");
239                    }
240
241                    builder.append(numberFormat.format(unit.fromBytes(upperLimit)));
242                    builder.append(' ');
243                    builder.append(unit.getShortName());
244                }
245
246                if (d.isAllowUnlimited()) {
247                    builder.append(" | unlimited");
248                }
249            }
250
251            return builder.toMessage();
252        }
253
254        /** {@inheritDoc} */
255        @Override
256        public LocalizableMessage visitString(StringPropertyDefinition d, Void p) {
257            if (d.getPattern() != null) {
258                if (isDetailed) {
259                    LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
260                    builder.append(d.getPatternUsage());
261                    builder.append(" - ");
262                    builder.append(d.getPatternSynopsis());
263                    return builder.toMessage();
264                } else {
265                    return LocalizableMessage.raw(d.getPatternUsage());
266                }
267            } else {
268                return LocalizableMessage.raw("STRING");
269            }
270        }
271
272        /** {@inheritDoc} */
273        @Override
274        public <T> LocalizableMessage visitUnknown(PropertyDefinition<T> d, Void p) {
275            return LocalizableMessage.raw("?");
276        }
277    }
278
279    /** Underlying implementation. */
280    private final MyPropertyDefinitionVisitor pimpl;
281
282    /**
283     * Creates a new property usage builder.
284     *
285     * @param isDetailed
286     *            Indicates whether or not the generated usage should contain
287     *            detailed information such as constraints.
288     */
289    public PropertyDefinitionUsageBuilder(boolean isDetailed) {
290        this.pimpl = new MyPropertyDefinitionVisitor(isDetailed);
291    }
292
293    /**
294     * Generates the usage information for the provided property definition.
295     *
296     * @param pd
297     *            The property definitions.
298     * @return Returns the usage information for the provided property
299     *         definition.
300     */
301    public LocalizableMessage getUsage(PropertyDefinition<?> pd) {
302        return pd.accept(pimpl, null);
303    }
304}