root/trunk/src/java/org/jcoderz/phoenix/sqlparser/SqlTransformer.java

Revision 1011, 15.5 kB (checked in by amandel, 4 years ago)

Aligned svn keyword settings.

  • 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.sqlparser;
34
35import java.io.File;
36import java.io.FileFilter;
37import java.io.FileInputStream;
38import java.io.FileNotFoundException;
39import java.io.FileOutputStream;
40import java.io.IOException;
41import java.io.PrintWriter;
42import java.util.ArrayList;
43import java.util.Arrays;
44import java.util.HashMap;
45import java.util.Iterator;
46import java.util.List;
47import java.util.Map;
48import javax.xml.bind.JAXBContext;
49import javax.xml.bind.JAXBException;
50import javax.xml.bind.Unmarshaller;
51
52import org.jcoderz.commons.util.Constants;
53import org.jcoderz.commons.util.IoUtil;
54import org.jcoderz.phoenix.sqlparser.jaxb.Index;
55import org.jcoderz.phoenix.sqlparser.jaxb.SqlMetainf;
56import org.jcoderz.phoenix.sqlparser.jaxb.Table;
57
58/**
59 * @author Albrecht Messner
60 */
61public class SqlTransformer
62{
63   private static final String DEFAULT_KEY = "default";
64   private static final int INITIAL = 0;
65   private static final int IN_CREATE = 1;
66   private final File mInputFile;
67   private final File mOutputFile;
68   private File mMetainfFile;
69   private final boolean mForce;
70   
71   private int mState;
72   private TokenType mObjectType;
73   private String mObjectName;
74
75   private final Map mIndexMap = new HashMap();
76   private final Map mTableMap = new HashMap();
77
78   /**
79    * Constructor.
80    * @param inFile the input file
81    * @param outFile the output file
82    * @param metainfFile file containing meta information
83    * @param force force transformation flag
84    */
85   public SqlTransformer (
86         String inFile, String outFile, String metainfFile, boolean force)
87   {
88      mInputFile = new File(inFile);
89      mOutputFile = new File(outFile);
90      if (metainfFile != null)
91      {
92         mMetainfFile = new File(metainfFile);
93         parseMetainfFile();
94      }
95      mForce = force;
96   }
97   
98   /**
99    * Executes transformation based on timestamp checking and force flag.
100    */
101   public void execute ()
102   {
103      try
104      {
105         if (checkFiles())
106         {
107            filterComments();
108         }
109         else if (mForce)
110         {
111            System.out.println("Forcing transformation...");
112            filterComments();
113         }
114         else
115         {
116            System.out.println(
117                  "Output file is newer than input file, skipping.");
118         }
119      }
120      // possible exceptions: FileNotFound, JAXB, IO, Parse
121      catch (Exception e)
122      {
123         System.err.println(
124               "SQL Transformation failed for file " + mInputFile);
125         throw new RuntimeException("SQL Transformation failed: " + e, e);
126      }
127   }
128   
129   /**
130    * Check status of input and output files.
131    * @return true if the transformation needs to be performed,
132    *       false otherwise
133    * @throws IOException if the input file does not exist
134    */
135   public boolean checkFiles ()
136         throws IOException
137   {
138      boolean result = true;
139      if (! mInputFile.exists())
140      {
141         throw new IOException("Input file " + mInputFile + " does not exist.");
142      }
143     
144      if (mOutputFile.exists()
145            && mInputFile.lastModified() < mOutputFile.lastModified())
146      {
147         result = false;
148      }
149     
150      return result;
151   }
152   
153   /**
154    * Filters all comments out of the input file and writes the filtered
155    * SQL to the output file.
156    *
157    * @throws FileNotFoundException if the input file could not be opened
158    * @throws ParseException if the input file could not be parsed
159    */
160   public void filterComments () throws FileNotFoundException, ParseException
161   {
162      //System.out.println("SqlCommentFilter: transforming "
163      //      + mInputFile + " to " + mOutputFile);
164      PrintWriter p2w = null; 
165      final FileInputStream in = new FileInputStream(mInputFile);
166      try
167      {
168          final ScannerInterface scanner = new SqlScanner(in);
169          scanner.setReportWhitespace(true);
170          p2w = new PrintWriter(new FileOutputStream(mOutputFile));
171   
172          Token token;
173          final StringBuffer sbuf = new StringBuffer();
174          while ((token = scanner.nextToken()).getType() != TokenType.EOF)
175          {
176             parserHook(token, sbuf);
177   
178             final TokenType type = token.getType();
179   
180             // System.out.println("Token: " + token);
181             if (type == TokenType.NEWLINE)
182             {
183                final String s = sbuf.toString();
184                if (! (s.trim().length() == 0))
185                {
186                   // System.out.println("Writing : '" + s + "'");
187                   p2w.println(s);
188                }
189                else
190                {
191                   // System.out.println("Skipping: '" + s + "'");
192                }
193                sbuf.setLength(0);
194             }
195             else if (type != TokenType.COMMENT)
196             {
197                sbuf.append(token.getValue());
198                if (type == TokenType.SEMICOLON)
199                {
200                   // separate statements with an empty line
201                   sbuf.append('\n');
202                }
203             }
204          }
205          if (! (sbuf.toString().trim().length() == 0))
206          {
207             p2w.println(sbuf.toString());
208          }
209      }
210      finally
211      {
212          IoUtil.close(p2w);
213          IoUtil.close(in);
214      }
215   }
216
217   private void parserHook (Token token, StringBuffer out)
218   {
219      final TokenType type = token.getType();
220      if (type == TokenType.WHITESPACE || type == TokenType.NEWLINE)
221      {
222         // nop
223      }
224      else if (type == TokenType.CREATE)
225      {
226         if (mState != INITIAL)
227         {
228            throw new IllegalStateException("Expected state to be INITIAL");
229         }
230         mState = IN_CREATE;
231      }
232      else if (type == TokenType.TABLE
233            || type == TokenType.INDEX)
234      {
235         if (mState == IN_CREATE)
236         {
237            mObjectType = type;
238         }
239      }
240      else if (mState == IN_CREATE && mObjectType != null
241            && mObjectName == null)
242      {
243         if (type != TokenType.IDENTIFIER)
244         {
245            throw new IllegalStateException(
246                  "Expected identifier but got " + token);
247         }
248         mObjectName = token.getValue();
249      }
250      else if (type == TokenType.SEMICOLON)
251      {
252         if (mState == IN_CREATE && mObjectType != null && mObjectName != null)
253         {
254            // now we're at the end of a "CREATE TABLE"
255            // or "CREATE INDEX" clause
256            printMetaInf(out);
257         }
258         mState = INITIAL;
259         mObjectType = null;
260         mObjectName = null;
261      }
262   }
263
264   
265   /**
266    * @param out
267    */
268   private void printMetaInf (StringBuffer out)
269   {
270      if (mMetainfFile != null)
271      {
272         final String metaInf;
273         final Map metainfMap = getMetainfMap(mObjectType);
274         
275         if (metainfMap.get(mObjectName.toUpperCase(
276                 Constants.SYSTEM_LOCALE)) == null)
277         {
278            metaInf = (String) metainfMap.get(DEFAULT_KEY);
279         }
280         else
281         {
282            metaInf = (String) metainfMap.get(mObjectName.toUpperCase());
283         }
284
285         out.append('\n');
286         out.append(metaInf);
287      }
288   }
289
290   private Map getMetainfMap (TokenType t)
291   {
292      final Map result;
293      if (t == TokenType.INDEX)
294      {
295         result = mIndexMap;
296      }
297      else if (t == TokenType.TABLE)
298      {
299         result = mTableMap;
300      }
301      else
302      {
303         throw new IllegalArgumentException("Illegal Token Type: " + t);
304      }
305      return result;
306   }
307   
308   private void parseMetainfFile ()
309   {
310      SqlMetainf metaInf = null;
311      try
312      {
313         final JAXBContext ctx
314               = JAXBContext.newInstance("org.jcoderz.phoenix.sqlparser.jaxb",
315                  this.getClass().getClassLoader());
316         final Unmarshaller unmarsh = ctx.createUnmarshaller();
317         unmarsh.setValidating(true);
318         metaInf = (SqlMetainf) unmarsh.unmarshal(mMetainfFile);
319      }
320      catch (JAXBException e)
321      {
322         e.printStackTrace();
323         System.exit(1);
324      }
325      mTableMap.put(DEFAULT_KEY, metaInf.getCreateTable().getDefault());
326      for (final Iterator it = metaInf.getCreateTable().getTable().iterator();
327         it.hasNext(); )
328      {
329         final Table tab = (Table) it.next();
330         if (mTableMap.containsKey(tab.getName()))
331         {
332            throw new IllegalArgumentException(
333                  "Table " + tab.getName() + " exists twice in "
334                  + mMetainfFile.getName());
335         }
336         mTableMap.put(tab.getName().toUpperCase(Constants.SYSTEM_LOCALE), 
337                 tab.getValue());
338      }
339     
340      mIndexMap.put(DEFAULT_KEY, metaInf.getCreateIndex().getDefault());
341      for (final Iterator it = metaInf.getCreateIndex().getIndex().iterator();
342         it.hasNext(); )
343      {
344         final Index ind = (Index) it.next();
345         if (mIndexMap.containsKey(ind.getName()))
346         {
347            throw new IllegalArgumentException(
348                  "Index " + ind.getName() + " exists twice in "
349                  + mMetainfFile.getName());
350         }
351         mIndexMap.put(ind.getName().toUpperCase(), ind.getValue());
352      }
353   }
354
355   /**
356    * Main method.
357    * @param args command line args
358    */
359   public static void main (String[] args)
360   {
361      final Options opts = parseCommandLine(args);
362      checkOptions(opts);
363
364      if (opts.mUseFiles)
365      {
366         final SqlTransformer filter = new SqlTransformer(
367               opts.mInputFile, opts.mOutputFile, opts.mMetainfFile,
368               opts.mForce);
369         filter.execute();
370      }
371      else
372      {
373         final List files = checkAndListFiles(opts);
374         for (final Iterator it = files.iterator(); it.hasNext(); )
375         {
376            final File inFile = (File) it.next();
377            final File outFile = new File(opts.mOutDir, inFile.getName());
378            final SqlTransformer filter = new SqlTransformer(
379                  inFile.getAbsolutePath(), outFile.getAbsolutePath(),
380                  opts.mMetainfFile, opts.mForce);
381            filter.execute();
382         }
383      }
384   }
385   
386   private static List checkAndListFiles (Options opts)
387   {
388      final File inDir = new File(opts.mInDir);
389      checkDir(inDir);
390      final File outDir = new File(opts.mOutDir);
391      checkDir(outDir);
392     
393      final List list = new ArrayList();
394      final FileFilter ff = new FileFilter() {
395         public boolean accept (File pathname)
396         {
397            final boolean result;
398            if (pathname.getName().endsWith(".sql"))
399            {
400               result = true;
401            }
402            else
403            {
404               result = false;
405            }
406            return result;
407         }
408      };
409      final File[] inFiles = inDir.listFiles(ff);
410      list.addAll(Arrays.asList(inFiles));
411      return list;
412   }
413   
414   private static void checkDir (File dir)
415   {
416      if (! dir.exists())
417      {
418         System.err.println("Directory " + dir + " does not exist");
419         System.exit(1);
420      }     
421   }
422
423   /**
424    * @param opts
425    */
426   private static void checkOptions (final Options opts)
427   {
428      if (opts.mUseFiles && opts.mUseDirs)
429      {
430         System.err.println("Specify either '-i' and '-o' options "
431               + "or '-d' and '-t' options.");
432         usage();
433      }
434     
435      if (opts.mUseFiles)
436      {
437         if (opts.mInputFile == null || opts.mOutputFile == null)
438         {
439            usage();
440         }
441      }
442      else if (opts.mUseDirs)
443      {
444         if (opts.mInDir == null || opts.mOutDir == null)
445         {
446            usage();
447         }
448      }
449      else
450      {
451         usage();
452      }
453   }
454
455   private static Options parseCommandLine (String[] args)
456   {
457      final Options opts = new Options();
458      int i = 0;
459      try
460      {
461         for (i = 0; i < args.length; i++)
462         {
463            if (args[i].equals("-i"))
464            {
465               opts.mInputFile = args[++i];
466               opts.mUseFiles = true;
467            }
468            else if (args[i].equals("-o"))
469            {
470               opts.mOutputFile = args[++i];
471               opts.mUseFiles = true;
472            }
473            else if (args[i].equals("-d"))
474            {
475               opts.mInDir = args[++i];
476               opts.mUseDirs = true;
477            }
478            else if (args[i].equals("-t"))
479            {
480               opts.mOutDir = args[++i];
481               opts.mUseDirs = true;
482            }
483            else if (args[i].equals("-f"))
484            {
485               opts.mForce = true;
486            }
487            else if (args[i].equals("-m"))
488            {
489               opts.mMetainfFile = args[++i]; 
490            }
491            else
492            {
493               usage();
494            }
495         }
496      }
497      catch (ArrayIndexOutOfBoundsException x)
498      {
499         System.err.println("Error: argument "
500            + args[i - 1] + " requires an option");
501         usage();
502      }
503      return opts;
504   }
505
506   private static void usage ()
507   {
508      System.err.println("Usage: SqlCommentFilter");
509      System.err.println("   -i <input_file>    ... input file to transform");
510      System.err.println("   -o <output_file>   ... output file to write to");
511      System.err.println("   -d <input_dir>     ... transform all files from"
512            + " directory");
513      System.err.println("   -t <to_dir>        ... write files to directory");
514      System.err.println("   -m <metainf_file>  ... use sql metainf file");
515      System.err.println("   -f                 ... force transformation even"
516            + " if input file is newer");
517      System.err.println("Note: you can either give '-i' and '-o' or ");
518      System.err.println("      '-d' and '-t'");
519      System.exit(1);
520   }
521   
522   private static class Options
523   {
524      private String mInputFile = null;
525      private String mOutputFile = null;
526      private boolean mUseFiles = false;
527     
528      private String mInDir = null;
529      private String mOutDir = null;
530      private boolean mUseDirs = false;
531     
532      private String mMetainfFile = null;
533      private boolean mForce = false;
534   }
535}
Note: See TracBrowser for help on using the browser.