/* ********************************************************************** /* * NOTE: This copyright does *not* cover user programs that use Hyperic * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004-2012], VMware, Inc. * This file is part of Hyperic. * * Hyperic is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.tools.ant.utils; import java.util.HashMap; import java.util.Map; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; /** * Commandline arguments parser supports the following formats:</b> * <p> * -D<<key>>=<<value> ...<br/> * -flag ...<br/> * --flag ...<br/> * </p> * <br/> * <h1><b>Example:</b></h1> * * <p> * -Dserver.database-url=jdbc:mysql://localhost:3306/hqdb -Dserver.database-driver=com.mysql.jdbc.Driver -Dserver.database=MySQL -Dserver.database-user=hqadmin -Dserver.database-password=hqadmin -Dserver.admin.username=hqadmin -Dserver.admin.password=hqadmin -Dexport -Dstaging.dir=/work/temp/hq-migration/export-workspace/staging-area -Dhqserver.install.path=/work/temp/hq-migration/servers/server-4.6.5.2.BUILD-SNAPSHOT-EE -Dimport -Dexport.archive.path=/work/temp/hq-migration/export-workspace/hq-migration-4.6.5.1.zip -Dhqserver.install.path=/work/temp/hq-migration/servers/server-5.0-EE -Dpath=\"C:\\Program Files\\dir1\\dir2\\file=.txt\" ; * </p> * <br/> * <p> * All arguments are added to the project's environment. * <p/> * */ public class ArgsParser extends Task{ private final String SPLIT_REGEX = "((?<=%1$2s)|(?=%1$2s))" ; private String cmdArgs ; private String interArgDelimiter ; private String keyValDelimiter ; private String argIndentifier ; /** * @param interArgDelimiter Example: -Dkey=value[interArgDel]-Dkey=value */ public final void setArgDel(final String interArgDelimiter) { this.interArgDelimiter = interArgDelimiter ; }//EOM /** * @param argIndentifier Argument prefix. for instance the '-D' in -Dkey=value */ public final void setArgIdentifier(final String argIndentifier) { this.argIndentifier = argIndentifier ; }//EOM /** * @param keyValDelimiter Example: -Dkey[keyValDel]value -Dkey[keyValDel]value */ public final void setKeyValDel(final String keyValDelimiter) { this.keyValDelimiter = keyValDelimiter ; }//EOM /** * @param cmdArgs arguments to parse */ public final void setCMDArgs(final String cmdArgs) { this.cmdArgs = cmdArgs ; }//EOM @Override public void execute() throws BuildException { this.parseArguments(this.cmdArgs) ; }//EOM private final void parseArguments(final String args) { final String delimiters = this.buildDelimitersString() ; final String splitRegex = String.format(SPLIT_REGEX, delimiters) ; this.log("CMD Args: " + args + ", delimiters split regex: " + splitRegex) ; final String[] arrSplitArgs = args.trim().split(splitRegex) ; //first element would be an empty element final Context context = new Context(1/*currIndex*/, this.argIndentifier, this.interArgDelimiter, this.keyValDelimiter) ; context.project = this.getProject() ; final int iLength = arrSplitArgs.length ; ArgPartParserType parser = ArgPartParserType.ArgIdentifier ; for(; context.currIndex < iLength && parser != null ;) { context.previousParser = parser ; parser = parser.parseArgument(arrSplitArgs, context); }//EO while there are more elements }//EOM private static enum ArgPartParserType { Key { @Override final ArgPartParserType parseArgument(final String[] arrArgParts, final Context context) { context.currentKey = arrArgParts[context.currIndex] ; if(context.currIndex+1 < arrArgParts.length) context.currIndex++ ; return Value ; }//EOM @Override final boolean shouldParse(final String argPart, final Context context) { return context.previousParser == ArgIdentifier ; }//EOM },//EO KeyVal Value { @Override final ArgPartParserType parseArgument(final String[] arrArgParts, final Context context) { //if there is no keyval delimiter, assign "true" to the value and return int iLength = arrArgParts.length ; String value = arrArgParts[context.currIndex] ; ArgPartParserType nextParser = null ; if(!this.shouldParse(value, context) || (context.currIndex+1 < iLength && ArgIdentifier.shouldParse(arrArgParts[context.currIndex+1], context))) { value = "true" ; nextParser = InterArg ; }else { context.currIndex++ ; String argPart = null ; value = "" ; for(; context.currIndex < iLength; context.currIndex++) { argPart = arrArgParts[context.currIndex] ; //if the arg part is not an inter arg del|| //arg part is an inter arg del but the one after it is not an arg identifier contact the arg part to the value if(InterArg.shouldParse(argPart, context) && (context.currIndex+1 < iLength && ArgIdentifier.shouldParse(arrArgParts[context.currIndex+1], context))){ nextParser = InterArg ; break ; }else { value = value + argPart ; }//EO else if arg type is not an inter arg delimiter }//EO while there are more arg parts }//EO else there was a keyval delimiter //context.project.log("Setting Property: " + context.currentKey + " with value: " + value + ", Current Index: " + context.currIndex, Project.MSG_DEBUG); context.project.setProperty(context.currentKey, value) ; context.currentKey = null ; context.currentValue = null ; return nextParser ; }//EOM @Override final boolean shouldParse(final String argPart, final Context context) { return context.keyValDelimiter.equals(argPart) ; }//EOM },//EO value InterArg { @Override final ArgPartParserType parseArgument(final String[] arrArgParts, final Context context) { final String argPart = arrArgParts[context.currIndex] ; context.currIndex++ ; context.project.log("Index: " + context.currIndex + " Argument Part: '" + argPart + "'", Project.MSG_DEBUG) ; return ArgIdentifier ; }//EOM @Override final boolean shouldParse(final String argPart, final Context context) { return context.interArgDelimiter.equals(argPart) ; }//EOM },//EO InterArg ArgIdentifier { @Override final ArgPartParserType parseArgument(final String[] arrArgParts, final Context context) { final String argPart = arrArgParts[context.currIndex] ; if(!this.shouldParse(argPart, context)) throw new BuildException("Argument part: '" + argPart + "' at index: " + context.currIndex + " is not a valid Argument Identifier") ; context.currIndex++ ; context.project.log("Index: " + context.currIndex + " Argument Part: '" + argPart + "'", Project.MSG_DEBUG) ; return Key ; }//EOM @Override final boolean shouldParse(final String argPart, final Context context) { return context.argIndentifier.equals(argPart) ; }//EOM };//EO ArgItentifier abstract ArgPartParserType parseArgument(final String[] arrArgParts, final Context context) ; abstract boolean shouldParse(final String argPart, final Context context) ; }//EO inner enum ArgPartParserType private final String buildDelimitersString() { final StringBuilder delimiterStringbuilder = new StringBuilder(), errorMsgBuidler = new StringBuilder() ; this.appendDelimiter(this.argIndentifier, "Argument Identifier", errorMsgBuidler, delimiterStringbuilder) ; this.appendDelimiter(this.interArgDelimiter, "Inter Argument Delimiter", errorMsgBuidler, delimiterStringbuilder) ; this.appendDelimiter(this.keyValDelimiter, "Key Value Delimiter", errorMsgBuidler, delimiterStringbuilder) ; if(errorMsgBuidler.length() > 0) throw new BuildException("ArgParserTask is misconfigured:" + errorMsgBuidler.toString()) ; else return delimiterStringbuilder.toString() ; }//EOM private final StringBuilder appendDelimiter(final String delimiter, final String delimiterName, final StringBuilder errorMsgBuilder, final StringBuilder delimiterStringBuilder) { if(delimiter == null || delimiter.isEmpty()) errorMsgBuilder.append("\n- ").append(delimiterName).append(" Must be defined") ; else { if(delimiterStringBuilder.length() > 0) delimiterStringBuilder.append("|") ; delimiterStringBuilder.append(delimiter) ; }//EO else if the delimiter was defined return errorMsgBuilder ; }//EOM private class Context extends HashMap<Object,Object> { private int currIndex ; private String argIndentifier ; private String interArgDelimiter ; private String keyValDelimiter ; private Project project ; private ArgPartParserType previousParser ; private String currentKey ; private String currentValue ; Context(final int currIndex, final String argIndentifier, final String interArgDelimiter, final String keyValDelimiter) { this.argIndentifier = argIndentifier ; this.interArgDelimiter = interArgDelimiter ; this.keyValDelimiter = keyValDelimiter ; this.currIndex = currIndex; }//EOM }//EO inner class Context ; //--------------------------------------------------------------------------------------------------------------- // DEBUG SECTION DEBUG SECTION DEBUG SECTION DEBUG SECTION DEBUG SECTION DEBUG SECTION DEBUG SECTION //---------------------------------------------------------------------------------------------------------------- public static void main(String[] args) throws Throwable { String cmdArgs = "-Dserver.database-url=jdbc:mysql://localhost:3306/hqdb -Dserver.database-driver=com.mysql.jdbc.Driver -Dserver.database=MySQL -Dserver.database-user=hqadmin -Dserver.database-password=hqadmin -Dserver.admin.username=hqadmin -Dserver.admin.password=hqadmin " + "-Dexport -Dstaging.dir=/work/temp/hq-migration/export-workspace/staging-area -Dhqserver.install.path=/work/temp/hq-migration/servers/server-4.6.5.2.BUILD-SNAPSHOT-EE " + "-Dimport -Dexport.archive.path=/work/temp/hq-migration/export-workspace/hq-migration-4.6.5.1.zip -Dhqserver.install.path=/work/temp/hq-migration/servers/server-5.0-EE " + "-Dpath=\"C:\\Program Files\\dir1\\dir2\\file=.txt\"" ; //cmdArgs = "-help -file file.txt -test1 -test2 file2 d -test3 " ; final Project project = new Project() { private final Map<String,String> properties = new HashMap<String,String>() ; @Override public void setProperty(String name, String value) { this.properties.put(name, value) ; }//EOM @Override public final String toString() { return this.properties.toString().replace(",", "\n") ; }//EOM }; final ArgsParser parser = new ArgsParser(); parser.setProject(project) ; parser.setArgDel(" ") ; parser.setArgIdentifier("-D") ; parser.setKeyValDel("=") ; parser.parseArguments(cmdArgs) ; System.out.println(project.toString()) ; }//EOM }//EOC