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}