root/trunk/src/java/org/jcoderz/phoenix/dbview/DbView.java

Revision 1066, 24.6 kB (checked in by amandel, 4 years ago)

Several fixes for Exceptions in current setup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 *
4 * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *    * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    * Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials
15 *      provided with the distribution.
16 *    * Neither the name of the jCoderZ.org Project nor the names of
17 *      its contributors may be used to endorse or promote products
18 *      derived from this software without specific prior written
19 *      permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33package org.jcoderz.phoenix.dbview;
34
35
36import java.io.File;
37import java.io.FileOutputStream;
38import java.io.IOException;
39import java.io.OutputStreamWriter;
40import java.io.PrintWriter;
41import java.io.Reader;
42import java.lang.reflect.InvocationTargetException;
43import java.lang.reflect.Method;
44import java.lang.reflect.Modifier;
45import java.nio.charset.Charset;
46import java.sql.Blob;
47import java.sql.Connection;
48import java.sql.DriverManager;
49import java.sql.PreparedStatement;
50import java.sql.ResultSet;
51import java.sql.ResultSetMetaData;
52import java.sql.SQLException;
53import java.sql.Timestamp;
54import java.sql.Types;
55import java.text.DateFormat;
56import java.text.SimpleDateFormat;
57import java.util.Date;
58import java.util.HashMap;
59import java.util.Map;
60import java.util.TimeZone;
61import java.util.logging.Level;
62import java.util.logging.Logger;
63import javax.naming.Context;
64import javax.naming.InitialContext;
65import javax.naming.NamingException;
66import javax.sql.DataSource;
67
68import org.jcoderz.commons.util.Constants;
69import org.jcoderz.commons.util.DbUtil;
70import org.jcoderz.commons.util.IoUtil;
71import org.jcoderz.commons.util.LoggingUtils;
72import org.jcoderz.commons.util.XmlUtil;
73
74
75/**
76 * Converts the db content into xml.
77 *
78 * @author Andreas Mandel
79 */
80public class DbView
81{
82   /** Default database jndi name. */
83   public static final String DATASOURCE = "java:comp/env/jdbc/svs/db";
84
85   /** Line separator to be used in output files. */
86   public static final String LINE_SEPARATOR = Constants.LINE_SEPARATOR;
87
88   private static final int MILLIS_PER_SECOND
89           = org.jcoderz.commons.types.Date.MILLIS_PER_SECOND;
90   private static final String SELECT_ALL_TABLES = "select * from tab";
91   private static final String CLASSNAME = DbView.class.getName();
92   private static final Logger logger = Logger.getLogger(CLASSNAME);
93
94   private final Map mTypeMapper = new HashMap();
95   private final DateFormat mDateFormater
96         = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS",
97                 Constants.SYSTEM_LOCALE);
98
99   {
100      mDateFormater.setTimeZone(TimeZone.getTimeZone("UTC"));
101   }
102
103   private String mDbDriver = Constants.ORACLE_DRIVER_CLASS_NAME;
104   private int mNumberOfColumns;
105   private String mSqlStatement;
106   private String mDbUrl;
107   private String mDbUser;
108   private String mDbPasswd;
109   private File mOutputDir;
110   private File mOutputFile;
111   private Level mLogLevel;
112
113
114   public static void main (String[] args)
115         throws SecurityException
116   {
117      final DbView main = new DbView();
118      try
119      {
120         main.parseArguments(args);
121         if (main.mSqlStatement == null)
122         {
123            main.dumpAllTables();
124         }
125         else
126         {
127            main.performQuery();
128         }
129      }
130      catch (IllegalArgumentException ex)
131      {
132         System.err.println(ex.getMessage());
133         System.err.println("Usage:");
134         System.err.println(DbView.class.getName());
135         System.err.println(" -dbUrl [-dbUser scott -dbPasswd tiger] "
136               + "-sql \"select * from tab\" "
137               + "-outFile foo.xml");
138         System.err.println("DbView -dbUrl [-dbUser scott -dbPasswd tiger] "
139               + "-outDir sql/");
140         System.err.println();
141         System.err.println("To add type converters use:");
142         System.err.println("-type ROW_NAME JAVA_CLASS FROM_DB TO_DISPLAY");
143         System.err.println("ex.: -type STATUS com.encous.foo.Status fromInt "
144               + "toDisplayString");
145         System.err.println();
146         System.err.println("To use a db driver other than oracle add:");
147         System.err.println("-dbDriver your.db.driver");
148      }
149      catch (Exception ex)
150      {
151         ex.printStackTrace();
152      }
153   }
154
155   public void dumpAllTables ()
156         throws IOException, ClassNotFoundException, SQLException
157   {
158      dumpAllTables(mOutputDir);
159   }
160
161   public void performQuery ()
162         throws IOException, ClassNotFoundException, SQLException
163   {
164      performConvertion (mOutputFile, mSqlStatement);
165   }
166
167   public void dumpAllTables (File dir)
168         throws IOException, ClassNotFoundException, SQLException
169   {
170      Connection dbConnection = null;
171      PreparedStatement statement = null;
172      try
173      {
174         dbConnection = getConnectionFromDbUrl();
175
176         performConvertion(new File(dir, "tables.xml").getCanonicalPath(),
177               dbConnection, SELECT_ALL_TABLES);
178
179         statement = dbConnection.prepareStatement(SELECT_ALL_TABLES);
180         final ResultSet rs = statement.executeQuery();
181         while (rs.next())
182         {
183            final String tableName = rs.getString(1);
184            performConvertion(
185                  new File(dir,
186                      escapeTableName(tableName) + ".xml").getCanonicalPath(),
187                      dbConnection, "select * from \"" + tableName + '"');
188         }
189      }
190      finally
191      {
192          DbUtil.close(statement);
193          DbUtil.close(dbConnection);
194      }
195
196   }
197
198   private void parseArguments (String[] args)
199         throws IOException, NoSuchMethodException, ClassNotFoundException
200   {
201      try
202      {
203         for (int i = 0; i < args.length; )
204         {
205            if ("-sql".equals(args[i]))
206            {
207               mSqlStatement = args[++i];
208            }
209            else if ("-dbUrl".equals(args[i]))
210            {
211               mDbUrl = args[++i];
212            }
213            else if ("-dbUser".equals(args[i]))
214            {
215               mDbUser = args[++i];
216            }
217            else if ("-dbPasswd".equals(args[i]))
218            {
219               mDbPasswd = args[++i];
220            }
221            else if (args[i].equals("-type"))
222            {
223               final String rowname = args[++i];
224               final String classname = args[++i];
225               final String fromDbType = args[++i];
226               final String toDisplay = args[++i];
227               addTypeMapping(rowname, classname, fromDbType, toDisplay);
228            }
229            else if ("-outDir".equals(args[i]))
230            {
231               mOutputDir = new File(args[++i]);
232            }
233            else if ("-outFile".equals(args[i]))
234            {
235               mOutputFile = new File(args[++i]);
236            }
237            else if ("-dbDriver".equals(args[i]))
238            {
239               mDbDriver = args[++i];
240            }
241            else if ("-loglevel".equals(args[i]))
242            {
243               mLogLevel = Level.parse(args[++i]);
244               Logger.getLogger("").setLevel(mLogLevel);
245               LoggingUtils.setGlobalHandlerLogLevel(mLogLevel);
246            }
247            else
248            {
249               throw new IllegalArgumentException(
250                     "Invalid argument '" + args[i]  + "'");
251            }
252            ++i;
253         }
254
255         checkParameters();
256      }
257      catch (IndexOutOfBoundsException e)
258      {
259         final IllegalArgumentException ex = new IllegalArgumentException(
260            "Missing value for " + args[args.length - 1]);
261         ex.initCause(e);
262         throw ex;
263      }
264   }
265
266   private void checkParameters ()
267        throws IllegalArgumentException
268   {
269       if (mOutputDir == null && mOutputFile == null)
270       {
271           throw new IllegalArgumentException(
272               "Need either output dir or output file.");
273       }
274       if (mSqlStatement != null && mOutputFile != null)
275       {
276           throw new IllegalArgumentException(
277              "Need output file for sql statement.");
278       }
279       if (mSqlStatement == null && mOutputDir == null)
280       {
281           throw new IllegalArgumentException(
282                   "Need output dir for global dump.");
283       }
284       if (!mOutputDir.isDirectory())
285       {
286          throw new RuntimeException("out dir must be a directory.");
287       }
288       if (mOutputFile.isDirectory())
289       {
290          throw new RuntimeException(
291                "out file must not be a directory.");
292       }
293   }
294
295   public void performConvertion (File file, String query)
296         throws IOException, ClassNotFoundException, SQLException
297   {
298      PrintWriter out = null;
299      Connection dbConnection = null;
300      PreparedStatement statement = null;
301      try
302      {
303         final FileOutputStream stream = new FileOutputStream(file);
304         final OutputStreamWriter writer = new OutputStreamWriter(stream,
305               Charset.forName("us-ascii"));
306         out = new PrintWriter(writer);
307         dbConnection = getConnectionFromDbUrl();
308         statement = dbConnection.prepareStatement(query);
309         final ResultSet rs = statement.executeQuery();
310         xmlOpen(null, dbConnection.getMetaData().getURL(), query, out);
311         metaData2Xml(rs.getMetaData(), out);
312         resultSet2Xml(rs, out);
313         xmlClose(out);
314
315      }
316      finally
317      {
318         IoUtil.close(out);
319         DbUtil.close(statement);
320         DbUtil.close(dbConnection);
321      }
322   }
323
324   public void addTypeMapping (String typeName, String typeClass,
325         String fromDb, String toString)
326         throws SecurityException, NoSuchMethodException, ClassNotFoundException
327   {
328      mTypeMapper.put(typeName,
329            new TypeMapper(typeName, typeClass, fromDb, toString));
330   }
331
332   private void performConvertion (String fileName,
333         final Connection dbConnection, String query)
334         throws IOException, SQLException
335   {
336      logger.fine("about to dump '" + query + "' into '" + fileName +"'.");
337      PrintWriter out = null;
338      PreparedStatement statement = null;
339      try
340      {
341         final FileOutputStream stream = new FileOutputStream(fileName);
342         final OutputStreamWriter writer = new OutputStreamWriter(stream,
343               Charset.forName("us-ascii"));
344         out = new PrintWriter(writer);
345         statement = dbConnection.prepareStatement(query);
346         final ResultSet rs = statement.executeQuery();
347         xmlOpen(null, dbConnection.getMetaData().getURL(), query, out);
348         metaData2Xml(rs.getMetaData(), out);
349         resultSet2Xml(rs, out);
350         xmlClose(out);
351
352      }
353      finally
354      {
355          IoUtil.close(out);
356          DbUtil.close(statement);
357      }
358   }
359
360   private Connection getConnectionFromDataSource ()
361       throws NamingException, SQLException
362   {
363      final Context ctx = new InitialContext();
364      final DataSource ds = (DataSource) ctx.lookup(DATASOURCE);
365      return ds.getConnection();
366   }
367
368   private Connection getConnectionFromDbUrl ()
369         throws ClassNotFoundException, SQLException
370   {
371      Class.forName(mDbDriver);
372      final Connection con;
373
374      if (mDbUser == null)
375      {
376         con = DriverManager.getConnection(mDbUrl);
377      }
378      else
379      {
380         con = DriverManager.getConnection(mDbUrl, mDbUser, mDbPasswd);
381      }
382
383      return con;
384   }
385
386   private void xmlOpen (String dataSource, String dataBaseUri,
387         String statement, PrintWriter out)
388         throws IOException, SQLException
389   {
390      out.print("<result statement='");
391      out.print(XmlUtil.attributeEscape(statement));
392      out.println("'");
393      if (dataSource != null)
394      {
395         out.print("        data-source='");
396         out.print(XmlUtil.attributeEscape(dataSource));
397         out.println("'");
398      }
399      if (dataBaseUri != null)
400      {
401         out.print("        db-uri='");
402         out.print(XmlUtil.attributeEscape(dataBaseUri));
403         out.println("'");
404      }
405      out.print("        creation-date='");
406      out.print(display(new Date()));
407      out.println("'>");
408   }
409
410   private void xmlClose (PrintWriter out)
411   {
412      out.println("</result>");
413   }
414
415   private void metaData2Xml (ResultSetMetaData md, PrintWriter out)
416         throws IOException, SQLException
417   {
418      mNumberOfColumns = md.getColumnCount();
419      out.print("   <meta-data number-of-columns='");
420      out.print(mNumberOfColumns);
421      out.println("'>");
422      for (int column = 1; column <= mNumberOfColumns; column++)
423      {
424         out.println("      <column");
425         metaDataAsString("name", md.getColumnName(column), out);
426         metaDataAsString("type-name", md.getColumnTypeName(column), out);
427         metaDataAsString("display-name", md.getColumnLabel(column), out);
428         metaDataAsString("class-name", md.getColumnClassName(column), out);
429         try
430         {
431            metaDataAsString("precision", md.getPrecision(column), out);
432         }
433         catch (NumberFormatException ex)
434         {
435            // this happens for lobs in oracle seems to denote infinity.
436         }
437         metaDataAsString("scale", md.getScale(column), out);
438         metaDataAsString("catalog-name", md.getCatalogName(column), out);
439         metaDataAsString("schema-name", md.getSchemaName(column), out);
440         metaDataAsString("table-name", md.getTableName(column), out);
441         metaDataAsStringNullable("is-nullable", md.isNullable(column), out);
442         metaDataAsString("type", md.getColumnType(column), out);
443         metaDataAsString("display-size", md.getColumnDisplaySize(column), out);
444         out.println("      />");
445      }
446      out.println("   </meta-data>");
447   }
448
449
450   private void metaDataAsStringNullable (
451         String attributeName, int nullable, PrintWriter out)
452   {
453      switch (nullable)
454      {
455         case ResultSetMetaData.columnNoNulls:
456            metaDataAsString(attributeName, "no-nulls", out);
457            break;
458         case ResultSetMetaData.columnNullable:
459            metaDataAsString(attributeName, "nullable", out);
460            break;
461         case ResultSetMetaData.columnNullableUnknown:
462            metaDataAsString(attributeName, "unknown", out);
463            break;
464         default:
465            metaDataAsString(attributeName, "illeagal", out);
466         break;
467      }
468   }
469
470   /**
471    * @param string
472    * @param i
473    * @param out
474    */
475   private void metaDataAsString (
476         String attributeName, int i, PrintWriter out)
477   {
478      metaDataAsString(attributeName, String.valueOf(i), out);
479   }
480
481
482   /**
483    * @param string
484    * @param string2
485    * @param out
486    */
487   private void metaDataAsString (String attributeName,
488         String attributeValue, PrintWriter out)
489   {
490      out.print("              ");
491      out.print(attributeName);
492      out.print("='");
493      out.print(XmlUtil.attributeEscape(attributeValue));
494      out.println("'");
495   }
496
497
498
499   private void resultSet2Xml (ResultSet rs, PrintWriter out)
500         throws IOException, SQLException
501   {
502      out.println("   <result-set>");
503      while (rs.next())
504      {
505         out.print("      <row row-number='");
506         out.print(rs.getRow());
507         out.println("'>");
508         result2Xml(rs, out);
509         out.println("      </row>");
510      }
511      out.println("   </result-set>");
512   }
513
514   private void result2Xml (ResultSet rs, PrintWriter out)
515         throws SQLException, IOException
516   {
517      for (int column = 1; column <= mNumberOfColumns; column++)
518      {
519         final Object o = typedGetter(rs, column);
520         out.print("         <column");
521         if (o == null)
522         {
523            out.println(" isNull='true'>");
524         }
525         else
526         {
527            out.println(">");
528         }
529         final String name = rs.getMetaData().getColumnName(column);
530         final String typeName = rs.getMetaData().getColumnTypeName(column);
531         object2Xml(o, name, typeName, out);
532         out.println("         </column>");
533      }
534
535   }
536
537   private Object typedGetter (ResultSet rs, int column)
538         throws SQLException, IOException
539   {
540      final Object result;
541      final int type = rs.getMetaData().getColumnType(column);
542      switch (type)
543      {
544         case Types.TIMESTAMP:
545            result = convertTimestamp(rs.getTimestamp(column));
546            break;
547         case Types.DATE:
548         case Types.TIME:
549            // FIXME: Take care for milisecond & timezone!?
550            result = rs.getDate(column);
551            break;
552         case Types.CLOB:
553            result = readNclob(rs, column);
554            break;
555         case Types.BLOB:
556            result = readBlob(rs, column);
557            break;
558         default:
559            result = rs.getObject(column);
560      }
561      return result;
562   }
563
564   private Object readBlob (ResultSet rs, int column)
565         throws SQLException
566   {
567      final Blob blob = rs.getBlob(column);
568      final byte [] data;
569      if (blob != null)
570      {
571         final long length = blob.length();
572         if (length > Integer.MAX_VALUE)
573         {
574            data = "Length of Blob exceeds maximum of MAX_INT".getBytes();
575         }
576         else
577         {
578            data = blob.getBytes(1, (int) length);
579         }
580      }
581      else
582      {
583         data = null;
584      }
585      return data;
586   }
587
588   private String readNclob (ResultSet rs, int column)
589         throws SQLException, IOException
590   {
591      final String result;
592      final Reader reader = rs.getCharacterStream(column);
593      if (reader != null)
594      {
595          try
596          {
597             result = IoUtil.readFully(reader);
598          }
599          finally
600          {
601             IoUtil.close(reader);
602          }
603      }
604      else
605      {
606          result = null;
607      }
608      return result;
609   }
610
611
612   private void object2Xml (Object object, String name, String typeName,
613         PrintWriter out)
614         throws SQLException
615   {
616      if (object != null)
617      {
618         out.print("            <raw>");
619         out.print(XmlUtil.escape(String.valueOf(object)));
620         out.println("</raw>");
621         final String display = objectFormater(object, name, typeName);
622         if (display != null)
623         {
624            out.print("            <display>");
625            out.print(XmlUtil.escape(display));
626            out.println("</display>");
627         }
628      }
629      else
630      {
631         out.println("            <raw/>");
632      }
633   }
634
635   private String objectFormater (Object object, String name, String typeName)
636         throws SQLException
637   {
638      String result = null;
639
640      if (mTypeMapper.containsKey(name))
641      {
642         try
643         {
644            final TypeMapper mapper = (TypeMapper) mTypeMapper.get(name);
645            result = mapper.toDisplay(object);
646         }
647         catch (IllegalArgumentException e)
648         {
649            result = "Failed to convert type '" + e.toString() + "'.";
650            logger.log(Level.WARNING, result, e);
651         }
652         catch (IllegalAccessException e)
653         {
654            result = "Failed to convert type '" + e.toString() + "'.";
655            logger.log(Level.WARNING, result, e);
656
657         }
658         catch (InvocationTargetException e)
659         {
660            result = "Failed to convert type '" + e.toString() + "'.";
661            logger.log(Level.WARNING, result, e);
662         }
663         catch (RuntimeException e)
664         {
665            result = "Failed to convert type '" + e.toString() + "'.";
666            logger.log(Level.WARNING, result, e);
667         }
668      }
669      else if (object instanceof Date)
670      {
671         result = display((Date) object);
672      }
673      else if (object instanceof String)
674      {
675         result = display((String) object);
676      }
677      else
678      {
679         result = null;
680      }
681      return result;
682   }
683
684
685   private String display (Date d)
686   {
687      final String result;
688      if (d == null)
689      {
690         result = null;
691      }
692      else
693      {
694         result = mDateFormater.format(d);
695      }
696      return result;
697   }
698
699   private String display (String str)
700   {
701      final String result;
702      if (str == null)
703      {
704         result = null;
705      }
706      else if (str.indexOf('<') != -1)
707      {
708         final String detect = str.trim();
709         if (detect.indexOf('<') == 0
710               && detect.lastIndexOf('>') == (detect.length() - 1))
711         {
712            result = XmlUtil.formatXml(str);
713         }
714         else
715         {
716            result = null;
717         }
718      }
719      else
720      {
721         result = null;
722      }
723
724      return result;
725   }
726
727   private static Date convertTimestamp (Timestamp ts)
728   {
729      final Date d;
730      if (ts != null)
731      {
732         // ts.getTime does not return millis....
733         d = new Date(((ts.getTime() / MILLIS_PER_SECOND) * MILLIS_PER_SECOND)
734               + (ts.getNanos()
735                   / org.jcoderz.commons.types.Date.NANOS_PER_MILLI));
736      }
737      else
738      {
739         d = null;
740      }
741      return d;
742   }
743
744   private static class TypeMapper
745   {
746      private final Class mTypeClass;
747      private final Method mFromDb;
748      private final Method mToString;
749      private final String mTypeName;
750      private final Class mInputType;
751
752      public TypeMapper (String typeName, String typeClass, String fromDb,
753            String toString)
754            throws SecurityException, NoSuchMethodException,
755               ClassNotFoundException
756      {
757         this(typeName, Class.forName(typeClass), fromDb, toString);
758      }
759
760      public TypeMapper (String typeName, Class typeClass, String fromDb,
761            String toString)
762            throws SecurityException, NoSuchMethodException
763      {
764         mTypeClass = typeClass;
765         mToString = typeClass.getMethod(toString, null);
766//         mFromDb = typeClass.getMethod(fromDb, new Class[] {Object.class});
767         final Method [] methods = typeClass.getMethods();
768         Method method = null;
769         for (int i = 0; i < methods.length; i++)
770         {
771            if (methods[i].getReturnType() == mTypeClass
772               &&  methods[i].getName().startsWith(fromDb)
773               &&  ((methods[i].getModifiers() & Modifier.STATIC) != 0))
774            {
775               method = methods[i];
776               break;
777            }
778         }
779
780         mFromDb = method;
781         mInputType = method.getParameterTypes()[0];
782         mTypeName = typeName;
783      }
784
785      public String toDisplay (Object in)
786            throws IllegalArgumentException, IllegalAccessException,
787               InvocationTargetException
788      {
789         Object type = null;
790         try
791         {
792            type = mFromDb.invoke(null, new Object[] {in});
793         }
794         catch (Exception ex)
795         {
796            if (in instanceof Number)
797            {
798               if (mInputType == Integer.TYPE)
799               {
800                  type = mFromDb.invoke(null,
801                        new Object[]
802                        {
803                           new Integer(((Number) in).intValue())
804                        });
805               }
806               else if (mInputType == Long.TYPE)
807               {
808                  type = mFromDb.invoke(null,
809                        new Object[]
810                        {
811                           new Long(((Number) in).longValue())
812                        });
813               }
814            }
815            else
816            {
817               final IllegalArgumentException axe
818                     = new IllegalArgumentException(
819                        "Could not map type for object '" + String.valueOf(in)
820                        + "' of class " + in.getClass().getName()
821                        + " into expected converter "
822                        + "type " + mInputType.getName() + ".");
823               axe.initCause(ex);
824               throw axe;
825            }
826         }
827         return (String) mToString.invoke(type, null);
828      }
829   }
830
831   public static String escapeTableName (String in)
832   {
833       return in.replaceAll("[/\\\\$]", "#");
834   }
835}
Note: See TracBrowser for help on using the browser.