You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by ta...@apache.org on 2013/11/05 02:26:22 UTC

git commit: More complete URL handling and tests.

Updated Branches:
  refs/heads/trunk a19bec7b2 -> 8db1d9ae2


More complete URL handling and tests.

Project: http://git-wip-us.apache.org/repos/asf/activemq-cpp/repo
Commit: http://git-wip-us.apache.org/repos/asf/activemq-cpp/commit/8db1d9ae
Tree: http://git-wip-us.apache.org/repos/asf/activemq-cpp/tree/8db1d9ae
Diff: http://git-wip-us.apache.org/repos/asf/activemq-cpp/diff/8db1d9ae

Branch: refs/heads/trunk
Commit: 8db1d9ae25a6df071ab6747c461357f0b3c1e67b
Parents: a19bec7
Author: Timothy Bish <ta...@gmai.com>
Authored: Mon Nov 4 20:26:09 2013 -0500
Committer: Timothy Bish <ta...@gmai.com>
Committed: Mon Nov 4 20:26:09 2013 -0500

----------------------------------------------------------------------
 activemq-cpp/src/main/Makefile.am               |   2 +
 .../src/main/decaf/internal/net/URLUtils.cpp    | 140 +++++++++
 .../src/main/decaf/internal/net/URLUtils.h      | 122 ++++++++
 activemq-cpp/src/main/decaf/lang/String.cpp     | 136 +++++----
 activemq-cpp/src/main/decaf/lang/String.h       |  25 +-
 activemq-cpp/src/main/decaf/net/URL.cpp         |  21 +-
 .../src/main/decaf/net/URLStreamHandler.cpp     |  81 ++---
 activemq-cpp/src/test/decaf/lang/StringTest.cpp | 157 +++++++++-
 activemq-cpp/src/test/decaf/lang/StringTest.h   |  14 +-
 activemq-cpp/src/test/decaf/net/URLTest.cpp     | 306 ++++++++++++++++++-
 activemq-cpp/src/test/decaf/net/URLTest.h       |  28 ++
 11 files changed, 897 insertions(+), 135 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/main/Makefile.am
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/main/Makefile.am b/activemq-cpp/src/main/Makefile.am
index a9b043c..9a85a5f 100644
--- a/activemq-cpp/src/main/Makefile.am
+++ b/activemq-cpp/src/main/Makefile.am
@@ -354,6 +354,7 @@ cc_sources = \
     decaf/internal/net/URIType.cpp \
     decaf/internal/net/URLStreamHandlerManager.cpp \
     decaf/internal/net/URLType.cpp \
+    decaf/internal/net/URLUtils.cpp \
     decaf/internal/net/file/FileHandler.cpp \
     decaf/internal/net/http/HttpHandler.cpp \
     decaf/internal/net/ssl/DefaultSSLContext.cpp \
@@ -1031,6 +1032,7 @@ h_sources = \
     decaf/internal/net/URIType.h \
     decaf/internal/net/URLStreamHandlerManager.h \
     decaf/internal/net/URLType.h \
+    decaf/internal/net/URLUtils.h \
     decaf/internal/net/file/FileHandler.h \
     decaf/internal/net/http/HttpHandler.h \
     decaf/internal/net/ssl/DefaultSSLContext.h \

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/main/decaf/internal/net/URLUtils.cpp
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/main/decaf/internal/net/URLUtils.cpp b/activemq-cpp/src/main/decaf/internal/net/URLUtils.cpp
new file mode 100644
index 0000000..6ca05c9
--- /dev/null
+++ b/activemq-cpp/src/main/decaf/internal/net/URLUtils.cpp
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+#include <decaf/internal/net/URLUtils.h>
+
+using namespace decaf;
+using namespace decaf::lang;
+using namespace decaf::net;
+using namespace decaf::internal;
+using namespace decaf::internal::net;
+
+////////////////////////////////////////////////////////////////////////////////
+URLUtils::URLUtils() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+String URLUtils::getHost(const URL& url) {
+    String host = url.getHost();
+    if (url.getProtocol().equals("file") && host.isEmpty()) {
+        return "localhost";
+    }
+    return host;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+String URLUtils::canonicalizePath(const String& original, bool discardRelativePrefix) {
+
+    String path = original;
+
+    // the first character of the current path segment
+    int segmentStart = 0;
+
+    // the number of segments seen thus far that can be erased by sequences of '..'.
+    int deletableSegments = 0;
+
+    for (int i = 0; i <= path.length(); ) {
+        int nextSegmentStart;
+        if (i == path.length()) {
+            nextSegmentStart = i;
+        } else if (path.charAt(i) == '/') {
+            nextSegmentStart = i + 1;
+        } else {
+            i++;
+            continue;
+        }
+
+        /*
+         * We've encountered either the end of a segment or the end of the
+         * complete path. If the final segment was "." or "..", remove the
+         * appropriate segments of the path.
+         */
+        if (i == segmentStart + 1 && path.regionMatches(segmentStart, ".", 0, 1)) {
+            // Given "abc/def/./ghi", remove "./" to get "abc/def/ghi".
+            path = path.substring(0, segmentStart) + path.substring(nextSegmentStart);
+            i = segmentStart;
+        } else if (i == segmentStart + 2 && path.regionMatches(segmentStart, "..", 0, 2)) {
+            if (deletableSegments > 0 || discardRelativePrefix) {
+                // Given "abc/def/../ghi", remove "def/../" to get "abc/ghi".
+                deletableSegments--;
+                int prevSegmentStart = path.lastIndexOf('/', segmentStart - 2) + 1;
+                path = path.substring(0, prevSegmentStart) + path.substring(nextSegmentStart);
+                i = segmentStart = prevSegmentStart;
+            } else {
+                // There's no segment to delete; this ".." segment must be retained.
+                i++;
+                segmentStart = i;
+            }
+        } else {
+            if (i > 0) {
+                deletableSegments++;
+            }
+            i++;
+            segmentStart = i;
+        }
+    }
+    return path;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+String URLUtils::authoritySafePath(const String& authority, const String& path) {
+    if (!authority.isEmpty() && !path.isEmpty() && !path.startsWith("/")) {
+        return String("/") + path;
+    }
+    return path;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+bool URLUtils::isValidSchemeChar(int index, char c) {
+
+    if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+        return true;
+    }
+    if (index > 0 && ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')) {
+        return true;
+    }
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+String URLUtils::getSchemePrefix(const String& spec) {
+    int colon = spec.indexOf(':');
+
+    if (colon < 1) {
+        return String();
+    }
+
+    for (int i = 0; i < colon; i++) {
+        char c = spec.charAt(i);
+        if (!isValidSchemeChar(i, c)) {
+            return String();
+        }
+    }
+
+    return spec.substring(0, colon).toLowerCase();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+int URLUtils::findFirstOf(const String& string, const String& chars, int start, int end) {
+    for (int i = start; i < end; i++) {
+        char c = string.charAt(i);
+        if (chars.indexOf(c) != -1) {
+            return i;
+        }
+    }
+    return end;
+}

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/main/decaf/internal/net/URLUtils.h
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/main/decaf/internal/net/URLUtils.h b/activemq-cpp/src/main/decaf/internal/net/URLUtils.h
new file mode 100644
index 0000000..b75cfb7
--- /dev/null
+++ b/activemq-cpp/src/main/decaf/internal/net/URLUtils.h
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+#ifndef _DECAF_INTERNAL_NET_URLUTILS_H_
+#define _DECAF_INTERNAL_NET_URLUTILS_H_
+
+#include <decaf/util/Config.h>
+#include <decaf/lang/String.h>
+#include <decaf/net/URL.h>
+
+namespace decaf {
+namespace internal {
+namespace net {
+
+    class DECAF_API URLUtils {
+    private:
+
+        URLUtils();
+
+    public:
+
+        /**
+         * File based URL instance with an empty host value are always considered
+         * to have a host value of "localhost".
+         *
+         * @param url
+         *      the URL whose host value is to be returned.
+         *
+         * @returns the host value or 'localhost' for file based protocols.
+         */
+        static decaf::lang::String getHost(const decaf::net::URL& url);
+
+        /**
+         * Returns the path will relative path segments like ".." and "." resolved.
+         * The returned path will not necessarily start with a "/" character. This
+         * handles ".." and "." segments at both the beginning and end of the path.
+         *
+         * @param discardRelativePrefix
+         *      true to remove leading ".." segments from the path. This is appropriate
+         *      for paths that are known to be absolute.
+         *
+         * @returns the canonicalized Path value.
+         */
+        static decaf::lang::String canonicalizePath(const decaf::lang::String& original, bool discardRelativePrefix);
+
+        /**
+         * Returns a path that can be safely concatenated with the given authority. If the
+         * authority is empty, this can be any path. Otherwise the paths run together like
+         * http://activemq.apache.html.
+         *
+         * @param authority
+         *      The authority value from a given URL.
+         * @param path
+         *      The path value from a given URL.
+         *
+         * @returns a safe version of the Path value.
+         */
+        static decaf::lang::String authoritySafePath(const decaf::lang::String& authority,
+                                                     const decaf::lang::String& path);
+
+        /**
+         * Returns true if the given char is valid for a URL scheme taking into account its
+         * position in the scheme string.
+         *
+         * @param index
+         *      The index in the scheme where the char value is from.
+         * @param c
+         *      The value from the given index.
+         *
+         * @returns true if the char value is valid for the given index.
+         */
+        static bool isValidSchemeChar(int index, char c);
+
+        /**
+         * Returns the scheme prefix like "http" from the URL spec, or empty if the
+         * spec doesn't start with a scheme. Scheme prefixes match this pattern:
+         * (alpha ( alpha | digit | '+' | '-' | '.' )* ':')
+         */
+        static decaf::lang::String getSchemePrefix(const decaf::lang::String& spec);
+
+        /**
+         * Returns the index of the first char of the given set in the passed in String
+         * bounded between start and end. This returns the end value if none of the characters
+         * exist in the requested range.
+         *
+         * This is an optimization used in URL processing as the return value is end if
+         * the chars are not found in the string so that processing can continue from the
+         * returned end value no matter what the result is.
+         *
+         * @param string
+         *      The string to search.
+         * @param chars
+         *      The set of characters to search for in the target String.
+         * @param start
+         *      The start index to search from.
+         * @param end
+         *      The end index to stop the search at.
+         *
+         * @returns the first index that matches one of the chars or the end value if no matches..
+         */
+        static int findFirstOf(const decaf::lang::String& string,
+                               const decaf::lang::String& chars, int start, int end);
+
+    };
+
+}}}
+
+#endif /* _DECAF_INTERNAL_NET_URLUTILS_H_ */

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/main/decaf/lang/String.cpp
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/main/decaf/lang/String.cpp b/activemq-cpp/src/main/decaf/lang/String.cpp
index eac8eee..7305fd4 100644
--- a/activemq-cpp/src/main/decaf/lang/String.cpp
+++ b/activemq-cpp/src/main/decaf/lang/String.cpp
@@ -45,17 +45,31 @@ namespace lang {
     class Contents {
     public:
 
-        ArrayPointer<unsigned char> value;
+        ArrayPointer<char> value;
         int length;
         int offset;
-
         int hashCode;
 
     public:
 
+        /**
+         * Contents as empty string.
+         */
         Contents() : value(), length(0), offset(0), hashCode(0) {}
-        Contents(int length) : value(length), length(length), offset(0), hashCode(0) {}
-        Contents(int offset, int length, ArrayPointer<unsigned char> value) :
+
+        /**
+         * Contents created with the given length, the array is length + 1 to add the
+         * null terminating character.
+         */
+        Contents(int length) : value(length + 1), length(length), offset(0), hashCode(0) {
+            value[length] = 0;  // Null terminated
+        }
+
+        /**
+         * Contents is a view of some other String which can either be all or a
+         * window allowing for substring methods to not need to copy the contents.
+         */
+        Contents(int offset, int length, ArrayPointer<char> value) :
             value(value), length(length), offset(offset), hashCode(0) {}
     };
 
@@ -63,7 +77,7 @@ namespace lang {
 
 ////////////////////////////////////////////////////////////////////////////////
 String::String(Contents* content) :
-    contents(new Contents(0, content->value.length(), content->value)) {
+    contents(new Contents(0, content->length, content->value)) {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -76,6 +90,20 @@ String::String() : contents(new Contents()) {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+String::String(const char value, int count) : contents() {
+
+    if (count < 0) {
+        throw IndexOutOfBoundsException(
+            __FILE__, __LINE__, "count parameter out of Bounds: %d.", count);
+    }
+
+    contents = new Contents(count);
+    for (int i = 0; i < count; ++i) {
+        contents->value[i] = value;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
 String::String(const String& source) : contents(new Contents(*source.contents)) {
 }
 
@@ -83,11 +111,11 @@ String::String(const String& source) : contents(new Contents(*source.contents))
 String::String(const std::string& source) : contents(new Contents((int)source.length())) {
 
     // load the passed string into the contents value.
-    System::arraycopy((unsigned char*)source.c_str(), 0, contents->value.get(), 0, source.length());
+    System::arraycopy(source.c_str(), 0, contents->value.get(), 0, source.length());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-String::String(const char* array) : contents(new Contents) {
+String::String(const char* array) : contents() {
 
     if (array == NULL) {
         throw NullPointerException(
@@ -97,16 +125,15 @@ String::String(const char* array) : contents(new Contents) {
     int size = StringUtils::stringLength(array);
 
     if (size > 0) {
-
-        this->contents->value = ArrayPointer<unsigned char>(size);
-        this->contents->length = size;
-
-        System::arraycopy((unsigned char*) array, 0, contents->value.get(), 0, size);
+        this->contents = new Contents(size);
+        System::arraycopy(array, 0, contents->value.get(), 0, size);
+    } else {
+        this->contents = new Contents();
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-String::String(const char* array, int size) : contents(new Contents) {
+String::String(const char* array, int size) : contents() {
 
     if (size < 0) {
         throw IndexOutOfBoundsException(
@@ -119,16 +146,15 @@ String::String(const char* array, int size) : contents(new Contents) {
     }
 
     if (size > 0) {
-
-        this->contents->value = ArrayPointer<unsigned char>(size);
-        this->contents->length = size;
-
-        System::arraycopy((unsigned char*) array, 0, contents->value.get(), 0, size);
+        this->contents = new Contents(size);
+        System::arraycopy(array, 0, contents->value.get(), 0, size);
+    } else {
+        this->contents = new Contents();
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-String::String(const char* array, int offset, int length) : contents(new Contents) {
+String::String(const char* array, int offset, int length) : contents() {
 
     int size = StringUtils::stringLength(array);
 
@@ -147,17 +173,16 @@ String::String(const char* array, int offset, int length) : contents(new Content
             __FILE__, __LINE__, "Buffer pointer passed was NULL.");
     }
 
-    if (size > 0) {
-
-        this->contents->value = ArrayPointer<unsigned char>(length);
-        this->contents->length = length;
-
-        System::arraycopy((unsigned char*) array, offset, contents->value.get(), 0, length);
+    if (size > 0 && length > 0) {
+        this->contents = new Contents(length);
+        System::arraycopy(array, offset, contents->value.get(), 0, length);
+    } else {
+        this->contents = new Contents();
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-String::String(const char* array, int size, int offset, int length) : contents(new Contents) {
+String::String(const char* array, int size, int offset, int length) : contents() {
 
     if (size < 0) {
         throw IndexOutOfBoundsException(
@@ -179,12 +204,11 @@ String::String(const char* array, int size, int offset, int length) : contents(n
             __FILE__, __LINE__, "Buffer pointer passed was NULL.");
     }
 
-    if (size > 0) {
-
-        this->contents->value = ArrayPointer<unsigned char>(length);
-        this->contents->length = length;
-
-        System::arraycopy((unsigned char*) array, offset, contents->value.get(), 0, length);
+    if (size > 0 && length > 0) {
+        this->contents = new Contents(length);
+        System::arraycopy(array, offset, contents->value.get(), 0, length);
+    } else {
+        this->contents = new Contents();
     }
 }
 
@@ -210,14 +234,14 @@ String& String::operator= (const String& other) {
 ////////////////////////////////////////////////////////////////////////////////
 String& String::operator= (const std::string& other) {
 
+    delete contents;
+
     if (!other.empty()) {
         int length = (int) other.length();
-
-        contents->value = ArrayPointer<unsigned char>(length);
-        contents->length = length;
-        contents->hashCode = 0;
-
-        System::arraycopy((unsigned char*)other.c_str(), 0, contents->value.get(), 0, length);
+        contents = new Contents(length);
+        System::arraycopy(other.c_str(), 0, contents->value.get(), 0, length);
+    } else {
+        contents = new Contents();
     }
 
     return *this;
@@ -231,12 +255,12 @@ String& String::operator= (const char* other) {
     }
 
     int length = StringUtils::stringLength(other);
+    delete contents;
     if (length > 0) {
-        contents->value = ArrayPointer<unsigned char>(length);
-        contents->length = length;
-        contents->hashCode = 0;
-
-        System::arraycopy((unsigned char*)other, 0, contents->value.get(), 0, length);
+        contents = new Contents(length);
+        System::arraycopy(other, 0, contents->value.get(), 0, length);
+    } else {
+        contents = new Contents();
     }
 
     return *this;
@@ -306,11 +330,11 @@ String String::operator+ (const char* other) const {
 const char* String::c_str() const {
 
     if (contents->length == 0) {
-        return NULL;
+        return "";
     }
 
-    if (contents->offset == 0 && contents->length == contents->value.length()) {
-        return (const char*) contents->value.get();
+    if (contents->length == contents->value.length() - 1) {
+        return (const char*) (contents->value.get() + contents->offset);
     }
 
     throw UnsupportedOperationException(__FILE__, __LINE__, "Not yet implemented for offset values");
@@ -527,8 +551,7 @@ String String::concat(const std::string& string) const {
                           buffer.value.get(), 0, contents->length);
     }
 
-    System::arraycopy((const unsigned char*) string.c_str(),
-                      0, buffer.value.get(), contents->length, string.length());
+    System::arraycopy(string.c_str(), 0, buffer.value.get(), contents->length, string.length());
 
     return String(&buffer);
 }
@@ -553,8 +576,7 @@ String String::concat(const char* string) const {
                           buffer.value.get(), 0, contents->length);
     }
 
-    System::arraycopy((const unsigned char*) string, 0,
-                      buffer.value.get(), contents->length, length);
+    System::arraycopy(string, 0, buffer.value.get(), contents->length, length);
 
     return String(&buffer);
 }
@@ -667,7 +689,7 @@ bool String::equalsIgnoreCase(const String& string) const {
     int end = contents->offset + contents->length;
 
     char c1, c2;
-    ArrayPointer<unsigned char> target = string.contents->value;
+    ArrayPointer<char> target = string.contents->value;
 
     while (offsetThis < end) {
         if ((c1 = contents->value[offsetThis++]) != (c2 = target[offsetOther++])) {
@@ -804,7 +826,7 @@ int String::indexOf(const String& subString, int start) const {
             return -1;
         }
 
-        unsigned char* target = subString.contents->value.get();
+        char* target = subString.contents->value.get();
         int subOffset = subString.contents->offset;
 
         char firstChar = target[subOffset];
@@ -961,7 +983,7 @@ int String::lastIndexOf(const String& subString, int start) const {
             }
 
             // count and subCount are both >= 1
-            unsigned char* target = subString.contents->value.get();
+            char* target = subString.contents->value.get();
             int subOffset = subString.contents->offset;
             char firstChar = target[subOffset];
             int end = subOffset + subCount;
@@ -1267,10 +1289,14 @@ String String::toUpperCase() const {
 ////////////////////////////////////////////////////////////////////////////////
 std::string String::toString() const {
 
-    if (this->contents->value == NULL) {
+    if (this->contents == NULL) {
         return "null";
     }
 
+    if (this->contents->length == 0) {
+        return "";
+    }
+
     return std::string((const char*) contents->value.get() + contents->offset, this->length());
 }
 
@@ -1344,7 +1370,7 @@ namespace lang {
     std::ostream& operator<<(std::ostream &out, const String& target) {
 
         if (target.isEmpty()) {
-            out << "NULL";
+            out << "";
         }
 
         for (int i = 0; i < target.length(); ++i) {

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/main/decaf/lang/String.h
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/main/decaf/lang/String.h b/activemq-cpp/src/main/decaf/lang/String.h
index 30d2caf..8865dd8 100644
--- a/activemq-cpp/src/main/decaf/lang/String.h
+++ b/activemq-cpp/src/main/decaf/lang/String.h
@@ -44,11 +44,26 @@ namespace lang {
     public:
 
         /**
-         * Creates a new empty String object.
+         * Creates a new empty String object.  This value is equivalent to
+         * calling String("") and all methods will behave as if the string is
+         * an empty string.
          */
         String();
 
         /**
+         * Create a new String instance that contains N copies of the given character
+         * value.
+         *
+         * @param value
+         *      The character to fill this String with.
+         * @param count
+         *      The number of copies of the character to fill.
+         *
+         * @throws IndexOutOfBoundsException if the count parameter is negative.
+         */
+        String(const char value, int count);
+
+        /**
          * Create a new String object that represents the given STL string
          *
          * @param source
@@ -223,15 +238,11 @@ namespace lang {
         String operator+ (const std::string& other) const;
         String operator+ (const char* other) const;
 
-    private:
-
-        // TODO - String is not always NULL terminated at the moment.
-
         /**
          * Returns a const char* value to allow easier coexistence with standard c++
-         * string operations.  The value returned will be NULL if the String is empty.
+         * string operations..
          *
-         * @returns a const char* value for this String or NULL if empty.
+         * @returns a const char* value for this String.
          */
         const char* c_str() const;
 

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/main/decaf/net/URL.cpp
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/main/decaf/net/URL.cpp b/activemq-cpp/src/main/decaf/net/URL.cpp
index 990e767..9094c07 100644
--- a/activemq-cpp/src/main/decaf/net/URL.cpp
+++ b/activemq-cpp/src/main/decaf/net/URL.cpp
@@ -27,6 +27,7 @@
 #include <decaf/net/MalformedURLException.h>
 #include <decaf/lang/exceptions/IllegalArgumentException.h>
 #include <decaf/internal/net/URLType.h>
+#include <decaf/internal/net/URLUtils.h>
 #include <decaf/util/HashMap.h>
 #include <decaf/util/concurrent/Mutex.h>
 
@@ -138,6 +139,7 @@ URL::URL(const String& protocol, const String& host, int port, const String& fil
 
 ////////////////////////////////////////////////////////////////////////////////
 void URL::initialize(const URL* context, const String& theSpec, URLStreamHandler* handler) {
+
     if (handler != NULL) {
         impl->streamHandler.reset(handler);
     }
@@ -160,15 +162,18 @@ void URL::initialize(const URL* context, const String& theSpec, URLStreamHandler
             // According to RFC 2396 scheme part should match the following expression:
             // alpha *( alpha | digit | "+" | "-" | "." )
             char c = protocol.charAt(0);
-            bool valid = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
-            for (int i = 1; valid && (i < protocol.length()); i++) {
+            bool valid = true;
+//            bool valid = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
+//            for (int i = 1; valid && (i < protocol.length()); i++) {
+            for (int i = 0; valid && (i < protocol.length()); i++) {
                 c = protocol.charAt(i);
-                valid = ('a' <= c && c <= 'z') ||
-                        ('A' <= c && c <= 'Z') ||
-                        ('0' <= c && c <= '9') ||
-                        (c == '+') ||
-                        (c == '-') ||
-                        (c == '.');
+                valid = URLUtils::isValidSchemeChar(i, c);
+//                valid = ('a' <= c && c <= 'z') ||
+//                        ('A' <= c && c <= 'Z') ||
+//                        ('0' <= c && c <= '9') ||
+//                        (c == '+') ||
+//                        (c == '-') ||
+//                        (c == '.');
             }
             if (!valid) {
                 impl->url.setProtocol(String());

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/main/decaf/net/URLStreamHandler.cpp
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/main/decaf/net/URLStreamHandler.cpp b/activemq-cpp/src/main/decaf/net/URLStreamHandler.cpp
index 45dcd90..d4d9b0c 100644
--- a/activemq-cpp/src/main/decaf/net/URLStreamHandler.cpp
+++ b/activemq-cpp/src/main/decaf/net/URLStreamHandler.cpp
@@ -24,6 +24,7 @@
 #include <decaf/internal/util/StringUtils.h>
 #include <decaf/net/UnknownHostException.h>
 #include <decaf/lang/Integer.h>
+#include <decaf/internal/net/URLUtils.h>
 
 using namespace decaf;
 using namespace decaf::net;
@@ -31,60 +32,28 @@ using namespace decaf::lang;
 using namespace decaf::lang::exceptions;
 using namespace decaf::internal;
 using namespace decaf::internal::util;
+using namespace decaf::internal::net;
 
 ////////////////////////////////////////////////////////////////////////////////
-namespace {
-
-    /**
-     * File based URL instance with an empty host value are always considered
-     * to have a host value of "localhost".
-     */
-    String getHost(const URL& url) {
-        String host = url.getHost();
-        if (url.getProtocol().equals("file") && host.isEmpty()) {
-            return "localhost";
-        }
-        return host;
-    }
-
-    /**
-     * Canonicalize the path, i.e. remove ".." and "." occurrences.
-     *
-     * @param path the path to be canonicalized
-     *
-     * @return the canonicalized path
-     */
-    String canonicalizePath(const String& original) {
-        String path = original;
-        int dirIndex;
-
-        while ((dirIndex = path.indexOf("/./")) >= 0) {
-            path = path.substring(0, dirIndex + 1) + path.substring(dirIndex + 3);
-        }
+URLStreamHandler::~URLStreamHandler() {}
 
-        if (path.endsWith("/.")) {
-            path = path.substring(0, path.length() - 1);
-        }
+////////////////////////////////////////////////////////////////////////////////
+namespace {
 
-        while ((dirIndex = path.indexOf("/../")) >= 0) {
-            if (dirIndex != 0) {
-                path = path.substring(0, path.lastIndexOf('/', dirIndex - 1)) + path.substring(dirIndex + 3);
-            } else {
-                path = path.substring(dirIndex + 3);
-            }
-        }
+    String relativePath(const String& base, const String& path) {
 
-        if (path.endsWith("/..") && path.length() > 3) {
-            path = path.substring(0, path.lastIndexOf('/', path.length() - 4) + 1);
+        if (path.startsWith("/")) {
+            return URLUtils::canonicalizePath(path, true);
+        } else if (base != "") {
+            String combined = base.substring(0, base.lastIndexOf('/') + 1) + path;
+            return URLUtils::canonicalizePath(combined, true);
+        } else {
+            return path;
         }
-        return path;
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-URLStreamHandler::~URLStreamHandler() {}
-
-////////////////////////////////////////////////////////////////////////////////
 URLConnection* URLStreamHandler::openConnection(const URL& url DECAF_UNUSED, const Proxy* proxy DECAF_UNUSED) {
     throw UnsupportedOperationException(__FILE__, __LINE__, "method has not been implemented yet");
 }
@@ -157,8 +126,8 @@ bool URLStreamHandler::hostsEqual(const URL& source, const URL& other) const {
 //    }
 
     // Compare by name.
-    String host1 = getHost(source);
-    String host2 = getHost(other);
+    String host1 = URLUtils::getHost(source);
+    String host2 = URLUtils::getHost(other);
     if (host1.isEmpty() && host2.isEmpty()) {
         return true;
     }
@@ -216,13 +185,12 @@ int URLStreamHandler::getDefaultPort() const {
 void URLStreamHandler::parseURL(URL& url, const String& spec, int start, int limit) {
 
     if (limit < start || limit < 0) {
-        if (((limit <= Integer::MIN_VALUE + 1) && (start >= spec.length() || start < 0)) ||
+        if ((limit <= Integer::MIN_VALUE + 1 && (start >= spec.length() || start < 0)) ||
             (spec.startsWith("//", start) && spec.indexOf('/', start + 2) == -1)) {
-
             throw StringIndexOutOfBoundsException(__FILE__, __LINE__, limit);
         }
         if (this != url.getURLStreamHandler()) {
-            throw new SecurityException();
+            throw SecurityException(__FILE__, __LINE__, "Only the URL's stream handler can modify");
         }
         return;
     }
@@ -230,7 +198,6 @@ void URLStreamHandler::parseURL(URL& url, const String& spec, int start, int lim
     String parseString = spec.substring(start, limit);
     limit -= start;
     int fileIdx = 0;
-    bool fileIsRelative = false;
 
     // Default is to use info from context
     String host = url.getHost();
@@ -258,7 +225,6 @@ void URLStreamHandler::parseURL(URL& url, const String& spec, int start, int lim
             fileIdx = limit;
             // Use default
             file = "";
-            fileIsRelative = true;
         }
         int hostEnd = fileIdx;
         if (refIdx != -1 && refIdx < fileIdx) {
@@ -272,9 +238,10 @@ void URLStreamHandler::parseURL(URL& url, const String& spec, int start, int lim
         }
 
         portIdx = parseString.indexOf(':', userIdx == -1 ? hostIdx : userIdx);
-        int endOfIPv6Addr = parseString.indexOf(']');
+        // TODO could add this to string and return -1
+        int endOfIPv6Addr = URLUtils::findFirstOf(parseString, "]", hostIdx, fileIdx);
         // if there are square braces, ie. IPv6 address, use last ':'
-        if (endOfIPv6Addr != -1) {
+        if (endOfIPv6Addr != fileIdx) {
             try {
                 if (parseString.length() > endOfIPv6Addr + 1) {
                     char c = parseString.charAt(endOfIPv6Addr + 1);
@@ -307,6 +274,7 @@ void URLStreamHandler::parseURL(URL& url, const String& spec, int start, int lim
     if (refIdx > -1) {
         ref = parseString.substring(refIdx + 1, limit);
     }
+
     int fileEnd = (refIdx == -1 ? limit : refIdx);
 
     int queryIdx = parseString.lastIndexOf('?', fileEnd);
@@ -332,7 +300,7 @@ void URLStreamHandler::parseURL(URL& url, const String& spec, int start, int lim
         if (fileIdx < limit && parseString.charAt(fileIdx) == '/') {
             file = parseString.substring(fileIdx, fileEnd);
         } else if (fileEnd > fileIdx) {
-            if (file.equals("") && fileIsRelative) {
+            if (file.equals("") && !host.equals("")) {
                 file = "/";
             } else if (file.startsWith("/")) {
                 canonicalize = true;
@@ -341,15 +309,14 @@ void URLStreamHandler::parseURL(URL& url, const String& spec, int start, int lim
             if (last == 0) {
                 file = parseString.substring(fileIdx, fileEnd);
             } else {
-                file = file.substring(0, last)
-                        + parseString.substring(fileIdx, fileEnd);
+                file = file.substring(0, last) + parseString.substring(fileIdx, fileEnd);
             }
         }
     }
 
     if (canonicalize) {
         // modify file if there's any relative referencing
-        file = canonicalizePath(file);
+        file = URLUtils::canonicalizePath(file, false);
     }
 
     setURL(url, url.getProtocol(), host, port, authority, userInfo, file, query, ref);

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/test/decaf/lang/StringTest.cpp
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/test/decaf/lang/StringTest.cpp b/activemq-cpp/src/test/decaf/lang/StringTest.cpp
index ef00bb6..28e35c1 100644
--- a/activemq-cpp/src/test/decaf/lang/StringTest.cpp
+++ b/activemq-cpp/src/test/decaf/lang/StringTest.cpp
@@ -36,7 +36,7 @@ StringTest::~StringTest() {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-void StringTest::testDEfaultConstructor() {
+void StringTest::testDefaultConstructor() {
     String test;
     CPPUNIT_ASSERT_MESSAGE("Default string should equal empty", test == "");
 
@@ -68,6 +68,126 @@ void StringTest::testConstructorStdString() {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+void StringTest::testConstructorCString() {
+
+    const char* cstring("ABCDE");
+
+    String test(cstring);
+
+    CPPUNIT_ASSERT(test.length() == 5);
+    CPPUNIT_ASSERT(test.isEmpty() == false);
+
+    CPPUNIT_ASSERT_MESSAGE("String and C string should be equal", test == cstring);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        test.charAt(5),
+        IndexOutOfBoundsException);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void StringTest::testConstructorCStringWithSize() {
+
+    const char* cstring("ABCDEF");
+    const char* expected("ABCDE");
+
+    String test(cstring, 5);
+
+    CPPUNIT_ASSERT(test.length() == 5);
+    CPPUNIT_ASSERT(test.isEmpty() == false);
+
+    CPPUNIT_ASSERT_MESSAGE("String and C string should be equal", test == expected);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        test.charAt(5),
+        IndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an NullPointerException",
+        String((const char*)NULL, 10),
+        NullPointerException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        String(cstring, -1),
+        IndexOutOfBoundsException);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void StringTest::testConstructorCStringOffsetAndLength() {
+
+    const char* cstring("1ABCDEF");
+    const char* expected("ABCDE");
+
+    String test(cstring, 1, 5);
+
+    CPPUNIT_ASSERT(test.length() == 5);
+    CPPUNIT_ASSERT(test.isEmpty() == false);
+
+    CPPUNIT_ASSERT_MESSAGE("String and C string should be equal", test == expected);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        test.charAt(5),
+        IndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an NullPointerException",
+        String((const char*)NULL, 1, 20),
+        NullPointerException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        String(cstring, -1, 5),
+        IndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        String(cstring, 1, -5),
+        IndexOutOfBoundsException);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void StringTest::testConstructorCStringSizeOffsetAndLength() {
+
+    const char* cstring("1ABCDEF");
+    const char* expected("ABCDE");
+
+    String test(cstring, 7, 1, 5);
+
+    CPPUNIT_ASSERT(test.length() == 5);
+    CPPUNIT_ASSERT(test.isEmpty() == false);
+
+    CPPUNIT_ASSERT_MESSAGE("String and C string should be equal", test == expected);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        test.charAt(5),
+        IndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an NullPointerException",
+        String((const char*)NULL, 7, 1, 4),
+        NullPointerException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        String(cstring, -1, 0, 5),
+        IndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        String(cstring, 7, -1, 5),
+        IndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an IndexOutOfBoundsException",
+        String(cstring, 7, 1, -5),
+        IndexOutOfBoundsException);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 void StringTest::testConstructorString() {
 
     String original("ABCDE");
@@ -102,6 +222,10 @@ void StringTest::testAssignmentString() {
     CPPUNIT_ASSERT_EQUAL_MESSAGE("String assignment failed", input, String("HelloWorld"));
     transient = world;
     CPPUNIT_ASSERT_EQUAL_MESSAGE("String assignment failed", transient, world);
+
+    String toEmpty("ABCDEF");
+    toEmpty = String("");
+    CPPUNIT_ASSERT_MESSAGE("String did not get set to empty", toEmpty.isEmpty());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -121,6 +245,10 @@ void StringTest::testAssignmentStdString() {
     CPPUNIT_ASSERT_EQUAL_MESSAGE("String assignment failed", input, String("HelloWorld"));
     transient = world;
     CPPUNIT_ASSERT_MESSAGE("String assignment failed", transient.equals(world));
+
+    String toEmpty("ABCDEF");
+    toEmpty = std::string("");
+    CPPUNIT_ASSERT_MESSAGE("String did not get set to empty", toEmpty.isEmpty());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -140,6 +268,10 @@ void StringTest::testAssignmentCString() {
     CPPUNIT_ASSERT_EQUAL_MESSAGE("String assignment failed", input, String("HelloWorld"));
     transient = world;
     CPPUNIT_ASSERT_MESSAGE("String assignment failed", transient.equals(world));
+
+    String toEmpty("ABCDEF");
+    toEmpty = "";
+    CPPUNIT_ASSERT_MESSAGE("String did not get set to empty", toEmpty.isEmpty());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -283,6 +415,27 @@ void StringTest::testToCharArray() {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+void StringTest::testCStr() {
+
+    String input("ABCDE");
+    const char* result = input.c_str();
+
+    for (int i = 0; i < input.length(); i++) {
+        CPPUNIT_ASSERT_MESSAGE("Returned incorrect char aray", input.charAt(i) == result[i]);
+    }
+
+    std::string empty("");
+
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid string returned", empty, std::string(String().c_str()));
+
+    const String hw("HelloWorld");
+    String substr = hw.substring(5);
+    String world = "World";
+
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid string returned", world, substr);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 void StringTest::testEndsWith() {
     const String input("HelloWorld");
 
@@ -496,6 +649,8 @@ void StringTest::testToLowerCase() {
     String lower = "helloworld";
     String upper = "HELLOWORLD";
 
+    upper.toLowerCase();
+
     CPPUNIT_ASSERT_MESSAGE("toLowerCase case conversion did not succeed",
                            upper.toLowerCase().equals(lower));
     CPPUNIT_ASSERT_MESSAGE("toLowerCase case non-conversion did not succeed",

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/test/decaf/lang/StringTest.h
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/test/decaf/lang/StringTest.h b/activemq-cpp/src/test/decaf/lang/StringTest.h
index 98cc294..c2dd2ed 100644
--- a/activemq-cpp/src/test/decaf/lang/StringTest.h
+++ b/activemq-cpp/src/test/decaf/lang/StringTest.h
@@ -27,7 +27,11 @@ namespace lang {
     class StringTest : public CppUnit::TestFixture {
 
         CPPUNIT_TEST_SUITE( StringTest );
-        CPPUNIT_TEST( testDEfaultConstructor );
+        CPPUNIT_TEST( testDefaultConstructor );
+        CPPUNIT_TEST( testConstructorCString );
+        CPPUNIT_TEST( testConstructorCStringWithSize );
+        CPPUNIT_TEST( testConstructorCStringOffsetAndLength );
+        CPPUNIT_TEST( testConstructorCStringSizeOffsetAndLength );
         CPPUNIT_TEST( testConstructorStdString );
         CPPUNIT_TEST( testConstructorString );
         CPPUNIT_TEST( testAssignmentString );
@@ -40,6 +44,7 @@ namespace lang {
         CPPUNIT_TEST( testTrim );
         CPPUNIT_TEST( testToString );
         CPPUNIT_TEST( testToCharArray );
+        CPPUNIT_TEST( testCStr );
         CPPUNIT_TEST( testRegionMatches );
         CPPUNIT_TEST( testRegionMatchesCaseSensitive );
         CPPUNIT_TEST( testStartsWith );
@@ -96,7 +101,11 @@ namespace lang {
         StringTest();
         virtual ~StringTest();
 
-        void testDEfaultConstructor();
+        void testDefaultConstructor();
+        void testConstructorCString();
+        void testConstructorCStringWithSize();
+        void testConstructorCStringOffsetAndLength();
+        void testConstructorCStringSizeOffsetAndLength();
         void testConstructorStdString();
         void testConstructorString();
         void testAssignmentString();
@@ -109,6 +118,7 @@ namespace lang {
         void testTrim();
         void testToString();
         void testToCharArray();
+        void testCStr();
         void testRegionMatches();
         void testRegionMatchesCaseSensitive();
         void testStartsWith();

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/test/decaf/net/URLTest.cpp
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/test/decaf/net/URLTest.cpp b/activemq-cpp/src/test/decaf/net/URLTest.cpp
index 15b53c4..e95f23e 100644
--- a/activemq-cpp/src/test/decaf/net/URLTest.cpp
+++ b/activemq-cpp/src/test/decaf/net/URLTest.cpp
@@ -21,6 +21,10 @@
 #include <decaf/net/URL.h>
 #include <decaf/lang/Integer.h>
 #include <decaf/lang/Boolean.h>
+#include <decaf/net/URLStreamHandler.h>
+#include <decaf/net/URLStreamHandlerFactory.h>
+#include <decaf/lang/exceptions/SecurityException.h>
+#include <decaf/lang/exceptions/StringIndexOutOfBoundsException.h>
 
 using namespace std;
 using namespace decaf;
@@ -29,6 +33,33 @@ using namespace decaf::lang;
 using namespace decaf::lang::exceptions;
 
 ////////////////////////////////////////////////////////////////////////////////
+namespace {
+
+    class MyURLStreamHandler : public URLStreamHandler {
+    protected:
+
+        virtual URLConnection* openConnection(const URL& url) {
+            return NULL;
+        }
+
+    public:
+
+        void parse(URL& url, const String& spec, int start, int end) {
+            parseURL(url, spec, start, end);
+        }
+    };
+
+    class MyURLStreamHandlerFactory : public URLStreamHandlerFactory {
+    public:
+
+        virtual URLStreamHandler* createURLStreamHandler(const std::string& protocol) {
+            return new MyURLStreamHandler();
+        }
+    };
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
 URLTest::URLTest() {
 }
 
@@ -67,11 +98,11 @@ void URLTest::testConstructor1() {
 
     // test for no port
     URL d("file://www.yahoo3.com/dir1/dir2/test.cgi#anchor1");
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("u2a returns a wrong protocol", String("file"), d.getProtocol());
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("u2a returns a wrong host", String("www.yahoo3.com"), d.getHost());
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("u2a returns a wrong port", -1, d.getPort());
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("u2a returns a wrong file", String("/dir1/dir2/test.cgi"), d.getFile());
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("u2a returns a wrong anchor", String("anchor1"), d.getRef());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("d returns a wrong protocol", String("file"), d.getProtocol());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("d returns a wrong host", String("www.yahoo3.com"), d.getHost());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("d returns a wrong port", -1, d.getPort());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("d returns a wrong file", String("/dir1/dir2/test.cgi"), d.getFile());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("d returns a wrong anchor", String("anchor1"), d.getRef());
 
     // test for no file, no port
     URL e("http://www.yahoo4.com/");
@@ -237,6 +268,53 @@ void URLTest::testConstructor3() {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+void URLTest::testConstructor4() {
+
+    URL context("http://www.yahoo.com");
+
+    // basic ones
+    URL a(context, "file.java", new MyURLStreamHandler);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("1 returns a wrong protocol", String("http"), a.getProtocol());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("1 returns a wrong host", String("www.yahoo.com"), a.getHost());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("1 returns a wrong port", -1, a.getPort());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("1 returns a wrong file", String("/file.java"), a.getFile());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("1 returns a wrong anchor", String(), a.getRef());
+
+    URL b(context, "systemresource:/+/FILE0/test.java", new MyURLStreamHandler);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("2 returns a wrong protocol", String("systemresource"), b.getProtocol());
+    CPPUNIT_ASSERT_MESSAGE("2 returns a wrong host", b.getHost().equals(""));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("2 returns a wrong port", -1, b.getPort());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("2 returns a wrong file", String("/+/FILE0/test.java"), b.getFile());
+    CPPUNIT_ASSERT_MESSAGE("2 returns a wrong anchor", b.getRef().equals(""));
+
+    URL c(context, "dir1/dir2/../file.java", NULL);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("3 returns a wrong protocol", String("http"), c.getProtocol());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("3 returns a wrong host", String("www.yahoo.com"), c.getHost());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("3 returns a wrong port", -1, c.getPort());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("3 returns a wrong file", String("/dir1/dir2/../file.java"), c.getFile());
+    CPPUNIT_ASSERT_MESSAGE("3 returns a wrong anchor", c.getRef().equals(""));
+
+    // test for question mark processing
+    URL d("http://www.foo.com/d0/d1/d2/cgi-bin?foo=bar/baz");
+
+    // test for relative file and out of bound "/../" processing
+    URL e(d, "../dir1/dir2/../file.java", new MyURLStreamHandler);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("A) returns a wrong file: ", String("/d0/d1/dir1/file.java"), e.getFile());
+
+    // test for absolute and relative file processing
+    URL f(d, "/../dir1/dir2/../file.java", NULL);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("B) returns a wrong file", String("/../dir1/dir2/../file.java"), f.getFile());
+
+    CPPUNIT_ASSERT_NO_THROW(URL("http://www.ibm.com"));
+
+    URL test("http://www.ibm.com");
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an MalformedURLException",
+        URL(test, String()),
+        MalformedURLException);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 void URLTest::testEquals() {
 
     URL u("http://www.apache.org:8080/dir::23??????????test.html");
@@ -289,3 +367,221 @@ void URLTest::testSameFile() {
 //    URL l("ftp://localhost/anyfile");
 //    CPPUNIT_ASSERT(!k.sameFile(l));
 }
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testToString() {
+
+    URL a("http://www.yahoo2.com:9999");
+    URL b("http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1");
+
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("a) Does not return the right url string",
+        std::string("http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1"), b.toString());
+
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("b) Does not return the right url string",
+                                 std::string("http://www.yahoo2.com:9999"), a.toString());
+
+    CPPUNIT_ASSERT_MESSAGE("c) Does not return the right url string",
+                           a.equals(URL(a.toString())));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testToExternalForm() {
+    URL b("http://www.yahoo2.com:9999");
+    URL a("http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1");
+
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("a) Does not return the right url string",
+        String("http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1"), a.toExternalForm());
+
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("b) Does not return the right url string",
+                                 String("http://www.yahoo2.com:9999"), b.toExternalForm());
+
+    CPPUNIT_ASSERT_MESSAGE("c) Does not return the right url string",
+                           a.equals(URL(a.toExternalForm())));
+
+    URL c("http:index");
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("2 wrong external form", String("http:index"), c.toExternalForm());
+
+    URL d("http", "", "index");
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("2 wrong external form", String("http:index"), d.toExternalForm());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testGetFile() {
+
+    URL a("http", "www.yahoo.com:8080", 1233, "test/!@$%^&*/test.html#foo");
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("returns a wrong file", String("test/!@$%^&*/test.html"), a.getFile());
+    URL b("http", "www.yahoo.com:8080", 1233, "");
+    CPPUNIT_ASSERT_MESSAGE("returns a wrong file", b.getFile().equals(""));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testGetHost() {
+
+    String ipv6Host = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210";
+    URL url("http", ipv6Host, -1, "myfile");
+    CPPUNIT_ASSERT_EQUAL((String("[") + ipv6Host + "]"), url.getHost());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testGetPort() {
+    URL a("http://member12.c++.com:9999");
+    CPPUNIT_ASSERT_MESSAGE("return wrong port number", a.getPort() == 9999);
+    URL b("http://member12.c++.com:9999/");
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("return wrong port number", 9999, b.getPort());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testGetDefaultPort() {
+    URL a("http://member12.c++.com:9999");
+    CPPUNIT_ASSERT_EQUAL(80, a.getDefaultPort());
+
+    URL b("http://www.google.com:80/example?language[id]=2");
+    CPPUNIT_ASSERT_EQUAL(String("www.google.com"), b.getHost());
+    CPPUNIT_ASSERT_EQUAL(80, b.getPort());
+
+    // TODO
+//    URL b("ftp://member12.c++.com:9999/");
+//    CPPUNIT_ASSERT_EQUAL(21, b.getDefaultPort());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testGetProtocol() {
+    URL a("http://www.yahoo2.com:9999");
+    CPPUNIT_ASSERT_MESSAGE("u returns a wrong protocol: ", a.getProtocol().equals("http"));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testGetRef() {
+    URL b("http://www.yahoo2.com:9999");
+    URL a("http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1");
+
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("returns a wrong anchor1", String("anchor1"), a.getRef());
+
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("returns a wrong anchor2", String(), b.getRef() );
+    URL c("http://www.yahoo2.com#ref");
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("returns a wrong anchor3", String("ref"), c.getRef());
+    URL d("http://www.yahoo2.com/file#ref1#ref2");
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("returns a wrong anchor4", String("ref1#ref2"), d.getRef());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testGetAuthority() {
+
+    URL a("http", "hostname", 80, "/java?q1#ref");
+    CPPUNIT_ASSERT_EQUAL(String("hostname:80"), a.getAuthority());
+    CPPUNIT_ASSERT_EQUAL(String("hostname"), a.getHost());
+    CPPUNIT_ASSERT_EQUAL(String(), a.getUserInfo());
+    CPPUNIT_ASSERT_EQUAL(String("/java?q1"), a.getFile());
+    CPPUNIT_ASSERT_EQUAL(String("/java"), a.getPath());
+    CPPUNIT_ASSERT_EQUAL(String("q1"), a.getQuery());
+    CPPUNIT_ASSERT_EQUAL(String("ref"), a.getRef());
+
+    URL b("http", "u:p@home", 80, "/java?q1#ref");
+    CPPUNIT_ASSERT_EQUAL(String("[u:p@home]:80"), b.getAuthority());
+    CPPUNIT_ASSERT_EQUAL(String("[u:p@home]"), b.getHost());
+    CPPUNIT_ASSERT_EQUAL(String(""), b.getUserInfo());
+    CPPUNIT_ASSERT_EQUAL(String("/java?q1"), b.getFile());
+    CPPUNIT_ASSERT_EQUAL(String("/java"), b.getPath());
+    CPPUNIT_ASSERT_EQUAL(String("q1"), b.getQuery());
+    CPPUNIT_ASSERT_EQUAL(String("ref"), b.getRef());
+
+    URL c("http", "home", -1, "/java");
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong authority2", String("home"), c.getAuthority());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong userInfo2", String(), c.getUserInfo());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong host2", String("home"), c.getHost());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong file2", String("/java"), c.getFile());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong path2", String("/java"), c.getPath());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong query2", String(), c.getQuery());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong ref2", String(), c.getRef());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testToURI() {
+    URL a("http://www.apache.org");
+    URI uri = a.toURI();
+    CPPUNIT_ASSERT(a.equals(uri.toURL()));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testURLStreamHandlerParseURL() {
+
+    URL url("http://localhost");
+    MyURLStreamHandler handler;
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an StringIndexOutOfBoundsException",
+        handler.parse(url, "//", 0, Integer::MIN_VALUE),
+        StringIndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an StringIndexOutOfBoundsException",
+        handler.parse(url, "1234//", 4, Integer::MIN_VALUE),
+        StringIndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an StringIndexOutOfBoundsException",
+        handler.parse(url, "1", -1, 0),
+        StringIndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an SecurityException",
+        handler.parse(url, "1", 3, 2),
+        SecurityException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an SecurityException",
+        handler.parse(url, "11", 1, Integer::MIN_VALUE),
+        SecurityException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an StringIndexOutOfBoundsException",
+        handler.parse(url, "any", 10, Integer::MIN_VALUE),
+        StringIndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an StringIndexOutOfBoundsException",
+        handler.parse(url, "any", 10, Integer::MIN_VALUE+1),
+        StringIndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an StringIndexOutOfBoundsException",
+        handler.parse(url, "any", Integer::MIN_VALUE, Integer::MIN_VALUE),
+        StringIndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an StringIndexOutOfBoundsException",
+        handler.parse(url, "any", Integer::MIN_VALUE, 2),
+        StringIndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an StringIndexOutOfBoundsException",
+        handler.parse(url, "any", -1, 2),
+        StringIndexOutOfBoundsException);
+
+    CPPUNIT_ASSERT_THROW_MESSAGE(
+        "Should have thrown an SecurityException",
+        handler.parse(url, "any", -1, -1),
+        SecurityException);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testUrlParts() {
+    URL url("http://username:password@host:8080/directory/file?query#ref");
+    CPPUNIT_ASSERT_EQUAL(String("http"), url.getProtocol());
+    CPPUNIT_ASSERT_EQUAL(String("username:password@host:8080"), url.getAuthority());
+    CPPUNIT_ASSERT_EQUAL(String("username:password"), url.getUserInfo());
+    CPPUNIT_ASSERT_EQUAL(String("host"), url.getHost());
+    CPPUNIT_ASSERT_EQUAL(8080, url.getPort());
+    CPPUNIT_ASSERT_EQUAL(80, url.getDefaultPort());
+    CPPUNIT_ASSERT_EQUAL(String("/directory/file?query"), url.getFile());
+    CPPUNIT_ASSERT_EQUAL(String("/directory/file"), url.getPath());
+    CPPUNIT_ASSERT_EQUAL(String("query"), url.getQuery());
+    CPPUNIT_ASSERT_EQUAL(String("ref"), url.getRef());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void URLTest::testFileEqualsWithEmptyHost() {
+    URL a("file", "", -1, "/a/");
+    URL b("file:/a/");
+    CPPUNIT_ASSERT(a.equals(b));
+}

http://git-wip-us.apache.org/repos/asf/activemq-cpp/blob/8db1d9ae/activemq-cpp/src/test/decaf/net/URLTest.h
----------------------------------------------------------------------
diff --git a/activemq-cpp/src/test/decaf/net/URLTest.h b/activemq-cpp/src/test/decaf/net/URLTest.h
index f203b2b..0d74156 100644
--- a/activemq-cpp/src/test/decaf/net/URLTest.h
+++ b/activemq-cpp/src/test/decaf/net/URLTest.h
@@ -30,8 +30,22 @@ namespace net {
         CPPUNIT_TEST( testConstructor1 );
         CPPUNIT_TEST( testConstructor2 );
         CPPUNIT_TEST( testConstructor3 );
+        CPPUNIT_TEST( testConstructor4 );
         CPPUNIT_TEST( testEquals );
         CPPUNIT_TEST( testSameFile );
+        CPPUNIT_TEST( testToString );
+        CPPUNIT_TEST( testToExternalForm );
+        CPPUNIT_TEST( testGetFile );
+        CPPUNIT_TEST( testGetHost );
+        CPPUNIT_TEST( testGetPort );
+        CPPUNIT_TEST( testGetDefaultPort );
+        CPPUNIT_TEST( testGetProtocol );
+        CPPUNIT_TEST( testGetRef );
+        CPPUNIT_TEST( testGetAuthority );
+        CPPUNIT_TEST( testToURI );
+        CPPUNIT_TEST( testURLStreamHandlerParseURL );
+        CPPUNIT_TEST( testUrlParts );
+        CPPUNIT_TEST( testFileEqualsWithEmptyHost );
         CPPUNIT_TEST_SUITE_END();
 
     public:
@@ -42,8 +56,22 @@ namespace net {
         void testConstructor1();
         void testConstructor2();
         void testConstructor3();
+        void testConstructor4();
         void testEquals();
         void testSameFile();
+        void testToString();
+        void testToExternalForm();
+        void testGetFile();
+        void testGetHost();
+        void testGetPort();
+        void testGetDefaultPort();
+        void testGetProtocol();
+        void testGetRef();
+        void testGetAuthority();
+        void testToURI();
+        void testURLStreamHandlerParseURL();
+        void testUrlParts();
+        void testFileEqualsWithEmptyHost();
 
     };