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   * @return database object
139   * @throws IOException if io error
140   */
141   protected abstract bradleyross.library.database.DatabaseProperties
142         makeConnection() throws IOException;
143   /**
144   * Will be overridden with the method for obtaining the SQL
145   * result set.
146   * <p>Changes to fields such as message and outputFilename can go in this
147   *    method.</p>
148   * @param req Request information
149   * @return Result set
150   * @throws IOException if io errors
151   * @see #message
152   * @see #outputFilename
153   */
154   protected abstract java.sql.ResultSet getResultSet(HttpServletRequest req)
155         throws java.io.IOException;
156   /**
157   * Will print header line for CSV file.
158   * @throws IOException if io errors
159   */
160   protected void printHeader ()
161         throws java.io.IOException
162      {
163      try
164         {
165         for (int i = 1; i <= columnCount; i++)
166            {
167            if (i != 1) { output.print(","); }
168            output.print(rsmd.getColumnName(i));
169            }
170         output.print("\r\n");
171         }
172      catch (Exception e)
173         { throw new java.io.IOException("Error detected: " + e.getClass().getName() +
174            " : " + e.getMessage()); }
175      }
176   /**
177   * Will print one line of CSV file.
178   * <p>This method will be changed at a later time to allow the
179   *    specification of overriding methods for formatting timestamps,
180   *    integers, floating point numbers, character strings, etc.</p>
181   * @throws IOException if io errors
182   */
183   protected void printLine ()
184         throws java.io.IOException
185      {
186      for (int i = 1; i <= columnCount; i++)
187         {
188         try
189            {
190            if (i != 1) { output.print(","); } 
191            int columnType = rsmd.getColumnType(i);
192            if (columnType == Types.VARCHAR || columnType == Types.CHAR ||
193                  columnType == Types.LONGVARCHAR)
194               {
195               String working = rs.getString(i);
196               if (rs.wasNull()) { working = new String(); }
197               output.print(formatString(working, i)); 
198               }
199            else if (columnType == Types.FLOAT || columnType == Types.REAL ||
200                  columnType == Types.DOUBLE)
201               {
202               double working = rs.getDouble(i);
203               if (rs.wasNull()) 
204                  { output.print(new String()); } 
205               else
206                  { output.print(formatDouble(working)); }
207               }
208            else if (columnType == Types.TINYINT || columnType == Types.SMALLINT ||
209                  columnType == Types.INTEGER || columnType == Types.BIGINT)
210               {
211               int working = rs.getInt(i);
212               if (rs.wasNull())
213                  { output.print(new String()); }
214               else
215                  { output.print(formatInteger(working)); }
216               }
217            else if (columnType == Types.TIMESTAMP)
218               { 
219               java.sql.Timestamp working = rs.getTimestamp(i);
220               if (rs.wasNull())
221                  { output.print(new String()); }
222               else
223                  {
224                  java.util.Date date = new java.util.Date(working.getTime());
225                  output.print(formatDateTime(date)); 
226                  }
227               }
228            else
229               /*
230               ** Haven't decided how to handle Types.DECIMAL and
231               ** TYPES.GENERIC.  However, this may be academic as I don't
232               ** think that these will occur in the databases
233               ** that I will be using.
234               */
235               {
236               String working = rs.getString(i);
237               if (rs.wasNull())
238                  { output.print(new String()); }
239               else
240                  { output.print(working); }
241               }
242            }
243         catch (Exception e)
244            { throw new java.io.IOException("Error detected in column " +
245               Integer.toString(i) + ":" +
246               e.getClass().getName() + " : " +
247               e.getMessage()); }
248         }
249      try
250         { output.print("\r\n"); }
251      catch (Exception e)
252         { throw new java.io.IOException("Error detected: " +
253            e.getClass().getName() + " : " +
254            e.getMessage()); }
255      }
256   /**
257   * Method of calling formatString when it doesn't
258   * depend on the column in the result set.
259   * @param valueIn String to be processed
260   * @return Processed string
261   */
262   protected String formatString(String valueIn)
263      { return formatString(valueIn, 1); }
264   /**
265   * Double up on all double quotes in string and surround the string with
266   * double quotes so that it will be in the proper format for the CSV file.
267   * @param valueIn String to be processed
268   * @param column Column number of item (not used unless method overriden)
269   * @return Processed string
270   */
271   protected String formatString(String valueIn, int column)
272      {
273      String working = null;
274      if (valueIn == null)
275         { working = " "; }
276      else if (valueIn.length() == 0)
277         { working = " "; }
278      else
279         {
280         java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\\"");
281         java.util.regex.Matcher matcher = pattern.matcher(valueIn);
282         working = matcher.replaceAll("\"\"");
283         }
284      if (working == null) { working = " "; }
285      return "\"" + working + "\"";
286      }
287   /**
288   * Alternate calling sequence, leaving out the column number.
289   * @param valueIn Floating point number to be formatted
290   * @return Formatted string
291   */
292   protected String formatDouble (double valueIn)
293      { return formatDouble (valueIn, 1); }
294   /**
295   * Formatter for floating point numbers.
296   * <p>This method can be overridden to control printing of
297   *    the values in the CSV file.</p>
298   * @param valueIn Floating point number to be processed
299   * @param column Column number
300   * @return Formatted string
301   */
302   protected String formatDouble (double valueIn, int column)
303      { return Double.toString(valueIn); }
304   /**
305   * Alternate calling sequence, omitting the column number.
306   * @param valueIn Integer value to be formatted
307   * @return Formatted string
308   */
309   protected String formatInteger (int valueIn)
310      { return formatInteger(valueIn, 1); }
311   /**
312   * Formatter for integer values.
313   * <p>This method can be overridden to control printing of
314   *    the values in the CSV file.</p>
315   * @param valueIn Integer to be formatted
316   * @param column Column number
317   * @return Formatted string
318   */
319   protected String formatInteger (int valueIn, int column)
320      { return Integer.toString(valueIn); }
321   /**
322   * Alternate calling sequence, leaving out the column number
323   * @param valueIn java.util.Date object containing date and time
324   * @return Formatted string
325   */
326   protected String formatDateTime (java.util.Date valueIn)
327      { return formatDateTime(valueIn, 1); }
328   /**
329   * Formatter for values involving date and time.
330   * <p>This method can be overridden to control printing of
331   *    the values in the CSV file.</p>
332   * @param valueIn java.util.Date object containing date and time
333   * @param column Column number
334   * @return Formatted string
335   */
336   protected String formatDateTime (java.util.Date valueIn, int column)
337      {
338      java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("MM/dd/yyyy HH:mm");
339      return format.format(valueIn);
340      }
341   }