001: /*
002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
003: * Reserved. Use is subject to license terms.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025: package gov.nist.siplite.parser;
026:
027: import gov.nist.siplite.address.*;
028: import gov.nist.core.*;
029: import java.util.Vector;
030:
031: /**
032: * Parser For SIP and Tel URLs. Other kinds of URL's are handled by the
033: * J2SE 1.4 URL class.
034: * @version JAIN-SIP-1.1
035: *
036: *
037: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
038: *
039: */
040: public class URLParser extends Parser {
041:
042: /** Symbols of phone digit (RFC 2806, 2.2) */
043: private static final String PHONE_DIGIT = "01234567890-.()";
044:
045: /** Symbols of dtmf digit (RFC 2806, 2.2) */
046: private static final String DTMF_DIGIT = "*#ABCD";
047:
048: /** Pause characters (RFC 2806, 2.2) */
049: private static final String PAUSE_CHAR = "pw";
050:
051: /**
052: * Constructor with initial URL string.
053: * @param url initial URL
054: */
055: public URLParser(String url) {
056: this .lexer = new Lexer("sip_urlLexer", url);
057:
058: }
059:
060: /**
061: * Constructor with initial lexer engine.
062: * @param lexer initial lexer engine
063: */
064: URLParser(Lexer lexer) {
065: this .lexer = lexer;
066: this .lexer.selectLexer("sip_urlLexer");
067: }
068:
069: /**
070: * Checks if character is punctuation mark.
071: * @param next character to be checked
072: * @return true if character is punctuation mark
073: */
074: protected static boolean isMark(char next) {
075: return next == '-' || next == '_' || next == '.' || next == '!'
076: || next == '~' || next == '*' || next == '\''
077: || next == '(' || next == ')';
078: }
079:
080: /**
081: * Checks if character is reserved.
082: * @param next character to be checked
083: * @return true if reserved character.
084: */
085: protected static boolean isUnreserved(char next) {
086: return Lexer.isAlpha(next) || Lexer.isDigit(next)
087: || isMark(next);
088:
089: }
090:
091: /**
092: * Checks if reserved character without a slash.
093: * @param next character to be checked
094: * @return true if reserved character without a slash
095: */
096: protected static boolean isReservedNoSlash(char next) {
097: return next == ';' || next == '?' || next == ':' || next == '@'
098: || next == '&' || next == '+' || next == '$'
099: || next == ',';
100:
101: }
102:
103: // Missing '=' CR in character set - discovered by interop testing
104: // at SIPIT 13 by Bob Johnson and Scott Holben.
105: // Replace . by;
106: /**
107: * Checks if user is unreserved.
108: * @param la the character to be checked
109: * @return true if user is not reserved
110: */
111: protected static boolean isUserUnreserved(char la) {
112: return la == '&' || la == '?' || la == '+' || la == '$'
113: || la == '#' || la == '/' || la == ',' || la == ';'
114: || la == '=';
115: }
116:
117: /**
118: * Gets the unreserved string of characters.
119: * @return unreserved characters
120: */
121: protected String unreserved() throws ParseException {
122: char next = lexer.lookAhead(0);
123: if (isUnreserved(next)) {
124: lexer.consume(1);
125: return new StringBuffer().append(next).toString();
126: } else
127: throw createParseException("unreserved");
128:
129: }
130:
131: /**
132: * Name or value of a parameter.
133: * @return parsed name or value
134: * @exception ParseException if a parsing error occurs
135: */
136: protected String paramNameOrValue() throws ParseException {
137: StringBuffer retval = new StringBuffer();
138: while (lexer.hasMoreChars()) {
139: char next = lexer.lookAhead(0);
140: if (next == '[' || next == '[' || next == '/'
141: || next == ':' || next == '&' || next == '+'
142: || next == '$' || next == '#' || isUnreserved(next)) {
143: retval.append(next);
144: lexer.consume(1);
145: } else if (isEscaped()) {
146: String esc = lexer.charAsString(3);
147: lexer.consume(3);
148: retval.append(esc);
149: } else
150: break;
151: }
152: return retval.toString();
153: }
154:
155: /**
156: * Gets the URI pamaeter.
157: * @return the parsed URI parameter
158: * @exception ParseException if a parsin error occurs
159: */
160: protected NameValue uriParam() throws ParseException {
161: if (debug)
162: dbg_enter("uriParam");
163: try {
164: String pvalue = null;
165: String pname = paramNameOrValue();
166: char next = lexer.lookAhead(0);
167: if (next == LexerCore.EQUALS) {
168: lexer.consume(1);
169: next = lexer.lookAhead(0);
170: if (next == LexerCore.DOUBLEQUOTE) {
171: pvalue = "\"" + lexer.quotedString() + "\""; // quoted param
172: } else { // unquoted parameter
173: pvalue = paramNameOrValue();
174: }
175: }
176: return new NameValue(pname, (pvalue == null) ? "" : pvalue);
177: } finally {
178: if (debug)
179: dbg_leave("uriParam");
180: }
181: }
182:
183: /**
184: * Checks if character is reserved.
185: * @param next character to be checked
186: * @return true if character is reserved
187: */
188: protected static boolean isReserved(char next) {
189: return next == ';' || next == '/' || next == '?' || next == ':'
190: || next == '@' || next == '&' || next == '+'
191: || next == '$' || next == '=' || next == ',';
192: }
193:
194: /**
195: * Gets the listof reserved characters.
196: * @return string of reserved characters.
197: */
198: protected String reserved() throws ParseException {
199: char next = lexer.lookAhead(0);
200: if (isReserved(next)) {
201: lexer.consume(1);
202: return new StringBuffer().append(next).toString();
203: } else
204: throw createParseException("reserved");
205: }
206:
207: /**
208: * Checks if current character is escaped.
209: * @return true if processing an escaped sequenec
210: */
211: protected boolean isEscaped() {
212: try {
213: char next = lexer.lookAhead(0);
214: char next1 = lexer.lookAhead(1);
215: char next2 = lexer.lookAhead(2);
216: return (next == '%' && Lexer.isHexDigit(next1) && Lexer
217: .isHexDigit(next2));
218: } catch (ParseException ex) {
219: return false;
220: }
221: }
222:
223: /**
224: * Gets the escaped character sequence.
225: * @return the escaped character sequence
226: */
227: protected String escaped() throws ParseException {
228: if (debug)
229: dbg_enter("escaped");
230: try {
231: StringBuffer retval = new StringBuffer();
232: char next = lexer.lookAhead(0);
233: char next1 = lexer.lookAhead(1);
234: char next2 = lexer.lookAhead(2);
235: if (next == '%' && Lexer.isHexDigit(next1)
236: && Lexer.isHexDigit(next2)) {
237: lexer.consume(3);
238: retval.append(next);
239: retval.append(next1);
240: retval.append(next2);
241: } else
242: throw createParseException("escaped");
243: return retval.toString();
244: } finally {
245: if (debug)
246: dbg_leave("escaped");
247: }
248: }
249:
250: /**
251: * Remembers the current stream position.
252: * @return the current marked position
253: */
254: protected String mark() throws ParseException {
255: if (debug)
256: dbg_enter("mark");
257: try {
258: char next = lexer.lookAhead(0);
259: if (isMark(next)) {
260: lexer.consume(1);
261: return new StringBuffer().append(next).toString();
262: } else
263: throw createParseException("mark");
264: } finally {
265: if (debug)
266: dbg_leave("mark");
267: }
268: }
269:
270: /**
271: * Gets the uric.
272: * @return the uric
273: */
274: protected String uric() {
275: if (debug)
276: dbg_enter("uric");
277: try {
278: try {
279: char la = lexer.lookAhead(0);
280: if (isUnreserved(la)) {
281: lexer.consume(1);
282: return Lexer.charAsString(la);
283: } else if (isReserved(la)) {
284: lexer.consume(1);
285: return Lexer.charAsString(la);
286: } else if (isEscaped()) {
287: String retval = lexer.charAsString(3);
288: lexer.consume(3);
289: return retval;
290: } else
291: return null;
292: } catch (ParseException ex) {
293: return null;
294: }
295: } finally {
296: if (debug)
297: dbg_leave("uric");
298: }
299:
300: }
301:
302: /**
303: * Gets the uric without slashes.
304: * @return the uric string without slashes.
305: */
306: protected String uricNoSlash() {
307: if (debug)
308: dbg_enter("uricNoSlash");
309: try {
310: try {
311: char la = lexer.lookAhead(0);
312: if (isEscaped()) {
313: String retval = lexer.charAsString(3);
314: lexer.consume(3);
315: return retval;
316: } else if (isUnreserved(la)) {
317: lexer.consume(1);
318: return Lexer.charAsString(la);
319: } else if (isReservedNoSlash(la)) {
320: lexer.consume(1);
321: return Lexer.charAsString(la);
322: } else
323: return null;
324: } catch (ParseException ex) {
325: return null;
326: }
327: } finally {
328: if (debug)
329: dbg_leave("uricNoSlash");
330: }
331: }
332:
333: /**
334: * Gets the uric string.
335: * @return the uric string
336: */
337: protected String uricString() {
338: StringBuffer retval = new StringBuffer();
339: while (true) {
340: String next = uric();
341: if (next == null)
342: break;
343: retval.append(next);
344: }
345: return retval.toString();
346: }
347:
348: /**
349: * Parses and return a structure for a generic URL.
350: * Note that non SIP URLs are just stored as a string (not parsed).
351: * @return URI is a URL structure for a SIP url.
352: * @throws ParsException if there was a problem parsing.
353: */
354: public URI uriReference() throws ParseException {
355: if (debug)
356: dbg_enter("uriReference");
357:
358: URI retval = null;
359: Vector vect = lexer.peekNextToken(2);
360: Token t1 = (Token) vect.elementAt(0);
361: Token t2 = (Token) vect.elementAt(1);
362:
363: try {
364: // System.out.println("token = " + t1.getTokenValue());
365: // System.out.println("tokentype = " + t1.getTokenType());
366:
367: int type1, type2;
368: type1 = t1.getTokenType();
369:
370: // Create an URI
371: if (t2.getTokenType() == ':') {
372: if (type1 == TokenTypes.SIP || type1 == TokenTypes.SIPS) {
373: // SIP or SIPS URL
374: retval = sipURL(t1);
375: } else if (type1 == TokenTypes.TEL) {
376: // TEL URL
377: retval = telURL();
378: } else {
379: // Generic URL
380:
381: /*
382: * We can't throw an exception here because according to
383: * RFC 3261, p. 224 the scheme may be different from
384: * sip/sips/tel.
385: * In Connector.open(scheme) only 'sip' and 'sips'
386: * schemes are allowed, but this is not a problem
387: * because the package path where VM will try to find
388: * Protocol.class contains the scheme's name and an
389: * exception will be thrown if it is invalid.
390: */
391: retval = new URI(lexer.getString('>'));
392:
393: int pos = lexer.markInputPosition();
394: // It's safe to use 'pos-1' because at least
395: // one character ('>') was read from the buffer.
396: lexer.rewindInputPosition(pos - 1);
397: }
398: } else {
399: throw createParseException("Expecting \':\'");
400: }
401:
402: } finally {
403: if (debug)
404: dbg_leave("uriReference");
405: }
406:
407: return retval;
408: }
409:
410: /**
411: * Parses for the base phone number.
412: * @return the base phone number
413: * @exception ParseException if a parsingerror occurs
414: */
415: private String base_phone_number() throws ParseException {
416: StringBuffer s = new StringBuffer();
417:
418: if (debug)
419: dbg_enter("base_phone_number");
420: try {
421: int lc = 0;
422: while (lexer.hasMoreChars()) {
423: char w = lexer.lookAhead(0);
424: if (LexerCore.isDigit(w) || w == '-' || w == '.'
425: || w == '(' || w == ')') {
426: lexer.consume(1);
427: s.append(w);
428: lc++;
429: } else if (lc > 0)
430: break;
431: else
432: throw createParseException("unexpected " + w);
433: }
434: return s.toString();
435: } finally {
436: if (debug)
437: dbg_leave("base_phone_number");
438: }
439:
440: }
441:
442: /**
443: * Parses for the local phone number.
444: * @return the local phone number
445: * @exception ParseException if a parsing error occurs
446: */
447: private String local_number() throws ParseException {
448: StringBuffer s = new StringBuffer();
449: if (debug)
450: dbg_enter("local_number");
451: try {
452: int lc = 0;
453: while (lexer.hasMoreChars()) {
454: char la = lexer.lookAhead(0);
455: if (la == '*' || la == '#' || la == '-' || la == '.'
456: || la == '(' || la == ')'
457: || LexerCore.isDigit(la)) {
458: lexer.consume(1);
459: s.append(la);
460: lc++;
461: } else if (lc > 0)
462: break;
463: else
464: throw createParseException("unexepcted " + la);
465: }
466: return s.toString();
467: } finally {
468: if (debug)
469: dbg_leave("local_number");
470: }
471:
472: }
473:
474: /**
475: * Parses for telephone subscriber.
476: *
477: * @return the parsed telephone number.
478: * @exception ParseException if a parsing error occurs
479: */
480: public final TelephoneNumber parseTelephoneNumber()
481: throws ParseException {
482: TelephoneNumber tn;
483:
484: if (debug)
485: dbg_enter("telephone_subscriber");
486: lexer.selectLexer("charLexer");
487: try {
488: char c = lexer.lookAhead(0);
489: if (c == '+')
490: tn = global_phone_number();
491: else if (LexerCore.isAlpha(c) || LexerCore.isDigit(c)
492: || c == '-' || c == '*' || c == '.' || c == '('
493: || c == ')' || c == '#') {
494: tn = local_phone_number();
495: } else
496: throw createParseException("unexpected char " + c);
497: return tn;
498: } finally {
499: if (debug)
500: dbg_leave("telephone_subscriber");
501: }
502:
503: }
504:
505: /**
506: * Gets the global phone number.
507: * @return the parsed phone number
508: * @exception ParseException ifa parsing error occurs
509: */
510: private final TelephoneNumber global_phone_number()
511: throws ParseException {
512: if (debug)
513: dbg_enter("global_phone_number");
514: try {
515: TelephoneNumber tn = new TelephoneNumber();
516: tn.setGlobal(true);
517: NameValueList nv = null;
518: this .lexer.match(PLUS);
519: String b = base_phone_number();
520: tn.setPhoneNumber(b);
521: if (lexer.hasMoreChars()) {
522: char tok = lexer.lookAhead(0);
523: if (tok == ';') {
524: nv = tel_parameters();
525: tn.setParameters(nv);
526: }
527: }
528: return tn;
529: } finally {
530: if (debug)
531: dbg_leave("global_phone_number");
532: }
533: }
534:
535: /**
536: * Gets the local phone number.
537: * @return the parsed phone number
538: * @exception ParseException ifa parsing error occurs
539: */
540: private TelephoneNumber local_phone_number() throws ParseException {
541: if (debug)
542: dbg_enter("local_phone_number");
543: TelephoneNumber tn = new TelephoneNumber();
544: tn.setGlobal(false);
545: NameValueList nv = null;
546: String b = null;
547: try {
548: b = local_number();
549: tn.setPhoneNumber(b);
550: if (lexer.hasMoreChars()) {
551: Token tok = this .lexer.peekNextToken();
552: switch (tok.getTokenType()) {
553: case SEMICOLON: {
554: nv = tel_parameters();
555: tn.setParameters(nv);
556: break;
557: }
558: default: {
559: break;
560: }
561: }
562: }
563: } finally {
564: if (debug)
565: dbg_leave("local_phone_number");
566: }
567: return tn;
568: }
569:
570: /**
571: * Gets the telephone field parameters.
572: * @return the telephone parameters
573: * @exception ParseException if a parsing error occurs
574: */
575: private NameValueList tel_parameters() throws ParseException {
576: NameValueList nvList = new NameValueList();
577: while (lexer.hasMoreChars()) {
578: lexer.consume(1);
579: NameValue nv = uriParam();
580: String nameParam = nv.getName();
581: String valueParam = (String) (nv.getValue());
582: if (nameParam.equalsIgnoreCase("isub")) {
583: // RFC 2806, 2.2
584: // isdn-subaddress = ";isub=" 1*phonedigit
585: // phonedigit = DIGIT / visual-separator
586: // visual-separator = "-" / "." / "(" / ")"
587: byte[] valueBytes = valueParam.getBytes();
588: for (int i = 0; i < valueBytes.length; i++) {
589: if (!isPhoneDigit(valueBytes[i])) {
590: throw new IllegalArgumentException(
591: "Wrong isdn-subaddress");
592: }
593: }
594: } else if (nameParam.equalsIgnoreCase("postd")) {
595: // RFC 2806, 2.2
596: // post-dial = ";postd=" 1*(phonedigit /
597: // dtmf-digit / pause-character)
598: // dtmf-digit = "*" / "#" / "A" / "B" / "C" / "D"
599: // pause-character = one-second-pause / wait-for-dial-tone
600: // one-second-pause = "p"
601: // wait-for-dial-tone = "w"
602: byte[] valueBytes = valueParam.getBytes();
603: for (int i = 0; i < valueBytes.length; i++) {
604: int ch = valueBytes[i];
605: if (!isPhoneDigit(ch) && !isDtmf_digit(ch)
606: && !isPauseChar(ch)) {
607: throw new IllegalArgumentException(
608: "Wrong post-dial");
609: }
610: }
611: }
612: nvList.add(nv);
613: char tok = lexer.lookAhead(0);
614: if (tok == ';')
615: continue;
616: else
617: break;
618: }
619: return nvList;
620: }
621:
622: /**
623: * Checks that the given symbol os a phonedigit.
624: * RFC 2806, 2.2
625: * phonedigit = DIGIT / visual-separator
626: * visual-separator = "-" / "." / "(" / ")"
627: * @param ch is a given byte to checking
628: * @return true if the given char is a phonedigit
629: */
630: private boolean isPhoneDigit(int ch) {
631: return isCharFromString(ch, PHONE_DIGIT);
632: }
633:
634: /**
635: * Checks that the given symbol is a dtmf digit.
636: * RFC 2806, 2.2
637: * dtmf-digit = "*" / "#" / "A" / "B" / "C" / "D"
638: * @param ch is a given byte to checking
639: * @return true if the given char is a dtmf digit
640: */
641: private boolean isDtmf_digit(int ch) {
642: return isCharFromString(ch, DTMF_DIGIT);
643: }
644:
645: /**
646: * Checks that the given symbol is a pause character.
647: * pause-character = one-second-pause / wait-for-dial-tone
648: * one-second-pause = "p"
649: * wait-for-dial-tone = "w"
650: * @param ch is a given byte to checking
651: * @return true if the given char is a dtmf digit
652: */
653: private boolean isPauseChar(int ch) {
654: return isCharFromString(ch, PAUSE_CHAR);
655: }
656:
657: /**
658: * Checks that the given symbol contains into a string.
659: * @param ch is a given byte to checking
660: * @param str is a string for checking
661: * @return true if the given char can be find in a string
662: */
663: private boolean isCharFromString(int ch, String str) {
664: return (str.indexOf(ch) != -1);
665: }
666:
667: /**
668: * Parses and returns a structure for a Tel URL.
669: * @return a parsed tel url structure.
670: * @exception ParseException if a parsing error occurs
671: */
672: public TelURL telURL() throws ParseException {
673: lexer.match(TokenTypes.TEL);
674: lexer.match(':');
675: TelephoneNumber tn = this .parseTelephoneNumber();
676: TelURL telUrl = new TelURL();
677: telUrl.setTelephoneNumber(tn);
678: return telUrl;
679: }
680:
681: /**
682: * Parses and returns a structure for a SIP URL.
683: * @param token the token of scheme (SIP or SIPS)
684: * @return a URL structure for a SIP url.
685: * @throws ParsException if there was a problem parsing.
686: * @throws IllegalArgumentException when parsing error is fatal.
687: */
688: public SipURI sipURL(Token token) throws ParseException {
689: if (debug)
690: dbg_enter("sipURL");
691:
692: char la;
693: SipURI retval = new SipURI();
694:
695: try {
696: lexer.match(token.getTokenType());
697: lexer.match(':');
698: retval.setScheme(token.getTokenValue());
699: if (!lexer.hasMoreChars()) { // sip: - server dedicated URI
700: retval.setServer();
701: return retval;
702: }
703: int m = lexer.markInputPosition();
704: // get user part
705: String user = user();
706: if (!lexer.hasMoreChars()) { // nosymbols after user name
707: lexer.rewindInputPosition(m); // move to start of host
708: } else { // check symbols after user name
709: // maybe sip:host
710: la = lexer.lookAhead(0);
711: // check that user field is not empty
712: if (la == ':' || la == '@') { // sip:user@... or sip:user:pass
713: if (user.length() == 0) {
714: throw new IllegalArgumentException(
715: "User field is missed");
716: }
717: if (la == ':') {
718: // sip:user:passw...
719: lexer.consume(1);
720: String password = password();
721: if (!lexer.hasMoreChars()
722: || lexer.lookAhead(0) != '@') {
723: if (StringTokenizer.isDigitString(password)) {
724: // maybe sip:host:port - move to start of host
725: lexer.rewindInputPosition(m);
726: } else { // sip:user:pass<wrong symbol>
727: throw new IllegalArgumentException(
728: "Expecting \"@\"");
729: }
730: } else { // sip:user:pass@...
731: retval.setUser(user);
732: retval.setUserPassword(password);
733: lexer.consume(1);
734: }
735: } else { // la == '@'
736: retval.setUser(user);
737: lexer.consume(1);
738: }
739: } else { // no '@' after user field - maybe sip:host...
740: lexer.rewindInputPosition(m); // move to start of host
741: }
742: }
743: // check for sip:*
744: la = lexer.lookAhead(0);
745: if (la == '*') {
746: lexer.consume(1);
747: // server shared URI
748: retval.setServer();
749: retval.setShared();
750: } else if (la == ';') { // sip:;...
751: retval.setServer();
752: } else { // try to read host
753: // host parsing
754: HostNameParser hnp = new HostNameParser(this .getLexer());
755: HostPort hp = hnp.hostPort();
756: String host = hp.getHost().getHostname();
757: if (host == null) { // maybe sip:5060
758: if (hp.hasPort()) { // port only - server URI
759: retval.setServer();
760: } else { // sip:1234:5678 - wrong format
761: throw new IllegalArgumentException(
762: "Illegal URI format");
763: }
764: }
765: retval.setHostPort(hp);
766: }
767:
768: lexer.selectLexer("charLexer");
769:
770: // parse parameters
771: while (lexer.hasMoreChars()) {
772: if (lexer.lookAhead(0) != ';')
773: break;
774: lexer.consume(1);
775: NameValue parms = uriParam();
776: if (retval.hasParameter(parms.getName())) {
777: throw new IllegalArgumentException(
778: "Found duplicate of parameter ");
779: }
780: if (parms.isValueQuoted() && !retval.isServer()) {
781: throw new IllegalArgumentException(
782: "Client URI cannot contain quoted parameter ");
783: }
784: retval.setUriParameter(parms);
785: }
786:
787: if (lexer.hasMoreChars() && lexer.lookAhead(0) == '?') {
788: if (retval.isServer()) {
789: throw new IllegalArgumentException(
790: "Server URI cannot contain headers");
791: }
792: lexer.consume(1);
793: while (lexer.hasMoreChars()) {
794: NameValue parms = qheader();
795: retval.setQHeader(parms);
796: if (lexer.hasMoreChars()
797: && lexer.lookAhead(0) != '&')
798: break;
799: else
800: lexer.consume(1);
801: }
802: }
803:
804: return retval;
805: } finally {
806: if (debug)
807: dbg_leave("sipURL");
808: }
809: }
810:
811: /**
812: * Peeks at the scheme field.
813: * @return the protocol scheme
814: * @exception ParseException if a parsing error occurs
815: */
816: public String peekScheme() throws ParseException {
817: Vector tokens = lexer.peekNextToken(1);
818:
819: if (tokens.size() == 0)
820: return null;
821:
822: String scheme = ((Token) tokens.elementAt(0)).getTokenValue();
823: return scheme;
824: }
825:
826: /**
827: * Gets a name value for a given query header (ie one that comes
828: * after the ?).
829: * @return name value pair for q-header
830: * @exception ParseException if a parsing error occurs
831: */
832: protected NameValue qheader() throws ParseException {
833:
834: String name = lexer.getNextToken('=');
835: lexer.consume(1);
836: String value = hvalue();
837: return new NameValue(name, value);
838:
839: }
840:
841: /**
842: * Gets a header value.
843: * @return value of current header
844: * @exception ParseException if a parsing error occurs
845: */
846: protected String hvalue() throws ParseException {
847: StringBuffer retval = new StringBuffer();
848: while (lexer.hasMoreChars()) {
849: char la = lexer.lookAhead(0);
850: // Look for a character that can terminate a URL.
851: if (la == '+' || la == '?' || la == ':' || la == '@'
852: || la == '[' || la == ']' || la == '/' || la == '$'
853: || la == '_' || la == '-' || la == '"' || la == '!'
854: || la == '~' || la == '*' || la == '.' || la == '('
855: || la == ')' || LexerCore.isAlpha(la)
856: || LexerCore.isDigit(la)) {
857: lexer.consume(1);
858: retval.append(la);
859: } else if (la == '%') {
860: retval.append(escaped());
861: } else
862: break;
863: }
864: return retval.toString();
865: }
866:
867: /**
868: * Scans forward until you hit a terminating character for a URL.
869: * We do not handle non sip urls in this implementation.
870: * @return the string that takes us to the end of this URL (i.e. to
871: * the next delimiter).
872: * @exception ParseException if a parsing error occurs
873: */
874: protected String urlString() throws ParseException {
875: StringBuffer retval = new StringBuffer();
876: lexer.selectLexer("charLexer");
877:
878: while (lexer.hasMoreChars()) {
879: char la = lexer.lookAhead(0);
880: // Look for a character that can terminate a URL.
881: if (la == ' ' || la == '\t' || la == '\n' || la == '>'
882: || la == '<')
883: break;
884: lexer.consume(0);
885: retval.append(la);
886: }
887: return retval.toString();
888: }
889:
890: /**
891: * Gets the user field from the URI.
892: * @return ths parsed user field
893: * @exception ParseException if a parsing error occurs
894: */
895: protected String user() throws ParseException {
896:
897: if (debug)
898: dbg_enter("user");
899: try {
900: StringBuffer retval = new StringBuffer();
901: while (lexer.hasMoreChars()) {
902: char la = lexer.lookAhead(0);
903: if (isUnreserved(la) || isUserUnreserved(la)) {
904: retval.append(la);
905: lexer.consume(1);
906: } else if (isEscaped()) {
907: String esc = lexer.charAsString(3);
908: lexer.consume(3);
909: retval.append(esc);
910: } else
911: break;
912: }
913: return retval.toString();
914: } finally {
915: if (debug)
916: dbg_leave("user");
917: }
918:
919: }
920:
921: /**
922: * Gets the password field from the URI.
923: * @return ths parsed password field
924: * @exception ParseException if a parsing error occurs
925: */
926: protected String password() throws ParseException {
927: StringBuffer retval = new StringBuffer();
928: while (true) {
929: char la = lexer.lookAhead(0);
930: if (isUnreserved(la) || la == '&' || la == '=' || la == '+'
931: || la == '$' || la == ',') {
932: retval.append(la);
933: lexer.consume(1);
934: } else if (isEscaped()) {
935: String esc = lexer.charAsString(3);
936: retval.append(esc);
937: // CR FIX from Jeff Haynie frm JAIN-SIP
938: lexer.consume(3);
939: } else
940: break;
941: }
942: return retval.toString();
943: }
944:
945: /**
946: * Default parse method. This method just calls uriReference.
947: * @return ths parsed URI
948: * @exception ParseException if a parsing error occurs
949: */
950: public URI parse() throws ParseException {
951: return uriReference();
952: }
953:
954: /**
955: * Parse method with checking the rest of input URL.
956: * @return ths parsed URI
957: * @exception ParseException if a parsing error occurs
958: */
959: public URI parseWholeString() throws ParseException {
960: URI retValue = uriReference();
961: if (lexer.hasMoreChars()) {
962: throw createParseException("redundant symbols");
963: }
964: return retValue;
965: }
966: }
|