1 /*
2 Copyright (C) 2003 Together
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package org.webdocwf.util.xml;
20
21 //xml imports
22 import org.w3c.dom.Document;
23 import org.w3c.dom.NodeList;
24 import org.w3c.dom.Node;
25 import org.w3c.dom.Element;
26 import org.enhydra.xml.SearchElement;
27 import org.enhydra.xml.XMLDocumentFactory;
28 import javax.xml.parsers.DocumentBuilder;
29 import javax.xml.parsers.DocumentBuilderFactory;
30
31 import java.sql.*;
32 import java.io.File;
33 import java.util.Properties;
34 import java.io.RandomAccessFile;
35 import java.util.ArrayList;
36
37 /***
38 * Load existing XML file , creating DOM from file or creating
39 * new DOM.Class has methods for insert,update,delete data from XML file and save new DOM in XML file.
40 *
41 * @author Zoran Milakovic
42 */
43
44 public class XmlWriter {
45
46 /***
47 * Document made from XML file, and in which will
48 * be made changes.Document will be saved in XML file.
49 */
50 public static SearchElement searchDocumentStatic;
51 private SearchElement searchDocument;
52 private Document document;
53 private static boolean isError = false;
54 /***
55 * If parameter is true document will be saved in xml file after each query,other wise
56 * document will be saved when Connection.commit() method is called.
57 */
58 private boolean autoCommit = false;
59 /***
60 * Full path of the XML file.
61 */
62 private String fileName;
63
64 /***
65 * Constructor used when autoCommit is set to false or true.
66 * @param fileName name of xml file.
67 * @param isAutoCommit define is mod auto commit or not.
68 */
69 public XmlWriter(String fileName, boolean isAutoCommit) throws SQLException {
70 try {
71 XmlWriter.isError = false;
72 this.autoCommit = isAutoCommit;
73 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
74 DocumentBuilder builder = factory.newDocumentBuilder();
75 //check if database exist, and if not, create one
76 this.fileName = fileName;
77 File file = new File( fileName );
78 if( !file.exists() ) {
79 file.getParentFile().mkdirs();
80 this.document = builder.newDocument();
81 Element newDatabaseElement = document.createElement("database");
82 newDatabaseElement.appendChild( document.createElement("dml") );
83 document.insertBefore( newDatabaseElement , null );
84 this.searchDocument = (SearchElement)SearchElement.newInstance( document );
85 this.createDatabase();
86 }
87
88 if( isAutoCommit ) {
89 this.document = builder.parse( file );
90 this.searchDocument = (SearchElement)SearchElement.newInstance( document );
91 } else {
92 if( this.searchDocumentStatic == null ) {
93 this.document = builder.parse( file );
94 this.searchDocument = (SearchElement)SearchElement.newInstance( this.document );
95 } else {
96 this.searchDocument = searchDocumentStatic;
97 }
98 }
99
100 /* set document to new value, to prevent exist of two same documents
101 in database to decrease memory usage */
102 this.document = builder.newDocument();
103
104 } catch( Exception e ) { throw new SQLException("Error in constructor : "+e.getMessage()); }
105 }
106
107 /***
108 * Adds sql statement CREATE TABLE in XML file.
109 * Method will throw SQLException with appropriate message if table already exist
110 *
111 * @param sqlStatement CREATE TABLE statement which will be add into XML file
112 * @param tableName name of table which will be created
113 * @throws SQLException
114 */
115
116 protected void createTable(String sqlStatement, String tableName) throws SQLException {
117 try {
118 boolean allreadyExist = false;
119 NodeList sqlStatements = searchDocument.getSubElementsByTagName("ddl");
120 XmlSqlParser parser = new XmlSqlParser();
121 for( int i = 0; i < sqlStatements.getLength(); i++ ) {
122 Node node = sqlStatements.item(i);
123 parser.parse( node.getFirstChild().toString() );
124 if ( parser.getTableName().equalsIgnoreCase( tableName ) ) {
125 allreadyExist = true;
126 }
127 }
128 NodeList existingTables = searchDocument.getSubElementsByTagName("dml/"+tableName);
129 if( existingTables.getLength() != 0 ) {
130 allreadyExist = true;
131 }
132 if( allreadyExist ) {
133 this.isError = true;
134 throw new SQLException("Table with specified name already exist ! ");
135 } else {
136 Element newDDLElement = document.createElement("ddl");
137 newDDLElement.appendChild( document.createTextNode( sqlStatement ) );
138 SearchElement newDDL = new SearchElement( newDDLElement );
139 NodeList dml = searchDocument.getSubElementsByTagName("dml");
140 Element before = null;
141 if( dml.getLength() != 0 )
142 before = (Element)dml.item(0);
143 searchDocument.insertBefore( newDDL , before );
144 }
145 saveDOM();
146 } catch( Exception e ) {
147 this.isError = true;
148 throw new SQLException("Error in creating table : "+e.getMessage());
149 }
150 }
151
152 /***
153 * Delete row(s) from XML file.
154 *
155 * @param tableName name of table from which will be deleted rows.
156 * @param whereColumnNames names of columns in WHERE clause.
157 * @param whereColumnValues values of columns in WHERE clause.
158 * @throws SQLException
159 */
160 protected void delete(String tableName, String[] whereColumnNames , String[] whereColumnValues) throws SQLException {
161 try {
162 NodeList tableRows = searchDocument.getSubElementsByTagName("dml/"+tableName);
163 //check if table row match conditions
164 for(int i = 0; i < tableRows.getLength(); i++) {
165 boolean isMatch = true;
166 if( whereColumnNames != null && whereColumnValues != null ) {
167 for(int k = 0; k < whereColumnNames.length; k++) {
168 NodeList columns = ( (SearchElement)tableRows.item(i) ).getSubElementsByCondition(whereColumnNames[k]+"="+whereColumnValues[k]);
169 if( columns.getLength() == 0 )
170 isMatch = false;
171 }
172 }
173 if( whereColumnNames.length == 0 )
174 isMatch=true;
175 if( isMatch ) {
176 //deleting row from XML database
177 SearchElement parent = new SearchElement( tableRows.item(i).getParentNode() );
178 parent.removeChild( tableRows.item(i) );
179 }
180 }
181 saveDOM();
182 } catch( Exception e ) {
183 this.isError = true;
184 throw new SQLException("Error in delete data: "+e.getMessage());
185 }
186 }
187
188 /***
189 * Delete table from XML file.
190 *
191 * @param tableName name of table which will be deleted.
192 * @throws SQLException
193 */
194 protected void dropTable( String tableName ) throws SQLException {
195 try {
196 //delete data
197 NodeList tableRows = searchDocument.getSubElementsByTagName("dml/"+tableName);
198 for( int i = 0; i < tableRows.getLength(); i++ ) {
199 SearchElement parent = new SearchElement( tableRows.item(i).getParentNode() );
200 parent.removeChild( tableRows.item(i) );
201 }
202 //delete CREATE TABLE statement if exist
203 NodeList sqlStatements = searchDocument.getSubElementsByTagName("ddl");
204 XmlSqlParser parser = new XmlSqlParser();
205 for( int i = 0; i < sqlStatements.getLength(); i++ ) {
206 Node node = sqlStatements.item(i);
207 try {
208 parser.parse( node.getFirstChild().toString() );
209 } catch(Exception e) {
210 this.isError = true;
211 throw new SQLException("Error in parsing statement : "+e.getMessage());
212 }
213 if ( parser.getTableName().equalsIgnoreCase( tableName ) && parser.sqlType.equalsIgnoreCase( parser.CREATE_TABLE ) ) {
214 SearchElement parent = new SearchElement( sqlStatements.item(i).getParentNode() );
215 parent.removeChild( sqlStatements.item(i) );
216 }
217 }
218 saveDOM();
219 } catch( Exception e ) {
220 this.isError = true;
221 throw new SQLException("Error in drop table : "+e.getMessage());
222 }
223 }
224
225
226 /***
227 * Insert row in XML file.
228 *
229 * @param tableName name of table in which will be added rows.
230 * @param columnNames names of columns in which will be added data.
231 * @param columnValues value which will be insert into table.
232 * @throws SQLException
233 */
234 protected void insert(String tableName, String[] columnNames , String[] columnValues) throws SQLException {
235 try {
236 String[] allColumnNames = (String[])this.getTableProperties( tableName ).get(0);
237 boolean hasCreateTable =
238 !this.getTableProperties( tableName ).get(1).toString().equalsIgnoreCase("NO CREATE TABLE");
239 if( hasCreateTable ) {
240 String[] primaryKeys = (String[])this.getTableProperties( tableName ).get(1);
241 //check if column is primarykey and if is it duplicate value
242 for(int k = 0; k < columnNames.length; k++) {
243 boolean isPrimarykey = false;
244 for(int i = 0; i < primaryKeys.length; i++) {
245 if( columnNames[k].equals( primaryKeys[i] ) )
246 isPrimarykey = true;
247 }
248 if( isPrimarykey ) {
249 NodeList columns = searchDocument.getSubElementsByCondition("dml/"+tableName+"/"+columnNames[k]+"="+columnValues[k]);
250 if( columns.getLength() != 0 ) {
251 this.isError = true;
252 throw new SQLException("Can not insert duplicate value in primary key column "+columnNames[k]+" !");
253 }
254 }
255 }
256 }
257 //create new table
258 Element newTable = document.createElement(tableName);
259 if( hasCreateTable ) {
260 //has CREATE TABLE statement
261 Element newColumn;
262 for( int i = 0; i < columnNames.length; i++ ) {
263 newColumn = document.createElement( columnNames[i] );
264 if( columnValues[i].equals("null") ) {
265 String[] notNullCols = (String[])this.getTableProperties( tableName ).get(2);
266 for( int ii=0; ii < notNullCols.length; ii++ ) {
267 if( notNullCols[ii].equalsIgnoreCase(columnNames[i]) )
268 throw new SQLException("Column '" + columnNames[i] + "' can not be NULL.");
269 }
270 //do not add empty column, because there are CREATE TABLE statement
271 continue;
272 }
273 else {
274 newColumn.appendChild( document.createTextNode( columnValues[i] ) );
275 }
276 newTable.appendChild( newColumn );
277 }
278 }
279 //no CREATE TABLE statement
280 else {
281 Element newColumn;
282 for( int i = 0; i < allColumnNames.length; i++ ) {
283 newColumn = document.createElement( allColumnNames[i] );
284 for( int j = 0; j < columnNames.length; j++ ) {
285 if( allColumnNames[i].equalsIgnoreCase( columnNames[j] ) ){
286 if( columnValues[j].equals("null") )
287 newColumn.appendChild( document.createTextNode( "" ) );
288 else
289 newColumn.appendChild( document.createTextNode( columnValues[j] ) );
290 }
291 else {
292 newColumn.appendChild( document.createTextNode("") );
293 }
294 }
295 newTable.appendChild( newColumn );
296 }
297 }
298
299 SearchElement newTableHash = new SearchElement( newTable );
300 NodeList tables = searchDocument.getSubElementsByTagName("dml/"+tableName);
301 Element before = null;
302 if( tables.getLength() != 0 ) {
303 before = (Element)tables.item(0);
304 (new SearchElement(searchDocument.getSubElementsByTagName("dml").item(0))).insertBefore( newTableHash , before );
305 } else {
306 searchDocument.getSubElementsByTagName("dml").item(0).insertBefore( newTableHash , null );
307 }
308 saveDOM();
309 } catch( Exception e ) {
310 this.isError = true;
311 throw new SQLException("Error in insert data : "+e.getMessage());
312 }
313 }
314
315 /***
316 * Update row in in XML file.
317 *
318 * @param tableName name of table which will be updatad.
319 * @param columnNames names of columns in which will be added data.
320 * @param columnValues value which will be insert into table.
321 * @param whereColumnNames names of columns in WHERE clause.
322 * @param whereColumnValues values of columns in WHERE clause.
323 * @throws SQLException
324 */
325 protected void update(String tableName, String[] columnNames , String[] columnValues , String[] whereColumnNames , String[] whereColumnValues ) throws SQLException {
326 try {
327 boolean hasCreateTable =
328 !this.getTableProperties( tableName ).get(1).toString().equalsIgnoreCase("NO CREATE TABLE");
329 if( hasCreateTable ) {
330 //check primary key column
331 String[] primaryKeys = (String[])this.getTableProperties( tableName ).get(1);
332 boolean isPrimarykey = false;
333 for(int i = 0; i < primaryKeys.length; i++) {
334 if( columnNames[0].equals( primaryKeys[i] ) )
335 isPrimarykey = true;
336 }
337 if( isPrimarykey ) {
338 NodeList columns = searchDocument.getSubElementsByCondition("dml/"+tableName+"/"+columnNames[0]+"="+columnValues[0]);
339 if( columns.getLength() != 0 ) {
340 this.isError = true;
341 throw new SQLException("Can not insert duplicate value in primarykey column "+columnNames[0]+" !");
342 }
343 }
344 }
345 NodeList tableRows = this.searchDocument.getSubElementsByTagName("dml/"+tableName);
346 //check if table row match conditions
347 for(int i = 0; i < tableRows.getLength(); i++) {
348 boolean isMatch = true;
349 if( whereColumnNames != null && whereColumnValues != null ) {
350 for(int k = 0; k < whereColumnNames.length; k++) {
351 NodeList columns = ( (SearchElement)tableRows.item(i) ).getSubElementsByCondition(whereColumnNames[k]+"="+whereColumnValues[k]);
352 if( columns.getLength() == 0 )
353 isMatch = false;
354 }
355 }
356 if( isMatch ) {
357 for( int k = 0; k < columnNames.length; k++ ) {
358 NodeList columns = ( (SearchElement)tableRows.item(i) ).getSubElementsByTagName(columnNames[k]);
359 String[] notNullCols = null;
360 if( hasCreateTable )
361 notNullCols = (String[])this.getTableProperties( tableName ).get(2);
362 if( columns.getLength() == 0 ) {
363 //if column tag do not exist in row
364 Element newColumn = document.createElement( columnNames[k] );
365 if( columnValues[k].equals("null") ) {
366 if( hasCreateTable ) {
367 for( int ii=0; ii < notNullCols.length; ii++ ) {
368 if( notNullCols[ii].equalsIgnoreCase(columnNames[k]) )
369 throw new SQLException("Column '" + columnNames[k] + "' can not be NULL.");
370 }
371 }
372 if ( hasCreateTable )
373 continue; //do not add empty column
374 else
375 newColumn.appendChild( document.createTextNode( "" ) );
376 }
377 else
378 newColumn.appendChild( document.createTextNode( columnValues[k] ) );
379 tableRows.item(i).appendChild( new SearchElement(newColumn) );
380 }
381 else {
382 //if column tag exist
383 SearchElement column = (SearchElement)columns.item(0);
384 Node textNode = column.getFirstChild();
385 if( textNode == null ) {
386 Element newColumn = document.createElement( columnNames[k] );
387 if( columnValues[k].equals("null") ) {
388 if( hasCreateTable ) {
389 for( int ii=0; ii < notNullCols.length; ii++ ) {
390 if( notNullCols[ii].equalsIgnoreCase(columnNames[k]) )
391 throw new SQLException("Column '" + columnNames[k] + "' can not be NULL.");
392 }
393 }
394 //when has CREATE TABLE statement, remove tag with null value
395 if( hasCreateTable )
396 column.getParentNode().removeChild(column);
397 else
398 newColumn.appendChild( document.createTextNode( "" ) );
399 }
400 //when new value is not null
401 else {
402 newColumn.appendChild( document.createTextNode( columnValues[k] ) );
403 SearchElement parent = new SearchElement( column.getParentNode() );
404 parent.replaceChild( new SearchElement(newColumn) , column );
405 }
406 }
407 else {
408 if( columnValues[k].equals("null") ) {
409 if( hasCreateTable ) {
410 for( int ii=0; ii < notNullCols.length; ii++ ) {
411 if( notNullCols[ii].equalsIgnoreCase(columnNames[k]) )
412 throw new SQLException("Column '" + columnNames[k] + "' can not be NULL.");
413 }
414 }
415 //when has CREATE TABLE statement, remove tag with null value
416 if( hasCreateTable )
417 column.getParentNode().removeChild(column);
418 else
419 column.getFirstChild().setNodeValue( "" );
420 }
421 else
422 column.getFirstChild().setNodeValue( columnValues[k] );
423 }
424 }
425 }
426 }
427 }
428 saveDOM();
429 } catch( Exception e ) {
430 this.isError = true;
431 throw new SQLException("Error in update data : "+e.getMessage());
432 }
433 }
434
435 /***
436 * Gets table properties in form ArrayList.
437 * ArrayList[0] is string array with ALL column names in table.
438 * ArrayList[1] is string array with colmn names which are PRIMARYKEYs.
439 * ArrayList[2] is string array with colmn names which can not be NULL.
440 *
441 * If table has no CREATE TABLE statement , ArrayList[1] will have value "NO CREATE TABLE"
442 *
443 * @param tableName name of table.
444 * @return list of table properties.
445 * @throws SQLException
446 */
447 public ArrayList getTableProperties(String tableName) throws SQLException {
448 ArrayList properties = new ArrayList();
449 NodeList sqlStatements = searchDocument.getSubElementsByTagName("ddl");
450 XmlSqlParser parser = new XmlSqlParser();
451 for( int i = 0; i < sqlStatements.getLength(); i++ ) {
452 Node node = sqlStatements.item(i);
453 try {
454 parser.parse( node.getFirstChild().toString() );
455 } catch(Exception e) {
456 this.isError = true;
457 throw new SQLException("Error in parsing statement : "+e.getMessage());
458 }
459 if ( parser.getTableName().equalsIgnoreCase( tableName ) && parser.sqlType.equalsIgnoreCase( parser.CREATE_TABLE ) ) {
460 properties.add( parser.getColumnNames() );
461 properties.add( parser.getPrimaryKeys() );
462 properties.add( parser.getNotnullColumns() );
463 }
464 }
465 // no CREATE TABLE statement
466 if( properties.size() == 0 ) {
467 NodeList tableTags = searchDocument.getSubElementsByTagName("dml/"+tableName);
468 if( tableTags.getLength() == 0 ) {
469 this.isError = true;
470 throw new SQLException("No existing table with specified name : "+tableName);
471 }
472 if( tableTags.getLength() != 0 ) {
473 NodeList tableColumns = tableTags.item(0).getChildNodes();
474 ArrayList arrColumns = new ArrayList();
475 for( int i = 0; i < tableColumns.getLength(); i++ ) {
476 String value = tableColumns.item(i).getNodeName();
477 if( value != null && !value.equalsIgnoreCase("#text") ) {
478 arrColumns.add( tableColumns.item(i).getNodeName() );
479 }
480 }
481 properties.add( arrColumns.toArray(new String[0]) );
482 properties.add( "NO CREATE TABLE" );
483 }
484 }
485 return properties;
486 }
487
488 /***
489 * Method is called when create new database file.
490 *
491 * @throws SQLException
492 */
493 protected void createDatabase() throws SQLException {
494 try {
495 Properties prop = new Properties();
496 prop.setProperty("version","1.0");
497 prop.setProperty("encoding","ISO-8859-1");
498 XMLDocumentFactory.serialize( this.searchDocument , fileName , prop );
499 } catch( Exception e ) {
500 throw new SQLException("Error in saving DOM : "+e.getMessage());
501 }
502 }
503
504 /***
505 * Save DOM as XML file.
506 *
507 * @throws SQLException
508 */
509 protected void saveDOM() throws SQLException {
510 if( !XmlWriter.isError ) {
511 try {
512 Properties prop = new Properties();
513 prop.setProperty("version","1.0");
514 prop.setProperty("encoding","ISO-8859-1");
515 if( this.autoCommit ) {
516 XMLDocumentFactory.serialize( this.searchDocument , fileName , prop );
517 }
518 else
519 this.searchDocumentStatic = searchDocument;
520
521 } catch( Exception e ) {
522 this.isError = true;
523 throw new SQLException("Error in saving DOM : "+e.getMessage());
524 }
525 }
526 }
527
528 /***
529 * Method is used for saving DOM in xml file from connection object,when XmlConnection.commit() method
530 * is called.
531 * @param fileName full path of xml file.
532 * @throws SQLException
533 */
534 public static void commit(String fileName) throws SQLException {
535 if( !XmlWriter.isError && XmlWriter.searchDocumentStatic != null ) {
536 try {
537 Properties prop = new Properties();
538 prop.setProperty("version","1.0");
539 prop.setProperty("encoding","ISO-8859-1");
540 XMLDocumentFactory.serialize( XmlWriter.searchDocumentStatic , fileName , prop );
541 XmlWriter.searchDocumentStatic = null;
542 } catch( Exception e ) {
543 throw new SQLException("Error in saving DOM : "+e.getMessage());
544 }
545 }
546 }
547
548 }
549
This page was automatically generated by Maven