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