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