13e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath/*
23e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * Copyright (C) 2006 The Android Open Source Project
33e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath *
43e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
53e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * you may not use this file except in compliance with the License.
63e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * You may obtain a copy of the License at
73e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath *
83e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
93e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath *
103e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * Unless required by applicable law or agreed to in writing, software
113e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
123e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * See the License for the specific language governing permissions and
143e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * limitations under the License.
153e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath */
163e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
173e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamathpackage android.net.compatibility;
183e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
193e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamathimport static android.util.Patterns.GOOD_IRI_CHAR;
203e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
213e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamathimport java.util.Locale;
223e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamathimport java.util.regex.Matcher;
233e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamathimport java.util.regex.Pattern;
243e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
253e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath/**
263e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * Web Address Parser
273e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath *
283e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * This is called WebAddress, rather than URL or URI, because it
293e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * attempts to parse the stuff that a user will actually type into a
303e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * browser address widget.
313e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath *
323e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * Unlike java.net.uri, this parser will not choke on URIs missing
333e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * schemes.  It will only throw a IllegalArgumentException if the input is
343e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * really hosed.
353e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath *
363e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath * If given an https scheme but no port, fills in port
373e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath *
383e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath */
393e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamathpublic class WebAddress {
403e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
413e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    private String mScheme;
423e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    private String mHost;
433e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    private int mPort;
443e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    private String mPath;
453e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    private String mAuthInfo;
463e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
473e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    static final int MATCH_GROUP_SCHEME = 1;
483e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    static final int MATCH_GROUP_AUTHORITY = 2;
493e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    static final int MATCH_GROUP_HOST = 3;
503e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    static final int MATCH_GROUP_PORT = 4;
513e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    static final int MATCH_GROUP_PATH = 5;
523e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
533e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    static Pattern sAddressPattern = Pattern.compile(
543e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            /* scheme    */ "(?:(http|https|file)\\:\\/\\/)?" +
553e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            /* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
563e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            /* host      */ "([" + GOOD_IRI_CHAR + "%_-][" + GOOD_IRI_CHAR + "%_\\.-]*|\\[[0-9a-fA-F:\\.]+\\])?" +
573e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            /* port      */ "(?:\\:([0-9]*))?" +
583e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            /* path      */ "(\\/?[^#]*)?" +
593e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            /* anchor    */ ".*", Pattern.CASE_INSENSITIVE);
603e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
613e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    /** parses given uriString. */
623e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public WebAddress(String address) throws IllegalArgumentException {
633e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        if (address == null) {
643e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            throw new NullPointerException();
653e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        }
663e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
673e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        // android.util.Log.d(LOGTAG, "WebAddress: " + address);
683e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
693e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        mScheme = "";
703e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        mHost = "";
713e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        mPort = -1;
723e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        mPath = "/";
733e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        mAuthInfo = "";
743e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
753e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        Matcher m = sAddressPattern.matcher(address);
763e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        String t;
773e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        if (m.matches()) {
783e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            t = m.group(MATCH_GROUP_SCHEME);
793e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            if (t != null) mScheme = t.toLowerCase(Locale.ROOT);
803e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            t = m.group(MATCH_GROUP_AUTHORITY);
813e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            if (t != null) mAuthInfo = t;
823e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            t = m.group(MATCH_GROUP_HOST);
833e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            if (t != null) mHost = t;
843e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            t = m.group(MATCH_GROUP_PORT);
853e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            if (t != null && t.length() > 0) {
863e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                // The ':' character is not returned by the regex.
873e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                try {
883e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                    mPort = Integer.parseInt(t);
893e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                } catch (NumberFormatException ex) {
903e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                    throw new IllegalArgumentException("Bad port");
913e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                }
923e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            }
933e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            t = m.group(MATCH_GROUP_PATH);
943e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            if (t != null && t.length() > 0) {
953e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                /* handle busted myspace frontpage redirect with
963e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                   missing initial "/" */
973e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                if (t.charAt(0) == '/') {
983e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                    mPath = t;
993e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                } else {
1003e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                    mPath = "/" + t;
1013e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                }
1023e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            }
1033e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1043e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        } else {
1053e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            // nothing found... outa here
1063e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            throw new IllegalArgumentException("Bad address");
1073e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        }
1083e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1093e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        /* Get port from scheme or scheme from port, if necessary and
1103e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath           possible */
1113e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        if (mPort == 443 && mScheme.equals("")) {
1123e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            mScheme = "https";
1133e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        } else if (mPort == -1) {
1143e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            if (mScheme.equals("https"))
1153e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                mPort = 443;
1163e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            else
1173e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath                mPort = 80; // default
1183e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        }
1193e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        if (mScheme.equals("")) mScheme = "http";
1203e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1213e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1223e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    @Override
1233e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public String toString() {
1243e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        String port = "";
1253e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        if ((mPort != 443 && mScheme.equals("https")) ||
1263e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            (mPort != 80 && mScheme.equals("http"))) {
1273e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            port = ":" + Integer.toString(mPort);
1283e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        }
1293e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        String authInfo = "";
1303e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        if (mAuthInfo.length() > 0) {
1313e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath            authInfo = mAuthInfo + "@";
1323e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        }
1333e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1343e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath        return mScheme + "://" + authInfo + mHost + port + mPath;
1353e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1363e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1373e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public void setScheme(String scheme) {
1383e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      mScheme = scheme;
1393e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1403e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1413e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public String getScheme() {
1423e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      return mScheme;
1433e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1443e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1453e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public void setHost(String host) {
1463e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      mHost = host;
1473e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1483e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1493e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public String getHost() {
1503e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      return mHost;
1513e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1523e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1533e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public void setPort(int port) {
1543e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      mPort = port;
1553e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1563e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1573e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public int getPort() {
1583e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      return mPort;
1593e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1603e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1613e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public void setPath(String path) {
1623e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      mPath = path;
1633e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1643e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1653e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public String getPath() {
1663e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      return mPath;
1673e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1683e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1693e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public void setAuthInfo(String authInfo) {
1703e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      mAuthInfo = authInfo;
1713e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1723e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath
1733e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    public String getAuthInfo() {
1743e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath      return mAuthInfo;
1753e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath    }
1763e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath}
177