package edu.vt.marian.uip; import java.io.*; import java.net.*; import java.util.*; import edu.vt.marian.common.*; /** Class name: StringPool Class description: This class is only for the easy operation of formit and webgate, since it's heavily used in the original system, we'd better keep it here otherwise significant modification will be needed to handle html format. But some function/methods are added to reduce coupling between different classes Author: Jianxin Zhao Finished time: June 4, 1998 Known bugs: none Platform: jdk1.1.5 under UNIX */ public class StringPool { /** currently this vector hold all the strings in the string pool */ private Vector strings; /** this is only for the convinence of debugging, it will be elimnated at last */ Debug my_debug; /** This constructor just create an empty string pool */ public StringPool(Debug debug) { strings = new Vector(); my_debug = debug; } /** This constructor will create a string pool object from a stream, this is a new method added to this class */ public StringPool(InputStream is, Debug debug) { my_debug = debug; strings = new Vector(); BufferedReader dis = new BufferedReader(new InputStreamReader(is)); String s; String tmp; String tmp1; String DATA_STRING_EQ = new String("WEBGATE_CONTROL_INFO="); int num_strings; int num_lines; int i, j; // first read out number of strings in the string pool try { s = dis.readLine(); // debug.dumpTrace(s); num_strings = Integer.parseInt(s); // read out each string for (i = 0; i < num_strings; i++) { // read out number of lines in this string s = dis.readLine(); // debug.dumpTrace(s); num_lines = Integer.parseInt(s); // read out each lines and put them into the string pool as a single string tmp = new String(""); for (j = 0; j < num_lines; j++) { s = dis.readLine(); if (j == num_lines - 1) // the last piece, no need to add "\n" { tmp += s; } else { tmp += s + "\n"; } } // debug.dumpTrace(tmp); if (! tmp.startsWith(DATA_STRING_EQ)) { AddString(tmp); } else { tmp1 = tmp.substring(DATA_STRING_EQ.length()); AddSubString(tmp1); } } // end for } catch (IOException e) { my_debug.dumpTrace("error reading string pool from stream"); my_debug.dumpTrace(e.getMessage()); } } /** This constructor will create a string pool object from a stream, this is a new method added to this class */ public StringPool(packet_input_stream pis, Debug debug) { my_debug = debug; strings = new Vector(); String s; String tmp; String tmp1; String DATA_STRING_EQ = new String("WEBGATE_CONTROL_INFO="); int num_strings; int num_lines; int i, j; // first read out number of strings in the string pool s = pis.readLine(); // debug.dumpTrace(s); num_strings = Integer.parseInt(s); // read out each string for (i = 0; i < num_strings; i++) { // read out number of lines in this string s = pis.readLine(); // debug.dumpTrace(s); num_lines = Integer.parseInt(s); // read out each lines and put them into the string pool as a single string tmp = new String(""); for (j = 0; j < num_lines; j++) { s = pis.readLine(); if (j == num_lines - 1) // the last piece, no need to add "\n" { tmp += s; } else { tmp += s + "\n"; } } // debug.dumpTrace(tmp); if (! tmp.startsWith(DATA_STRING_EQ)) { AddString(tmp); } else { tmp1 = tmp.substring(DATA_STRING_EQ.length()); AddSubString(tmp1); } } // end for } /** This method will print the content of this string pool object to a stream this method can be used with the above constructor in network environment */ public void toStream(OutputStream os) { int i, j, num_strings, num_lines; // create a print writer so that we can use println() function, also we set flush flag to true PrintWriter pw = new PrintWriter(os, true); // first print number of strings in the string pool num_strings = GetNumStrings(); pw.println(Integer.toString(num_strings)); // then print each string for (i = 0; i < num_strings; i++) { // print number of lines in this string num_lines = GetNumLines(i); pw.println(Integer.toString(num_lines)); // print the string pw.println(GetString(i)); } // Date d = new Date(); // pw.println(d.toString()); // pw.println("======================================"); pw.flush(); } /** This method will print the content of this string pool object to a stream this method can be used with the above constructor in network environment */ public void to_stream(PrintWriter pw) { int i, j, num_strings, num_lines; // first print number of strings in the string pool num_strings = GetNumStrings(); pw.println(Integer.toString(num_strings)); // then print each string for (i = 0; i < num_strings; i++) { // print number of lines in this string num_lines = GetNumLines(i); pw.println(Integer.toString(num_lines)); // print the string pw.println(GetString(i)); } // Date d = new Date(); // pw.println(d.toString()); // pw.println("======================================"); } /** This method will print the content of this string pool object to a stream this method can be used with the above constructor in network environment */ public void to_stream(packet_output_stream pos) { int i, j, num_strings, num_lines; // first print number of strings in the string pool num_strings = GetNumStrings(); pos.println(Integer.toString(num_strings)); // then print each string for (i = 0; i < num_strings; i++) { // print number of lines in this string num_lines = GetNumLines(i); pos.println(Integer.toString(num_lines)); // print the string pos.println(GetString(i)); } // Date d = new Date(); // pw.println(d.toString()); // pw.println("======================================"); } /** This method will print the content of this string pool object to a stream this method can be used with the above constructor in network environment */ public String toString() { int i, j, num_strings, num_lines; // first print number of strings in the string pool num_strings = GetNumStrings(); StringBuffer sb = new StringBuffer(num_strings*100); // then print each string sb.append("{"); for (i = 0; i < num_strings; i++) { if (i != 0) sb.append(", "); sb.append(GetString(i)); } sb.append("}"); return( sb.toString() ); } /** This method will add the string to the string pool */ public void AddString(String s) { strings.addElement(s); } /** This method will add part of the string (strings between data seperaters) into the string pool */ public void AddSubString(String s) { char data_separator = '/'; boolean reach_end = false; int i = 0, j; while ((i < s.length()) && (s.charAt(i) == data_separator)) // Remove leading data_separators. { i++; } if (i >= s.length()) // Empty string! Bizarre, but not dangerous. { return; } // add the sub strings between data seperators to string pool j = i; while (! reach_end) { while ((j < s.length()) && (s.charAt(j) != data_separator)) { j++; } if (j >= s.length()) { reach_end = true; } // it's a little tricky here, but string between index i and j is the one we want AddString(s.substring(i, j)); j++; i = j; } } /** This method will add the name value pair to the string pool, this method and the method GetValue will make handling cgi data easy */ public void AddValue(String name, String value) { String s = new String(name + "=" + value); AddString(s); } /** This method will return the value of the name in the string pool, null will be returned if no such value exist in the string pool */ public String GetValue(String name) { int i; String s, s1, value; StringTokenizer st; for (i = 0; i < strings.size(); i++) { s = (String) strings.elementAt(i); st = new StringTokenizer(s); if (st.hasMoreTokens()) { s1 = st.nextToken("="); if (s1.equals(name)) // find the name, return the value (things after "=") { value = s.substring(s1.length() + 1); return value; } } } // no such name is find in the string pool, return null to indicate it return null; } /** this method will return the name list in this object which have the specified value */ public Vector get_names(String value) { Vector names = new Vector(); int i, index; String s, name, tail; for (i = 0; i < strings.size(); i++) { s = (String) strings.elementAt(i); index = s.lastIndexOf("="); if (index != -1) { // this string is a name value pair tail = s.substring(index + 1); if (tail.equals(value)) { // a match is found, add the name to the return list name = s.substring(0, index); names.addElement(name); } } } return names; } /** this method will return the name list in this object */ public Vector get_names() { Vector names = new Vector(); int i, index; String s, name, tail; for (i = 0; i < strings.size(); i++) { s = (String) strings.elementAt(i); index = s.lastIndexOf("="); if (index != -1) { // this string is a name value pair name = s.substring(0, index); names.addElement(name); } } return names; } /** This method will return the number of strings currently exist in the string pool */ public int GetNumStrings() { return strings.size(); } /** This method will return the numth string currently in the string pool, null will be returned if the num is out of bound */ public String GetString(int num) { if ((num < 0) || (num >= strings.size())) { // invalid index return null; } // valid index return (String) strings.elementAt(num); } /** This method will delete the numth string from the string pool. This method is for the completeness of this class though it's not used in the current system. */ public void DeleteString(int num) { if ((num < 0) || (num >= strings.size())) { // invalid index return; } // valid index strings.removeElementAt(num); } /** This method will return the number of lines in the numth string of the string pool */ public int GetNumLines(int num) { if ((num < 0) || (num >= strings.size())) { // invalid index return 0; } // valid index LinedString ms = new LinedString(my_debug); String s = (String) strings.elementAt(num); return ms.count_lines(s); } /** This method will add the cgi data to the string pool, it will first decode special chars like '+', '%', '&' then add the strings seperated by them to the string pool I believe this method reduced the coupling between different classes */ public void AddCGIData(String data) { if (data == null) // we will not add null to string pool { return; } String s; char new_char = ' '; int index; int data_length = data.length(); // I think this is for the speed up s = new String(""); index = 0; while (index < data_length) { switch (data.charAt(index)) { case '&' : // add the current string to the string pool if (! s.equals("")) // we will not add an empty string to string pool { AddString(s); s = new String(""); // so if there is "&&" in the data, nothing will be added // to the string pool } index++; break; case '%' : // decode %xx if necessary if (index < (data_length - 2) && (GetHexValue(data.substring(index + 1, index + 3)) != -1)) { new_char = (char) GetHexValue(data.substring(index + 1, index + 3)); if (new_char != 13) // disallow CR's because they throw off stuff { s = s + String.valueOf(new_char); } index += 3; } else { s = s + String.valueOf(new_char); index++; } break; case '+' : // '+' means space in CGI data s += " "; index++; break; default : s = s + String.valueOf(data.charAt(index++)); } } if (! s.equals("")) { AddString(s); } } /** This method will return the hex value of the parameter digits, -1 will be returned if digits is not a valid hex digit string, by using ...length() insteadof 16 we improve the function's flexibility. */ public int GetHexValue(String digits) { String hex_digits = new String("0123456789ABCDEF"); int hex_val = 0; int i, j; boolean valid_hex; for (i = 0; i < digits.length(); i++) // process the chars in digits one by one { valid_hex = false; for (j = 0; j < hex_digits.length(); j++) { if (digits.charAt(i) == hex_digits.charAt(j)) // this is a valid hex digit { hex_val = hex_val * hex_digits.length() + j; valid_hex = true; } } if (! valid_hex) // an invalid hex digit is identified, return -1 to indicate this { return -1; } } // this is a valid hex digit string, return it's value return hex_val; } }