/* * $Id$ * * Copyright 2006, The jCoderZ.org Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the jCoderZ.org Project nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jcoderz.phoenix.sqlparser; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import org.jcoderz.commons.util.Constants; import org.jcoderz.commons.util.IoUtil; import org.jcoderz.phoenix.sqlparser.jaxb.Index; import org.jcoderz.phoenix.sqlparser.jaxb.SqlMetainf; import org.jcoderz.phoenix.sqlparser.jaxb.Table; /** * @author Albrecht Messner */ public class SqlTransformer { private static final String DEFAULT_KEY = "default"; private static final int INITIAL = 0; private static final int IN_CREATE = 1; private final File mInputFile; private final File mOutputFile; private File mMetainfFile; private final boolean mForce; private int mState; private TokenType mObjectType; private String mObjectName; private final Map mIndexMap = new HashMap(); private final Map mTableMap = new HashMap(); /** * Constructor. * @param inFile the input file * @param outFile the output file * @param metainfFile file containing meta information * @param force force transformation flag */ public SqlTransformer ( String inFile, String outFile, String metainfFile, boolean force) { mInputFile = new File(inFile); mOutputFile = new File(outFile); if (metainfFile != null) { mMetainfFile = new File(metainfFile); parseMetainfFile(); } mForce = force; } /** * Executes transformation based on timestamp checking and force flag. */ public void execute () { try { if (checkFiles()) { filterComments(); } else if (mForce) { System.out.println("Forcing transformation..."); filterComments(); } else { System.out.println( "Output file is newer than input file, skipping."); } } // possible exceptions: FileNotFound, JAXB, IO, Parse catch (Exception e) { System.err.println( "SQL Transformation failed for file " + mInputFile); throw new RuntimeException("SQL Transformation failed: " + e, e); } } /** * Check status of input and output files. * @return true if the transformation needs to be performed, * false otherwise * @throws IOException if the input file does not exist */ public boolean checkFiles () throws IOException { boolean result = true; if (! mInputFile.exists()) { throw new IOException("Input file " + mInputFile + " does not exist."); } if (mOutputFile.exists() && mInputFile.lastModified() < mOutputFile.lastModified()) { result = false; } return result; } /** * Filters all comments out of the input file and writes the filtered * SQL to the output file. * * @throws FileNotFoundException if the input file could not be opened * @throws ParseException if the input file could not be parsed */ public void filterComments () throws FileNotFoundException, ParseException { //System.out.println("SqlCommentFilter: transforming " // + mInputFile + " to " + mOutputFile); PrintWriter p2w = null; final FileInputStream in = new FileInputStream(mInputFile); try { final ScannerInterface scanner = new SqlScanner(in); scanner.setReportWhitespace(true); p2w = new PrintWriter(new FileOutputStream(mOutputFile)); Token token; final StringBuffer sbuf = new StringBuffer(); while ((token = scanner.nextToken()).getType() != TokenType.EOF) { parserHook(token, sbuf); final TokenType type = token.getType(); // System.out.println("Token: " + token); if (type == TokenType.NEWLINE) { final String s = sbuf.toString(); if (! (s.trim().length() == 0)) { // System.out.println("Writing : '" + s + "'"); p2w.println(s); } else { // System.out.println("Skipping: '" + s + "'"); } sbuf.setLength(0); } else if (type != TokenType.COMMENT) { sbuf.append(token.getValue()); if (type == TokenType.SEMICOLON) { // separate statements with an empty line sbuf.append('\n'); } } } if (! (sbuf.toString().trim().length() == 0)) { p2w.println(sbuf.toString()); } } finally { IoUtil.close(p2w); IoUtil.close(in); } } private void parserHook (Token token, StringBuffer out) { final TokenType type = token.getType(); if (type == TokenType.WHITESPACE || type == TokenType.NEWLINE) { // nop } else if (type == TokenType.CREATE) { if (mState != INITIAL) { throw new IllegalStateException("Expected state to be INITIAL"); } mState = IN_CREATE; } else if (type == TokenType.TABLE || type == TokenType.INDEX) { if (mState == IN_CREATE) { mObjectType = type; } } else if (mState == IN_CREATE && mObjectType != null && mObjectName == null) { if (type != TokenType.IDENTIFIER) { throw new IllegalStateException( "Expected identifier but got " + token); } mObjectName = token.getValue(); } else if (type == TokenType.SEMICOLON) { if (mState == IN_CREATE && mObjectType != null && mObjectName != null) { // now we're at the end of a "CREATE TABLE" // or "CREATE INDEX" clause printMetaInf(out); } mState = INITIAL; mObjectType = null; mObjectName = null; } } /** * @param out */ private void printMetaInf (StringBuffer out) { if (mMetainfFile != null) { final String metaInf; final Map metainfMap = getMetainfMap(mObjectType); if (metainfMap.get(mObjectName.toUpperCase( Constants.SYSTEM_LOCALE)) == null) { metaInf = (String) metainfMap.get(DEFAULT_KEY); } else { metaInf = (String) metainfMap.get(mObjectName.toUpperCase()); } out.append('\n'); out.append(metaInf); } } private Map getMetainfMap (TokenType t) { final Map result; if (t == TokenType.INDEX) { result = mIndexMap; } else if (t == TokenType.TABLE) { result = mTableMap; } else { throw new IllegalArgumentException("Illegal Token Type: " + t); } return result; } private void parseMetainfFile () { SqlMetainf metaInf = null; try { final JAXBContext ctx = JAXBContext.newInstance("org.jcoderz.phoenix.sqlparser.jaxb", this.getClass().getClassLoader()); final Unmarshaller unmarsh = ctx.createUnmarshaller(); unmarsh.setValidating(true); metaInf = (SqlMetainf) unmarsh.unmarshal(mMetainfFile); } catch (JAXBException e) { e.printStackTrace(); System.exit(1); } mTableMap.put(DEFAULT_KEY, metaInf.getCreateTable().getDefault()); for (final Iterator it = metaInf.getCreateTable().getTable().iterator(); it.hasNext(); ) { final Table tab = (Table) it.next(); if (mTableMap.containsKey(tab.getName())) { throw new IllegalArgumentException( "Table " + tab.getName() + " exists twice in " + mMetainfFile.getName()); } mTableMap.put(tab.getName().toUpperCase(Constants.SYSTEM_LOCALE), tab.getValue()); } mIndexMap.put(DEFAULT_KEY, metaInf.getCreateIndex().getDefault()); for (final Iterator it = metaInf.getCreateIndex().getIndex().iterator(); it.hasNext(); ) { final Index ind = (Index) it.next(); if (mIndexMap.containsKey(ind.getName())) { throw new IllegalArgumentException( "Index " + ind.getName() + " exists twice in " + mMetainfFile.getName()); } mIndexMap.put(ind.getName().toUpperCase(), ind.getValue()); } } /** * Main method. * @param args command line args */ public static void main (String[] args) { final Options opts = parseCommandLine(args); checkOptions(opts); if (opts.mUseFiles) { final SqlTransformer filter = new SqlTransformer( opts.mInputFile, opts.mOutputFile, opts.mMetainfFile, opts.mForce); filter.execute(); } else { final List files = checkAndListFiles(opts); for (final Iterator it = files.iterator(); it.hasNext(); ) { final File inFile = (File) it.next(); final File outFile = new File(opts.mOutDir, inFile.getName()); final SqlTransformer filter = new SqlTransformer( inFile.getAbsolutePath(), outFile.getAbsolutePath(), opts.mMetainfFile, opts.mForce); filter.execute(); } } } private static List checkAndListFiles (Options opts) { final File inDir = new File(opts.mInDir); checkDir(inDir); final File outDir = new File(opts.mOutDir); checkDir(outDir); final List list = new ArrayList(); final FileFilter ff = new FileFilter() { public boolean accept (File pathname) { final boolean result; if (pathname.getName().endsWith(".sql")) { result = true; } else { result = false; } return result; } }; final File[] inFiles = inDir.listFiles(ff); list.addAll(Arrays.asList(inFiles)); return list; } private static void checkDir (File dir) { if (! dir.exists()) { System.err.println("Directory " + dir + " does not exist"); System.exit(1); } } /** * @param opts */ private static void checkOptions (final Options opts) { if (opts.mUseFiles && opts.mUseDirs) { System.err.println("Specify either '-i' and '-o' options " + "or '-d' and '-t' options."); usage(); } if (opts.mUseFiles) { if (opts.mInputFile == null || opts.mOutputFile == null) { usage(); } } else if (opts.mUseDirs) { if (opts.mInDir == null || opts.mOutDir == null) { usage(); } } else { usage(); } } private static Options parseCommandLine (String[] args) { final Options opts = new Options(); int i = 0; try { for (i = 0; i < args.length; i++) { if (args[i].equals("-i")) { opts.mInputFile = args[++i]; opts.mUseFiles = true; } else if (args[i].equals("-o")) { opts.mOutputFile = args[++i]; opts.mUseFiles = true; } else if (args[i].equals("-d")) { opts.mInDir = args[++i]; opts.mUseDirs = true; } else if (args[i].equals("-t")) { opts.mOutDir = args[++i]; opts.mUseDirs = true; } else if (args[i].equals("-f")) { opts.mForce = true; } else if (args[i].equals("-m")) { opts.mMetainfFile = args[++i]; } else { usage(); } } } catch (ArrayIndexOutOfBoundsException x) { System.err.println("Error: argument " + args[i - 1] + " requires an option"); usage(); } return opts; } private static void usage () { System.err.println("Usage: SqlCommentFilter"); System.err.println(" -i <input_file> ... input file to transform"); System.err.println(" -o <output_file> ... output file to write to"); System.err.println(" -d <input_dir> ... transform all files from" + " directory"); System.err.println(" -t <to_dir> ... write files to directory"); System.err.println(" -m <metainf_file> ... use sql metainf file"); System.err.println(" -f ... force transformation even" + " if input file is newer"); System.err.println("Note: you can either give '-i' and '-o' or "); System.err.println(" '-d' and '-t'"); System.exit(1); } private static class Options { private String mInputFile = null; private String mOutputFile = null; private boolean mUseFiles = false; private String mInDir = null; private String mOutDir = null; private boolean mUseDirs = false; private String mMetainfFile = null; private boolean mForce = false; } }