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 * <p>Allows Options to be created from a single String. 027 * The pattern contains various single character flags and via 028 * an optional punctuation character, their expected type. 029 * </p> 030 * 031 * <table border="1"> 032 * <caption>Overview of PatternOptionBuilder patterns</caption> 033 * <tr><td>a</td><td>-a flag</td></tr> 034 * <tr><td>b@</td><td>-b [classname]</td></tr> 035 * <tr><td>c></td><td>-c [filename]</td></tr> 036 * <tr><td>d+</td><td>-d [classname] (creates object via empty constructor)</td></tr> 037 * <tr><td>e%</td><td>-e [number] (creates Double/Long instance depending on existing of a '.')</td></tr> 038 * <tr><td>f/</td><td>-f [url]</td></tr> 039 * <tr><td>g:</td><td>-g [string]</td></tr> 040 * </table> 041 * 042 * <p> 043 * For example, the following allows command line flags of '-v -p string-value -f /dir/file'. 044 * The exclamation mark precede a mandatory option. 045 * </p> 046 * 047 * <pre> 048 * Options options = PatternOptionBuilder.parsePattern("vp:!f/"); 049 * </pre> 050 * 051 * <p> 052 * TODO: These need to break out to OptionType and also to be pluggable. 053 * </p> 054 * 055 * @version $Id: PatternOptionBuilder.java 1677406 2015-05-03 14:27:31Z britter $ 056 */ 057public class PatternOptionBuilder 058{ 059 /** String class */ 060 public static final Class<String> STRING_VALUE = String.class; 061 062 /** Object class */ 063 public static final Class<Object> OBJECT_VALUE = Object.class; 064 065 /** Number class */ 066 public static final Class<Number> NUMBER_VALUE = Number.class; 067 068 /** Date class */ 069 public static final Class<Date> DATE_VALUE = Date.class; 070 071 /** Class class */ 072 public static final Class<?> CLASS_VALUE = Class.class; 073 074 /// can we do this one?? 075 // is meant to check that the file exists, else it errors. 076 // ie) it's for reading not writing. 077 078 /** FileInputStream class */ 079 public static final Class<FileInputStream> EXISTING_FILE_VALUE = FileInputStream.class; 080 081 /** File class */ 082 public static final Class<File> FILE_VALUE = File.class; 083 084 /** File array class */ 085 public static final Class<File[]> FILES_VALUE = File[].class; 086 087 /** URL class */ 088 public static final Class<URL> URL_VALUE = URL.class; 089 090 /** 091 * Retrieve the class that <code>ch</code> represents. 092 * 093 * @param ch the specified character 094 * @return The class that <code>ch</code> represents 095 */ 096 public static Object getValueClass(char ch) 097 { 098 switch (ch) 099 { 100 case '@': 101 return PatternOptionBuilder.OBJECT_VALUE; 102 case ':': 103 return PatternOptionBuilder.STRING_VALUE; 104 case '%': 105 return PatternOptionBuilder.NUMBER_VALUE; 106 case '+': 107 return PatternOptionBuilder.CLASS_VALUE; 108 case '#': 109 return PatternOptionBuilder.DATE_VALUE; 110 case '<': 111 return PatternOptionBuilder.EXISTING_FILE_VALUE; 112 case '>': 113 return PatternOptionBuilder.FILE_VALUE; 114 case '*': 115 return PatternOptionBuilder.FILES_VALUE; 116 case '/': 117 return PatternOptionBuilder.URL_VALUE; 118 } 119 120 return null; 121 } 122 123 /** 124 * Returns whether <code>ch</code> is a value code, i.e. 125 * whether it represents a class in a pattern. 126 * 127 * @param ch the specified character 128 * @return true if <code>ch</code> is a value code, otherwise false. 129 */ 130 public static boolean isValueCode(char ch) 131 { 132 return ch == '@' 133 || ch == ':' 134 || ch == '%' 135 || ch == '+' 136 || ch == '#' 137 || ch == '<' 138 || ch == '>' 139 || ch == '*' 140 || ch == '/' 141 || ch == '!'; 142 } 143 144 /** 145 * Returns the {@link Options} instance represented by <code>pattern</code>. 146 * 147 * @param pattern the pattern string 148 * @return The {@link Options} instance 149 */ 150 public static Options parsePattern(String pattern) 151 { 152 char opt = ' '; 153 boolean required = false; 154 Class<?> type = null; 155 156 Options options = new Options(); 157 158 for (int i = 0; i < pattern.length(); i++) 159 { 160 char ch = pattern.charAt(i); 161 162 // a value code comes after an option and specifies 163 // details about it 164 if (!isValueCode(ch)) 165 { 166 if (opt != ' ') 167 { 168 final Option option = Option.builder(String.valueOf(opt)) 169 .hasArg(type != null) 170 .required(required) 171 .type(type) 172 .build(); 173 174 // we have a previous one to deal with 175 options.addOption(option); 176 required = false; 177 type = null; 178 opt = ' '; 179 } 180 181 opt = ch; 182 } 183 else if (ch == '!') 184 { 185 required = true; 186 } 187 else 188 { 189 type = (Class<?>) getValueClass(ch); 190 } 191 } 192 193 if (opt != ' ') 194 { 195 final Option option = Option.builder(String.valueOf(opt)) 196 .hasArg(type != null) 197 .required(required) 198 .type(type) 199 .build(); 200 201 // we have a final one to deal with 202 options.addOption(option); 203 } 204 205 return options; 206 } 207}