001: /*
002: Copyright (C) 2003 Together
003:
004: This library is free software; you can redistribute it and/or
005: modify it under the terms of the GNU Lesser General Public
006: License as published by the Free Software Foundation; either
007: version 2.1 of the License, or (at your option) any later version.
008:
009: This library is distributed in the hope that it will be useful,
010: but WITHOUT ANY WARRANTY; without even the implied warranty of
011: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: Lesser General Public License for more details.
013:
014: You should have received a copy of the GNU Lesser General Public
015: License along with this library; if not, write to the Free Software
016: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package org.webdocwf.util.xml;
020:
021: //xml imports
022: import org.w3c.dom.Document;
023: import org.w3c.dom.NodeList;
024: import org.w3c.dom.Node;
025: import org.w3c.dom.Element;
026: import org.enhydra.xml.SearchElement;
027: import org.enhydra.xml.XMLDocumentFactory;
028: import javax.xml.parsers.DocumentBuilder;
029: import javax.xml.parsers.DocumentBuilderFactory;
030:
031: import java.sql.*;
032: import java.io.File;
033: import java.util.Properties;
034: import java.io.RandomAccessFile;
035: import java.util.ArrayList;
036:
037: /**
038: * Load existing XML file , creating DOM from file or creating
039: * new DOM.Class has methods for insert,update,delete data from XML file and save new DOM in XML file.
040: *
041: * @author Zoran Milakovic
042: */
043:
044: public class XmlWriter {
045:
046: /**
047: * Document made from XML file, and in which will
048: * be made changes.Document will be saved in XML file.
049: */
050: public static SearchElement searchDocumentStatic;
051: private SearchElement searchDocument;
052: private Document document;
053: private static boolean isError = false;
054: /**
055: * If parameter is true document will be saved in xml file after each query,other wise
056: * document will be saved when Connection.commit() method is called.
057: */
058: private boolean autoCommit = false;
059: /**
060: * Full path of the XML file.
061: */
062: private String fileName;
063:
064: /**
065: * Constructor used when autoCommit is set to false or true.
066: * @param fileName name of xml file.
067: * @param isAutoCommit define is mod auto commit or not.
068: */
069: public XmlWriter(String fileName, boolean isAutoCommit)
070: throws SQLException {
071: try {
072: XmlWriter.isError = false;
073: this .autoCommit = isAutoCommit;
074: DocumentBuilderFactory factory = DocumentBuilderFactory
075: .newInstance();
076: DocumentBuilder builder = factory.newDocumentBuilder();
077: //check if database exist, and if not, create one
078: this .fileName = fileName;
079: File file = new File(fileName);
080: if (!file.exists()) {
081: file.getParentFile().mkdirs();
082: this .document = builder.newDocument();
083: Element newDatabaseElement = document
084: .createElement("database");
085: newDatabaseElement.appendChild(document
086: .createElement("dml"));
087: document.insertBefore(newDatabaseElement, null);
088: this .searchDocument = (SearchElement) SearchElement
089: .newInstance(document);
090: this .createDatabase();
091: }
092:
093: if (isAutoCommit) {
094: this .document = builder.parse(file);
095: this .searchDocument = (SearchElement) SearchElement
096: .newInstance(document);
097: } else {
098: if (this .searchDocumentStatic == null) {
099: this .document = builder.parse(file);
100: this .searchDocument = (SearchElement) SearchElement
101: .newInstance(this .document);
102: } else {
103: this .searchDocument = searchDocumentStatic;
104: }
105: }
106:
107: /* set document to new value, to prevent exist of two same documents
108: in database to decrease memory usage */
109: this .document = builder.newDocument();
110:
111: } catch (Exception e) {
112: throw new SQLException("Error in constructor : "
113: + e.getMessage());
114: }
115: }
116:
117: /**
118: * Adds sql statement CREATE TABLE in XML file.
119: * Method will throw SQLException with appropriate message if table already exist
120: *
121: * @param sqlStatement CREATE TABLE statement which will be add into XML file
122: * @param tableName name of table which will be created
123: * @throws SQLException
124: */
125:
126: protected void createTable(String sqlStatement, String tableName)
127: throws SQLException {
128: try {
129: boolean allreadyExist = false;
130: NodeList sqlStatements = searchDocument
131: .getSubElementsByTagName("ddl");
132: XmlSqlParser parser = new XmlSqlParser();
133: for (int i = 0; i < sqlStatements.getLength(); i++) {
134: Node node = sqlStatements.item(i);
135: parser.parse(node.getFirstChild().toString());
136: if (parser.getTableName().equalsIgnoreCase(tableName)) {
137: allreadyExist = true;
138: }
139: }
140: NodeList existingTables = searchDocument
141: .getSubElementsByTagName("dml/" + tableName);
142: if (existingTables.getLength() != 0) {
143: allreadyExist = true;
144: }
145: if (allreadyExist) {
146: this .isError = true;
147: throw new SQLException(
148: "Table with specified name already exist ! ");
149: } else {
150: Element newDDLElement = document.createElement("ddl");
151: newDDLElement.appendChild(document
152: .createTextNode(sqlStatement));
153: SearchElement newDDL = new SearchElement(newDDLElement);
154: NodeList dml = searchDocument
155: .getSubElementsByTagName("dml");
156: Element before = null;
157: if (dml.getLength() != 0)
158: before = (Element) dml.item(0);
159: searchDocument.insertBefore(newDDL, before);
160: }
161: saveDOM();
162: } catch (Exception e) {
163: this .isError = true;
164: throw new SQLException("Error in creating table : "
165: + e.getMessage());
166: }
167: }
168:
169: /**
170: * Delete row(s) from XML file.
171: *
172: * @param tableName name of table from which will be deleted rows.
173: * @param whereColumnNames names of columns in WHERE clause.
174: * @param whereColumnValues values of columns in WHERE clause.
175: * @throws SQLException
176: */
177: protected void delete(String tableName, String[] whereColumnNames,
178: String[] whereColumnValues) throws SQLException {
179: try {
180: NodeList tableRows = searchDocument
181: .getSubElementsByTagName("dml/" + tableName);
182: //check if table row match conditions
183: for (int i = 0; i < tableRows.getLength(); i++) {
184: boolean isMatch = true;
185: if (whereColumnNames != null
186: && whereColumnValues != null) {
187: for (int k = 0; k < whereColumnNames.length; k++) {
188: NodeList columns = ((SearchElement) tableRows
189: .item(i))
190: .getSubElementsByCondition(whereColumnNames[k]
191: + "=" + whereColumnValues[k]);
192: if (columns.getLength() == 0)
193: isMatch = false;
194: }
195: }
196: if (whereColumnNames.length == 0)
197: isMatch = true;
198: if (isMatch) {
199: //deleting row from XML database
200: SearchElement parent = new SearchElement(tableRows
201: .item(i).getParentNode());
202: parent.removeChild(tableRows.item(i));
203: }
204: }
205: saveDOM();
206: } catch (Exception e) {
207: this .isError = true;
208: throw new SQLException("Error in delete data: "
209: + e.getMessage());
210: }
211: }
212:
213: /**
214: * Delete table from XML file.
215: *
216: * @param tableName name of table which will be deleted.
217: * @throws SQLException
218: */
219: protected void dropTable(String tableName) throws SQLException {
220: try {
221: //delete data
222: NodeList tableRows = searchDocument
223: .getSubElementsByTagName("dml/" + tableName);
224: for (int i = 0; i < tableRows.getLength(); i++) {
225: SearchElement parent = new SearchElement(tableRows
226: .item(i).getParentNode());
227: parent.removeChild(tableRows.item(i));
228: }
229: //delete CREATE TABLE statement if exist
230: NodeList sqlStatements = searchDocument
231: .getSubElementsByTagName("ddl");
232: XmlSqlParser parser = new XmlSqlParser();
233: for (int i = 0; i < sqlStatements.getLength(); i++) {
234: Node node = sqlStatements.item(i);
235: try {
236: parser.parse(node.getFirstChild().toString());
237: } catch (Exception e) {
238: this .isError = true;
239: throw new SQLException(
240: "Error in parsing statement : "
241: + e.getMessage());
242: }
243: if (parser.getTableName().equalsIgnoreCase(tableName)
244: && parser.sqlType
245: .equalsIgnoreCase(parser.CREATE_TABLE)) {
246: SearchElement parent = new SearchElement(
247: sqlStatements.item(i).getParentNode());
248: parent.removeChild(sqlStatements.item(i));
249: }
250: }
251: saveDOM();
252: } catch (Exception e) {
253: this .isError = true;
254: throw new SQLException("Error in drop table : "
255: + e.getMessage());
256: }
257: }
258:
259: /**
260: * Insert row in XML file.
261: *
262: * @param tableName name of table in which will be added rows.
263: * @param columnNames names of columns in which will be added data.
264: * @param columnValues value which will be insert into table.
265: * @throws SQLException
266: */
267: protected void insert(String tableName, String[] columnNames,
268: String[] columnValues) throws SQLException {
269: try {
270: String[] allColumnNames = (String[]) this
271: .getTableProperties(tableName).get(0);
272: boolean hasCreateTable = !this
273: .getTableProperties(tableName).get(1).toString()
274: .equalsIgnoreCase("NO CREATE TABLE");
275: if (hasCreateTable) {
276: String[] primaryKeys = (String[]) this
277: .getTableProperties(tableName).get(1);
278: //check if column is primarykey and if is it duplicate value
279: for (int k = 0; k < columnNames.length; k++) {
280: boolean isPrimarykey = false;
281: for (int i = 0; i < primaryKeys.length; i++) {
282: if (columnNames[k].equals(primaryKeys[i]))
283: isPrimarykey = true;
284: }
285: if (isPrimarykey) {
286: NodeList columns = searchDocument
287: .getSubElementsByCondition("dml/"
288: + tableName + "/"
289: + columnNames[k] + "="
290: + columnValues[k]);
291: if (columns.getLength() != 0) {
292: this .isError = true;
293: throw new SQLException(
294: "Can not insert duplicate value in primary key column "
295: + columnNames[k] + " !");
296: }
297: }
298: }
299: }
300: //create new table
301: Element newTable = document.createElement(tableName);
302: if (hasCreateTable) {
303: //has CREATE TABLE statement
304: Element newColumn;
305: for (int i = 0; i < columnNames.length; i++) {
306: newColumn = document.createElement(columnNames[i]);
307: if (columnValues[i].equals("null")) {
308: String[] notNullCols = (String[]) this
309: .getTableProperties(tableName).get(2);
310: for (int ii = 0; ii < notNullCols.length; ii++) {
311: if (notNullCols[ii]
312: .equalsIgnoreCase(columnNames[i]))
313: throw new SQLException("Column '"
314: + columnNames[i]
315: + "' can not be NULL.");
316: }
317: //do not add empty column, because there are CREATE TABLE statement
318: continue;
319: } else {
320: newColumn.appendChild(document
321: .createTextNode(columnValues[i]));
322: }
323: newTable.appendChild(newColumn);
324: }
325: }
326: //no CREATE TABLE statement
327: else {
328: Element newColumn;
329: for (int i = 0; i < allColumnNames.length; i++) {
330: newColumn = document
331: .createElement(allColumnNames[i]);
332: for (int j = 0; j < columnNames.length; j++) {
333: if (allColumnNames[i]
334: .equalsIgnoreCase(columnNames[j])) {
335: if (columnValues[j].equals("null"))
336: newColumn.appendChild(document
337: .createTextNode(""));
338: else
339: newColumn
340: .appendChild(document
341: .createTextNode(columnValues[j]));
342: } else {
343: newColumn.appendChild(document
344: .createTextNode(""));
345: }
346: }
347: newTable.appendChild(newColumn);
348: }
349: }
350:
351: SearchElement newTableHash = new SearchElement(newTable);
352: NodeList tables = searchDocument
353: .getSubElementsByTagName("dml/" + tableName);
354: Element before = null;
355: if (tables.getLength() != 0) {
356: before = (Element) tables.item(0);
357: (new SearchElement(searchDocument
358: .getSubElementsByTagName("dml").item(0)))
359: .insertBefore(newTableHash, before);
360: } else {
361: searchDocument.getSubElementsByTagName("dml").item(0)
362: .insertBefore(newTableHash, null);
363: }
364: saveDOM();
365: } catch (Exception e) {
366: this .isError = true;
367: throw new SQLException("Error in insert data : "
368: + e.getMessage());
369: }
370: }
371:
372: /**
373: * Update row in in XML file.
374: *
375: * @param tableName name of table which will be updatad.
376: * @param columnNames names of columns in which will be added data.
377: * @param columnValues value which will be insert into table.
378: * @param whereColumnNames names of columns in WHERE clause.
379: * @param whereColumnValues values of columns in WHERE clause.
380: * @throws SQLException
381: */
382: protected void update(String tableName, String[] columnNames,
383: String[] columnValues, String[] whereColumnNames,
384: String[] whereColumnValues) throws SQLException {
385: try {
386: boolean hasCreateTable = !this
387: .getTableProperties(tableName).get(1).toString()
388: .equalsIgnoreCase("NO CREATE TABLE");
389: if (hasCreateTable) {
390: //check primary key column
391: String[] primaryKeys = (String[]) this
392: .getTableProperties(tableName).get(1);
393: boolean isPrimarykey = false;
394: for (int i = 0; i < primaryKeys.length; i++) {
395: if (columnNames[0].equals(primaryKeys[i]))
396: isPrimarykey = true;
397: }
398: if (isPrimarykey) {
399: NodeList columns = searchDocument
400: .getSubElementsByCondition("dml/"
401: + tableName + "/" + columnNames[0]
402: + "=" + columnValues[0]);
403: if (columns.getLength() != 0) {
404: this .isError = true;
405: throw new SQLException(
406: "Can not insert duplicate value in primarykey column "
407: + columnNames[0] + " !");
408: }
409: }
410: }
411: NodeList tableRows = this .searchDocument
412: .getSubElementsByTagName("dml/" + tableName);
413: //check if table row match conditions
414: for (int i = 0; i < tableRows.getLength(); i++) {
415: boolean isMatch = true;
416: if (whereColumnNames != null
417: && whereColumnValues != null) {
418: for (int k = 0; k < whereColumnNames.length; k++) {
419: NodeList columns = ((SearchElement) tableRows
420: .item(i))
421: .getSubElementsByCondition(whereColumnNames[k]
422: + "=" + whereColumnValues[k]);
423: if (columns.getLength() == 0)
424: isMatch = false;
425: }
426: }
427: if (isMatch) {
428: for (int k = 0; k < columnNames.length; k++) {
429: NodeList columns = ((SearchElement) tableRows
430: .item(i))
431: .getSubElementsByTagName(columnNames[k]);
432: String[] notNullCols = null;
433: if (hasCreateTable)
434: notNullCols = (String[]) this
435: .getTableProperties(tableName).get(
436: 2);
437: if (columns.getLength() == 0) {
438: //if column tag do not exist in row
439: Element newColumn = document
440: .createElement(columnNames[k]);
441: if (columnValues[k].equals("null")) {
442: if (hasCreateTable) {
443: for (int ii = 0; ii < notNullCols.length; ii++) {
444: if (notNullCols[ii]
445: .equalsIgnoreCase(columnNames[k]))
446: throw new SQLException(
447: "Column '"
448: + columnNames[k]
449: + "' can not be NULL.");
450: }
451: }
452: if (hasCreateTable)
453: continue; //do not add empty column
454: else
455: newColumn.appendChild(document
456: .createTextNode(""));
457: } else
458: newColumn
459: .appendChild(document
460: .createTextNode(columnValues[k]));
461: tableRows.item(i).appendChild(
462: new SearchElement(newColumn));
463: } else {
464: //if column tag exist
465: SearchElement column = (SearchElement) columns
466: .item(0);
467: Node textNode = column.getFirstChild();
468: if (textNode == null) {
469: Element newColumn = document
470: .createElement(columnNames[k]);
471: if (columnValues[k].equals("null")) {
472: if (hasCreateTable) {
473: for (int ii = 0; ii < notNullCols.length; ii++) {
474: if (notNullCols[ii]
475: .equalsIgnoreCase(columnNames[k]))
476: throw new SQLException(
477: "Column '"
478: + columnNames[k]
479: + "' can not be NULL.");
480: }
481: }
482: //when has CREATE TABLE statement, remove tag with null value
483: if (hasCreateTable)
484: column.getParentNode()
485: .removeChild(column);
486: else
487: newColumn.appendChild(document
488: .createTextNode(""));
489: }
490: //when new value is not null
491: else {
492: newColumn
493: .appendChild(document
494: .createTextNode(columnValues[k]));
495: SearchElement parent = new SearchElement(
496: column.getParentNode());
497: parent
498: .replaceChild(
499: new SearchElement(
500: newColumn),
501: column);
502: }
503: } else {
504: if (columnValues[k].equals("null")) {
505: if (hasCreateTable) {
506: for (int ii = 0; ii < notNullCols.length; ii++) {
507: if (notNullCols[ii]
508: .equalsIgnoreCase(columnNames[k]))
509: throw new SQLException(
510: "Column '"
511: + columnNames[k]
512: + "' can not be NULL.");
513: }
514: }
515: //when has CREATE TABLE statement, remove tag with null value
516: if (hasCreateTable)
517: column.getParentNode()
518: .removeChild(column);
519: else
520: column.getFirstChild()
521: .setNodeValue("");
522: } else
523: column.getFirstChild()
524: .setNodeValue(
525: columnValues[k]);
526: }
527: }
528: }
529: }
530: }
531: saveDOM();
532: } catch (Exception e) {
533: this .isError = true;
534: throw new SQLException("Error in update data : "
535: + e.getMessage());
536: }
537: }
538:
539: /**
540: * Gets table properties in form ArrayList.
541: * ArrayList[0] is string array with ALL column names in table.
542: * ArrayList[1] is string array with colmn names which are PRIMARYKEYs.
543: * ArrayList[2] is string array with colmn names which can not be NULL.
544: *
545: * If table has no CREATE TABLE statement , ArrayList[1] will have value "NO CREATE TABLE"
546: *
547: * @param tableName name of table.
548: * @return list of table properties.
549: * @throws SQLException
550: */
551: public ArrayList getTableProperties(String tableName)
552: throws SQLException {
553: ArrayList properties = new ArrayList();
554: NodeList sqlStatements = searchDocument
555: .getSubElementsByTagName("ddl");
556: XmlSqlParser parser = new XmlSqlParser();
557: for (int i = 0; i < sqlStatements.getLength(); i++) {
558: Node node = sqlStatements.item(i);
559: try {
560: parser.parse(node.getFirstChild().toString());
561: } catch (Exception e) {
562: this .isError = true;
563: throw new SQLException("Error in parsing statement : "
564: + e.getMessage());
565: }
566: if (parser.getTableName().equalsIgnoreCase(tableName)
567: && parser.sqlType
568: .equalsIgnoreCase(parser.CREATE_TABLE)) {
569: properties.add(parser.getColumnNames());
570: properties.add(parser.getPrimaryKeys());
571: properties.add(parser.getNotnullColumns());
572: }
573: }
574: // no CREATE TABLE statement
575: if (properties.size() == 0) {
576: NodeList tableTags = searchDocument
577: .getSubElementsByTagName("dml/" + tableName);
578: if (tableTags.getLength() == 0) {
579: this .isError = true;
580: throw new SQLException(
581: "No existing table with specified name : "
582: + tableName);
583: }
584: if (tableTags.getLength() != 0) {
585: NodeList tableColumns = tableTags.item(0)
586: .getChildNodes();
587: ArrayList arrColumns = new ArrayList();
588: for (int i = 0; i < tableColumns.getLength(); i++) {
589: String value = tableColumns.item(i).getNodeName();
590: if (value != null
591: && !value.equalsIgnoreCase("#text")) {
592: arrColumns.add(tableColumns.item(i)
593: .getNodeName());
594: }
595: }
596: properties.add(arrColumns.toArray(new String[0]));
597: properties.add("NO CREATE TABLE");
598: }
599: }
600: return properties;
601: }
602:
603: /**
604: * Method is called when create new database file.
605: *
606: * @throws SQLException
607: */
608: protected void createDatabase() throws SQLException {
609: try {
610: Properties prop = new Properties();
611: prop.setProperty("version", "1.0");
612: prop.setProperty("encoding", "ISO-8859-1");
613: XMLDocumentFactory.serialize(this .searchDocument, fileName,
614: prop);
615: } catch (Exception e) {
616: throw new SQLException("Error in saving DOM : "
617: + e.getMessage());
618: }
619: }
620:
621: /**
622: * Save DOM as XML file.
623: *
624: * @throws SQLException
625: */
626: protected void saveDOM() throws SQLException {
627: if (!XmlWriter.isError) {
628: try {
629: Properties prop = new Properties();
630: prop.setProperty("version", "1.0");
631: prop.setProperty("encoding", "ISO-8859-1");
632: if (this .autoCommit) {
633: XMLDocumentFactory.serialize(this .searchDocument,
634: fileName, prop);
635: } else
636: this .searchDocumentStatic = searchDocument;
637:
638: } catch (Exception e) {
639: this .isError = true;
640: throw new SQLException("Error in saving DOM : "
641: + e.getMessage());
642: }
643: }
644: }
645:
646: /**
647: * Method is used for saving DOM in xml file from connection object,when XmlConnection.commit() method
648: * is called.
649: * @param fileName full path of xml file.
650: * @throws SQLException
651: */
652: public static void commit(String fileName) throws SQLException {
653: if (!XmlWriter.isError
654: && XmlWriter.searchDocumentStatic != null) {
655: try {
656: Properties prop = new Properties();
657: prop.setProperty("version", "1.0");
658: prop.setProperty("encoding", "ISO-8859-1");
659: XMLDocumentFactory.serialize(
660: XmlWriter.searchDocumentStatic, fileName, prop);
661: XmlWriter.searchDocumentStatic = null;
662: } catch (Exception e) {
663: throw new SQLException("Error in saving DOM : "
664: + e.getMessage());
665: }
666: }
667: }
668:
669: }
|