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}