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