001package bradleyross.j2ee.servlets;
002import java.io.*;
003import java.sql.Types;
004import javax.servlet.*;
005import javax.servlet.http.*;
006/**
007* Serves as a template for writing servlets that
008* display the contains of a java.sql.ResultSet object
009* as a CSV (comma separated values) page.
010* <p>The methods getResultSet and makeConnection are
011*    abstract and will need to be overridden before
012*    this class can be used.  The user may also wish to
013*    override several of the methods for formatting
014*    information.</p>
015* <p>This is the class that is currently used by
016*    com.amtrak.eng.servlets.downloadDataServlet and
017*    com.amtrak.eng.tests.testServlet.  After
018*    bradleyross.library.servlets.csvWriter is modified
019*    to make the variables local to the service method,
020*    the classes in com.amtrak.eng should be modified to
021*    make use of that version.</p>
022* @author Bradley Ross
023* @deprecated
024*/
025public abstract class csvWriter2 extends HttpServlet
026   {
027   /**
028         * 
029         */
030        private static final long serialVersionUID = 1L;
031/**
032   * File name to be used when creating file.  File will be
033   * suffixed with .csv.
034   * <p>Changes to this field can be placed in the method
035   *    getResultSet.</p>
036   * @see #getResultSet(javax.servlet.http.HttpServletRequest)
037   */
038   protected String outputFilename = "default";
039   /**
040   * String to be placed in CSV file as the first line in the file.
041   * <p>Changes to this field can be placed in the method
042   *    getResultSet.</p>
043   * @see #getResultSet(javax.servlet.http.HttpServletRequest)
044   */
045   protected String message = null;
046   /**
047   * Object containing database connection information.
048   * <p>This field is set by the makeConnection method.</p>
049   * @see #makeConnection()
050   */
051   protected bradleyross.library.database.DatabaseProperties data = null;
052   /**
053   * Object containing the result set.
054   * <p>This object is created by the getResultMethod method which
055   *    is called by the service method.
056   */
057   protected java.sql.ResultSet rs = null;
058   /**
059   * Object containing meta data on result set.
060   * <p>This object is created by the service method after the
061   *    execution of the getResultSet method and before the
062   *    execution of the printHeader method.</p>
063   */
064   protected java.sql.ResultSetMetaData rsmd = null;
065   /**
066   * Object representing the output to the HTTP response.
067   */
068   protected java.io.PrintWriter output = null;
069   /**
070   * Number of columns in result set
071   */
072   protected int columnCount;
073   /**
074   * ServletConfig object as passed to the init method.
075   */
076   protected ServletConfig config = null;
077   public void init(ServletConfig configIn) throws ServletException
078      { 
079      super.init(configIn); 
080      config = configIn;
081      }
082   public void destroy()
083      { super.destroy(); }
084   /**
085   * @param req Object containing request information
086   * @param res Object containing response information
087   */
088   public void service (HttpServletRequest req,
089      HttpServletResponse res) throws IOException
090      {
091      try
092         {
093         data = makeConnection();
094         data.connect();
095         output = res.getWriter();
096         rs = getResultSet(req);
097         res.setContentType("text/csv");
098         res.setHeader("Content-Disposition", "attachment;filename=" +
099            outputFilename + ".csv");
100         rsmd = rs.getMetaData();
101         if (message != null)
102            { output.print(message + "\r\n"); }
103         columnCount = rsmd.getColumnCount();
104         if (rs == null)
105            { 
106            output.print("No result set provided\r\n"); 
107            return;
108            }
109         boolean moreRecords = rs.next();
110         if (!moreRecords)
111            {
112            output.print("Result set is empty\r\n");
113            return;
114            }
115         else 
116            { printHeader(); }
117         while (true)
118            {
119            printLine();
120            moreRecords = rs.next();
121            if (!moreRecords) { break; }
122            }
123         rs.close();
124         data.close();
125         }
126      catch (java.io.IOException e)
127         { throw new java.io.IOException("Error in processing file: " +
128               e.getClass().getName() + " : " + e.getMessage()); }
129      catch (java.sql.SQLException e)
130         { 
131         System.out.println("SQL error in processing CSV file");
132         throw new java.io.IOException("SQL error: " + e.getClass().getName() + 
133            " : " + e.getMessage());
134         }
135      }
136   /**
137   * Obtain the database connection information.
138   */
139   protected abstract bradleyross.library.database.DatabaseProperties
140         makeConnection() throws IOException;
141   /**
142   * Will be overridden with the method for obtaining the SQL
143   * result set.
144   * <p>Changes to fields such as message and outputFilename can go in this
145   *    method.</p>
146   * @param req Request information
147   * @return Result set
148   * @see #message
149   * @see #outputFilename
150   */
151   protected abstract java.sql.ResultSet getResultSet(HttpServletRequest req)
152         throws java.io.IOException;
153   /**
154   * Will print header line for CSV file.
155   */
156   protected void printHeader ()
157         throws java.io.IOException
158      {
159      try
160         {
161         for (int i = 1; i <= columnCount; i++)
162            {
163            if (i != 1) { output.print(","); }
164            output.print(rsmd.getColumnName(i));
165            }
166         output.print("\r\n");
167         }
168      catch (Exception e)
169         { throw new java.io.IOException("Error detected: " + e.getClass().getName() +
170            " : " + e.getMessage()); }
171      }
172   /**
173   * Will print one line of CSV file.
174   * <p>This method will be changed at a later time to allow the
175   *    specification of overriding methods for formatting timestamps,
176   *    integers, floating point numbers, character strings, etc.</p>
177   */
178   protected void printLine ()
179         throws java.io.IOException
180      {
181      for (int i = 1; i <= columnCount; i++)
182         {
183         try
184            {
185            if (i != 1) { output.print(","); } 
186            int columnType = rsmd.getColumnType(i);
187            if (columnType == Types.VARCHAR || columnType == Types.CHAR ||
188                  columnType == Types.LONGVARCHAR)
189               {
190               String working = rs.getString(i);
191               if (rs.wasNull()) { working = new String(); }
192               output.print(formatString(working, i)); 
193               }
194            else if (columnType == Types.FLOAT || columnType == Types.REAL ||
195                  columnType == Types.DOUBLE)
196               {
197               double working = rs.getDouble(i);
198               if (rs.wasNull()) 
199                  { output.print(new String()); } 
200               else
201                  { output.print(formatDouble(working)); }
202               }
203            else if (columnType == Types.TINYINT || columnType == Types.SMALLINT ||
204                  columnType == Types.INTEGER || columnType == Types.BIGINT)
205               {
206               int working = rs.getInt(i);
207               if (rs.wasNull())
208                  { output.print(new String()); }
209               else
210                  { output.print(formatInteger(working)); }
211               }
212            else if (columnType == Types.TIMESTAMP)
213               { 
214               java.sql.Timestamp working = rs.getTimestamp(i);
215               if (rs.wasNull())
216                  { output.print(new String()); }
217               else
218                  {
219                  java.util.Date date = new java.util.Date(working.getTime());
220                  output.print(formatDateTime(date)); 
221                  }
222               }
223            else
224               /*
225               ** Haven't decided how to handle Types.DECIMAL and
226               ** TYPES.GENERIC.  However, this may be academic as I don't
227               ** think that these will occur in the databases
228               ** that I will be using.
229               */
230               {
231               String working = rs.getString(i);
232               if (rs.wasNull())
233                  { output.print(new String()); }
234               else
235                  { output.print(working); }
236               }
237            }
238         catch (Exception e)
239            { throw new java.io.IOException("Error detected in column " +
240               Integer.toString(i) + ":" +
241               e.getClass().getName() + " : " +
242               e.getMessage()); }
243         }
244      try
245         { output.print("\r\n"); }
246      catch (Exception e)
247         { throw new java.io.IOException("Error detected: " +
248            e.getClass().getName() + " : " +
249            e.getMessage()); }
250      }
251   /**
252   * Method of calling formatString when it doesn't
253   * depend on the column in the result set.
254   * @param valueIn String to be processed
255   * @return Processed string
256   */
257   protected String formatString(String valueIn)
258      { return formatString(valueIn, 1); }
259   /**
260   * Double up on all double quotes in string and surround the string with
261   * double quotes so that it will be in the proper format for the CSV file.
262   * @param valueIn String to be processed
263   * @param column Column number of item (not used unless method overriden)
264   * @return Processed string
265   */
266   protected String formatString(String valueIn, int column)
267      {
268      String working = null;
269      if (valueIn == null)
270         { working = " "; }
271      else if (valueIn.length() == 0)
272         { working = " "; }
273      else
274         {
275         java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\\"");
276         java.util.regex.Matcher matcher = pattern.matcher(valueIn);
277         working = matcher.replaceAll("\"\"");
278         }
279      if (working == null) { working = " "; }
280      return "\"" + working + "\"";
281      }
282   /**
283   * Alternate calling sequence, leaving out the column number.
284   * @param valueIn Floating point number to be formatted
285   * @return Formatted string
286   */
287   protected String formatDouble (double valueIn)
288      { return formatDouble (valueIn, 1); }
289   /**
290   * Formatter for floating point numbers.
291   * <p>This method can be overridden to control printing of
292   *    the values in the CSV file.</p>
293   * @param valueIn Floating point number to be processed
294   * @param column Column number
295   * @return Formatted string
296   */
297   protected String formatDouble (double valueIn, int column)
298      { return Double.toString(valueIn); }
299   /**
300   * Alternate calling sequence, omitting the column number.
301   * @param valueIn Integer value to be formatted
302   * @return Formatted string
303   */
304   protected String formatInteger (int valueIn)
305      { return formatInteger(valueIn, 1); }
306   /**
307   * Formatter for integer values.
308   * <p>This method can be overridden to control printing of
309   *    the values in the CSV file.</p>
310   * @param valueIn Integer to be formatted
311   * @param column Column number
312   * @return Formatted string
313   */
314   protected String formatInteger (int valueIn, int column)
315      { return Integer.toString(valueIn); }
316   /**
317   * Alternate calling sequence, leaving out the column number
318   * @param valueIn java.util.Date object containing date and time
319   * @return Formatted string
320   */
321   protected String formatDateTime (java.util.Date valueIn)
322      { return formatDateTime(valueIn, 1); }
323   /**
324   * Formatter for values involving date and time.
325   * <p>This method can be overridden to control printing of
326   *    the values in the CSV file.</p>
327   * @param valueIn java.util.Date object containing date and time
328   * @param column Column number
329   * @return Formatted string
330   */
331   protected String formatDateTime (java.util.Date valueIn, int column)
332      {
333      java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("MM/dd/yyyy HH:mm");
334      return format.format(valueIn);
335      }
336   }