/* * Copyright 2008 biaoping.yin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.frameworkset.orm.engine.transform; /* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import com.frameworkset.orm.engine.model.Column; import com.frameworkset.orm.engine.model.Database; import com.frameworkset.orm.engine.model.ForeignKey; import com.frameworkset.orm.engine.model.IDMethod; import com.frameworkset.orm.engine.model.Table; import com.frameworkset.orm.sql.ParseException; import com.frameworkset.orm.sql.SQLScanner; import com.frameworkset.orm.sql.Token; /** * A Class that converts an sql input file to a Database structure. * The class makes use of SQL Scanner to get * sql tokens and the parses these to create the Database * class. SQLToAppData is in effect a simplified sql parser. * * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a> * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a> * @version $Id: SQLToAppData.java,v 1.7 2004/02/22 06:27:19 jmcnally Exp $ */ public class SQLToAppData implements Serializable { private String sqlFile; private List tokens; private Token token; private Database appDataDB; private int count; private String databaseType; /** * Create a new class with an input Reader * * @param sqlFile the sql file */ public SQLToAppData(String sqlFile) { this.sqlFile = sqlFile; } /** * Create a new class with an input Reader. This ctor is not used * but putting here in the event db.props properties are found to * be useful converting sql to xml, the infrastructure will exist * * @param sqlFile the sql file * @param databaseType */ public SQLToAppData(String sqlFile, String databaseType) { this.sqlFile = sqlFile; this.databaseType = databaseType; } /** * Get the current input sql file * * @return the sql file */ public String getSqlFile() { return sqlFile; } /** * Set the current input sql file * * @param sqlFile the sql file */ public void setSqlFile(String sqlFile) { this.sqlFile = sqlFile; } /** * Move to the next token. * * @throws ParseException if there is no more tokens available. */ private void next() throws ParseException { if (count < tokens.size()) { token = (Token) tokens.get(count++); } else { throw new ParseException("No More Tokens"); } } /** * Creates an error condition and adds the line and * column number of the current token to the error * message. * * @param name name of the error * @throws ParseException */ private void err(String name) throws ParseException { throw new ParseException (name + " at [ line: " + token.getLine() + " col: " + token.getCol() + " ]"); } /** * Check if there is more tokens available for parsing. * * @return true if there are more tokens available */ private boolean hasTokens() { return count < tokens.size(); } /** * Parses a CREATE TABLE FOO command. * * @throws ParseException */ private void create() throws ParseException { next(); if (token.getStr().toUpperCase().equals("TABLE")) { create_Table(); } } /** * Parses a CREATE TABLE sql command * * @throws ParseException error parsing the input file */ private void create_Table() throws ParseException { next(); String tableName = token.getStr(); // name of the table next(); if (!token.getStr().equals("(")) { err("( expected"); } next(); Table tbl = new Table (tableName); //tbl.setIdMethod("none"); while (!token.getStr().equals(";")) { create_Table_Column(tbl); } if (tbl.getPrimaryKey().size() == 1) { tbl.setIdMethod(IDMethod.ID_BROKER); } else { tbl.setIdMethod(IDMethod.NO_ID_METHOD); } appDataDB.addTable (tbl); } /** * Parses column information between the braces of a CREATE * TABLE () sql statement. * * @throws ParseException error parsing the input file */ private void create_Table_Column(Table tbl) throws ParseException { // The token should be the first item // which is the name of the column or // PRIMARY/FOREIGN/UNIQUE if (token.getStr().equals(",")) { next(); } if (token.getStr().toUpperCase().equals("PRIMARY")) { create_Table_Column_Primary(tbl); } else if (token.getStr().toUpperCase().equals("FOREIGN")) { create_Table_Column_Foreign(tbl); } else if (token.getStr().toUpperCase().equals("UNIQUE")) { create_Table_Column_Unique(tbl); } else { create_Table_Column_Data(tbl); } } /** * Parses PRIMARY KEY (FOO,BAR) statement * * @throws ParseException error parsing the input file */ private void create_Table_Column_Primary (Table tbl) throws ParseException { next(); if (!token.getStr().toUpperCase().equals("KEY")) { err("KEY expected"); } next(); if (!token.getStr().toUpperCase().equals("(")) { err("( expected"); } next(); String colName = token.getStr(); Column c = tbl.getColumn(colName); if (c == null) { err("Invalid column name: " + colName); } c.setPrimaryKey(true); next(); while (token.getStr().equals(",")) { next(); colName = token.getStr(); c = tbl.getColumn(colName); if (c == null) { err("Invalid column name: " + colName); } c.setPrimaryKey(true); next(); } if (!token.getStr().toUpperCase().equals(")")) { err(") expected"); } next(); // skip the ) } /** * Parses UNIQUE (NAME,FOO,BAR) statement * * @throws ParseException error parsing the input file */ private void create_Table_Column_Unique(Table tbl) throws ParseException { next(); if (!token.getStr().toUpperCase().equals("(")) { err("( expected"); } next(); while (!token.getStr().equals(")")) { if (!token.getStr().equals(",")) { String colName = token.getStr(); Column c = tbl.getColumn(colName); if (c == null) { err("Invalid column name: " + colName); } c.setUnique(true); } next(); } if (!token.getStr().toUpperCase().equals(")")) { err(") expected got: " + token.getStr()); } next(); // skip the ) } /** * Parses FOREIGN KEY (BAR) REFERENCES TABLE (BAR) statement * * @throws ParseException error parsing the input file */ private void create_Table_Column_Foreign(Table tbl) throws ParseException { next(); if (!token.getStr().toUpperCase().equals("KEY")) { err("KEY expected"); } next(); if (!token.getStr().toUpperCase().equals("(")) { err("( expected"); } next(); ForeignKey fk = new ForeignKey(); List localColumns = new ArrayList(); tbl.addForeignKey(fk); String colName = token.getStr(); localColumns.add(colName); next(); while (token.getStr().equals(",")) { next(); colName = token.getStr(); localColumns.add(colName); next(); } if (!token.getStr().toUpperCase().equals(")")) { err(") expected"); } next(); if (!token.getStr().toUpperCase().equals("REFERENCES")) { err("REFERENCES expected"); } next(); fk.setForeignTableName(token.getStr()); next(); if (token.getStr().toUpperCase().equals("(")) { next(); int i = 0; fk.addReference((String) localColumns.get(i++), token.getStr()); next(); while (token.getStr().equals(",")) { next(); fk.addReference((String) localColumns.get(i++), token.getStr()); next(); } if (!token.getStr().toUpperCase().equals(")")) { err(") expected"); } next(); } } /** * Parse the data definition of the column statement. * * @throws ParseException error parsing the input file */ private void create_Table_Column_Data(Table tbl) throws ParseException { String columnSize = null; String columnPrecision = null; String columnDefault = null; boolean inEnum = false; String columnName = token.getStr(); next(); String columnType = token.getStr(); if (columnName.equals(")") && columnType.equals(";")) { return; } next(); // special case for MySQL ENUM's which are stupid anyway // and not properly handled by Torque. if (columnType.toUpperCase().equals("ENUM")) { inEnum = true; next(); // skip ( while (!token.getStr().equals(")")) { // skip until ) next(); } while (!token.getStr().equals(",")) { if (token.getStr().toUpperCase().equals("DEFAULT")) { next(); if (token.getStr().equals("'")) { next(); } columnDefault = token.getStr(); next(); if (token.getStr().equals("'")) { next(); } } // skip until , next(); } next(); // skip , columnType = "VARCHAR"; } else if (token.getStr().toUpperCase().equals("(")) { next(); columnSize = token.getStr(); next(); if (token.getStr().equals(",")) { next(); columnPrecision = token.getStr(); next(); } if (!token.getStr().equals(")")) { err(") expected"); } next(); } Column col = new Column(columnName); if (columnPrecision != null) { columnSize = columnSize + columnPrecision; } col.setTypeFromString(columnType, columnSize); tbl.addColumn(col); if (inEnum) { col.setNotNull(true); if (columnDefault != null) { col.setDefaultValue(columnDefault); } } else { while (!token.getStr().equals(",") && !token.getStr().equals(")")) { if (token.getStr().toUpperCase().equals("NOT")) { next(); if (!token.getStr().toUpperCase().equals("NULL")) { err("NULL expected after NOT"); } col.setNotNull(true); next(); } else if (token.getStr().toUpperCase().equals("PRIMARY")) { next(); if (!token.getStr().toUpperCase().equals("KEY")) { err("KEY expected after PRIMARY"); } col.setPrimaryKey(true); next(); } else if (token.getStr().toUpperCase().equals("UNIQUE")) { col.setUnique(true); next(); } else if (token.getStr().toUpperCase().equals("NULL")) { col.setNotNull(false); next(); } else if (token.getStr().toUpperCase().equals("AUTO_INCREMENT")) { col.setAutoIncrement(true); next(); } else if (token.getStr().toUpperCase().equals("DEFAULT")) { next(); if (token.getStr().equals("'")) { next(); } col.setDefaultValue(token.getStr()); next(); if (token.getStr().equals("'")) { next(); } } } next(); // eat the , } } /** * Execute the parser. * * @throws IOException If an I/O error occurs * @throws ParseException error parsing the input file */ public Database execute() throws IOException, ParseException { count = 0; appDataDB = new Database(databaseType); FileReader fr = new FileReader(sqlFile); BufferedReader br = new BufferedReader(fr); SQLScanner scanner = new SQLScanner(br); tokens = scanner.scan(); br.close(); while (hasTokens()) { if (token == null) { next(); } if (token.getStr().toUpperCase().equals("CREATE")) { create(); } if (hasTokens()) { next(); } } return appDataDB; } }