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