/* Copyright 1996-2008 Ariba, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. $Id: //ariba/platform/util/core/ariba/util/core/ReferenceSyntaxParser.java#5 $ */ package ariba.util.core; import ariba.util.log.Log; /** This class provides methods to parse Strings that may contain remote and/or local references. The syntax is as follows: $<remoteReference^alias>$, where 'remoteReference' specifies a String that locates the remote reference. This class does not interpret this String. It merely parse a given input string for this substring and hands it back to its caller for processing. It is illegal for this reference string to contain the '^' scope delimiter or the substring "$<" or ">$". 'alias' specifies a key whose value can be obtained from the resource specified 'remoteReference'. Again, this class does not interpret this string. It is illegal for this reference string to contain the '^' scope delimiter or the substring "$<" or ">$". The carat (^) scope delimiter is optional. If absent, this means there is no remote reference, and the substring enclosed within the $<...>$ delimiters is a local alias. Again, it is up to the caller to interpret the local alias. Note that nested references are not supported. @aribaapi ariba */ public class ReferenceSyntaxParser { /** Indicates the beginning of the reference token. @aribaapi ariba */ public static final String BeginDelimiter = "$<"; /** Indicates the end of the reference token. @aribaapi ariba */ public static final String EndDelimiter = ">$"; /** Separates the remote reference from the alias key Would like to use colon (':') but that is too popular Don't want to confuse those tokenizers that tokenize with the colon as delimiters. @aribaapi ariba */ public static final char RemoteRefDelimiter = '^'; /** Separates the remote reference from the alias key, represented as a String Would like to use colon (':') but that is too popular Don't want to confuse those tokenizers that tokenize with the colon as delimiters. @aribaapi ariba */ public static final String RemoteRefDelimiterString = "^"; /** Empty Constructor to instantiate this class @aribaapi ariba */ public ReferenceSyntaxParser () { } /** Returns the interval of the specified string that denotes a substring that begins and ends with the specified delimiters that this class understands. @param str the input string that may contain 0 or more subsequences that begin and end with the specified delimiters that this class understands. If it is null or blank or empty, just return null. @return an int array (i, j) of 2 integers. The first element points to the position in the given str where the beginning delmiter starts, in this case, str[i] == '$', str[i+1] == '<'. The second elements points to the position one past the first occurrence of the end delimiter, in this case, str[j-2] = '>', str[j-1] = '$'. Clearly, i > j. Returns <b>null</b> if no such interval can be found. Examples: getNextReference("noReference") return null getNextReference(">$xx$<alias>$yy") return (4, 13) getNextReference("xx$<alias>$yy") return (2, 11) getNextReference("x$<missingEndDelimiter") returns null getNextReference("xx$<aa$<bbb>$yy") return (2, 13) getNextReference("$<>$") return (0,4), note that in this case it is up to the caller to handler the empty string contained by the delimiters. @aribaapi ariba */ public int[] getNextReference (String str) { Log.paramReference.debug("getNextReference for input String: %s", str); if (StringUtil.nullOrEmptyOrBlankString(str)) { return null; } int beginIndex = str.indexOf(BeginDelimiter); if (beginIndex == -1) { return null; } /* skip the length of the delimiter */ int searchIndex = beginIndex + BeginDelimiter.length(); int endIndex = str.indexOf(EndDelimiter, searchIndex); if (endIndex == -1) { return null; } /* cook up the interval to return. Note that the we add back the length of the end delimiter so that the substring specified by the interval contains the delimiters. */ int[] interval = new int[2]; interval[0] = beginIndex; interval[1] = endIndex + EndDelimiter.length(); Log.paramReference.debug("getNextReference returning interval (%s, %s)", interval[0], interval[1]); return interval; } /** Return a array of String (of at most 2 elements) that specifies the (optional) remote reference, and the alias key. @param strWithDelimiters is a string that begins with BeginDelimiter and ends with EndDelimiter. @return string array of 2 elements (all stripped of any delimiters): Element 0: specifies the id to locate the reference resource. Note that this can be null, which means that the next element (see below) refers to a local alias. Element 1: specifies the alias to be resolved by the reference specified by element 0. Note that the elements can contain empty or blank Strings. It is up to the caller to decide if the contents of the String elements make sense. @aribaapi ariba */ public String[] getReferenceAndAlias (String strWithDelimiters) { Log.paramReference.debug("getReferenceAndAlias input String: %s", strWithDelimiters); Assert.that(strWithDelimiters != null && strWithDelimiters.startsWith(BeginDelimiter) && strWithDelimiters.endsWith(EndDelimiter), "invalid input string '%s'", strWithDelimiters); String[] refAndAlias = new String[2]; String strippedStr = strWithDelimiters.substring( BeginDelimiter.length(), strWithDelimiters.length() - BeginDelimiter.length()); // get the reference id int index = strippedStr.indexOf(RemoteRefDelimiter); if (index == -1) { refAndAlias[0] = null; refAndAlias[1] = strippedStr; } else { refAndAlias[0] = strippedStr.substring(0, index); refAndAlias[1] = strippedStr.substring(index+1); // make sure there is only one occurrence of RemoteRefDelimiter ('^') index = strippedStr.indexOf(RemoteRefDelimiter, index+1); Assert.that(index == -1, "input String '%s' cannot contain " + "multiple reference delimiters ('%s')", strippedStr, RemoteRefDelimiterString); } Log.paramReference.debug("getReferenceAndAlias returning array (%s, %s)", refAndAlias[0], refAndAlias[1]); return refAndAlias; } /** Creates a string with the correct syntax understood by this class to specify a reference to a given reference/alias pair. This created String can later be passed back as arguments to the getNextReference or getReferenceAndAlias methods for processing by possibly another component. @param reference the reference. Cannot contain the carat ('^') character, or the ">$" or "$>" substrings. A value of null designates local reference. @param alias the alias, cannot be null. Cannot contain the carat ('^') character, or the ">$" or "$>" substrings. @return a reference String of the right syntax understood by this class. @aribaapi ariba */ public static String makeReferenceString (String reference, String alias) { if (reference != null) { checkReferenceSyntax(reference); } checkAliasSyntax(alias); if (reference == null) { return StringUtil.strcat(BeginDelimiter, alias, EndDelimiter); } return StringUtil.strcat(BeginDelimiter, reference, RemoteRefDelimiterString, alias, EndDelimiter); } /** Checks the syntax of the given alias String for correct syntax. Assert if the syntax is wrong. @param the given alias String, must not be null. */ private static void checkAliasSyntax (String alias) { checkSyntax(alias); } /** Checks the syntax of the given reference for correct syntax. Assert if the syntax is wrong. @param the given reference String, must not be null. */ private static void checkReferenceSyntax (String reference) { checkSyntax(reference); } /** check the syntax of the given String for correct syntax understood by this class. See the description for this class for the correct syntax. Assert if the syntax is incorrect. @param str the given input String whose syntax is to be checked. */ private static void checkSyntax (String str) { Assert.that(str != null, "input string cannot be null"); Assert.that(str.indexOf(RemoteRefDelimiterString) == -1, "input string %s cannot contain '%s'", str, RemoteRefDelimiterString); Assert.that(!StringUtil.contains(str, BeginDelimiter), "input string '%s' cannot contain '%s'", str, BeginDelimiter); Assert.that(!StringUtil.contains(str, EndDelimiter), "input string '%s' cannot contain '%s'", str, EndDelimiter); } }