001/* 002 Licensed to the Apache Software Foundation (ASF) under one or more 003 contributor license agreements. See the NOTICE file distributed with 004 this work for additional information regarding copyright ownership. 005 The ASF licenses this file to You under the Apache License, Version 2.0 006 (the "License"); you may not use this file except in compliance with 007 the License. You may obtain a copy of the License at 008 009 http://www.apache.org/licenses/LICENSE-2.0 010 011 Unless required by applicable law or agreed to in writing, software 012 distributed under the License is distributed on an "AS IS" BASIS, 013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 See the License for the specific language governing permissions and 015 limitations under the License. 016 */ 017 018package org.apache.commons.cli; 019 020import java.io.File; 021import java.io.FileInputStream; 022import java.net.URL; 023import java.util.Date; 024 025/** 026 * Allows Options to be created from a single String. The pattern contains various single character flags and via an 027 * optional punctuation character, their expected type. 028 * 029 * <table border="1"> 030 * <caption>Overview of PatternOptionBuilder patterns</caption> 031 * <tr> 032 * <td>a</td> 033 * <td>-a flag</td> 034 * </tr> 035 * <tr> 036 * <td>b@</td> 037 * <td>-b [classname]</td> 038 * </tr> 039 * <tr> 040 * <td>c></td> 041 * <td>-c [filename]</td> 042 * </tr> 043 * <tr> 044 * <td>d+</td> 045 * <td>-d [classname] (creates object via empty constructor)</td> 046 * </tr> 047 * <tr> 048 * <td>e%</td> 049 * <td>-e [number] (creates Double/Long instance depending on existing of a '.')</td> 050 * </tr> 051 * <tr> 052 * <td>f/</td> 053 * <td>-f [url]</td> 054 * </tr> 055 * <tr> 056 * <td>g:</td> 057 * <td>-g [string]</td> 058 * </tr> 059 * </table> 060 * 061 * <p> 062 * For example, the following allows command line flags of '-v -p string-value -f /dir/file'. The exclamation mark 063 * precede a mandatory option. 064 * </p> 065 * 066 * <pre> 067 * Options options = PatternOptionBuilder.parsePattern("vp:!f/"); 068 * </pre> 069 * 070 * <p> 071 * TODO These need to break out to OptionType and also to be pluggable. 072 * </p> 073 */ 074public class PatternOptionBuilder { 075 /** String class */ 076 public static final Class<String> STRING_VALUE = String.class; 077 078 /** Object class */ 079 public static final Class<Object> OBJECT_VALUE = Object.class; 080 081 /** Number class */ 082 public static final Class<Number> NUMBER_VALUE = Number.class; 083 084 /** Date class */ 085 public static final Class<Date> DATE_VALUE = Date.class; 086 087 /** Class class */ 088 public static final Class<?> CLASS_VALUE = Class.class; 089 090 /// can we do this one?? 091 // is meant to check that the file exists, else it errors. 092 // ie) it's for reading not writing. 093 094 /** FileInputStream class */ 095 public static final Class<FileInputStream> EXISTING_FILE_VALUE = FileInputStream.class; 096 097 /** File class */ 098 public static final Class<File> FILE_VALUE = File.class; 099 100 /** File array class */ 101 public static final Class<File[]> FILES_VALUE = File[].class; 102 103 /** URL class */ 104 public static final Class<URL> URL_VALUE = URL.class; 105 106 /** 107 * Retrieve the class that {@code ch} represents. 108 * 109 * @param ch the specified character 110 * @return The class that {@code ch} represents 111 */ 112 public static Object getValueClass(final char ch) { 113 switch (ch) { 114 case '@': 115 return PatternOptionBuilder.OBJECT_VALUE; 116 case ':': 117 return PatternOptionBuilder.STRING_VALUE; 118 case '%': 119 return PatternOptionBuilder.NUMBER_VALUE; 120 case '+': 121 return PatternOptionBuilder.CLASS_VALUE; 122 case '#': 123 return PatternOptionBuilder.DATE_VALUE; 124 case '<': 125 return PatternOptionBuilder.EXISTING_FILE_VALUE; 126 case '>': 127 return PatternOptionBuilder.FILE_VALUE; 128 case '*': 129 return PatternOptionBuilder.FILES_VALUE; 130 case '/': 131 return PatternOptionBuilder.URL_VALUE; 132 } 133 134 return null; 135 } 136 137 /** 138 * Returns whether {@code ch} is a value code, i.e. whether it represents a class in a pattern. 139 * 140 * @param ch the specified character 141 * @return true if {@code ch} is a value code, otherwise false. 142 */ 143 public static boolean isValueCode(final char ch) { 144 return ch == '@' || ch == ':' || ch == '%' || ch == '+' || ch == '#' || ch == '<' || ch == '>' || ch == '*' || ch == '/' || ch == '!'; 145 } 146 147 /** 148 * Returns the {@link Options} instance represented by {@code pattern}. 149 * 150 * @param pattern the pattern string 151 * @return The {@link Options} instance 152 */ 153 public static Options parsePattern(final String pattern) { 154 char opt = ' '; 155 boolean required = false; 156 Class<?> type = null; 157 158 final Options options = new Options(); 159 160 for (int i = 0; i < pattern.length(); i++) { 161 final char ch = pattern.charAt(i); 162 163 // a value code comes after an option and specifies 164 // details about it 165 if (!isValueCode(ch)) { 166 if (opt != ' ') { 167 final Option option = Option.builder(String.valueOf(opt)).hasArg(type != null).required(required).type(type).build(); 168 169 // we have a previous one to deal with 170 options.addOption(option); 171 required = false; 172 type = null; 173 opt = ' '; 174 } 175 176 opt = ch; 177 } else if (ch == '!') { 178 required = true; 179 } else { 180 type = (Class<?>) getValueClass(ch); 181 } 182 } 183 184 if (opt != ' ') { 185 final Option option = Option.builder(String.valueOf(opt)).hasArg(type != null).required(required).type(type).build(); 186 187 // we have a final one to deal with 188 options.addOption(option); 189 } 190 191 return options; 192 } 193}