View Javadoc

1   /***
2    * Copyright 2007 ATG DUST Project
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * 
7    * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8    * 
9    * Unless required by applicable law or agreed to in writing, software 
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and limitations under the License.
13   */
14  
15  package atg.adapter.gsa;
16  
17  import java.io.BufferedReader;
18  import java.io.File;
19  import java.io.FileInputStream;
20  import java.io.InputStreamReader;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import org.apache.log4j.Logger;
28  
29  /***
30  * THIS IS A REALLY SIMPLE APPLICATION THAT
31  * TAKES A STRING ARRAY OF SQL FILES AND
32  * RETURNS ALL OF THE SQL STATEMENTS THAT
33  * MUST BE EXECUTED WITHIN THE FILES
34  * DATE: MAY 23, 2000
35  * 
36  *
37  * @author jb
38  * 7/14/2002
39  */
40  public class SQLFileParser
41  {
42    
43    private static Logger log = Logger.getLogger(SQLFileParser.class);
44  
45  // ****************************************
46  // THERE IS ONLY ONE CLASS VARIABLE
47  // FOR THIS CLASS.  THAT VARIABLE IS TO
48  // STORE THE FILE NAMES.
49  // ****************************************
50  
51      private String[] sFileNameArray = {""};
52      /*** sets the names of files to be parsed */
53      private void setFileNameArray(String[] pFileNameArray)
54      {
55              sFileNameArray = pFileNameArray;
56      }
57      
58  //    /*** returns the names of files to be parsed */
59  //    private String[] getFileNameArray()
60  //    {
61  //            return (sFileNameArray);
62  //    }
63  
64  // ============== CONSTRUCTORS =================
65        /***
66        * no arg constructor
67        */
68        public SQLFileParser() {}
69  
70  // ================= PUBLIC METHODS =================
71  
72  
73  	private String trimDebuggingCharacters(String sText)
74  	{
75              if ( sText == null ) return sText;
76              return sText.trim();
77              /*
78  		char[] ch = new char [sText.length()];
79  		ch = sText.toCharArray();
80  		String sTemp = "";
81  		for (int i=0; i < sText.length(); i++)
82  		{
83  			if ((ch[i] == '\n') || (ch[i] == '\r') || (ch[i] == '\t') || (ch[i] == '	'))
84  			{
85  				// Keep going
86  				sTemp = sTemp + " ";
87  			}
88  			else
89  			{
90  				sTemp = sTemp + ch[i];
91  			}
92  		}
93  		if (sTemp.length() > DEBUGGING_LENGTH)
94  		{
95  			return (sTemp.substring(0,DEBUGGING_LENGTH));
96  		}
97  		else
98  		{
99  			return (sTemp);
100 		}
101               */
102 	}
103 
104 	private  String RemoveWhiteSpaceInFront (String sText)
105 	{
106 // **********************************************************************************
107 //  THIS FUNCTION REMOVES WHITESPACE IN THE FRONT OF A STRING. FOR INSTANCE
108 //  IF A STRING IS PASSED TO IT AS '   SPACE', THEN IT WILL RETURN 'SPACE.
109 // **********************************************************************************
110 		char[] ch = new char [sText.length()];
111 		ch = sText.toCharArray();
112 		//String sTemp;
113 		for (int i=0; i < sText.length(); i++)
114 		{
115 			if ((ch[i] == '\n') || (ch[i] == '\r') || (ch[i] == '\t')|| (ch[i] == ' ') || (ch[i] == '	'))
116 			{
117 				// Keep going
118 			}
119 			else
120 			{
121 				return (sText.substring (i, sText.length()));
122 			}
123 		}
124 		return (sText);
125 	}
126 
127 //	private  String RemoveWhiteSpaceInEnd (String sText)
128 //	{
129 //// **********************************************************************************
130 ////  THIS FUNCTION REMOVES WHITESPACE IN THE END OF A STRING. FOR INSTANCE
131 ////  IF A STRING IS PASSED TO IT AS 'SPACE   ', THEN IT WILL RETURN 'SPACE.
132 //// **********************************************************************************
133 //		char[] ch = new char [sText.length()];
134 //		ch = sText.toCharArray();
135 //		String sTemp;
136 //		for (int i= sText.length() - 1; i > 0; i--)
137 //		{
138 //			if ((ch[i] == '\n') || (ch[i] == '\r') || (ch[i] == '\t')|| (ch[i] == ' ') || (ch[i] == '	'))
139 //			{
140 //				// Keep going
141 //			}
142 //			else
143 //			{
144 //				return (sText.substring (0, i +1));
145 //			}
146 //		}
147 //		return (sText);
148 //	}
149 
150 	private  String RemoveWhiteSpaceFromString (String sText)
151 	{
152             if ( sText == null ) return null;
153             return sText.trim();
154 
155 // **********************************************************************************
156 //  THIS FUNCTION REMOVES WHITESPACE IN THE FRONT AND END OF A STRING. FOR INSTANCE
157 //  IF A STRING IS PASSED TO IT AS '   SPACE   ', THEN IT WILL RETURN 'SPACE.
158 // **********************************************************************************
159 /*
160 		sText = RemoveWhiteSpaceInFront (sText);
161 		sText = RemoveWhiteSpaceInEnd (sText);
162 		return (sText);
163 */
164 	}
165 
166 
167 	private int countFileArraySize(String[] sArray)
168 	{
169 
170 // ****************************************
171 // THIS IS A USEFUL FUNCTION TO
172 // COUNT THE SIZE OF AN ARRAY. IT
173 // TAKES AN ARRAY AS A PARAMETER
174 // AND IT RETURNS AN INTEGER AS
175 // THE COUNT.  IN THIS CASE WE
176 // DO NOT NEED TO PASS THE ARRAY
177 // SINCE IT IS A CLASS VARIABLE
178 // ****************************************
179 
180 		List<String> newList = Arrays.asList(sArray);
181 		return (newList.size());
182 	}
183 
184 	private boolean checkForKeywords (String sLineRead)
185 	{
186 
187 // ****************************************
188 // THIS IS ONE OF THE MOST CRITICAL
189 // FUNCTIONS IN THIS PROGRAM.  IT
190 // TAKES THE RESERVED WORDS FROM
191 // SQL INSTALL SCRIPTS AND RESERVES
192 // THEM WITHIN THIS APPLICATION.
193 // EACH WORD LISTED BELOW STARTS
194 // A SQL STATEMENT.
195 // ****************************************
196 
197 		String[] sKeywordList = {"BEGIN ", "CREATE ", "INSERT ", "UPDATE ", "DELETE ", "COMMIT ", "COMMIT", "ALTER ", "DROP ", "GRANT ", "GO", "IF OBJECT_ID"};
198 		List<String> lList = Arrays.asList(sKeywordList);
199 		int iArraySize = lList.size();
200 
201 		sLineRead = RemoveWhiteSpaceInFront(sLineRead);
202                 //sLineRead = sLineRead.trim();
203                 // something about using trim() doesn't work with the overall logic of the program.
204 
205 		for (int i=0; i< iArraySize; i++)
206 		{
207 			if (sLineRead.toUpperCase().startsWith(sKeywordList[i].toUpperCase()) )
208 			{
209                             // we need to check more than just whether the line starts
210                             // with the keyword.  we also have to try to determine that
211                             // the keyword we found is actually NOT just the beginning of a
212                             // bigger word.  for example, GOogle.
213                             // so...
214                             if ( sLineRead.length() == sKeywordList[i].trim().length() ) {
215                                 // if the whole line is just the keyword...
216 				return true;
217                             } else if ( sLineRead.charAt( sKeywordList[i].trim().length() ) == ' ' ) {
218                                 // if the char after the word is a space
219                                 return true;
220                             } else if ( sLineRead.charAt( sKeywordList[i].trim().length() ) == '(' ) {
221                                 // if the char after the word is a right paren "("
222                                 return true;
223                             }
224 			}
225 		}
226 		return (false);
227 	}
228 
229 	private String checkForComments(String sLineRead)
230 	{
231 
232 // ****************************************
233 // THIS FUNCTION HAS ONE DUTY.  IT CHECKS
234 // FOR LINES IN  SQL SCRIPT THAT HAVE
235 // COMMENTS IN THEM.  IF IT FINDS A LINE
236 // IN A SQL SCRIPT THAT WOULD BE RESERVED
237 // FOR A COMMENT IT WILL SKIP THAT LINE.
238 // ****************************************
239 
240 		String[] sKeywordList = {"--", "//"};
241 		List<String> lList = Arrays.asList(sKeywordList);
242 		int iArraySize = lList.size();
243 
244 		//sLineRead = sLineRead;
245 		for (int i=0; i< iArraySize; i++)
246 		{
247 			if (sLineRead.toUpperCase().indexOf(sKeywordList[i]) > -1)
248 			{
249 				return (sLineRead.substring(0,sLineRead.indexOf(sKeywordList[i])));
250 			}
251 		}
252 		return (sLineRead);
253 	}
254 
255 	private String checkForSemiColons(String sCurrentLine)
256 	{
257 
258 // ****************************************
259 // THIS FUNCTION HAS ONE DUTY.  IT CHECKS
260 // FOR SQL STATEMENTS THAT END IN THE
261 // SEMICOLON PUNCTUATION AND REMOVES
262 // THE SEMICOLON.
263 // ****************************************
264 
265 		if (sCurrentLine.indexOf(";") > -1)
266 		{
267 			sCurrentLine = sCurrentLine.substring (0, sCurrentLine.indexOf(";"));
268 		}
269 		return (sCurrentLine);
270 
271 	}
272 
273 	private String checkForEndLines(String sCurrentLine)
274 	{
275 
276 // ****************************************
277 // THIS FUNCTION HAS ONE DUTY.  IT CHECKS
278 // FOR SQL STATEMENTS THAT END IN THE
279 // SEMICOLON PUNCTUATION AND REMOVES
280 // THE SEMICOLON.  Sometimes if there
281 // is division in a view that is created
282 // the end line will be used, and sometimes
283 // it is used to execute a statement.
284 // ****************************************
285 		sCurrentLine = RemoveWhiteSpaceFromString(sCurrentLine);
286 		int iLength = sCurrentLine.length() -2;
287 		//int iIndex = sCurrentLine.indexOf("/");
288 		if ((sCurrentLine.indexOf("/") > -1) && (sCurrentLine.indexOf("/") >= iLength))
289 		{
290 			sCurrentLine = sCurrentLine.substring (0, sCurrentLine.indexOf("/"));
291 		}
292 		else if ((sCurrentLine.indexOf("/") > -1) && (sCurrentLine.indexOf("/") == 0))
293 		{
294 			sCurrentLine = "";
295 		}
296 
297 		sCurrentLine = sCurrentLine + " ";
298 		return (sCurrentLine);
299 	}
300 
301 
302 
303 	private List<String> readFileAndLoadData ()
304 	{
305 
306 // ****************************************
307 // THIS FUNCTION IS THE ENGINE FOR THIS
308 // APPLICATION. THIS FUNCTION OPENS
309 // THE FIRST FILE FROM THE FILE ARRAY
310 // LIST.  THEN IT READS THROUGH THE
311 // FILE LINE BY LINE BUILDING A VECTOR
312 // OF SQL SCRIPT COMMANDS.  WHEN IT IS
313 // DONE PROCESSING IT PASSES A NEAT
314 // VECTOR OF SQL COMMANDS TO THE
315 // PROGRAM THAT CALLED THIS FUNCTION
316 // ****************************************
317 // THIS PARTICULAR METHOD IS LONG AND
318 // TERRIBLE TO READ BECAUSE IT WAS WRITTEN
319 // IN ONE SITTING. AND SINCE IT SEEMED
320 // LIKE THE TYPE OF FUNCTION THAT LOOKS
321 // SO BAD TO A CODE REVIEWER, BY LEAVING
322 // IT IN THE CODE IT PROVIDES GOOD
323 // HUMOR RELIEF FOR THE DEVELOPER GIVING
324 // A CODE REVIEW TO THIS PROGRAM.
325 // ****************************************
326 
327 // ****************************************
328 // VARIABLE DECLARATIONS
329 		String sFileName = "";
330 		String line = "";
331 		String lineTemp = "";
332 		String sSqlBuffer = "";
333 		//String sFindSemiColon = "";
334 
335 		boolean bKeywordAlreadyFound = false;
336 		boolean bKeywordInString = false;
337 
338 		List<String> vSql = new ArrayList<String>();
339 
340 		BufferedReader in = null;
341 
342 		File fFile = null;
343 		FileInputStream fis = null;
344 
345 		InputStreamReader Fin = null;
346 
347 		//Object obj = null;
348 
349 		int iSizeOfVector = countFileArraySize(sFileNameArray);
350 // END VARIABLE DECLARATIONS
351 // ****************************************
352 
353 // ****************************************
354 // LOOPING THROUGH THE VECTOR OF FILE NAMES
355 		for (int iFile = 0; iFile < iSizeOfVector; iFile++)
356 		{
357 
358 		// GETTING THE FIRST FILE NAME FROM THE VECTOR
359 			sFileName = sFileNameArray[iFile];
360 
361 			try // Try to open the file
362 			{
363 				//File fFile = new File (sDirectory, sFilename);
364 				fFile = new File (sFileName);
365 				fis = new FileInputStream(fFile);
366 				Fin = new InputStreamReader(fis);
367 				in = new BufferedReader(Fin);
368 
369 				//if (isLoggingDebug()) logDebug ("Able to open file: " + sFileName);
370 			}
371 			catch (Exception e)
372 			{
373 			//	logError("* Error opening file :" + sFileName + ": *");
374 				return (vSql);
375 			} // Unable to open the file
376 
377 
378 			try // Try to read the file line by line
379 			{
380 				//String sSqlStatement = "";
381 				sSqlBuffer = "";
382 				// Open file and read first line
383 				line = in.readLine();
384 
385 				lineTemp = line;
386 
387 				while (line != null) // It is not the end of the file, LOOK FOR KEY WORDS
388 				{
389 					//log.info ("DEBUG:" + line);
390 
391 					lineTemp = line;
392 					lineTemp = checkForComments(lineTemp);
393 					bKeywordInString = checkForKeywords(lineTemp);
394 
395 					// if this line is starting a stored procedure declaration, parse it
396 					// specially. added this block to correctly parse stored procedures in oracle.
397 					// marty - 7/11/2002
398 					if ( bKeywordInString && isStartOfStoredProcedure( lineTemp ) ) {
399 
400 						String command = parseStoredProcedure( lineTemp, in );
401 						vSql.add( command );
402 						line = in.readLine();
403 						continue;
404 					}
405 
406                                         // if this line is starting an 'if exists...' statement for MS SQL Server, parse
407                                         // if specially.  added to work around bug 64622
408                                         if ( bKeywordInString && isStartOfIfExistsCommand( lineTemp ) ) {
409                                                 String command = parseIfExistsCommand( lineTemp, in );
410                                                 vSql.add( command );
411                                                 line = in.readLine();
412                                                 continue;
413                                         }
414 
415 					if (bKeywordAlreadyFound)
416 					{
417 						if (bKeywordInString)
418 						{
419 							sSqlBuffer = checkForSemiColons(sSqlBuffer);
420 							sSqlBuffer = checkForEndLines(sSqlBuffer);
421 							vSql.add(sSqlBuffer);
422 							sSqlBuffer = lineTemp;
423 						}
424 						else // (!bKeywordInString}
425 						{
426 							sSqlBuffer = sSqlBuffer + " " + lineTemp;
427 						}
428 					}
429 					else
430 					{
431 						if (bKeywordInString)
432 						{
433 							bKeywordAlreadyFound = bKeywordInString;
434 							sSqlBuffer = sSqlBuffer + lineTemp;
435 						}
436 					}
437 
438 					//Get the next line
439 					line = in.readLine();
440 				} // End while
441 
442 				try
443 				{
444 					sSqlBuffer = checkForSemiColons(sSqlBuffer);
445 					sSqlBuffer = checkForEndLines(sSqlBuffer);
446 					vSql.add(sSqlBuffer);
447 				}
448 				catch (Exception ex)
449 				{
450 					//logError ("No element to add" + ex);
451 
452 				}
453 
454 			}
455 			catch (Exception e)
456 			{
457 				//logError("Could not read one line in file: " + sFileName + "\n");
458 				return (vSql);
459 			} // Unable to read one of the lines in the file
460 
461 			try // To close the file streams
462 			{
463 				Fin.close();
464 				fis.close();
465 				//fFile.close();
466 			}
467 			catch (Exception ex)
468 			{
469 				//logError ("Unable to close files\n");
470 				return (vSql);
471 			} // If the files could not be closed
472 		}
473 		return (vSql);
474 	}
475 
476         /*** this method returns true if this line is the start of a check for whether an object exists
477          *  in MS SQL Server.  otherwise it returns false.
478          */
479         private boolean isStartOfIfExistsCommand( String pLine ) {
480             // i think this is only on MS SQL Server.  hard-coding the logic.
481             return pLine.trim().toUpperCase().startsWith("IF OBJECT_ID");
482         }
483 
484         /*** used to parse an if-exists method.  the parsing logic is different than our usual behavior
485          *  so it has been special cased.
486 	 *  @return String the parsed SQL command
487 	 *  @exception Exception if an error occurs
488          */
489         private String parseIfExistsCommand( String pCommand, BufferedReader pIn )
490             throws Exception
491         {
492 		// a pretty big hack, but just parse until we find the next "go"
493 		String line = pIn.readLine();
494 		if ( line != null ) line = line.trim();
495  		while ( line != null && (! line.toUpperCase().startsWith("GO")) )
496  		{
497 			pCommand = pCommand + " " + line;
498 			line = pIn.readLine();
499 			if ( line != null ) line = line.trim();
500 		}
501 
502 		return pCommand;
503         }
504 
505 	/*** this method returns true if this line is the start of a definition for a stored procedure.
506 	 *  otherwise, it returns false.
507 	 */
508 	 private boolean isStartOfStoredProcedure( String pLine ) {
509 		 // i only know of this on oracle, so i'm temporarily coding it to work specifically
510 		 // with oracle.
511 		 return pLine.trim().toUpperCase().startsWith( "CREATE OR REPLACE PROCEDURE" );
512 	 }
513 
514 	/*** this method is used to parse a stored procedure.  since stored procedures may contain
515 	 *  sub-commands within them, we use a special process to parse them.
516 	 *
517 	 *  @return String the parsed SQL command
518 	 *  @exception Exception if an error occurs
519 	 */
520 	private String parseStoredProcedure( String pCommand, BufferedReader pIn )
521 		throws Exception
522 	{
523 		// this is pretty much a total hack, but i just want to get it working for now
524 		// ASSUME we just parse until we find a line that starts with "/"
525 		String line = pIn.readLine();
526 		if ( line != null ) line = line.trim();
527  		while ( line != null && (! line.startsWith("/")) )
528  		{
529 			pCommand = pCommand + " " + line;
530 			line = pIn.readLine();
531 			if ( line != null ) line = line.trim();
532 		}
533 
534 		return pCommand;
535 	}
536 
537 
538 	public synchronized Collection<String> parseSQLFiles( String[] pFiles )
539 	{
540                 setFileNameArray( pFiles );
541 // ****************************************
542 // THIS FUNCTION CALLS THE FUNCTION THAT
543 // READS THROUGH THE ARRAY OF FILES PASSED
544 // TO IT AND IT RETURNS A VECTOR OF SQL
545 // STATEMENTS.  MOST OF THESE WILL TEND
546 // TO BE CREATE AND DROP STATEMENTS.
547 // ****************************************
548 		List<String> v = new ArrayList<String>();
549 		v = readFileAndLoadData();
550 		String s = "";
551 		for (int i=0;i<v.size();i++)
552 		{
553 			s = v.get(i).toString();
554 			s = trimDebuggingCharacters (s);
555 
556 			if ( logToSystemOut ) {
557 				//log.info("\n\n" + s );
558 			} else {
559 			  //  if (isLoggingInfo ()) logInfo(s);
560 			}
561 		}
562 
563 		return v;
564 	}
565 
566         public Collection<String> parseSQLFile( String pFile )
567         {
568             String[] files = { pFile };
569             return parseSQLFiles( files );
570         }
571 
572 	// This is useful for debugging this application
573 	public boolean logToSystemOut = false;
574 	public static void main (String[] args)
575 	{
576 		SQLFileParser t = new SQLFileParser();
577 		t.logToSystemOut = true;
578 		Iterator<String> cmds = t.parseSQLFiles( args ).iterator();
579 		  while ( cmds.hasNext() ) {
580 		   log.info("\n\n" + cmds.next() );
581 		  }
582 	}
583 
584 
585 
586 }
587