001: /*
002: * NEMESIS-FORUM.
003: * Copyright (C) 2002 David Laurent(lithium2@free.fr). All rights reserved.
004: *
005: * Copyright (c) 2000 The Apache Software Foundation. All rights reserved.
006: *
007: * Copyright (C) 2001 Yasna.com. All rights reserved.
008: *
009: * Copyright (C) 2000 CoolServlets.com. All rights reserved.
010: *
011: * NEMESIS-FORUM. is free software; you can redistribute it and/or
012: * modify it under the terms of the Apache Software License, Version 1.1,
013: * or (at your option) any later version.
014: *
015: * NEMESIS-FORUM core framework, NEMESIS-FORUM backoffice, NEMESIS-FORUM frontoffice
016: * application are parts of NEMESIS-FORUM and are distributed under
017: * same terms of licence.
018: *
019: *
020: * NEMESIS-FORUM includes software developed by the Apache Software Foundation (http://www.apache.org/)
021: * and software developed by CoolServlets.com (http://www.coolservlets.com).
022: * and software developed by Yasna.com (http://www.yasna.com).
023: *
024: */
025: package org.nemesis.forum.impl;
026:
027: import java.sql.Connection;
028: import java.sql.PreparedStatement;
029: import java.sql.ResultSet;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.nemesis.forum.Message;
034: import org.nemesis.forum.TreeWalker;
035: import org.nemesis.forum.util.jdbc.DbConnectionManager;
036:
037: /**
038: * Database implementation of the TreeWalker interface. This class is relatively
039: * inefficient compared to how it will eventually be implemented. However,
040: * a schema change is needed before that optimization is needed (represent
041: * tree structure directly in schema).
042: */
043: public class DbTreeWalker implements TreeWalker {
044: static protected Log log = LogFactory.getLog(DbTreeWalker.class);
045: /** DATABASE QUERIES **/
046: private static final String GET_CHILD = "SELECT yazdMessageTree.childID, yazdMessage.creationDate FROM "
047: + "yazdMessageTree, yazdMessage WHERE "
048: + "yazdMessageTree.childID=yazdMessage.messageID AND "
049: + "yazdMessageTree.parentID=? ORDER BY yazdMessage.creationDate";
050: private static final String CHILD_COUNT = "SELECT count(*) FROM yazdMessageTree WHERE parentID=?";
051: private static final String INDEX_OF_CHILD = "SELECT yazdMessageTree.childID, yazdMessage.creationDate "
052: + "FROM yazdMessageTree, yazdMessage WHERE yazdMessageTree.childID=yazdMessage.messageID "
053: + "AND yazdMessageTree.parentID=? ORDER BY yazdMessage.creationDate";
054:
055: private static final String GET_CHILD_APPROVED = "SELECT yazdMessageTree.childID, yazdMessage.creationDate FROM "
056: + "yazdMessageTree, yazdMessage WHERE "
057: + "yazdMessageTree.childID=yazdMessage.messageID AND "
058: + "yazdMessageTree.parentID=? AND yazdMessage.approved=? ORDER BY yazdMessage.creationDate";
059: private static final String CHILD_COUNT_APPROVED = "SELECT count(*) FROM yazdMessageTree, yazdMessage WHERE yazdMessageTree.childID=yazdMessage.messageID AND yazdMessageTree.parentID=? AND yazdMessage.approved=? ";
060: private static final String INDEX_OF_CHILD_APPROVED = "SELECT yazdMessageTree.childID, yazdMessage.creationDate "
061: + "FROM yazdMessageTree, yazdMessage WHERE yazdMessageTree.childID=yazdMessage.messageID "
062: + "AND yazdMessageTree.parentID=? AND yazdMessage.approved=? ORDER BY yazdMessage.creationDate";
063:
064: private DbForumThread thread;
065: private DbForumFactory factory;
066: private boolean approved;
067: private boolean filter = false;
068:
069: public DbTreeWalker(DbForumThread thread, DbForumFactory factory) {
070: this .thread = thread;
071: this .factory = factory;
072: }
073:
074: public DbTreeWalker(boolean approved, DbForumThread thread,
075: DbForumFactory factory) {
076: this .thread = thread;
077: this .factory = factory;
078: this .approved = approved;
079: this .filter = true;
080: }
081:
082: /**
083: * Returns the root of the tree. Returns null only if the tree has no nodes.
084: *
085: * @returns the root of the tree
086: */
087: public Message getRoot() {
088: return thread.getRootMessage();
089: }
090:
091: /**
092: * Returns the child of parent at index index in the parent's child array.
093: * This should not return null if index is a valid index for parent (that
094: * is index >= 0 && index < getChildCount(parent)).
095: *
096: * @param parent the parent message.
097: * @param index the index of the child.
098: * @returns the child of parent at index.
099: */
100: public Message getChild(Message parent, int index) {
101: Message message = null;
102: Connection con = null;
103: PreparedStatement pstmt = null;
104: try {
105: con = DbConnectionManager.getConnection();
106: if (filter) {
107: pstmt = con.prepareStatement(GET_CHILD_APPROVED);
108: pstmt.setInt(1, parent.getID());
109: pstmt.setInt(2, approved ? 1 : 0);
110: } else {
111: pstmt = con.prepareStatement(GET_CHILD);
112: pstmt.setInt(1, parent.getID());
113: }
114:
115: ResultSet rs = pstmt.executeQuery();
116: while (rs.next() && index > 0) {
117: index--;
118: }
119: if (index == 0) {
120: int messageID = rs.getInt(1);
121: message = thread.getMessage(messageID);
122: }
123: } catch (Exception e) {
124: log.error("Error in DbMessageTreeWalker:getChild(" + index
125: + ")-", e);
126:
127: } finally {
128: try {
129: pstmt.close();
130: } catch (Exception e) {
131: log.error("", e);
132: }
133: try {
134: con.close();
135: } catch (Exception e) {
136: log.error("", e);
137: }
138: }
139: return message;
140: }
141:
142: /**
143: * Returns the number of children of parent. Returns 0 if the node is a
144: * leaf or if it has no children.
145: *
146: * @param parent a node in the tree, obtained from this data source.
147: * @returns the number of children of the node parent.
148: */
149: public int getChildCount(Message parent) {
150: int childCount = 0;
151: Connection con = null;
152: PreparedStatement pstmt = null;
153: try {
154: con = DbConnectionManager.getConnection();
155:
156: if (filter) {
157: pstmt = con.prepareStatement(CHILD_COUNT_APPROVED);
158: pstmt.setInt(1, parent.getID());
159: pstmt.setInt(2, approved ? 1 : 0);
160: } else {
161: pstmt = con.prepareStatement(CHILD_COUNT);
162: pstmt.setInt(1, parent.getID());
163: }
164:
165: ResultSet rs = pstmt.executeQuery();
166: rs.next();
167: childCount = rs.getInt(1);
168: } catch (Exception e) {
169: log.error("Error in DbTreeWalker:getChildCount()-", e);
170:
171: } finally {
172: try {
173: pstmt.close();
174: } catch (Exception e) {
175: log.error("", e);
176: }
177: try {
178: con.close();
179: } catch (Exception e) {
180: log.error("", e);
181: }
182: }
183: return childCount;
184: }
185:
186: /**
187: * Returns the total number of recursive children of a parent. Returns 0
188: * if there are no children. This method is not intended to aid in
189: * navigation of a Thread but is included as an added utility.
190: */
191: public int getRecursiveChildCount(Message parent) {
192: int numChildren = 0;
193: int num = getChildCount(parent);
194: numChildren += num;
195: for (int i = 0; i < num; i++) {
196: Message child = getChild(parent, i);
197: if (child != null) {
198: numChildren += getRecursiveChildCount(child);
199: }
200: }
201: return numChildren;
202: }
203:
204: /**
205: * Returns the index of child in parent.
206: */
207: public int getIndexOfChild(Message parent, Message child) {
208: int index = 0;
209: Connection con = null;
210: PreparedStatement pstmt = null;
211: ResultSet rs = null;
212: try {
213: con = DbConnectionManager.getConnection();
214:
215: if (filter) {
216: pstmt = con.prepareStatement(INDEX_OF_CHILD_APPROVED);
217: pstmt.setInt(1, parent.getID());
218: pstmt.setInt(2, approved ? 1 : 0);
219: } else {
220: pstmt = con.prepareStatement(INDEX_OF_CHILD);
221: pstmt.setInt(1, parent.getID());
222: }
223:
224: rs = pstmt.executeQuery();
225: while (rs.next()) {
226: if (rs.getInt(1) == child.getID()) {
227: break;
228: }
229: index++;
230: }
231: } catch (Exception e) {
232: log.error("Error in DbTreeWalker:getIndexOfChild()-", e);
233: } finally {
234: try {
235: pstmt.close();
236: } catch (Exception e) {
237: log.error("", e);
238: }
239: try {
240: con.close();
241: } catch (Exception e) {
242: log.error("", e);
243: }
244: }
245: return index;
246: }
247:
248: /**
249: * Returns true if node is a leaf. A node is a leaf when it has no children
250: * messages.
251: *
252: * @param node a node in the tree, obtained from this data source
253: * @returns true if node is a leaf
254: */
255: public boolean isLeaf(Message node) {
256: return (getChildCount(node) == 0);
257: }
258: }
|