Author: jkaputin Date: Thu Jan 4 06:02:03 2007 New Revision: 492571 URL: http://svn.apache.org/viewvc?view=rev&rev=492571 Log: WODEN-86 Added behaviour to instantiate an object from a string, parse and validate the string and to derive a new location string based on the current state of the object. Work-in-progress with 'get' and 'substitute' methods declared but not yet implemented.
Modified: incubator/woden/trunk/java/src/org/apache/woden/wsdl20/extensions/http/HTTPLocation.java Modified: incubator/woden/trunk/java/src/org/apache/woden/wsdl20/extensions/http/HTTPLocation.java URL: http://svn.apache.org/viewvc/incubator/woden/trunk/java/src/org/apache/woden/wsdl20/extensions/http/HTTPLocation.java?view=diff&rev=492571&r1=492570&r2=492571 ============================================================================== --- incubator/woden/trunk/java/src/org/apache/woden/wsdl20/extensions/http/HTTPLocation.java (original) +++ incubator/woden/trunk/java/src/org/apache/woden/wsdl20/extensions/http/HTTPLocation.java Thu Jan 4 06:02:03 2007 @@ -1,5 +1,5 @@ /** - * Copyright 2006 Apache Software Foundation + * Copyright 2006,2007 Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,125 +15,124 @@ */ package org.apache.woden.wsdl20.extensions.http; -import java.net.URI; -import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Vector; import org.apache.woden.types.NCName; /** - * This class represents the value of the {http location} property from the - * HTTP extensions to the WSDL <code>BindingOperation</code> component. - * This property may be used as a template in which local names of elements + * This class represents the {http location} extension property of the + * <code>BindingOperation</code> component and the <code>whttp:location</code> extension + * attribute of the WSDL binding <operation> element, as defined by the + * WSDL 2.0 HTTP binding extensions. + * + * The HTTP location may be used as a template in which local names of elements * from the instance data of the message to be serialized in request IRI are * cited by enclosing the element name within curly braces (e.g. "temperature/{town}"). - * The curly brace-enclosed element name is substituted with the value of that + * A curly brace-enclosed element name is substituted with the value of that * element from the instance data when the request IRI is constructed. * <p> - * This class can verify that any such templated value is in a form that will - * allow it to be converted to a URI after substitution has taken place. - * It can return the original value of the property, as defined in the WSDL. - * It can also return a template value substituted with instance data. + * This class has a single constructor which takes a String argument representing + * the original value of the HTTP location, which may be templated with the curly + * brace syntax described above. An object of this class represents only that HTTP + * location string (i.e. the location template cannot be modified). The class does have + * methods to substitute element values for local names and to return the derived HTTP + * location after substitution has occurred. + * <p> + * This class performs syntax validation of the original HTTP location template, checking that + * any single left and right curly braces are used correctly as pairs enclosing a String that + * is of the correct format to represent an element local name (i.e. of type xs:NCName). + * It also handles the double curly brace syntax defined by this extension to represent + * literal single curly braces in the derived location value. For example, + * "abc{{def" becomes "abc{def" in the derived location and this literal left brace is + * not used for matching purposes with a right curly brace. + * <p> + * The specification of the WSDL 2.0 HTTP Binding extensions does not explicitly state how to + * interpret extraneous, unmatched curly braces, so this class adopts the following strategy: * <p> - * TODO are further methods required to access templated local names and/or perform value substitution? - * <br>For example: + * If multiple left braces precede a right brace, pair the right most one with the right brace + * and treat the others as unmatched left braces. * <pre> - * String[] getLocalNames() - * String getLocalName(int position) - * void substituteValue(int position, String value) - * int countOccurrences(String localName) - * void substituteValue(String localName, int occurrence, String value) - * void substituteValues(String localName, String[] values) + * e.g. "{ { {..}" + * </pre> + * If multiple right braces follow a left brace, pair the left most one with the left brace + * and treat the others as unmatched right braces. + * <pre> + * e.g. "{..} } }" * </pre> * * @author John Kaputin ([EMAIL PROTECTED]) */ public class HTTPLocation { - private String fLocation; + private String fLocationTemplate; + private String fDerivedLocation = null; + boolean fValid = true; - private String leftBrace = "{".intern(); - private String rightBrace = "}".intern(); + private List fValidatedList = new Vector(); //identifies syntax errors in the template + private List fParsedList = new Vector(); //working list for value substitution - public HTTPLocation(String httpLocation) { - fLocation = httpLocation; - } + private final String leftBrace = "{".intern(); + private final String rightBrace = "}".intern(); + private final String doubleLeftBraces = "{{".intern(); + private final String doubleRightBraces = "}}".intern(); + private final List curlyBraces = Arrays.asList(new String[] { + leftBrace, rightBrace, doubleLeftBraces, doubleRightBraces}); /** - * Validates that the value of the {http location} property is in a form - * that can be converted to a URI when constructing the request, assuming - * that any required template substitution has taken place. - * It first checks that any curly braces appear as matched pairs, - * left and right, enclosing a string value that could represent the local - * name of an element (i.e. an NCName). - * It then checks that a java.net.URI object can be created from the property - * value by simulating template substitution to eliminate any curly braces. - * Note that this method just checks the format of the {http location} value, - * but not the validity of any substituted values. - * + * This constructor creates an HTTPLocation object from the specified String. This + * String is the original value of the {http location} property, prior to any template + * substitution of element local names enclosed in curly braces with element values. + * If the property was parsed from a WSDL document, this will be the value of the + * <code>whttp:location</code> attribute within a binding operation element. + * <p> + * The location template String argument must not be null. + * + * @param location the String value of the http location */ - public boolean isTemplateValid() { - - boolean valid = true; - - //step 1: check all curly braces match and enclose an NCName string. - - int left, right, temp = 0; - - while(valid) { - left = fLocation.indexOf(leftBrace, temp); - if(left != -1) { - right = fLocation.indexOf(rightBrace, left); - if(right != -1) { - String localName = fLocation.substring(left+1, right); - if(NCName.isValid(localName)) { - //we have matching curly braces enclosing a valid NCName - temp = right+1; - continue; - } else { - valid = false; //invalid local name string - } - } else { - valid = false; //unmatched left curly brace - } - } else { - right = fLocation.indexOf(rightBrace, temp); - if(right == -1) { - break; - } else { - valid = false; //unmatched right curly brace - } - } - } + public HTTPLocation(String location) { + fLocationTemplate = location; - //step 2: simulate substitution to eliminate curly braces - // and pass the resulting string to the URI constuctor. - - if (valid) { - String substituted = fLocation.replace('{', 'X'); - substituted = substituted.replace('}', 'X'); - try { - new URI(substituted); - } catch (URISyntaxException e) { - valid = false; + if(fLocationTemplate != null) { + List tokenizedList = tokenizeTemplate(); + validateTemplate(tokenizedList); + if(fValidatedList.size() > 0) { + parseTemplate(); } + } else { + //TODO decide whether to throw NPE or simply flag location template as invalid + fValid = false; } - - //step 3: return the result of these checks - return valid; } /** - * Returns the original value of the {http location} property, - * prior to any template substitution. + * Returns true if the format of the the {http location} property prior to any + * template substitution is valid, otherwise false. + * + * TODO add more javadoc + * + */ + public boolean isTemplateValid() { + return fValid; + } + + /** + * Returns the original value of the HTTP location, prior to any template + * substitution of element local names enclosed in curly braces with element values. + * If the property was parsed from a WSDL document, this will be the value of the + * <code>whttp:location</code> attribute within a binding operation element. */ public String getLocationTemplate() { - return fLocation; + return fLocationTemplate; } /** * Takes a String array containing the instance data values to be substituted * for curly brace-enclosed local names in the HTTP location template and returns - * the a String representing the substituted template. + * a String representing the substituted template. * The array values correspond positionally to the local names in the template. * <p> * If the HTTP location template is not valid, this method will return null; @@ -143,6 +142,7 @@ * than there are values in the String array, the unmatched local names will remain * enclosed in curly braces in the returned string. * + * TODO remove this method as it is now replaced by the method substitute(String[] values), which changes the object state and conforms to naming convention for other substitution methods. */ public String getLocationSubstituted(String[] values) { if(!isTemplateValid()) { @@ -155,18 +155,414 @@ for(int i=0; i<values.length; i++) { next = values[i]; - left = fLocation.indexOf(leftBrace, temp); + left = fLocationTemplate.indexOf(leftBrace, temp); if(left == -1) { break; //there are no more templated local names } - substituted.append(fLocation.substring(temp, left)); + substituted.append(fLocationTemplate.substring(temp, left)); substituted.append(next); - right = fLocation.indexOf(rightBrace, left+1); + right = fLocationTemplate.indexOf(rightBrace, left+1); temp = right+1; } - substituted.append(fLocation.substring(temp)); + substituted.append(fLocationTemplate.substring(temp)); return substituted.toString(); } + + /** + * Returns a String array containing the element local names that appear + * enclosed in curly braces in the location template, in the order that they + * appear in the template. Note that if a local name appears multiple times in + * the template, multiple corresponding occurrences will be present in the array. + * + * @return a String array containing all element local names from the template + */ + public String[] getLocalNames() { + return null; //TODO implement this method + } + + /** + * Returns a String array containing the distinct element local names that appear + * enclosed in curly braces in the location template, in the order that they appear + * in the template. If a local name appears multiple times in the template, + * only the first occurrence will be present in the array. + * + * @return a String array containing the distinct element local names from the template + */ + public String[] getDistinctLocalNames() { + return null; //TODO implement this method + } + + /** + * Counts the number of element local names enclosed in curly braces within the location template. + * This is equal to <code>getLocalNames().length</code>. + * + * @return the number of occurrences of <code>localName</code> + */ + public int countLocalNames() { + return 0; //TODO implement this method + } + + /** + * Counts the occurrences of the specified element local name enclosed in curly braces + * within the location template. + * A null argument will return zero. + * + * @param localName the local name of an element from the instance data of the message + * @return the number of occurrences of <code>localName</code> + */ + public int countOccurrences(String localName) { + return 0; //TODO implement this method + } + + //TODO public void substitute(String localName, int occurrence, String value) {}, required? + + /** + * Substitute the specified element local name and its enclosing curly braces within the + * location template with the specified String value of that element. + * Note, this method assumes a single occurrence only of the specified local name, so if + * the the local name appears multiple times in the template only the first occurrence + * will be substituted. It does not handle multiple substitution. + * + * @param localName the local name of an element from the instance data of the message + * @param value the value to be substitute for <code>localName</code> + */ + public void substitute(String localName, String value) { + + fDerivedLocation = null; + + } + + /** + * Substitute either the first occurrence or all occurrences of the specified element local name + * and its enclosing curly braces within the location template with the specified String value + * of that element. + * If the <code>allOccurrences</code> argument is <it>false</it> only the first occurrence + * of the specified local name will be substituted (this is the same behaviour as the method + * <code>substitute(String localName, String value)</code>). If it is true + * then all occurrences will be substituted with the specified value. + * + * @param localName the local name of an element from the instance data of the message + * @param value the value to be substitute for <code>localName</code> + * @param allOccurrences indicates whether to substitute all occurrences of <code>localName</code> + * or just the first + * + * //TODO determine if this method is actually needed and if not, remove it. + */ + public void substitute(String localName, String value, boolean allOccurrences) { + + fDerivedLocation = null; + + } + + /** + * Substitute the occurrences of the specified element local name and its enclosing curly braces + * within the location template with the values contained in the specified String array, in the + * order they appear. + * The size of the array should be equal to the number of occurrences of the local name appearing + * within the template (i.e. equal to the result of the <code>countOccurrences(String localName)</code> + * method). + * If it is shorter, any additional local name occurrences in the template will be ignored. + * If it is longer, any additional values in the array will be ignored. + * <p> + * A null element in the array will remove any previously substituted value for the + * corresponding occurrence of the local name within the template. + * + * @param localName the local name of an element from the instance data of the message + * @param values a String array containing the values to be substituted for occurrences of + * <code>localName</code>, in order. + */ + public void substitute(String localName, String[] values) { + + fDerivedLocation = null; + + } + + /** + * Substitute the element local names and their enclosing curly braces within the location + * template with the values contained in the specified String array, in the order they appear. + * The size of the array should be equal to the number of local names appearing within the + * template (i.e. equal to the result of the <code>countLocalNames()</code> method). + * If it is shorter, any additional local names in the template will be ignored. + * If it is longer, any additional values in the array will be ignored. + * <p> + * A null element in the array will remove any previously substituted value for the + * corresponding local name within the template. + * + * @param values a String array containing the values to be substituted for any occurrences of an + * element local name within the template, in order. + */ + public void substitute(String[] values) { + + fDerivedLocation = null; + } + + /** + * Return the String value associated with the specified element local name. + * If template substitution has already happened via any of the + * <code>substitute</code> methods, this String should represent the value of + * some element from the instance data of the message. If substitution has not + * yet happened, a null value is returned. + * <p> + * Note, this method assumes a single occurrence of the specified local name + * within the template. If the local name appears multiple times in the template, + * the method returns the value associated with the first occurrence of this + * local name. + * + * @param localName the local name of an element from the instance data of the message + * @return the String value associated with <code>localName</code> + */ + public String getValue(String localName) { + return null; //TODO implement this method + } + + /** + * Returns a String array containing the values associated with occurrences + * of the specified element local name, in the order they appear within the template. + * If template substitution has already happened via any of the + * <code>substitute</code> methods, each String should represent the value of + * some element from the instance data of the message. If substitution has not + * yet happened for some occurrence of the local name, the corresponding + * array element will be null. + * + * @param localName the local name of an element from the instance data of the message + * @return an array of String values associated with occurrences of <code>localName</code> + */ + public String[] getValues(String localName) { + return null; //TODO implement this method + } + + /** + * Returns a String array containing the values associated with all element local names, + * in the order they appear within the template. + * If template substitution has already happened via any of the + * <code>substitute</code> methods, each String should represent the value of + * some element from the instance data of the message. If substitution has not + * yet happened for a local name, the corresponding array element will be null. + * + * @return an array of String values associated with element local names within the template + */ + public String[] getValues() { + return null; //TODO implement this method + } + + /** + * Returns a String representing the HTTP Location template with any element name/value + * substitution that has taken place via the <code>substitute</code> methods. + * If any element local name has not yet been substituted with a value then that + * local name will appear in the String in its original template form, enclosed in + * curly braces. + * <p> + * If the HTTP Location does not contain any curly brace template syntax then substitution + * is not applicable and this method will return the same String value as the + * <code>getLocationTemplate()</code> method. + */ + public String toString() { + + if(fDerivedLocation != null) { + return fDerivedLocation; + } + + StringBuffer buffer = new StringBuffer(); + Iterator it = fParsedList.iterator(); + + while(it.hasNext()) { + Object currToken = it.next(); + if(currToken instanceof String[]) { + String[] nameValue = (String[])currToken; + if(nameValue[1] == null) { + //no value substitution, so append the original curly brace-enclosed string + buffer.append(leftBrace); + buffer.append(nameValue[0]); + buffer.append(rightBrace); + } else { + //append the substituted element value + buffer.append(nameValue[1]); + } + } else { + buffer.append(currToken); + } + } + + fDerivedLocation = buffer.toString(); + + return fDerivedLocation; + } + + /* + * Scan location char by char left to right. Add {, }, {{ or }} or any + * string between these add as tokens in a new ordered list. Return this new + * tokenized list to be used as input to the template validation step. + */ + private List tokenizeTemplate() { + + StringBuffer buffer = new StringBuffer(); + int len = fLocationTemplate.length(); + char currChar; + int lastPos = len-1; + List tokens = new Vector(); + + for(int i=0; i<len; i++) { + currChar = fLocationTemplate.charAt(i); + if(currChar == '{') { + if(buffer.length() > 0) { + tokens.add(buffer.toString()); + buffer = new StringBuffer(); + } + if(i < lastPos && fLocationTemplate.charAt(i+1) == '{') { + tokens.add(doubleLeftBraces); + i++; //move loop position to the 2nd left curly brace + } else { + tokens.add(leftBrace); + } + } else if(currChar == '}') { + if(buffer.length() > 0) { + tokens.add(buffer.toString()); + buffer = new StringBuffer(); + } + if(i < lastPos && fLocationTemplate.charAt(i+1) == '}') { + tokens.add(doubleRightBraces); + i++; //move loop position to the 2nd right curly brace + } else { + tokens.add(rightBrace); + } + + } else { + buffer.append(currChar); + } + } + + if(buffer.length() > 0) { + tokens.add(buffer.toString()); + } + + return tokens; + } + + /* + * Identify matching pairs of left and right curly braces with a string in between and check + * that the string between them is of type xs:NCName (i.e. the type required for an element + * local name). + * If multiple left braces precede a right brace, pair the right most one with the right brace + * and treat the others as unmatched left braces. + * If multiple right braces follow a left brace, pair the left most one with the left brace + * and treat the others as unmatched right braces. + * Any unmatched left or right braces are added to the output list. Their presence in this list + * can be used later to identify this type of error. + * + * Replace valid curly brace-enclosed strings with a 2 element String array in the output list. + * The first element is the local name string and the second element, initialized to null, + * represents the element value. + * If the enclosed string is not of type NCName, add it to the output list as a single string + * with its enclosing left and right braces (e.g. "{invalid:name}"). This type of string can be + * used later to identify this type of error. + * + * Any double curly braces '{{' or '}}' are copied directly to the output list. + * Any other strings in the input list are also copied directly to the output list. + * + * This output list of valid and in-error items can be used to identify particular types of + * errors present in the template and is also used as input to the final parsing step. + * + */ + private void validateTemplate(List tokenizedList) { + + if(tokenizedList.size() == 0) { + fValid = false; + return; + } + + boolean valid = true; + List tokens = new Vector(); + int size = tokenizedList.size(); + ListIterator it = tokenizedList.listIterator(); + + while(it.hasNext()) { + Object currToken = it.next(); + int currIndex = it.previousIndex(); + if(currToken == leftBrace) { + if(size-currIndex < 3) { + valid = false; //left brace not followed by a name and a right brace + tokens.add(leftBrace); + } else { + String nextToken = (String)tokenizedList.get(currIndex+1); + Object nextNextToken = tokenizedList.get(currIndex+2); + if(!curlyBraces.contains(nextToken) && nextNextToken == rightBrace) { + //we have a string enclosed by a pair of left and right curly braces + if(NCName.isValid(nextToken)) { + String[] nameValue = new String[] {nextToken, null}; + tokens.add(nameValue); + } else { + valid = false; //invalid format for a local name + StringBuffer buffer = new StringBuffer(leftBrace); + buffer.append(nextToken); + buffer.append(rightBrace); + tokens.add(buffer.toString()); + } + it.next(); //local name + it.next(); //right curly brace + } else { + //TODO handle a string containing double curly brace as an invalid local name, not an unmatched left brace. + valid = false; //left brace not followed by a name and a right brace + tokens.add(leftBrace); + } + } + } else if(currToken == rightBrace) { + //right brace not preceded by a name and a left brace + valid = false; + tokens.add(rightBrace); + } else { + //This token is double left or double right curly braces or some string + //other than a single curly brace. + tokens.add(currToken); + } + + } //end while loop + + fValid = valid; + fValidatedList = tokens; + } + + /* + * Create a final parsed list of items from the location template, which can be used + * for value substitution and for creating the external representation of the + * location after substitution. The output list will contain either the 2 element + * string arrays representing templated local names or strings concatenating any + * other content that appears between templated names. These string concatentations + * will include any double curly braces, replaced by a single brace. They will also + * include any invalid curly brace-enclosed strings (e.g. "{invalid:name}"). + */ + private void parseTemplate() { + + StringBuffer buffer = new StringBuffer(); + List tokens = new Vector(); + Iterator it = fValidatedList.iterator(); + + while(it.hasNext()) { + Object currToken = it.next(); + if(currToken instanceof String[]) { + //This is a templated local name, so output any previously concatentated + //string content then output this string array. + if(buffer.length() > 0) { + tokens.add(buffer.toString()); + buffer = new StringBuffer(); + } + tokens.add(currToken); + } else if(currToken == doubleLeftBraces) { + buffer.append(leftBrace); + } else if(currToken == doubleRightBraces) { + buffer.append(rightBrace); + } else { + //just append this to any previous string content + buffer.append(currToken); + } + } + + if(buffer.length() > 0) { + tokens.add(buffer.toString()); + } + + fParsedList = tokens; + } + } --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]