KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > lib > UrlModule


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.quercus.lib;
31
32 import com.caucho.quercus.annotation.Optional;
33 import com.caucho.quercus.env.*;
34 import com.caucho.quercus.lib.file.BinaryInput;
35 import com.caucho.quercus.lib.file.BinaryStream;
36 import com.caucho.quercus.lib.file.FileModule;
37 import com.caucho.quercus.module.AbstractQuercusModule;
38 import com.caucho.util.Base64;
39 import com.caucho.util.CharBuffer;
40 import com.caucho.util.L10N;
41 import com.caucho.vfs.TempBuffer;
42
43 import java.io.IOException JavaDoc;
44 import java.io.InputStream JavaDoc;
45 import java.io.InputStreamReader JavaDoc;
46 import java.io.LineNumberReader JavaDoc;
47 import java.io.OutputStream JavaDoc;
48 import java.io.OutputStreamWriter JavaDoc;
49 import java.net.Socket JavaDoc;
50 import java.net.URL JavaDoc;
51 import java.util.LinkedHashMap JavaDoc;
52 import java.util.Map JavaDoc;
53 import java.util.Set JavaDoc;
54 import java.util.logging.Logger JavaDoc;
55
56 /**
57  * PHP URL
58  */

59 public class UrlModule extends AbstractQuercusModule {
60   private static final L10N L = new L10N(UrlModule.class);
61   private static final Logger JavaDoc log
62     = Logger.getLogger(UrlModule.class.getName());
63
64   /**
65    * Encodes base64
66    */

67   public static String JavaDoc base64_encode(InputStream JavaDoc is)
68   {
69     CharBuffer cb = new CharBuffer();
70
71     TempBuffer tb = TempBuffer.allocate();
72     byte []buffer = tb.getBuffer();
73
74     int len;
75     int offset = 0;
76
77     try {
78       while ((len = is.read(buffer, offset, buffer.length)) >= 0) {
79         int tail = len % 3;
80
81         Base64.encode(cb, buffer, 0, len - tail);
82
83         System.arraycopy(buffer, len - tail, buffer, 0, tail);
84         offset = tail;
85       }
86
87       if (offset > 0)
88         Base64.encode(cb, buffer, 0, offset);
89     } catch (IOException JavaDoc e) {
90       throw new RuntimeException JavaDoc(e);
91     }
92
93     TempBuffer.free(tb);
94
95     return cb.toString();
96   }
97
98   /**
99    * Decodes base64
100    */

101   public static String JavaDoc base64_decode(String JavaDoc str)
102   {
103     if (str == null)
104       return "";
105
106     return Base64.decode(str);
107   }
108
109   /**
110    * Creates a http string.
111    */

112   public String JavaDoc http_build_query(Value value,
113                                  @Optional String JavaDoc prefix)
114   {
115     StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
116
117     int index = 0;
118     if (value instanceof ArrayValue) {
119       ArrayValue array = (ArrayValue) value;
120
121       for (Map.Entry JavaDoc<Value,Value> entry : array.entrySet()) {
122         Value keyValue = entry.getKey();
123         Value v = entry.getValue();
124
125         String JavaDoc key;
126
127         if (keyValue.isLongConvertible())
128           key = prefix + keyValue;
129         else
130           key = keyValue.toString();
131
132         if (v instanceof ArrayValue)
133           http_build_query(sb, key, (ArrayValue) v);
134         else {
135           if (sb.length() > 0)
136             sb.append('&');
137
138           sb.append(key);
139           sb.append('=');
140           urlencode(sb, v.toString());
141         }
142       }
143     }
144
145     return sb.toString();
146   }
147
148   /**
149    * Creates a http string.
150    */

151   private void http_build_query(StringBuilder JavaDoc sb,
152                                 String JavaDoc prefix,
153                                 ArrayValue array)
154   {
155     for (Map.Entry JavaDoc<Value,Value> entry : array.entrySet()) {
156       Value keyValue = entry.getKey();
157       Value v = entry.getValue();
158
159       String JavaDoc key = prefix + '[' + keyValue + ']';
160
161       if (v instanceof ArrayValue)
162         http_build_query(sb, key, (ArrayValue) v);
163       else {
164         if (sb.length() > 0)
165           sb.append('&');
166
167         sb.append(key);
168         sb.append('=');
169         urlencode(sb, v.toString());
170       }
171     }
172   }
173
174   /**
175    * Gets the magic quotes value.
176    */

177   public static String JavaDoc urlencode(String JavaDoc str)
178   {
179     StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
180
181     urlencode(sb, str);
182
183     return sb.toString();
184   }
185
186   /**
187    * Gets the magic quotes value.
188    */

189   private static void urlencode(StringBuilder JavaDoc sb, String JavaDoc str)
190   {
191     int len = str.length();
192
193     for (int i = 0; i < len; i++) {
194       char ch = str.charAt(i);
195
196       if ('a' <= ch && ch <= 'z')
197         sb.append(ch);
198       else if ('A' <= ch && ch <= 'Z')
199         sb.append(ch);
200       else if ('0' <= ch && ch <= '9')
201         sb.append(ch);
202       else if (ch == '-' || ch == '_' || ch == '.')
203         sb.append(ch);
204       else if (ch == ' ')
205         sb.append('+');
206       else {
207         sb.append('%');
208         sb.append(toHexDigit(ch / 16));
209         sb.append(toHexDigit(ch));
210       }
211     }
212   }
213
214   /**
215    * Returns the decoded string.
216    */

217   public static String JavaDoc urldecode(String JavaDoc s)
218   {
219     int len = s.length();
220     StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
221
222     for (int i = 0; i < len; i++) {
223       char ch = s.charAt(i);
224
225       if (ch == '%' && i + 2 < len) {
226         int d1 = s.charAt(i + 1);
227         int d2 = s.charAt(i + 2);
228
229         int v = 0;
230
231         if ('0' <= d1 && d1 <= '9')
232           v = 16 * (d1 - '0');
233         else if ('a' <= d1 && d1 <= 'f')
234           v = 16 * (d1 - 'a' + 10);
235         else if ('A' <= d1 && d1 <= 'F')
236           v = 16 * (d1 - 'A' + 10);
237         else {
238           sb.append('%');
239           continue;
240         }
241
242         if ('0' <= d2 && d2 <= '9')
243           v += (d2 - '0');
244         else if ('a' <= d2 && d2 <= 'f')
245           v += (d2 - 'a' + 10);
246         else if ('A' <= d2 && d2 <= 'F')
247           v += (d2 - 'A' + 10);
248         else {
249           sb.append('%');
250           continue;
251         }
252
253         i += 2;
254         sb.append((char) v);
255       }
256       else if (ch == '+')
257         sb.append(' ');
258       else
259         sb.append(ch);
260     }
261
262     return sb.toString();
263   }
264
265   /**
266    * Returns the decoded string.
267    */

268   public static String JavaDoc rawurldecode(String JavaDoc s)
269   {
270     if (s == null)
271       return "";
272
273     int len = s.length();
274     StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
275
276     for (int i = 0; i < len; i++) {
277       char ch = s.charAt(i);
278
279       if (ch == '%' && i + 2 < len) {
280         int d1 = s.charAt(i + 1);
281         int d2 = s.charAt(i + 2);
282
283         int v = 0;
284
285         if ('0' <= d1 && d1 <= '9')
286           v = 16 * (d1 - '0');
287         else if ('a' <= d1 && d1 <= 'f')
288           v = 16 * (d1 - 'a' + 10);
289         else if ('A' <= d1 && d1 <= 'F')
290           v = 16 * (d1 - 'A' + 10);
291         else {
292           sb.append('%');
293           continue;
294         }
295
296         if ('0' <= d2 && d2 <= '9')
297           v += (d2 - '0');
298         else if ('a' <= d2 && d2 <= 'f')
299           v += (d2 - 'a' + 10);
300         else if ('A' <= d2 && d2 <= 'F')
301           v += (d2 - 'A' + 10);
302         else {
303           sb.append('%');
304           continue;
305         }
306
307         i += 2;
308         sb.append((char) v);
309       }
310       else
311         sb.append(ch);
312     }
313
314     return sb.toString();
315   }
316
317   /**
318    * Encodes the url
319    */

320   public static String JavaDoc rawurlencode(String JavaDoc str)
321   {
322     StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
323
324     for (int i = 0; i < str.length(); i++) {
325       char ch = str.charAt(i);
326
327       if ('a' <= ch && ch <= 'z' ||
328           'A' <= ch && ch <= 'Z' ||
329           '0' <= ch && ch <= '9' ||
330           ch == '-' || ch == '_' || ch == '.') {
331         sb.append(ch);
332       }
333       else {
334         sb.append('%');
335         sb.append(toHexDigit(ch >> 4));
336         sb.append(toHexDigit(ch));
337       }
338     }
339
340     return sb.toString();
341   }
342
343   enum ParseUrlState {
344     INIT, USER, PASS, HOST, PORT, PATH, QUERY, FRAGMENT
345   };
346
347   /**
348    * Parses the URL into an array.
349    */

350   public static Value parse_url(Env env, String JavaDoc str)
351   {
352     if (str == null)
353       str = "";
354
355     int i = 0;
356     int length = str.length();
357
358     CharBuffer sb = new CharBuffer();
359
360     ArrayValueImpl value = new ArrayValueImpl();
361     value.put("path", "");
362
363     ParseUrlState state = ParseUrlState.INIT;
364
365     String JavaDoc user = null;
366
367     for (; i < length; i++) {
368       char ch = str.charAt(i);
369
370       switch (ch) {
371       case ':':
372         if (state == ParseUrlState.INIT) {
373           value.put("scheme", sb.toString());
374           sb.clear();
375
376           if (length <= i + 1 || str.charAt(i + 1) != '/') {
377             state = ParseUrlState.PATH;
378           }
379           else if (length <= i + 2 || str.charAt(i + 2) != '/') {
380             state = ParseUrlState.PATH;
381           }
382           else if (length <= i + 3 || str.charAt(i + 3) != '/') {
383             i += 2;
384             state = ParseUrlState.USER;
385           }
386           else {
387             // file:///foo
388

389             i += 2;
390             state = ParseUrlState.PATH;
391           }
392         }
393         else if (state == ParseUrlState.USER) {
394           user = sb.toString();
395           sb.clear();
396           state = ParseUrlState.PASS;
397         }
398         else if (state == ParseUrlState.HOST) {
399           value.put("host", sb.toString());
400           sb.clear();
401           state = ParseUrlState.PORT;
402         }
403         else
404           sb.append(ch);
405         break;
406
407       case '@':
408         if (state == ParseUrlState.USER) {
409           value.put("user", sb.toString());
410           sb.clear();
411           state = ParseUrlState.HOST;
412         }
413         else if (state == ParseUrlState.PASS) {
414           value.put("user", user);
415           value.put("pass", sb.toString());
416           sb.clear();
417           state = ParseUrlState.HOST;
418         }
419         else
420           sb.append(ch);
421         break;
422
423       case '/':
424         if (state == ParseUrlState.USER || state == ParseUrlState.HOST) {
425           value.put("host", sb.toString());
426           sb.clear();
427           state = ParseUrlState.PATH;
428           sb.append(ch);
429         }
430         else if (state == ParseUrlState.PASS) {
431           value.put("host", user);
432           value.put(new StringValueImpl("port"),
433                     new LongValue(new StringValueImpl(sb.toString()).toLong()));
434           sb.clear();
435           state = ParseUrlState.PATH;
436           sb.append(ch);
437         }
438         else if (state == ParseUrlState.PORT) {
439           value.put(new StringValueImpl("port"),
440                     new LongValue(new StringValueImpl(sb.toString()).toLong()));
441           sb.clear();
442           state = ParseUrlState.PATH;
443           sb.append(ch);
444         }
445         else
446           sb.append(ch);
447         break;
448
449       case '?':
450         if (state == ParseUrlState.USER || state == ParseUrlState.HOST) {
451           value.put("host", sb.toString());
452           sb.clear();
453           state = ParseUrlState.QUERY;
454         }
455         else if (state == ParseUrlState.PASS) {
456           value.put("host", user);
457           value.put(new StringValueImpl("port"),
458                     new LongValue(new StringValueImpl(sb.toString()).toLong()));
459           sb.clear();
460           state = ParseUrlState.QUERY;
461         }
462         else if (state == ParseUrlState.PORT) {
463           value.put(new StringValueImpl("port"),
464                     new LongValue(new StringValueImpl(sb.toString()).toLong()));
465           sb.clear();
466           state = ParseUrlState.QUERY;
467         }
468         else if (state == ParseUrlState.PATH) {
469           if (sb.length() > 0)
470             value.put("path", sb.toString());
471           sb.clear();
472           state = ParseUrlState.QUERY;
473         }
474         else
475           sb.append(ch);
476         break;
477
478       case '#':
479         if (state == ParseUrlState.USER || state == ParseUrlState.HOST) {
480           value.put("host", sb.toString());
481           sb.clear();
482           state = ParseUrlState.FRAGMENT;
483         }
484         else if (state == ParseUrlState.PASS) {
485           value.put("host", user);
486           value.put(new StringValueImpl("port"),
487                     new LongValue(new StringValueImpl(sb.toString()).toLong()));
488           sb.clear();
489           state = ParseUrlState.FRAGMENT;
490         }
491         else if (state == ParseUrlState.PORT) {
492           value.put(new StringValueImpl("port"),
493                     new LongValue(new StringValueImpl(sb.toString()).toLong()));
494           sb.clear();
495           state = ParseUrlState.FRAGMENT;
496         }
497         else if (state == ParseUrlState.PATH) {
498           if (sb.length() > 0)
499             value.put("path", sb.toString());
500           sb.clear();
501           state = ParseUrlState.FRAGMENT;
502         }
503         else if (state == ParseUrlState.QUERY) {
504           if (sb.length() > 0)
505             value.put("query", sb.toString());
506           sb.clear();
507           state = ParseUrlState.FRAGMENT;
508         }
509         else
510           sb.append(ch);
511         break;
512
513       default:
514         sb.append((char) ch);
515         break;
516       }
517     }
518
519     if (sb.length() == 0) {
520     }
521     else if (state == ParseUrlState.USER ||
522              state == ParseUrlState.HOST)
523       value.put("host", sb.toString());
524     else if (state == ParseUrlState.PASS) {
525       value.put("host", user);
526       value.put(new StringValueImpl("port"),
527                 new LongValue(new StringValueImpl(sb.toString()).toLong()));
528     }
529     else if (state == ParseUrlState.PORT) {
530       value.put(new StringValueImpl("port"),
531                 new LongValue(new StringValueImpl(sb.toString()).toLong()));
532     }
533     else if (state == ParseUrlState.QUERY)
534       value.put("query", sb.toString());
535     else if (state == ParseUrlState.FRAGMENT)
536       value.put("fragment", sb.toString());
537     else
538       value.put("path", sb.toString());
539
540     return value;
541   }
542
543   public static Value http_build_query(Env env, Value formdata,
544                                        @Optional String JavaDoc numeric_prefix)
545   {
546     String JavaDoc result =
547       httpBuildQueryImpl(env, "", formdata, numeric_prefix).toString();
548
549     return new StringValueImpl(result);
550   }
551
552   public static StringBuilder JavaDoc httpBuildQueryImpl(Env env, String JavaDoc path,
553                                                  Value formdata,
554                                                  String JavaDoc numeric_prefix)
555   {
556     StringBuilder JavaDoc result = new StringBuilder JavaDoc();
557
558     Set JavaDoc<Map.Entry JavaDoc<Value,Value>> entrySet;
559
560     if (formdata.isArray())
561       entrySet = ((ArrayValue)formdata).entrySet();
562     else if (formdata.isObject()) {
563       Set JavaDoc<Map.Entry JavaDoc<String JavaDoc,Value>> stringEntrySet
564         = ((ObjectValue)formdata).entrySet();
565
566       LinkedHashMap JavaDoc<Value,Value> valueMap = new LinkedHashMap JavaDoc<Value,Value>();
567
568       for (Map.Entry JavaDoc<String JavaDoc,Value> entry : stringEntrySet)
569         valueMap.put(new StringValueImpl(entry.getKey()), entry.getValue());
570
571       entrySet = valueMap.entrySet();
572     } else {
573       env.warning(L.l("formdata must be an array or object"));
574
575       return result;
576     }
577
578     for (Map.Entry JavaDoc<Value,Value> entry : entrySet) {
579       String JavaDoc newPath = makeNewPath(path, entry.getKey(), numeric_prefix);
580
581       if (entry.getValue().isArray() || entry.getValue().isObject()) {
582         // can always throw away the numeric prefix on recursive calls
583
result.append(httpBuildQueryImpl(env, newPath, entry.getValue(), null));
584         result.append("&");
585       } else {
586         result.append(newPath + "=");
587         result.append(urlencode(entry.getValue().toString()));
588         result.append("&");
589       }
590     }
591
592     // trim any trailing &'s
593
if (result.length() > 0)
594       result.deleteCharAt(result.length() - 1);
595
596     return result;
597   }
598
599   private static String JavaDoc makeNewPath(String JavaDoc oldPath, Value key,
600                                     String JavaDoc numeric_prefix)
601   {
602     if (oldPath.length() == 0) {
603       if (key.isLongConvertible() && numeric_prefix != null)
604         return urlencode(numeric_prefix + key.toString());
605       else
606         return urlencode(key.toString());
607     } else
608       return oldPath + "[" + urlencode(key.toString()) + "]";
609   }
610
611   /**
612    * Connects to the given URL using a HEAD request to retreive
613    * the headers sent in the response.
614    */

615   public static Value get_headers(Env env, String JavaDoc urlString,
616                                   @Optional Value format)
617   {
618     Socket JavaDoc socket = null;
619
620     try {
621       URL JavaDoc url = new URL JavaDoc(urlString);
622
623       if (! url.getProtocol().equals("http") &&
624           ! url.getProtocol().equals("https")) {
625         env.warning(L.l("Not an HTTP URL"));
626         return null;
627       }
628
629       int port = 80;
630
631       if (url.getPort() < 0) {
632         if (url.getProtocol().equals("http"))
633           port = 80;
634         else if (url.getProtocol().equals("https"))
635           port = 443;
636       } else {
637         port = url.getPort();
638       }
639
640       socket = new Socket JavaDoc(url.getHost(), port);
641
642       OutputStream JavaDoc out = socket.getOutputStream();
643       InputStream JavaDoc in = socket.getInputStream();
644
645       StringBuilder JavaDoc request = new StringBuilder JavaDoc();
646
647       request.append("HEAD ");
648
649       if (url.getPath() != null)
650         request.append(url.getPath());
651
652       if (url.getQuery() != null)
653         request.append("?" + url.getQuery());
654
655       if (url.getRef() != null)
656         request.append("#" + url.getRef());
657
658       request.append(" HTTP/1.0\r\n");
659
660       if (url.getHost() != null)
661         request.append("Host: " + url.getHost() + "\r\n");
662
663       request.append("\r\n");
664
665       OutputStreamWriter JavaDoc writer = new OutputStreamWriter JavaDoc(out);
666       writer.write(request.toString());
667       writer.flush();
668
669       LineNumberReader JavaDoc reader = new LineNumberReader JavaDoc(new InputStreamReader JavaDoc(in));
670
671       ArrayValue result = new ArrayValueImpl();
672
673       if (format.toBoolean()) {
674         for (String JavaDoc line = reader.readLine();
675              line != null;
676              line = reader.readLine()) {
677           line = line.trim();
678
679           if (line.length() == 0)
680             continue;
681
682           int colon = line.indexOf(':');
683
684           ArrayValue values;
685
686           if (colon < 0)
687             result.put(new StringValueImpl(line.trim()));
688           else {
689             StringValueImpl key =
690               new StringValueImpl(line.substring(0, colon).trim());
691
692             StringValueImpl value;
693
694             if (colon < line.length())
695               value = new StringValueImpl(line.substring(colon + 1).trim());
696             else
697               value = new StringValueImpl("");
698
699
700             if (result.get(key) != UnsetValue.UNSET)
701               values = (ArrayValue)result.get(key);
702             else {
703               values = new ArrayValueImpl();
704
705               result.put(key, values);
706             }
707
708             values.put(value);
709           }
710         }
711
712         // collapse single entries
713
for (Value key : result.keySet()) {
714           Value value = result.get(key);
715
716           if (value.isArray() && ((ArrayValue)value).getSize() == 1)
717             result.put(key, ((ArrayValue)value).get(LongValue.ZERO));
718         }
719       } else {
720         for (String JavaDoc line = reader.readLine();
721              line != null;
722              line = reader.readLine()) {
723           line = line.trim();
724
725           if (line.length() == 0)
726             continue;
727
728           result.put(new StringValueImpl(line.trim()));
729         }
730       }
731
732       return result;
733     } catch (Exception JavaDoc e) {
734       env.warning(e);
735
736       return BooleanValue.FALSE;
737     } finally {
738       try {
739         if (socket != null)
740           socket.close();
741       } catch (IOException JavaDoc e) {
742         env.warning(e);
743       }
744     }
745   }
746
747   /**
748    * Extracts the meta tags from a file and returns them as an array.
749    */

750   public static Value get_meta_tags(Env env, String JavaDoc filename,
751                                     @Optional("false") boolean use_include_path)
752   {
753     InputStream JavaDoc in = null;
754
755     ArrayValue result = new ArrayValueImpl();
756
757     try {
758       BinaryStream stream =
759         FileModule.fopen(env, filename, "r", use_include_path, null);
760
761       if (stream == null || ! (stream instanceof BinaryInput))
762         return result;
763
764       BinaryInput input = (BinaryInput) stream;
765
766       while (! input.isEOF()) {
767         String JavaDoc tag = getNextTag(input);
768
769         if (tag.equalsIgnoreCase("meta")) {
770           String JavaDoc name = null;
771           String JavaDoc content = null;
772
773           String JavaDoc [] attr;
774
775           while ((attr = getNextAttribute(input)) != null) {
776             if (name == null && attr[0].equalsIgnoreCase("name")) {
777               if (attr.length > 1)
778                 name = attr[1];
779             } else if (content == null && attr[0].equalsIgnoreCase("content")) {
780               if (attr.length > 1)
781                 content = attr[1];
782             }
783
784             if (name != null && content != null) {
785               result.put(new StringValueImpl(name),
786                          new StringValueImpl(content));
787               break;
788             }
789           }
790         } else if (tag.equalsIgnoreCase("/head"))
791           break;
792       }
793     } catch (IOException JavaDoc e) {
794       env.warning(e);
795     } finally {
796       try {
797         if (in != null)
798           in.close();
799       } catch (IOException JavaDoc e) {
800         env.warning(e);
801       }
802     }
803
804     return result;
805   }
806
807   private static String JavaDoc getNextTag(BinaryInput input)
808     throws IOException JavaDoc
809   {
810     StringBuilder JavaDoc tag = new StringBuilder JavaDoc();
811
812     for (int ch = 0; ! input.isEOF() && ch != '<'; ch = input.read()) {}
813
814     while (! input.isEOF()) {
815       int ch = input.read();
816
817       if (Character.isWhitespace(ch))
818         break;
819
820       tag.append((char) ch);
821     }
822
823     return tag.toString();
824   }
825
826   /**
827    * Finds the next attribute in the stream and return the key and value
828    * as an array.
829    */

830   private static String JavaDoc [] getNextAttribute(BinaryInput input)
831     throws IOException JavaDoc
832   {
833     int ch;
834
835     consumeWhiteSpace(input);
836
837     StringBuilder JavaDoc attribute = new StringBuilder JavaDoc();
838
839     while (! input.isEOF()) {
840       ch = input.read();
841
842       if (isValidAttributeCharacter(ch))
843         attribute.append((char) ch);
844       else {
845         input.unread();
846         break;
847       }
848     }
849
850     if (attribute.length() == 0)
851       return null;
852
853     consumeWhiteSpace(input);
854
855     if (input.isEOF())
856       return new String JavaDoc[] { attribute.toString() };
857
858     ch = input.read();
859     if (ch != '=') {
860       input.unread();
861
862       return new String JavaDoc[] { attribute.toString() };
863     }
864
865     consumeWhiteSpace(input);
866
867     // check for quoting
868
int quote = ' ';
869     boolean quoted = false;
870
871     if (input.isEOF())
872       return new String JavaDoc[] { attribute.toString() };
873
874     ch = input.read();
875
876     if (ch == '"' || ch == '\'') {
877       quoted = true;
878       quote = ch;
879     } else
880       input.unread();
881
882     StringBuilder JavaDoc value = new StringBuilder JavaDoc();
883
884     while (! input.isEOF()) {
885       ch = input.read();
886
887       // mimics PHP behavior
888
if ((quoted && ch == quote) ||
889           (! quoted && Character.isWhitespace(ch)) || ch == '>')
890         break;
891
892       value.append((char) ch);
893     }
894
895     return new String JavaDoc[] { attribute.toString(), value.toString() };
896   }
897
898   private static void consumeWhiteSpace(BinaryInput input)
899     throws IOException JavaDoc
900   {
901     int ch = 0;
902
903     while (! input.isEOF() && Character.isWhitespace(ch = input.read())) {}
904
905     if (! Character.isWhitespace(ch))
906       input.unread();
907   }
908
909   private static boolean isValidAttributeCharacter(int ch)
910   {
911     return Character.isLetterOrDigit(ch) ||
912            (ch == '-') || (ch == '.') || (ch == '_') || (ch == ':');
913   }
914
915   private static char toHexDigit(int d)
916   {
917     d = d & 0xf;
918
919     if (d < 10)
920       return (char) ('0' + d);
921     else
922       return (char) ('a' + d - 10);
923   }
924 }
925
926
Popular Tags