class BaseURLResourceName extends BasePrefixResourceName<T, E> {

    public String canonicalize(String urlStr) throws E {

         /* if no http or https protocol resources
         * only call super.canonicalize()
         * to validate the wildcard usage and
         * remove extra delimiters.
         */
        if (!ACCEPTABLE_URLS.matcher(urlStr).matches()) {
            return super.canonicalize(urlStr);
        }

        int index = urlStr.indexOf("://");
        String proto = urlStr.substring(0, index);
        String resource = urlStr.substring(index + 3); // host.sample.com...

        String hostAndPort = resource;
        String urlPath = "";
        if (resource.startsWith(delimiter)) {
            int len = resource.length();
            char[] oldchars = resource.toCharArray();
            char[] newchars = new char[len];
            int j = 0;
            // charAt(0) assuming delimiter is only one character.
            while (j < len && oldchars[j] == delimiter.charAt(0)) {
                j++; // skip leading '/'
            }

            int i = 0;
            while (j < len) {
                newchars[i++] = oldchars[j++];
            }
            resource =  String.valueOf(newchars, 0, i);
        }
        String hostName = "";
        String port = "";
        String query = null;
        if (resource != null && resource.length() != 0) {
            index = resource.indexOf('/');
            if (index == -1) {
                index = resource.indexOf('?');
            }
            if (index != -1) {
                hostAndPort = resource.substring(0, index);
                urlPath = resource.substring(index);
            }
            hostName = hostAndPort;
            index = hostAndPort.indexOf(':');
            if (index != -1) {
                hostName = hostAndPort.substring(0, index);
                port = hostAndPort.substring(index + 1);
                validatePort(port);
            }
            index = urlPath.indexOf('?');
            if (index != -1) {
                query = urlPath.substring(index + 1);
            }

            // If no port has been specified set a default port value based on the protocol.
            // Should the protocol contain a wildcard, set the port to be a wildcard also.
            if (port.length() == 0) {
                if (proto.equals(DEFAULT_WEB_PROTOCOL)) {
                    port = DEFAULT_PORT;
                } else if (proto.equals(SECURE_WEB_PROTOCOL)) {
                    port = SECURE_PORT;
                } else {
                    port = wildcard;
                }
            }
        }

        StringBuilder sb = new StringBuilder(100);
        sb.append(proto);
        sb.append("://");
        sb.append(hostName);
        if (hostName.length() != 0) {
            sb.append(":");
            sb.append(port);

            if (hostName.equals(wildcard) && hostAndPort.equals(wildcard) && resource.equals(wildcard)) {
                sb.append(wildcard);
            }
        }

        if (debug.messageEnabled()) {
            debug.message("URLResourceName: url query=" + query);
        }

        if (query != null) {
            int indexQuery = urlPath.lastIndexOf(query);
            String prefix = super.canonicalize(urlPath.substring(0, indexQuery - 1));
            sb.append(prefix);
            sb.append('?');
            // check if there are more than one query parameters
            int indexAmp = query.indexOf(QUERY_PARAMETER_DELIMITER);
            if (indexAmp != -1) {
                // there are more than query parameters in the url
                String suffix = urlPath.substring(indexQuery + query.length());
                ArrayList al = new ArrayList();
                StringTokenizer st = new StringTokenizer(query, QUERY_PARAMETER_DELIMITER);
                while (st.hasMoreTokens()) {
                    al.add(st.nextToken());
                }
                // sort the query parameters based on rules of
                // the comparator
                Collections.sort(al, comparator);
                int size = al.size();
                // reconstruct the url in canonicalized form
                for (int i = 0; i < size; i++) {
                    if (i < (size - 1)) {
                        sb.append((String) al.get(i)).append(QUERY_PARAMETER_DELIMITER);
                    } else {
                        sb.append((String) al.get(i));
                    }
                }
                sb.append(suffix);
            } else {
                // there is only one query parameter in the url
                sb.append(query);
            }
        } else {
            // there is no query string in the url
            sb.append(super.canonicalize(urlPath));
        }

        return sb.toString();
    }

    protected  BaseURLResourceName(Debug debug, T exactMatch, T noMatch, T subResourceMatch, T superResourceMatch,
            T wildcardMatch);

}