/*
* ConcourseConnect
* Copyright 2009 Concursive Corporation
* http://www.concursive.com
*
* This file is part of ConcourseConnect, an open source social business
* software and community platform.
*
* Concursive ConcourseConnect is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, version 3 of the License.
*
* Under the terms of the GNU Affero General Public License you must release the
* complete source code for any application that uses any part of ConcourseConnect
* (system header files and libraries used by the operating system are excluded).
* These terms must be included in any work that has ConcourseConnect components.
* If you are developing and distributing open source applications under the
* GNU Affero General Public License, then you are free to use ConcourseConnect
* under the GNU Affero General Public License.
*
* If you are deploying a web site in which users interact with any portion of
* ConcourseConnect over a network, the complete source code changes must be made
* available. For example, include a link to the source archive directly from
* your web site.
*
* For OEMs, ISVs, SIs and VARs who distribute ConcourseConnect with their
* products, and do not license and distribute their source code under the GNU
* Affero General Public License, Concursive provides a flexible commercial
* license.
*
* To anyone in doubt, we recommend the commercial license. Our commercial license
* is competitively priced and will eliminate any confusion about how
* ConcourseConnect can be used and distributed.
*
* ConcourseConnect is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with ConcourseConnect. If not, see <http://www.gnu.org/licenses/>.
*
* Attribution Notice: ConcourseConnect is an Original Work of software created
* by Concursive Corporation
*/
package com.concursive.connect.web.modules.plans.dao;
import com.concursive.commons.db.DatabaseUtils;
import com.concursive.connect.web.modules.plans.utils.MoveCounter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Object that represents an item in an outline, used for moving items around
* and inserting new items.
*
* @author matt rajkowski
* @version $Id$
* @created July 15, 2003
*/
public class RequirementMapItem {
public final static int LEFT = 1;
public final static int RIGHT = 2;
public final static int UP = 3;
public final static int DOWN = 4;
private int id = -1;
private int projectId = -1;
private int requirementId = -1;
private int position = -1;
private int indent = -1;
private int folderId = -1;
private int assignmentId = -1;
private RequirementMapItem parent = null;
private ArrayList children = new ArrayList();
private RequirementMapItem previousSameIndent = null;
private RequirementMapItem nextSameIndent = null;
private boolean finalNode = true;
private int prevIndent = -1;
private int prevMapId = -1;
/**
* Constructor for the RequirementMapItem object
*/
public RequirementMapItem() {
}
/**
* Constructor for the RequirementMapItem object
*
* @param rs Description of the Parameter
* @throws SQLException Description of the Exception
*/
public RequirementMapItem(ResultSet rs) throws SQLException {
id = rs.getInt("map_id");
projectId = rs.getInt("project_id");
requirementId = rs.getInt("requirement_id");
position = rs.getInt("position");
indent = rs.getInt("indent");
folderId = DatabaseUtils.getInt(rs, "folder_id");
assignmentId = DatabaseUtils.getInt(rs, "assignment_id");
}
/**
* Sets the id attribute of the RequirementMapItem object
*
* @param tmp The new id value
*/
public void setId(int tmp) {
this.id = tmp;
}
/**
* Sets the projectId attribute of the RequirementMapItem object
*
* @param tmp The new projectId value
*/
public void setProjectId(int tmp) {
this.projectId = tmp;
}
/**
* Sets the projectId attribute of the RequirementMapItem object
*
* @param tmp The new projectId value
*/
public void setProjectId(String tmp) {
this.projectId = Integer.parseInt(tmp);
}
/**
* Sets the requirementId attribute of the RequirementMapItem object
*
* @param tmp The new requirementId value
*/
public void setRequirementId(int tmp) {
this.requirementId = tmp;
}
/**
* Sets the requirementId attribute of the RequirementMapItem object
*
* @param tmp The new requirementId value
*/
public void setRequirementId(String tmp) {
this.requirementId = Integer.parseInt(tmp);
}
/**
* Sets the position attribute of the RequirementMapItem object
*
* @param tmp The new position value
*/
public void setPosition(int tmp) {
this.position = tmp;
}
/**
* Sets the position attribute of the RequirementMapItem object
*
* @param tmp The new position value
*/
public void setPosition(String tmp) {
this.position = Integer.parseInt(tmp);
}
/**
* Sets the indent attribute of the RequirementMapItem object
*
* @param tmp The new indent value
*/
public void setIndent(int tmp) {
this.indent = tmp;
}
/**
* Sets the indent attribute of the RequirementMapItem object
*
* @param tmp The new indent value
*/
public void setIndent(String tmp) {
this.indent = Integer.parseInt(tmp);
}
/**
* Sets the folderId attribute of the RequirementMapItem object
*
* @param tmp The new folderId value
*/
public void setFolderId(int tmp) {
this.folderId = tmp;
}
/**
* Sets the folderId attribute of the RequirementMapItem object
*
* @param tmp The new folderId value
*/
public void setFolderId(String tmp) {
this.folderId = Integer.parseInt(tmp);
}
/**
* Sets the assignmentId attribute of the RequirementMapItem object
*
* @param tmp The new assignmentId value
*/
public void setAssignmentId(int tmp) {
this.assignmentId = tmp;
}
/**
* Sets the assignmentId attribute of the RequirementMapItem object
*
* @param tmp The new assignmentId value
*/
public void setAssignmentId(String tmp) {
this.assignmentId = Integer.parseInt(tmp);
}
/**
* Sets the children attribute of the RequirementMapItem object
*
* @param tmp The new children value
*/
public void setChildren(ArrayList tmp) {
this.children = tmp;
}
/**
* Sets the finalNode attribute of the RequirementMapItem object
*
* @param tmp The new finalNode value
*/
public void setFinalNode(boolean tmp) {
this.finalNode = tmp;
}
/**
* Sets the finalNode attribute of the RequirementMapItem object
*
* @param tmp The new finalNode value
*/
public void setFinalNode(String tmp) {
this.finalNode = DatabaseUtils.parseBoolean(tmp);
}
/**
* Sets the parent attribute of the RequirementMapItem object
*
* @param tmp The new parent value
*/
public void setParent(RequirementMapItem tmp) {
this.parent = tmp;
}
/**
* Sets the previousSameIndent attribute of the RequirementMapItem object
*
* @param tmp The new previousSameIndent value
*/
public void setPreviousSameIndent(RequirementMapItem tmp) {
this.previousSameIndent = tmp;
}
/**
* Sets the nextSameIndent attribute of the RequirementMapItem object
*
* @param tmp The new nextSameIndent value
*/
public void setNextSameIndent(RequirementMapItem tmp) {
this.nextSameIndent = tmp;
}
/**
* Sets the prevIndent attribute of the RequirementMapItem object
*
* @param tmp The new prevIndent value
*/
public void setPrevIndent(int tmp) {
this.prevIndent = tmp;
}
/**
* Sets the prevIndent attribute of the RequirementMapItem object
*
* @param tmp The new prevIndent value
*/
public void setPrevIndent(String tmp) {
this.prevIndent = Integer.parseInt(tmp);
}
/**
* Sets the prevMapId attribute of the RequirementMapItem object
*
* @param tmp The new prevMapId value
*/
public void setPrevMapId(int tmp) {
this.prevMapId = tmp;
}
/**
* Sets the prevMapId attribute of the RequirementMapItem object
*
* @param tmp The new prevMapId value
*/
public void setPrevMapId(String tmp) {
this.prevMapId = Integer.parseInt(tmp);
}
/**
* Gets the id attribute of the RequirementMapItem object
*
* @return The id value
*/
public int getId() {
return id;
}
/**
* Gets the projectId attribute of the RequirementMapItem object
*
* @return The projectId value
*/
public int getProjectId() {
return projectId;
}
/**
* Gets the requirementId attribute of the RequirementMapItem object
*
* @return The requirementId value
*/
public int getRequirementId() {
return requirementId;
}
/**
* Gets the position attribute of the RequirementMapItem object
*
* @return The position value
*/
public int getPosition() {
return position;
}
/**
* Gets the indent attribute of the RequirementMapItem object
*
* @return The indent value
*/
public int getIndent() {
return indent;
}
/**
* Gets the folderId attribute of the RequirementMapItem object
*
* @return The folderId value
*/
public int getFolderId() {
return folderId;
}
/**
* Gets the assignmentId attribute of the RequirementMapItem object
*
* @return The assignmentId value
*/
public int getAssignmentId() {
return assignmentId;
}
/**
* Gets the children attribute of the RequirementMapItem object
*
* @return The children value
*/
public ArrayList getChildren() {
return children;
}
/**
* Gets the finalNode attribute of the RequirementMapItem object
*
* @return The finalNode value
*/
public boolean getFinalNode() {
return finalNode;
}
/**
* Gets the parent attribute of the RequirementMapItem object
*
* @return The parent value
*/
public RequirementMapItem getParent() {
return parent;
}
/**
* Gets the previousSameIndent attribute of the RequirementMapItem object
*
* @return The previousSameIndent value
*/
public RequirementMapItem getPreviousSameIndent() {
return previousSameIndent;
}
/**
* Gets the nextSameIndent attribute of the RequirementMapItem object
*
* @return The nextSameIndent value
*/
public RequirementMapItem getNextSameIndent() {
return nextSameIndent;
}
/**
* Gets the prevIndent attribute of the RequirementMapItem object
*
* @return The prevIndent value
*/
public int getPrevIndent() {
return prevIndent;
}
/**
* Gets the prevMapId attribute of the RequirementMapItem object
*
* @return The prevMapId value
*/
public int getPrevMapId() {
return prevMapId;
}
/**
* Appends this item to the bottom of the requirement map list
*
* @param db Description of the Parameter
* @throws SQLException Description of the Exception
*/
public synchronized void append(Connection db) throws SQLException {
PreparedStatement pst = null;
ResultSet rs = null;
if (prevMapId > -1) {
//Get the next position to store the item if a previous mapId specified
pst = db.prepareStatement(
"SELECT position " +
"FROM project_requirements_map " +
"WHERE project_id = ? " +
"AND requirement_id = ? " +
"AND map_id = ? ");
pst.setInt(1, projectId);
pst.setInt(2, requirementId);
pst.setInt(3, prevMapId);
} else {
//Get the next position to store the item if no previous mapId specified
pst = db.prepareStatement(
"SELECT max(position) AS position " +
"FROM project_requirements_map " +
"WHERE project_id = ? " +
"AND requirement_id = ? ");
pst.setInt(1, projectId);
pst.setInt(2, requirementId);
}
rs = pst.executeQuery();
if (rs.next()) {
position = rs.getInt("position");
if (rs.wasNull()) {
position = 0;
}
} else {
position = 0;
}
rs.close();
pst.close();
if (indent == -1) {
//Get the ident of the last item since a new one is not specified
if (position > 0) {
pst = db.prepareStatement(
"SELECT indent " +
"FROM project_requirements_map " +
"WHERE project_id = ? " +
"AND requirement_id = ? " +
"AND position = ? ");
pst.setInt(1, projectId);
pst.setInt(2, requirementId);
pst.setInt(3, position);
rs = pst.executeQuery();
if (rs.next()) {
indent = rs.getInt("indent");
} else {
indent = 0;
}
rs.close();
pst.close();
} else {
indent = 0;
}
}
if (prevMapId > -1) {
//Move the items below, down...
//TODO: What if the item below was indented? should be ok?
pst = db.prepareStatement(
"UPDATE project_requirements_map " +
"SET position = position + 1 " +
"WHERE project_id = ? " +
"AND requirement_id = ? " +
"AND position > ? ");
pst.setInt(1, projectId);
pst.setInt(2, requirementId);
pst.setInt(3, position);
pst.executeUpdate();
}
//Append the item
++position;
pst = db.prepareStatement(
"INSERT INTO project_requirements_map " +
"(project_id, requirement_id, position, indent, folder_id, assignment_id) " +
"VALUES (?, ?, ?, ?, ?, ?) ");
int i = 0;
pst.setInt(++i, projectId);
pst.setInt(++i, requirementId);
pst.setInt(++i, position);
pst.setInt(++i, indent);
DatabaseUtils.setInt(pst, ++i, folderId);
DatabaseUtils.setInt(pst, ++i, assignmentId);
pst.execute();
pst.close();
id = DatabaseUtils.getCurrVal(db, "project_requirements_map_map_id_seq", -1);
}
/**
* Removes this item from the requirement map list
*
* @param db Description of the Parameter
* @throws SQLException Description of the Exception
*/
public synchronized void remove(Connection db) throws SQLException {
boolean autoCommit = db.getAutoCommit();
try {
if (autoCommit) {
db.setAutoCommit(false);
}
PreparedStatement pst;
//Get the current position of the item to remove if not known
if (position == -1) {
buildRecord(db);
}
RequirementMapList mapList = new RequirementMapList();
mapList.setProjectId(projectId);
mapList.setRequirementId(requirementId);
mapList.buildList(db);
RequirementMapItem thisItem = mapList.getItem(position);
//Move the children left
Iterator i = thisItem.getChildren().iterator();
while (i.hasNext()) {
RequirementMapItem child = (RequirementMapItem) i.next();
child.move(db, LEFT, false, mapList);
}
//Delete the requested item
if (position > -1) {
pst = db.prepareStatement(
"DELETE FROM project_requirements_map " +
"WHERE project_id = ? " +
"AND requirement_id = ? " +
"AND position = ? ");
pst.setInt(1, projectId);
pst.setInt(2, requirementId);
pst.setInt(3, thisItem.getPosition());
pst.execute();
pst.close();
}
//Shift all the other items up
if (position > -1) {
pst = db.prepareStatement(
"UPDATE project_requirements_map " +
"SET position = position - 1 " +
"WHERE project_id = ? " +
"AND requirement_id = ? " +
"AND position > ? ");
pst.setInt(1, projectId);
pst.setInt(2, requirementId);
pst.setInt(3, position);
pst.execute();
pst.close();
}
if (autoCommit) {
db.commit();
}
} catch (Exception e) {
if (autoCommit) {
db.rollback();
}
throw new SQLException(e.getMessage());
} finally {
if (autoCommit) {
db.setAutoCommit(true);
}
}
}
/**
* Moves this item right in the requirement map list
*
* @param db Description of the Parameter
* @throws SQLException Description of the Exception
*/
public void moveRight(Connection db) throws SQLException {
this.move(db, RequirementMapItem.RIGHT, true, null);
}
/**
* Moves this item left in the requirement map list
*
* @param db Description of the Parameter
* @throws SQLException Description of the Exception
*/
public void moveLeft(Connection db) throws SQLException {
this.move(db, RequirementMapItem.LEFT, true, null);
}
/**
* Handles the complexity of moving items LEFT and RIGHT
*
* @param db Description of the Parameter
* @param direction Description of the Parameter
* @param isRoot Description of the Parameter
* @param mapList Description of the Parameter
* @throws SQLException Description of the Exception
*/
private void move(Connection db, int direction, boolean isRoot, RequirementMapList mapList) throws SQLException {
if (position == -1) {
buildRecord(db);
}
int moveCount = 0;
//Load the current map once to work on child operations
if (mapList == null) {
mapList = new RequirementMapList();
mapList.setProjectId(projectId);
mapList.setRequirementId(requirementId);
mapList.buildList(db);
}
RequirementMapItem thisItem = mapList.getItem(position);
//Move the item right if NOT 1st in node
if (direction == RIGHT && thisItem.getPosition() > 1) {
//Ask parent to make sure this position is not the first child
if ((thisItem.getParent() != null && thisItem.getParent().getChildren().indexOf(thisItem) > 0) ||
(thisItem.getParent() == null && thisItem.getPosition() > 1) ||
(!isRoot)) {
//Good, now move it right and its children
PreparedStatement pst = db.prepareStatement(
"UPDATE project_requirements_map " +
"SET indent = indent + 1 " +
"WHERE project_id = ? AND requirement_id = ? AND position = ? ");
int i = 0;
pst.setInt(++i, projectId);
pst.setInt(++i, requirementId);
pst.setInt(++i, thisItem.getPosition());
moveCount = pst.executeUpdate();
pst.close();
}
}
//Move the item left if it has a parent
if (direction == LEFT && position > 1 && indent > 0 && thisItem.getParent() != null) {
PreparedStatement pst = db.prepareStatement(
"UPDATE project_requirements_map " +
"SET indent = indent - 1 " +
"WHERE project_id = ? AND requirement_id = ? AND position = ? " +
"AND indent > 0 ");
int i = 0;
pst.setInt(++i, projectId);
pst.setInt(++i, requirementId);
pst.setInt(++i, thisItem.getPosition());
moveCount = pst.executeUpdate();
pst.close();
}
//Now move the children
if (moveCount == 1) {
Iterator i = thisItem.getChildren().iterator();
while (i.hasNext()) {
RequirementMapItem child = (RequirementMapItem) i.next();
child.move(db, direction, false, mapList);
}
}
}
/**
* Moves this item up in the requirement map list
*
* @param db Description of the Parameter
* @throws SQLException Description of the Exception
*/
public void moveUp(Connection db) throws SQLException {
if (position == -1) {
buildRecord(db);
}
//Load the current map once to work on child operations
RequirementMapList mapList = new RequirementMapList();
mapList.setProjectId(projectId);
mapList.setRequirementId(requirementId);
mapList.buildList(db);
//Find the next item
RequirementMapItem thisItem = mapList.getItem(position);
//Ask parent to make sure this position is not the last child
if ((thisItem.getParent() != null && thisItem.getParent().getChildren().indexOf(thisItem) > 0) ||
(thisItem.getParent() == null && thisItem.getPosition() > 1)) {
thisItem.moveUp(db, true, mapList, new MoveCounter());
}
}
/**
* Handles the complexity of moving an item up in the requirement map list
*
* @param db Description of the Parameter
* @param isRoot Description of the Parameter
* @param mapList Description of the Parameter
* @param counter Description of the Parameter
* @throws SQLException Description of the Exception
*/
private void moveUp(Connection db, boolean isRoot, RequirementMapList mapList, MoveCounter counter) throws SQLException {
int moveCount = 0;
RequirementMapItem thisItem = mapList.getItem(position);
//Ask parent to make sure this position is not the first child
if ((thisItem.getParent() != null && thisItem.getParent().getChildren().indexOf(thisItem) > 0) ||
(thisItem.getParent() == null && thisItem.getPosition() > 1) ||
(!isRoot)) {
PreparedStatement pst = db.prepareStatement(
"UPDATE project_requirements_map " +
"SET position = ? " +
"WHERE map_id = ? ");
int i = 0;
if (isRoot) {
counter.setAmount(thisItem.getPosition() - thisItem.getPreviousSameIndent().getPosition());
}
pst.setInt(++i, thisItem.getPosition() - counter.getAmount());
pst.setInt(++i, id);
moveCount = pst.executeUpdate();
pst.close();
}
//Now move the children
if (moveCount == 1) {
counter.add(moveCount);
Iterator i = thisItem.getChildren().iterator();
while (i.hasNext()) {
RequirementMapItem child = (RequirementMapItem) i.next();
child.moveUp(db, false, mapList, counter);
}
}
//Now move the items that were above, underneath all of these
if (isRoot && counter.getAmount() > 0) {
counter.setAmount(counter.getTotal());
thisItem.getPreviousSameIndent().moveDown(db, false, mapList, counter);
}
}
/**
* Moves this item down in the requirement map list
*
* @param db Description of the Parameter
* @throws SQLException Description of the Exception
*/
public void moveDown(Connection db) throws SQLException {
if (position == -1) {
buildRecord(db);
}
//Load the current map once to work on child operations
RequirementMapList mapList = new RequirementMapList();
mapList.setProjectId(projectId);
mapList.setRequirementId(requirementId);
mapList.buildList(db);
//Find the next item
RequirementMapItem thisItem = mapList.getItem(position);
//Ask parent to make sure this position is not the last child
if ((thisItem.getParent() != null && thisItem.getParent().getChildren().indexOf(thisItem) < thisItem.getParent().getChildren().size() - 1) ||
(thisItem.getParent() == null && thisItem.getNextSameIndent() != null)) {
mapList.getItem(thisItem.getNextSameIndent().getPosition()).moveUp(db, true, mapList, new MoveCounter());
}
}
/**
* Handles the complexity of moving an item down in the requirement map list
*
* @param db Description of the Parameter
* @param isRoot Description of the Parameter
* @param mapList Description of the Parameter
* @param counter Description of the Parameter
* @throws SQLException Description of the Exception
*/
private void moveDown(Connection db, boolean isRoot, RequirementMapList mapList, MoveCounter counter) throws SQLException {
int moveCount = 0;
//Load the current map once to work on child operations
RequirementMapItem thisItem = mapList.getItem(position);
//Ask parent to make sure this position is not the last child
if ((thisItem.getParent() != null && thisItem.getParent().getChildren().indexOf(thisItem) < thisItem.getParent().getChildren().size() - 1) ||
(thisItem.getParent() == null && thisItem.getNextSameIndent() != null) ||
(!isRoot)) {
PreparedStatement pst = db.prepareStatement(
"UPDATE project_requirements_map " +
"SET position = ? " +
"WHERE map_id = ? ");
int i = 0;
if (isRoot) {
counter.setAmount(thisItem.getNextSameIndent().getPosition() - thisItem.getPosition());
}
pst.setInt(++i, thisItem.getPosition() + counter.getAmount());
pst.setInt(++i, id);
moveCount = pst.executeUpdate();
pst.close();
}
//Now move the children
if (moveCount == 1) {
counter.add(moveCount);
Iterator i = thisItem.getChildren().iterator();
while (i.hasNext()) {
RequirementMapItem child = (RequirementMapItem) i.next();
child.moveDown(db, false, mapList, counter);
}
}
//Now move the items that were below, above all of these
if (isRoot && counter.getAmount() > 0) {
counter.setAmount(counter.getTotal());
thisItem.getNextSameIndent().moveUp(db, false, mapList, counter);
}
}
/**
* Reads in missing requirement map item data
*
* @param db Description of the Parameter
* @throws SQLException Description of the Exception
*/
public void buildRecord(Connection db) throws SQLException {
PreparedStatement pst = db.prepareStatement(
"SELECT map_id, project_id, position, indent " +
"FROM project_requirements_map " +
"WHERE requirement_id = ? " +
"AND " + (folderId > -1 ? "folder_id" : "assignment_id") + " = ? ");
int i = 0;
pst.setInt(++i, requirementId);
if (folderId > -1) {
pst.setInt(++i, folderId);
} else {
pst.setInt(++i, assignmentId);
}
ResultSet rs = pst.executeQuery();
if (rs.next()) {
id = rs.getInt("map_id");
projectId = rs.getInt("project_id");
position = rs.getInt("position");
indent = rs.getInt("indent");
}
rs.close();
pst.close();
}
/**
* Description of the Method
*/
public void printObject() {
System.out.println("=== RequirementMapItem ===");
System.out.println("Id: " + id);
System.out.println("ProjectId: " + projectId);
System.out.println("RequirementId: " + requirementId);
System.out.println("AssignmentId: " + assignmentId);
System.out.println("FolderId: " + folderId);
System.out.println("Position: " + position);
System.out.println("Indent: " + indent);
}
}