1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.geronimo.cli; 18 19 import java.io.BufferedReader; 20 import java.io.IOException; 21 import java.io.OutputStream; 22 import java.io.PrintWriter; 23 import java.io.StringReader; 24 import java.util.ArrayList; 25 import java.util.Collection; 26 import java.util.Collections; 27 import java.util.Comparator; 28 import java.util.Iterator; 29 import java.util.List; 30 31 import org.apache.commons.cli.Option; 32 import org.apache.commons.cli.OptionGroup; 33 import org.apache.commons.cli.Options; 34 35 /** 36 * This code is borrowed from commons-cli <code>org.apache.commons.cli.HelpFormatter</code> class. Its authors are 37 * Slawek Zachcial and John Keyes (john at integralsource.com). This class has been slightly updated to meet specific 38 * requirements. 39 * 40 * @version $Rev: 476049 $ $Date: 2006-11-17 15:35:17 +1100 (Fri, 17 Nov 2006) $ 41 */ 42 public class PrintHelper { 43 44 public static String reformat(String source, int indent, int width) { 45 int endCol = width; 46 if (endCol == 0) { 47 endCol = DEFAULT_WIDTH; 48 } 49 if(endCol-indent < 10) { 50 throw new IllegalArgumentException("Need at least 10 spaces for " + 51 "printing, but indent=" + indent + " and endCol=" + endCol); 52 } 53 StringBuffer buf = new StringBuffer((int)(source.length()*1.1)); 54 String prefix = indent == 0 ? "" : buildIndent(indent); 55 try { 56 BufferedReader in = new BufferedReader(new StringReader(source)); 57 String line; 58 int pos; 59 while((line = in.readLine()) != null) { 60 if(buf.length() > 0) { 61 buf.append('\n'); 62 } 63 while(line.length() > 0) { 64 line = prefix + line; 65 if(line.length() > endCol) { 66 pos = line.lastIndexOf(' ', endCol); 67 if(pos < indent) { 68 pos = line.indexOf(' ', endCol); 69 if(pos < indent) { 70 pos = line.length(); 71 } 72 } 73 buf.append(line.substring(0, pos)).append('\n'); 74 if(pos < line.length()-1) { 75 line = line.substring(pos+1); 76 } else { 77 break; 78 } 79 } else { 80 buf.append(line).append("\n"); 81 break; 82 } 83 } 84 } 85 } catch (IOException e) { 86 throw new AssertionError("This should be impossible"); 87 } 88 return buf.toString(); 89 } 90 91 private static String buildIndent(int indent) { 92 StringBuffer buf = new StringBuffer(indent); 93 for(int i=0; i<indent; i++) { 94 buf.append(' '); 95 } 96 return buf.toString(); 97 } 98 99 public static final int DEFAULT_WIDTH = 76; 100 public static final int DEFAULT_LEFT_PAD = 1; 101 public static final int DEFAULT_DESC_PAD = 3; 102 public static final String DEFAULT_SYNTAX_PREFIX = "usage: "; 103 public static final String DEFAULT_OPT_PREFIX = "-"; 104 public static final String DEFAULT_LONG_OPT_PREFIX = "--"; 105 public static final String DEFAULT_ARG_NAME = "arg"; 106 107 private final OutputStream outputStream; 108 public int defaultWidth; 109 public int defaultLeftPad; 110 public int defaultDescPad; 111 public String defaultSyntaxPrefix; 112 public String defaultNewLine; 113 public String defaultOptPrefix; 114 public String defaultLongOptPrefix; 115 public String defaultArgName; 116 117 public PrintHelper(OutputStream outputStream) { 118 if (null == outputStream) { 119 throw new IllegalArgumentException("outputStream is required"); 120 } 121 this.outputStream = outputStream; 122 123 defaultWidth = DEFAULT_WIDTH; 124 defaultLeftPad = DEFAULT_LEFT_PAD; 125 defaultDescPad = DEFAULT_DESC_PAD; 126 defaultSyntaxPrefix = DEFAULT_SYNTAX_PREFIX; 127 defaultNewLine = System.getProperty("line.separator"); 128 defaultOptPrefix = DEFAULT_OPT_PREFIX; 129 defaultLongOptPrefix = DEFAULT_LONG_OPT_PREFIX; 130 defaultArgName = DEFAULT_ARG_NAME; 131 } 132 133 public void printHelp(String cmdLineSyntax, String header, Options options, String footer, boolean autoUsage) { 134 printHelp(defaultWidth, cmdLineSyntax, header, options, footer, autoUsage); 135 } 136 137 public void printHelp(int width, 138 String cmdLineSyntax, 139 String header, 140 Options options, 141 String footer, 142 boolean autoUsage) { 143 PrintWriter pw = new PrintWriter(outputStream); 144 printHelp(pw, width, cmdLineSyntax, header, options, defaultLeftPad, defaultDescPad, footer, autoUsage); 145 pw.flush(); 146 } 147 148 public void printHelp(PrintWriter pw, 149 int width, 150 String cmdLineSyntax, 151 String header, 152 Options options, 153 int leftPad, 154 int descPad, 155 String footer, 156 boolean autoUsage) throws IllegalArgumentException { 157 if (cmdLineSyntax == null || cmdLineSyntax.length() == 0) { 158 throw new IllegalArgumentException("cmdLineSyntax not provided"); 159 } 160 161 if (autoUsage) { 162 printUsage(pw, width, cmdLineSyntax, options); 163 } else { 164 printUsage(pw, width, cmdLineSyntax); 165 } 166 167 if (header != null && header.trim().length() > 0) { 168 printWrapped(pw, width, header); 169 } 170 printOptions(pw, width, options, leftPad, descPad); 171 if (footer != null && footer.trim().length() > 0) { 172 printWrapped(pw, width, footer); 173 } 174 } 175 176 public void printUsage(PrintWriter pw, int width, String app, Options options) { 177 // create a list for processed option groups 178 ArrayList list = new ArrayList(); 179 180 StringBuffer optionsBuff = new StringBuffer(); 181 182 // temp variable 183 Option option; 184 185 // iterate over the options 186 for (Iterator i = options.getOptions().iterator(); i.hasNext();) { 187 // get the next Option 188 option = (Option) i.next(); 189 190 // check if the option is part of an OptionGroup 191 OptionGroup group = options.getOptionGroup(option); 192 193 // if the option is part of a group and the group has not already 194 // been processed 195 if (group != null && !list.contains(group)) { 196 197 // add the group to the processed list 198 list.add(group); 199 200 // get the names of the options from the OptionGroup 201 Collection names = group.getNames(); 202 203 optionsBuff.append("["); 204 205 // for each option in the OptionGroup 206 for (Iterator iter = names.iterator(); iter.hasNext();) { 207 optionsBuff.append(iter.next()); 208 if (iter.hasNext()) { 209 optionsBuff.append("|"); 210 } 211 } 212 optionsBuff.append("] "); 213 } else if (group == null) { 214 // if the Option is not part of an OptionGroup 215 // if the Option is not a required option 216 if (!option.isRequired()) { 217 optionsBuff.append("["); 218 } 219 220 if (!" ".equals(option.getOpt())) { 221 optionsBuff.append("-").append(option.getOpt()); 222 } else { 223 optionsBuff.append("--").append(option.getLongOpt()); 224 } 225 226 if (option.hasArg()) { 227 optionsBuff.append(" "); 228 } 229 230 // if the Option has a value 231 if (option.hasArg()) { 232 optionsBuff.append(option.getArgName()); 233 } 234 235 // if the Option is not a required option 236 if (!option.isRequired()) { 237 optionsBuff.append("]"); 238 } 239 optionsBuff.append(" "); 240 } 241 } 242 243 app = app.replace("$options", optionsBuff.toString()); 244 245 // call printWrapped 246 printWrapped(pw, width, app.indexOf(' ') + 1, app); 247 } 248 249 public void printUsage(PrintWriter pw, int width, String cmdLineSyntax) { 250 int argPos = cmdLineSyntax.indexOf(' ') + 1; 251 printWrapped(pw, width, defaultSyntaxPrefix.length() + argPos, defaultSyntaxPrefix + cmdLineSyntax); 252 } 253 254 public void printOptions(PrintWriter pw, int width, Options options, int leftPad, int descPad) { 255 StringBuffer sb = new StringBuffer(); 256 renderOptions(sb, width, options, leftPad, descPad, true); 257 pw.println(sb.toString()); 258 } 259 260 public void printOptions(PrintWriter pw, Options options) { 261 StringBuffer sb = new StringBuffer(); 262 renderOptions(sb, defaultWidth, options, defaultLeftPad, defaultDescPad, true); 263 pw.println(sb.toString()); 264 } 265 266 public void printOptionsNoDesc(PrintWriter pw, Options options) { 267 StringBuffer sb = new StringBuffer(); 268 renderOptions(sb, defaultWidth, options, defaultLeftPad, defaultDescPad, false); 269 pw.println(sb.toString()); 270 } 271 272 public void printWrapped(PrintWriter pw, int width, String text) { 273 printWrapped(pw, width, 0, text); 274 } 275 276 public void printWrapped(PrintWriter pw, int width, int nextLineTabStop, String text) { 277 StringBuffer sb = new StringBuffer(text.length()); 278 renderWrappedText(sb, width, nextLineTabStop, text); 279 pw.println(sb.toString()); 280 } 281 282 protected StringBuffer renderOptions(StringBuffer sb, int width, Options options, int leftPad, int descPad, boolean displayDesc) { 283 final String lpad = createPadding(leftPad); 284 final String dpad = createPadding(descPad); 285 286 //first create list containing only <lpad>-a,--aaa where -a is opt and --aaa is 287 //long opt; in parallel look for the longest opt string 288 //this list will be then used to sort options ascending 289 int max = 0; 290 StringBuffer optBuf; 291 List prefixList = new ArrayList(); 292 Option option; 293 List optList = new ArrayList(options.getOptions()); 294 Collections.sort(optList, new StringBufferComparator()); 295 for (Iterator i = optList.iterator(); i.hasNext();) { 296 option = (Option) i.next(); 297 optBuf = new StringBuffer(8); 298 299 if (option.getOpt().equals(" ")) { 300 optBuf.append(lpad).append(" " + defaultLongOptPrefix).append(option.getLongOpt()); 301 } else { 302 optBuf.append(lpad).append(defaultOptPrefix).append(option.getOpt()); 303 if (option.hasLongOpt()) { 304 optBuf.append(',').append(defaultLongOptPrefix).append(option.getLongOpt()); 305 } 306 307 } 308 309 if (option.hasArg()) { 310 if (option.hasArgName()) { 311 optBuf.append(" <").append(option.getArgName()).append('>'); 312 } else { 313 optBuf.append(' '); 314 } 315 } 316 317 prefixList.add(optBuf); 318 max = optBuf.length() > max ? optBuf.length() : max; 319 } 320 int x = 0; 321 for (Iterator i = optList.iterator(); i.hasNext();) { 322 option = (Option) i.next(); 323 optBuf = new StringBuffer(prefixList.get(x++).toString()); 324 325 if (optBuf.length() < max) { 326 optBuf.append(createPadding(max - optBuf.length())); 327 } 328 optBuf.append(dpad); 329 330 if (displayDesc) { 331 optBuf.append(option.getDescription()); 332 } 333 int nextLineTabStop = max + descPad; 334 renderWrappedText(sb, width, nextLineTabStop, optBuf.toString()); 335 if (i.hasNext()) { 336 sb.append(defaultNewLine); 337 if (displayDesc) { 338 sb.append(defaultNewLine); 339 } 340 } 341 } 342 343 return sb; 344 } 345 346 protected StringBuffer renderWrappedText(StringBuffer sb, int width, int nextLineTabStop, String text) { 347 int pos = findWrapPos(text, width, 0); 348 if (pos == -1) { 349 sb.append(rtrim(text)); 350 return sb; 351 } else { 352 sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine); 353 } 354 355 //all following lines must be padded with nextLineTabStop space characters 356 final String padding = createPadding(nextLineTabStop); 357 358 while (true) { 359 text = padding + text.substring(pos).trim(); 360 pos = findWrapPos(text, width, 0); 361 if (pos == -1) { 362 sb.append(text); 363 return sb; 364 } 365 366 sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine); 367 } 368 369 } 370 371 protected int findWrapPos(String text, int width, int startPos) { 372 int pos = -1; 373 // the line ends before the max wrap pos or a new line char found 374 if (((pos = text.indexOf('\n', startPos)) != -1 && pos <= width) 375 || ((pos = text.indexOf('\t', startPos)) != -1 && pos <= width)) { 376 return pos; 377 } else if ((startPos + width) >= text.length()) { 378 return -1; 379 } 380 381 //look for the last whitespace character before startPos+width 382 pos = startPos + width; 383 char c; 384 while (pos >= startPos && (c = text.charAt(pos)) != ' ' && c != '\n' && c != '\r') { 385 --pos; 386 } 387 //if we found it - just return 388 if (pos > startPos) { 389 return pos; 390 } else { 391 //must look for the first whitespace chearacter after startPos + width 392 pos = startPos + width; 393 while (pos <= text.length() && (c = text.charAt(pos)) != ' ' && c != '\n' && c != '\r') { 394 ++pos; 395 } 396 return pos == text.length() ? -1 : pos; 397 } 398 } 399 400 protected String createPadding(int len) { 401 StringBuffer sb = new StringBuffer(len); 402 for (int i = 0; i < len; ++i) { 403 sb.append(' '); 404 } 405 return sb.toString(); 406 } 407 408 protected String rtrim(String s) { 409 if (s == null || s.length() == 0) { 410 return s; 411 } 412 413 int pos = s.length(); 414 while (pos >= 0 && Character.isWhitespace(s.charAt(pos - 1))) { 415 --pos; 416 } 417 return s.substring(0, pos); 418 } 419 420 private static class StringBufferComparator implements Comparator { 421 422 public int compare(Object o1, Object o2) { 423 String str1 = stripPrefix(o1.toString()); 424 String str2 = stripPrefix(o2.toString()); 425 return (str1.compareTo(str2)); 426 } 427 428 private String stripPrefix(String strOption) { 429 // Strip any leading '-' characters 430 int iStartIndex = strOption.lastIndexOf('-'); 431 if (iStartIndex == -1) { 432 iStartIndex = 0; 433 } 434 return strOption.substring(iStartIndex); 435 436 } 437 } 438 439 }