FileDocCategorySizeDatePackage
URLParser.javaAPI DocphoneME MR2 API (J2ME)32492Wed May 02 18:00:42 BST 2007gov.nist.siplite.parser

URLParser

public class URLParser extends Parser
Parser For SIP and Tel URLs. Other kinds of URL's are handled by the J2SE 1.4 URL class.
version
JAIN-SIP-1.1 This code is in the public domain.

Fields Summary
private static final String
PHONE_DIGIT
Symbols of phone digit (RFC 2806, 2.2)
private static final String
DTMF_DIGIT
Symbols of dtmf digit (RFC 2806, 2.2)
private static final String
PAUSE_CHAR
Pause characters (RFC 2806, 2.2)
Constructors Summary
public URLParser(String url)
Constructor with initial URL string.

param
url initial URL

    
                  
       
        this.lexer = new Lexer("sip_urlLexer", url);

    
URLParser(Lexer lexer)
Constructor with initial lexer engine.

param
lexer initial lexer engine

        this.lexer = lexer;
        this.lexer.selectLexer("sip_urlLexer");
    
Methods Summary
private java.lang.Stringbase_phone_number()
Parses for the base phone number.

return
the base phone number
exception
ParseException if a parsingerror occurs

        StringBuffer s = new StringBuffer();

        if (debug) dbg_enter("base_phone_number");
        try {
            int lc = 0;
            while (lexer.hasMoreChars()) {
                char w = lexer.lookAhead(0);
                if (LexerCore.isDigit(w) || w == '-" || w == '." || w == '("
                        || w == ')") {
                    lexer.consume(1);
                    s.append(w);
                    lc ++;
                } else if (lc > 0) break;
                else throw createParseException("unexpected " + w);
            }
            return s.toString();
        } finally {
            if (debug) dbg_leave("base_phone_number");
        }

    
protected java.lang.Stringescaped()
Gets the escaped character sequence.

return
the escaped character sequence

        if (debug) dbg_enter("escaped");
        try {
            StringBuffer retval = new StringBuffer();
            char next = lexer.lookAhead(0);
            char next1 = lexer.lookAhead(1);
            char next2 = lexer.lookAhead(2);
            if (next == '%" && Lexer.isHexDigit(next1)
            && Lexer.isHexDigit(next2)) {
                lexer.consume(3);
                retval.append(next);
                retval.append(next1);
                retval.append(next2);
            } else throw createParseException("escaped");
            return retval.toString();
        } finally {
            if (debug) dbg_leave("escaped");
        }
    
private final TelephoneNumberglobal_phone_number()
Gets the global phone number.

return
the parsed phone number
exception
ParseException ifa parsing error occurs

        if (debug) dbg_enter("global_phone_number");
        try {
            TelephoneNumber tn = new TelephoneNumber();
            tn.setGlobal(true);
            NameValueList nv = null;
            this.lexer.match(PLUS);
            String b = base_phone_number();
            tn.setPhoneNumber(b);
            if (lexer.hasMoreChars()) {
                char tok = lexer.lookAhead(0);
                if (tok == ';") {
                    nv = tel_parameters();
                    tn.setParameters(nv);
                }
            }
            return tn;
        } finally {
            if (debug) dbg_leave("global_phone_number");
        }
    
protected java.lang.Stringhvalue()
Gets a header value.

return
value of current header
exception
ParseException if a parsing error occurs

        StringBuffer retval = new StringBuffer();
        while (lexer.hasMoreChars()) {
            char la = lexer.lookAhead(0);
            // Look for a character that can terminate a URL.
            if (la == '+" || la == '?" || la == ':" || la == '@"
                    || la == '[" || la == ']" || la == '/" || la == '$"
                    || la == '_" || la == '-" || la == '"" || la == '!"
                    || la == '~" || la == '*" || la == '." || la == '("
                    || la == ')" || LexerCore.isAlpha(la)
                    || LexerCore.isDigit(la)) {
                lexer.consume(1);
                retval.append(la);
            } else if (la == '%") {
                retval.append(escaped());
            } else break;
        }
        return retval.toString();
    
private booleanisCharFromString(int ch, java.lang.String str)
Checks that the given symbol contains into a string.

param
ch is a given byte to checking
param
str is a string for checking
return
true if the given char can be find in a string

		return (str.indexOf(ch) != -1);
	
private booleanisDtmf_digit(int ch)
Checks that the given symbol is a dtmf digit. RFC 2806, 2.2 dtmf-digit = "*" / "#" / "A" / "B" / "C" / "D"

param
ch is a given byte to checking
return
true if the given char is a dtmf digit

		return isCharFromString(ch, DTMF_DIGIT);
	
protected booleanisEscaped()
Checks if current character is escaped.

return
true if processing an escaped sequenec

        try {
            char next = lexer.lookAhead(0);
            char next1 = lexer.lookAhead(1);
            char next2 = lexer.lookAhead(2);
            return (next == '%" && Lexer.isHexDigit(next1)
            && Lexer.isHexDigit(next2));
        } catch (ParseException ex) {
            return false;
        }
    
protected static booleanisMark(char next)
Checks if character is punctuation mark.

param
next character to be checked
return
true if character is punctuation mark

        return
                next == '-" ||
                next == '_" ||
                next == '." ||
                next == '!" ||
                next == '~" ||
                next == '*" ||
                next == '\'"||
                next == '(" ||
                next == ')";
    
private booleanisPauseChar(int ch)
Checks that the given symbol is a pause character. pause-character = one-second-pause / wait-for-dial-tone one-second-pause = "p" wait-for-dial-tone = "w"

param
ch is a given byte to checking
return
true if the given char is a dtmf digit

		return isCharFromString(ch, PAUSE_CHAR);
	
private booleanisPhoneDigit(int ch)
Checks that the given symbol os a phonedigit. RFC 2806, 2.2 phonedigit = DIGIT / visual-separator visual-separator = "-" / "." / "(" / ")"

param
ch is a given byte to checking
return
true if the given char is a phonedigit

		return isCharFromString(ch, PHONE_DIGIT);
	
protected static booleanisReserved(char next)
Checks if character is reserved.

param
next character to be checked
return
true if character is reserved

        return next == ';" ||
                next == '/" ||
                next == '?" ||
                next == ':" ||
                next == '@" ||
                next == '&" ||
                next == '+" ||
                next == '$" ||
                next == '=" ||
                next == ',";
    
protected static booleanisReservedNoSlash(char next)
Checks if reserved character without a slash.

param
next character to be checked
return
true if reserved character without a slash

        return
                next == ';" ||
                next == '?" ||
                next == ':" ||
                next == '@" ||
                next == '&" ||
                next == '+" ||
                next == '$" ||
                next == ',";

    
protected static booleanisUnreserved(char next)
Checks if character is reserved.

param
next character to be checked
return
true if reserved character.

        return Lexer.isAlpha(next) || Lexer.isDigit(next) || isMark(next);

    
protected static booleanisUserUnreserved(char la)
Checks if user is unreserved.

param
la the character to be checked
return
true if user is not reserved

        return la == '&" ||
                la == '?" ||
                la == '+" ||
                la == '$" ||
                la == '#" ||
                la == '/" ||
                la == '," ||
                la == ';" ||
                la == '=";
    
private java.lang.Stringlocal_number()
Parses for the local phone number.

return
the local phone number
exception
ParseException if a parsing error occurs

        StringBuffer s = new StringBuffer();
        if (debug) dbg_enter("local_number");
        try {
            int lc = 0;
            while (lexer.hasMoreChars()) {
                char la = lexer.lookAhead(0);
                if (la == '*" || la == '#" || la == '-" ||
                        la == '." || la == '(" || la == ')" ||
                        LexerCore.isDigit(la)) {
                    lexer.consume(1);
                    s.append(la);
                    lc ++;
                } else if (lc > 0) break;
                else throw createParseException("unexepcted " + la);
            }
            return s.toString();
        } finally {
            if (debug) dbg_leave("local_number");
        }

    
private TelephoneNumberlocal_phone_number()
Gets the local phone number.

return
the parsed phone number
exception
ParseException ifa parsing error occurs

        if (debug) dbg_enter("local_phone_number");
        TelephoneNumber tn = new TelephoneNumber();
        tn.setGlobal(false);
        NameValueList nv = null;
        String b = null;
        try {
            b = local_number();
            tn.setPhoneNumber(b);
            if (lexer.hasMoreChars()) {
                Token tok = this.lexer.peekNextToken();
                switch (tok.getTokenType()) {
                    case SEMICOLON: {
                        nv = tel_parameters();
                        tn.setParameters(nv);
                        break;
                    }
                    default: {
                        break;
                    }
                }
            }
        } finally {
            if (debug) dbg_leave("local_phone_number");
        }
        return tn;
    
protected java.lang.Stringmark()
Remembers the current stream position.

return
the current marked position

        if (debug) dbg_enter("mark");
        try {
            char next = lexer.lookAhead(0);
            if (isMark(next)) {
                lexer.consume(1);
                return new StringBuffer().append(next).toString();
            } else throw createParseException("mark");
        } finally {
            if (debug) dbg_leave("mark");
        }
    
protected java.lang.StringparamNameOrValue()
Name or value of a parameter.

return
parsed name or value
exception
ParseException if a parsing error occurs

        StringBuffer retval = new StringBuffer();
        while (lexer.hasMoreChars()) {
            char next = lexer.lookAhead(0);
            if (next == '[" || next == '[" || next == '/" ||
                    next == ':" || next == '&" || next == '+" ||
                    next == '$" || next == '#" || isUnreserved(next)) {
                retval.append(next);
                lexer.consume(1);
            } else if (isEscaped()) {
                String esc = lexer.charAsString(3);
                lexer.consume(3);
                retval.append(esc);
            } else break;
        }
        return retval.toString();
    
public URIparse()
Default parse method. This method just calls uriReference.

return
ths parsed URI
exception
ParseException if a parsing error occurs

        return uriReference();
    
public final TelephoneNumberparseTelephoneNumber()
Parses for telephone subscriber.

return
the parsed telephone number.
exception
ParseException if a parsing error occurs

        TelephoneNumber tn;

        if (debug) dbg_enter("telephone_subscriber");
        lexer.selectLexer("charLexer");
        try {
            char c = lexer.lookAhead(0);
            if (c == '+") tn = global_phone_number();
            else if (LexerCore.isAlpha(c) || LexerCore.isDigit(c) ||
                    c == '-" || c == '*" || c == '." ||
                    c == '(" || c == ')" || c == '#") {
                tn = local_phone_number();
            } else throw createParseException("unexpected char " + c);
            return tn;
        } finally {
            if (debug) dbg_leave("telephone_subscriber");
        }

    
public URIparseWholeString()
Parse method with checking the rest of input URL.

return
ths parsed URI
exception
ParseException if a parsing error occurs

        URI retValue = uriReference();
        if (lexer.hasMoreChars()) {
            throw createParseException("redundant symbols");
        }
        return retValue;
    
protected java.lang.Stringpassword()
Gets the password field from the URI.

return
ths parsed password field
exception
ParseException if a parsing error occurs

        StringBuffer retval = new StringBuffer();
        while (true) {
            char la = lexer.lookAhead(0);
            if (isUnreserved(la) || la == '&" || la == '=" ||
                    la == '+" || la == '$" || la == ',") {
                retval.append(la);
                lexer.consume(1);
            } else if (isEscaped()) {
                String esc = lexer.charAsString(3);
                retval.append(esc);
                // CR FIX from Jeff Haynie frm JAIN-SIP
                lexer.consume(3);
            } else break;
        }
        return retval.toString();
    
public java.lang.StringpeekScheme()
Peeks at the scheme field.

return
the protocol scheme
exception
ParseException if a parsing error occurs

        Vector tokens = lexer.peekNextToken(1);

        if (tokens.size() == 0)
            return null;

        String scheme = ((Token)tokens.elementAt(0)).getTokenValue();
        return scheme;
    
protected NameValueqheader()
Gets a name value for a given query header (ie one that comes after the ?).

return
name value pair for q-header
exception
ParseException if a parsing error occurs


        String name = lexer.getNextToken('=");
        lexer.consume(1);
        String value = hvalue();
        return new NameValue(name, value);

    
protected java.lang.Stringreserved()
Gets the listof reserved characters.

return
string of reserved characters.

        char next = lexer.lookAhead(0);
        if (isReserved(next)) {
            lexer.consume(1);
            return new StringBuffer().append(next).toString();
        } else throw createParseException("reserved");
    
public SipURIsipURL(Token token)
Parses and returns a structure for a SIP URL.

param
token the token of scheme (SIP or SIPS)
return
a URL structure for a SIP url.
throws
ParsException if there was a problem parsing.
throws
IllegalArgumentException when parsing error is fatal.

        if (debug) dbg_enter("sipURL");

        char la;
        SipURI retval = new SipURI();

        try {
            lexer.match(token.getTokenType());
            lexer.match(':");
            retval.setScheme(token.getTokenValue());
            if (!lexer.hasMoreChars()) { // sip: - server dedicated URI
                retval.setServer();
                return retval;
            }
            int m = lexer.markInputPosition();
            // get user part
            String user = user();
            if (!lexer.hasMoreChars()) { // nosymbols after user name  
                lexer.rewindInputPosition(m); // move to start of host
            } else { // check symbols after user name    
                // maybe sip:host
                la = lexer.lookAhead(0);
                // check that user field is not empty
                if (la == ':" || la == '@") { // sip:user@... or sip:user:pass
                    if (user.length() == 0) {
                        throw new
                            IllegalArgumentException("User field is missed");
                    }
                    if (la == ':") {
                        // sip:user:passw...
                        lexer.consume(1);
                        String password = password();
                        if (!lexer.hasMoreChars() 
                            || lexer.lookAhead(0) != '@") {
                            if (StringTokenizer.isDigitString(password)) {
                                // maybe sip:host:port - move to start of host
                                lexer.rewindInputPosition(m);
                            } else { // sip:user:pass<wrong symbol>
                                throw new IllegalArgumentException
                                    ("Expecting \"@\"");
                            }
                        } else { // sip:user:pass@...
                            retval.setUser(user);
                            retval.setUserPassword(password);
                            lexer.consume(1);
                        }
                    } else { // la == '@'
                        retval.setUser(user);
                        lexer.consume(1);
                    }
                } else { // no '@' after user field - maybe sip:host...
                    lexer.rewindInputPosition(m); // move to start of host
                }
            }
            // check for sip:*
            la = lexer.lookAhead(0);
            if (la == '*") {
                lexer.consume(1);
                // server shared URI
                retval.setServer();
                retval.setShared();
            } else if (la == ';") { // sip:;...
                retval.setServer();
            } else { // try to read host
                // host parsing
                HostNameParser hnp = new HostNameParser(this.getLexer());
                HostPort hp = hnp.hostPort();
                String host = hp.getHost().getHostname();
                if (host == null) { // maybe sip:5060
                    if (hp.hasPort()) { // port only - server URI
                        retval.setServer();
                    } else { // sip:1234:5678 - wrong format
                        throw new
                            IllegalArgumentException("Illegal URI format");
                    }
                }
                retval.setHostPort(hp);
            }

            lexer.selectLexer("charLexer");

            // parse parameters
            while (lexer.hasMoreChars()) {
                if (lexer.lookAhead(0) != ';") break;
                lexer.consume(1);
                NameValue parms = uriParam();
                if (retval.hasParameter(parms.getName())) {
                    throw new IllegalArgumentException(
                                "Found duplicate of parameter ");
                }
                if (parms.isValueQuoted() && !retval.isServer()) {
                    throw new IllegalArgumentException(
                        "Client URI cannot contain quoted parameter ");
                }
                retval.setUriParameter(parms);
            }

            if (lexer.hasMoreChars() && lexer.lookAhead(0) == '?") {
                if (retval.isServer()) {
                    throw new IllegalArgumentException(
                        "Server URI cannot contain headers");
                }
                lexer.consume(1);
                while (lexer.hasMoreChars()) {
                    NameValue parms = qheader();
                    retval.setQHeader(parms);
                    if (lexer.hasMoreChars() &&
                            lexer.lookAhead(0) != '&") break;
                    else lexer.consume(1);
                }
            }
            
            return retval;
        } finally {
            if (debug) dbg_leave("sipURL");
        }
    
public TelURLtelURL()
Parses and returns a structure for a Tel URL.

return
a parsed tel url structure.
exception
ParseException if a parsing error occurs

        lexer.match(TokenTypes.TEL);
        lexer.match(':");
        TelephoneNumber tn = this.parseTelephoneNumber();
        TelURL telUrl = new TelURL();
        telUrl.setTelephoneNumber(tn);
        return telUrl;
    
private NameValueListtel_parameters()
Gets the telephone field parameters.

return
the telephone parameters
exception
ParseException if a parsing error occurs

        NameValueList nvList = new NameValueList();
        while (lexer.hasMoreChars()) {
            lexer.consume(1);
            NameValue nv = uriParam();
            String nameParam = nv.getName();
            String valueParam = (String)(nv.getValue());
            if (nameParam.equalsIgnoreCase("isub")) {
                // RFC 2806, 2.2
                // isdn-subaddress       = ";isub=" 1*phonedigit
                // phonedigit            = DIGIT / visual-separator
                // visual-separator      = "-" / "." / "(" / ")"
                byte[] valueBytes = valueParam.getBytes();
                for (int i = 0; i < valueBytes.length; i++) {
                    if (!isPhoneDigit(valueBytes[i])) {
                        throw new 
                            IllegalArgumentException("Wrong isdn-subaddress");
                    }
                }
            } else if (nameParam.equalsIgnoreCase("postd")) {
                // RFC 2806, 2.2
                // post-dial             = ";postd=" 1*(phonedigit /
                //                         dtmf-digit / pause-character)
                // dtmf-digit            = "*" / "#" / "A" / "B" / "C" / "D"
                // pause-character       = one-second-pause / wait-for-dial-tone
                // one-second-pause      = "p"
                // wait-for-dial-tone    = "w"
                byte[] valueBytes = valueParam.getBytes();
                for (int i = 0; i < valueBytes.length; i++) {
                    int ch = valueBytes[i];
                    if (!isPhoneDigit(ch) && !isDtmf_digit(ch) &&
                        !isPauseChar(ch)) {
                        throw new 
                            IllegalArgumentException("Wrong post-dial");
                    }
                }
            }
            nvList.add(nv);
            char tok = lexer.lookAhead(0);
            if (tok == ';") continue;
            else break;
        }
        return nvList;
    
protected java.lang.Stringunreserved()
Gets the unreserved string of characters.

return
unreserved characters

        char next = lexer.lookAhead(0);
        if (isUnreserved(next)) {
            lexer.consume(1);
            return new StringBuffer().append(next).toString();
        } else throw createParseException("unreserved");

    
protected NameValueuriParam()
Gets the URI pamaeter.

return
the parsed URI parameter
exception
ParseException if a parsin error occurs

        if (debug) dbg_enter("uriParam");
        try {
            String pvalue = null;
            String pname = paramNameOrValue();
            char next = lexer.lookAhead(0);
            if (next == LexerCore.EQUALS) {
                lexer.consume(1);
                next = lexer.lookAhead(0);
                if (next == LexerCore.DOUBLEQUOTE) {
                    pvalue = 
                        "\"" + lexer.quotedString() + "\""; // quoted param
                } else { // unquoted parameter
                    pvalue = paramNameOrValue();
                }
            }
            return new NameValue(pname, (pvalue == null) ? "" : pvalue);
        } finally {
            if (debug) dbg_leave("uriParam");
        }
    
public URIuriReference()
Parses and return a structure for a generic URL. Note that non SIP URLs are just stored as a string (not parsed).

return
URI is a URL structure for a SIP url.
throws
ParsException if there was a problem parsing.

        if (debug) dbg_enter("uriReference");

        URI retval = null;
        Vector vect = lexer.peekNextToken(2);
        Token t1 = (Token) vect.elementAt(0);
        Token t2 = (Token) vect.elementAt(1);

        try {
            // System.out.println("token = " + t1.getTokenValue());
            // System.out.println("tokentype = " + t1.getTokenType());

            int type1, type2;
            type1 = t1.getTokenType();

            // Create an URI
            if (t2.getTokenType() == ':") {
                if (type1 == TokenTypes.SIP || type1 == TokenTypes.SIPS) {
                    // SIP or SIPS URL
                    retval = sipURL(t1);
                } else if (type1 == TokenTypes.TEL) {
                    // TEL URL
                    retval = telURL();
                } else {
                    // Generic URL
                    
                    /*
                     * We can't throw an exception here because according to
                     * RFC 3261, p. 224 the scheme may be different from
                     * sip/sips/tel.
                     * In Connector.open(scheme) only 'sip' and 'sips'
                     * schemes are allowed, but this is not a problem
                     * because the package path where VM will try to find
                     * Protocol.class contains the scheme's name and an
                     * exception will be thrown if it is invalid.
                     */
                    retval = new URI(lexer.getString('>"));
                    
                    int pos = lexer.markInputPosition();
                    // It's safe to use 'pos-1' because at least
                    // one character ('>') was read from the buffer.
                    lexer.rewindInputPosition(pos - 1);
                }
            } else {
                throw createParseException("Expecting \':\'");
            }

        } finally {
            if (debug) dbg_leave("uriReference");
        }

        return retval;
    
protected java.lang.Stringuric()
Gets the uric.

return
the uric

        if (debug) dbg_enter("uric");
        try {
            try {
                char la = lexer.lookAhead(0);
                if (isUnreserved(la)) {
                    lexer.consume(1);
                    return Lexer.charAsString(la);
                } else if (isReserved(la)) {
                    lexer.consume(1);
                    return Lexer.charAsString(la);
                } else if (isEscaped()) {
                    String retval = lexer.charAsString(3);
                    lexer.consume(3);
                    return retval;
                } else return null;
            } catch (ParseException ex) {
                return null;
            }
        } finally {
            if (debug) dbg_leave("uric");
        }

    
protected java.lang.StringuricNoSlash()
Gets the uric without slashes.

return
the uric string without slashes.

        if (debug) dbg_enter("uricNoSlash");
        try {
            try {
                char la = lexer.lookAhead(0);
                if (isEscaped()) {
                    String retval = lexer.charAsString(3);
                    lexer.consume(3);
                    return retval;
                } else if (isUnreserved(la)) {
                    lexer.consume(1);
                    return Lexer.charAsString(la);
                } else if (isReservedNoSlash(la)) {
                    lexer.consume(1);
                    return Lexer.charAsString(la);
                } else return null;
            } catch (ParseException ex) {
                return null;
            }
        } finally {
            if (debug) dbg_leave("uricNoSlash");
        }
    
protected java.lang.StringuricString()
Gets the uric string.

return
the uric string

        StringBuffer retval = new StringBuffer();
        while (true) {
            String next = uric();
            if (next == null) break;
            retval.append(next);
        }
        return retval.toString();
    
protected java.lang.StringurlString()
Scans forward until you hit a terminating character for a URL. We do not handle non sip urls in this implementation.

return
the string that takes us to the end of this URL (i.e. to the next delimiter).
exception
ParseException if a parsing error occurs

        StringBuffer retval = new StringBuffer();
        lexer.selectLexer("charLexer");

        while (lexer.hasMoreChars()) {
            char la = lexer.lookAhead(0);
            // Look for a character that can terminate a URL.
            if (la == ' " || la == '\t" || la == '\n" ||
                    la == '>" || la == '<") break;
            lexer.consume(0);
            retval.append(la);
        }
        return retval.toString();
    
protected java.lang.Stringuser()
Gets the user field from the URI.

return
ths parsed user field
exception
ParseException if a parsing error occurs


        if (debug) dbg_enter("user");
        try {
            StringBuffer retval = new StringBuffer();
            while (lexer.hasMoreChars()) {
                char la = lexer.lookAhead(0);
                if (isUnreserved(la) ||
                        isUserUnreserved(la)) {
                    retval.append(la);
                    lexer.consume(1);
                } else if (isEscaped()) {
                    String esc = lexer.charAsString(3);
                    lexer.consume(3);
                    retval.append(esc);
                } else break;
            }
            return retval.toString();
        } finally {
            if (debug) dbg_leave("user");
        }