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}