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 &lt;operation&gt; 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]

Reply via email to