/******************************************************************************* * Copyright (c) 2011 Arapiki Solutions Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * "Peter Smith <psmith@arapiki.com>" - initial API and * implementation and/or initial documentation *******************************************************************************/ package com.buildml.model.impl; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import com.buildml.model.FatalBuildStoreError; import com.buildml.model.IActionMgr; import com.buildml.model.IBuildStore; import com.buildml.model.IFileGroupMgr; import com.buildml.model.IFileMgr; import com.buildml.model.IFileMgr.PathType; import com.buildml.model.IPackageMemberMgr; import com.buildml.model.IPackageMemberMgrListener; import com.buildml.model.IPackageMgr; import com.buildml.model.IPackageRootMgr; import com.buildml.model.ISlotTypes; import com.buildml.model.ISubPackageMgr; import com.buildml.model.types.FileSet; import com.buildml.model.types.ActionSet; import com.buildml.utils.errors.ErrorCode; /** * A manager class (that supports the BuildStore class) responsible for managing all * BuildStore information pertaining to the membership of packages. * <p> * There should be exactly one PackageMemberMgr object per BuildStore object. Use the * BuildStore's getPackageMemberMgr() method to obtain that one instance. * * @author "Peter Smith <psmith@arapiki.com>" */ /* package private */ class PackageMemberMgr implements IPackageMemberMgr { /*=====================================================================================* * FIELDS/TYPES *=====================================================================================*/ /** The BuildStore that owns this package manager */ private IBuildStore buildStore; /** * Our database manager object, used to access the database content. This is provided * to us when the Packages object is first instantiated. */ private BuildStoreDB db = null; /** The FileMgr object that manages the files in our packages. */ private IFileMgr fileMgr = null; /** The ActionMgr object that manages the actions in our packages. */ private IActionMgr actionMgr = null; /** The PackageMgr object that manages the list of packages. */ private IPackageMgr pkgMgr = null; /** The SubPackageMgr object that manages the list of packages. */ private ISubPackageMgr subPkgMgr = null; /** The FileGroupMgr object that manages the file groups in our package. */ private IFileGroupMgr fileGroupMgr = null; /** * Various prepared statements for database access. */ private PreparedStatement findMemberPackagePrepStmt = null, updatePackagePrepStmt = null, findFilesInPackage1PrepStmt = null, findFilesInPackage2PrepStmt = null, findFilesOutsidePackage1PrepStmt = null, findFilesOutsidePackage2PrepStmt = null, findActionPackagePrepStmt = null, findActionsInPackagePrepStmt = null, findActionsOutsidePackagePrepStmt = null, findLocationPrepStmt = null, updateLocationPrepStmt = null, findActionNeighbourPrepStmt = null, findFileGroupNeighbourPrepStmt = null, findMergeFileGroupMembersPrepStmt = null, findMergeFileGroupsContainingFileGroup = null, findFiltersContainingFileGroup = null; /** The event listeners who are registered to learn about package membership changes */ List<IPackageMemberMgrListener> listeners = new ArrayList<IPackageMemberMgrListener>(); /*=====================================================================================* * CONSTRUCTORS *=====================================================================================*/ /** * Create a new Packages object, which represents the file/action packages that * are part of the BuildStore. * * @param buildStore The BuildStore that this Packages object belongs to. */ public PackageMemberMgr(BuildStore buildStore) { this.buildStore = buildStore; this.db = buildStore.getBuildStoreDB(); this.fileMgr = buildStore.getFileMgr(); this.actionMgr = buildStore.getActionMgr(); this.pkgMgr = buildStore.getPackageMgr(); this.fileGroupMgr = buildStore.getFileGroupMgr(); this.subPkgMgr = buildStore.getSubPackageMgr(); findMemberPackagePrepStmt = db.prepareStatement( "select pkgId, scopeId from packageMembers where memberType = ? and memberId = ?"); updatePackagePrepStmt = db.prepareStatement("update packageMembers set pkgId = ?, scopeId = ? " + "where memberType = ? and memberId = ?"); findFilesInPackage1PrepStmt = db.prepareStatement( "select memberId from packageMembers where pkgId = ? and memberType = " + TYPE_FILE); findFilesInPackage2PrepStmt = db.prepareStatement( "select memberId from packageMembers where pkgId = ? and memberType = " + TYPE_FILE + " and scopeId = ?"); findFilesOutsidePackage1PrepStmt = db.prepareStatement( "select memberId from packageMembers where pkgId != ? and memberType = " + TYPE_FILE); findFilesOutsidePackage2PrepStmt = db.prepareStatement( "select memberId from packageMembers where memberType = " + TYPE_FILE + " and not (pkgId = ? and scopeId = ?)"); findActionPackagePrepStmt = db.prepareStatement("select pkgId from packageMembers where memberId = ?" + " and memberType = " + TYPE_ACTION); findActionsInPackagePrepStmt = db.prepareStatement( "select memberId from packageMembers where pkgId = ? and memberType = " + TYPE_ACTION); findActionsOutsidePackagePrepStmt = db.prepareStatement("select memberId from packageMembers " + "where pkgId != ? and memberId != 0 and memberType = " + TYPE_ACTION); findLocationPrepStmt = db.prepareStatement( "select x, y from packageMembers where memberType = ? and memberId = ?"); updateLocationPrepStmt = db.prepareStatement( "update packageMembers set x = ?, y = ? where memberType = ? and memberId = ?"); findActionNeighbourPrepStmt = db.prepareStatement( "select value, x, y from slotValues, slotTypes, packageMembers where " + "slotValues.ownerType = " + ISlotTypes.SLOT_OWNER_ACTION + " " + "and slotValues.ownerId = ? " + "and slotValues.slotId = slotTypes.slotId " + "and slotTypes.slotPos = ? " + "and packageMembers.memberType = " + IPackageMemberMgr.TYPE_FILE_GROUP + " " + "and packageMembers.memberId = value"); findFileGroupNeighbourPrepStmt = db.prepareStatement( "select slotValues.ownerId, x, y from slotValues, slotTypes, packageMembers where " + "slotValues.ownerType = " + ISlotTypes.SLOT_OWNER_ACTION + " " + "and slotValues.value = ? " + "and slotValues.slotId = slotTypes.slotId " + "and (slotTypes.slotPos = ? or slotTypes.slotPos = ?) " + "and packageMembers.memberType = " + IPackageMemberMgr.TYPE_ACTION + " " + "and packageMembers.memberId = slotValues.ownerId"); findMergeFileGroupMembersPrepStmt = db.prepareStatement( "select distinct pathId, x, y from fileGroupPaths, packageMembers where groupId = ? " + "and packageMembers.memberType = " + IPackageMemberMgr.TYPE_FILE_GROUP + " " + "and packageMembers.memberId = fileGroupPaths.pathId"); findMergeFileGroupsContainingFileGroup = db.prepareStatement( "select distinct groupId, x, y from fileGroups, fileGroupPaths, packageMembers " + "where packageMembers.memberType = " + IPackageMemberMgr.TYPE_FILE_GROUP + " " + "and packageMembers.memberId = fileGroupPaths.groupId " + "and fileGroups.id = fileGroupPaths.groupId " + "and fileGroups.type = " + IFileGroupMgr.MERGE_GROUP + " " + "and fileGroupPaths.pathId = ?"); findFiltersContainingFileGroup = db.prepareStatement( "select id from fileGroups where predId = ?"); } /*=====================================================================================* * PUBLIC METHODS *=====================================================================================*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getScopeName(int) */ @Override public String getScopeName(int id) { /* the names are a static mapping, so no need for database look-ups */ switch (id) { case SCOPE_NONE: return "None"; case SCOPE_PRIVATE: return "Private"; case SCOPE_PUBLIC: return "Public"; default: return null; } } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getScopeId(java.lang.String) */ @Override public int getScopeId(String name) { /* the mapping is static, so no need for a database look up */ if (name.equalsIgnoreCase("None")) { return SCOPE_NONE; } if (name.equalsIgnoreCase("priv") || name.equalsIgnoreCase("private")) { return SCOPE_PRIVATE; } if (name.equalsIgnoreCase("pub") || name.equalsIgnoreCase("public")) { return SCOPE_PUBLIC; } return ErrorCode.NOT_FOUND; } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#parsePkgSpec(java.lang.String) */ @Override public PackageDesc parsePkgSpec(String pkgSpec) { /* parse the pkgSpec to separate it into "pkg" and "scope" portions */ String pkgName = pkgSpec; String scopeName = null; /* check if there's a '/' in the string, to separate "package" from "scope" */ int slashIndex = pkgSpec.indexOf('/'); if (slashIndex != -1) { pkgName = pkgSpec.substring(0, slashIndex); scopeName = pkgSpec.substring(slashIndex + 1); } /* * Convert the package's name into it's internal ID. If there's an error, * we simply pass it back to our own caller. */ int pkgId = pkgMgr.getId(pkgName); /* if the user provided a /scope portion, convert that to an ID too */ int scopeId = 0; if (scopeName != null) { scopeId = getScopeId(scopeName); } PackageDesc result = new PackageDesc(); result.pkgId = pkgId; result.pkgScopeId = scopeId; return result; } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#setPackageOfMember(int, int, int, int) */ @Override public int setPackageOfMember(int memberType, int memberId, int pkgId, int pkgScopeId) { /* we can't assign files into folders (only into packages) */ if (!pkgMgr.isValid(pkgId) || pkgMgr.isFolder(pkgId)) { return ErrorCode.BAD_VALUE; } /* TODO: check pkgScopeId is a valid scope - else return BAD_VALUE */ /* determine which package the member is currently in - if no change, we're done */ PackageDesc oldPkg = getPackageOfMember(memberType, memberId); if (oldPkg == null) { return ErrorCode.NOT_FOUND; } if ((oldPkg.pkgId == pkgId) && (oldPkg.pkgScopeId == pkgScopeId)) { return ErrorCode.OK; } /* * Perform MEMBER_TYPE_FILE-specific validation checks. */ if (memberType == TYPE_FILE) { /* the path must be valid and not-trashed */ if ((fileMgr.getPathType(memberId) == PathType.TYPE_INVALID) || (fileMgr.isPathTrashed(memberId))) { return ErrorCode.NOT_FOUND; } /* * Check that the path falls under the package's root (except for <import> which * doesn't have root restrictions). */ if (pkgId != pkgMgr.getImportPackage()) { IPackageRootMgr pkgRootMgr = buildStore.getPackageRootMgr(); int pkgRootPathId = pkgRootMgr.getPackageRoot(pkgId, IPackageRootMgr.SOURCE_ROOT); if (pkgRootPathId == ErrorCode.NOT_FOUND) { return ErrorCode.NOT_FOUND; } if ((pkgRootPathId != memberId) && !fileMgr.isAncestorOf(pkgRootPathId, memberId)) { return ErrorCode.OUT_OF_RANGE; } } } /* update the database table with the new pkgId/pkgScopeId */ try { updatePackagePrepStmt.setInt(1, pkgId); updatePackagePrepStmt.setInt(2, pkgScopeId); updatePackagePrepStmt.setInt(3, memberType); updatePackagePrepStmt.setInt(4, memberId); int rowCount = db.executePrepUpdate(updatePackagePrepStmt); if (rowCount == 0) { return ErrorCode.NOT_FOUND; } } catch (SQLException e) { throw new FatalBuildStoreError("Unable to execute SQL statement", e); } /* * Notify listeners about the change in package content. */ notifyListeners(oldPkg.pkgId, IPackageMemberMgrListener.CHANGED_MEMBERSHIP, memberType, memberId); if (oldPkg.pkgId != pkgId) { notifyListeners(pkgId, IPackageMemberMgrListener.CHANGED_MEMBERSHIP, memberType, memberId); } return ErrorCode.OK; } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#setPackageOfMember(int, int, int) */ @Override public int setPackageOfMember(int memberType, int memberId, int pkgId) { return setPackageOfMember(memberType, memberId, pkgId, IPackageMemberMgr.SCOPE_NONE); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getPackageOfMember(int, int) */ @Override public PackageDesc getPackageOfMember(int memberType, int memberId) { try { findMemberPackagePrepStmt.setInt(1, memberType); findMemberPackagePrepStmt.setInt(2, memberId); ResultSet rs = db.executePrepSelectResultSet(findMemberPackagePrepStmt); if (!rs.next()){ return null; } PackageDesc result = new PackageDesc(); result.pkgId = rs.getInt(1); result.pkgScopeId = rs.getInt(2); rs.close(); return result; } catch (SQLException e) { throw new FatalBuildStoreError("Unable to execute SQL statement", e); } } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getMembersInPackage(int, int, int) */ @Override public MemberDesc[] getMembersInPackage(int pkgId, int pkgScopeId, int memberTypeFilter) { List<MemberDesc> members; /* * There are multiple cases to handle in this query, depending on whether pkgScopeId * is defined (versus being SCOPE_NONE) and whether memberTypeFilter is defined * (versus being MEMBER_TYPE_ANY). */ String query = "select memberType, memberId, x, y from packageMembers where pkgId = " + pkgId; if (pkgScopeId != SCOPE_NONE) { query += " and scopeId = " + pkgScopeId; } if (memberTypeFilter != TYPE_ANY) { query += " and memberType = " + memberTypeFilter; } /* query the database to find the relevant package members */ try { ResultSet rs = db.executeSelectResultSet(query); if (!rs.next()){ return new MemberDesc[0]; } /* copy results into a MemberDesc[] */ members = new ArrayList<IPackageMemberMgr.MemberDesc>(); do { MemberDesc newMember = new MemberDesc( rs.getInt(1), rs.getInt(2), rs.getInt(3), rs.getInt(4)); /* don't show trashed actions */ boolean isTrashed = false; if ((newMember.memberType == IPackageMemberMgr.TYPE_ACTION) && actionMgr.isActionTrashed(newMember.memberId)) { isTrashed = true; } else if ((newMember.memberType == IPackageMemberMgr.TYPE_SUB_PACKAGE) && subPkgMgr.isSubPackageTrashed(newMember.memberId)) { isTrashed = true; } if (!isTrashed){ members.add(newMember); } } while (rs.next()); rs.close(); return members.toArray(new MemberDesc[members.size()]); } catch (SQLException e) { throw new FatalBuildStoreError("Unable to execute SQL statement", e); } } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#setMemberLocation(int, int, int, int) */ @Override public int setMemberLocation(int memberType, int memberId, int x, int y) { /* If the actionId is invalid, or the (x, y) is unchanged, do nothing */ MemberLocation currentLocation = getMemberLocation(memberType, memberId); if (currentLocation == null) { return ErrorCode.BAD_VALUE; } if ((x == currentLocation.x) && (y == currentLocation.y)) { return ErrorCode.OK; } /* a database change is required */ try { updateLocationPrepStmt.setInt(1, x); updateLocationPrepStmt.setInt(2, y); updateLocationPrepStmt.setInt(3, memberType); updateLocationPrepStmt.setInt(4, memberId); db.executePrepUpdate(updateLocationPrepStmt); } catch (SQLException e) { new FatalBuildStoreError("Error in SQL: " + e); } PackageDesc pkg = getPackageOfMember(memberType, memberId); if (pkg != null) { notifyListeners(pkg.pkgId, IPackageMemberMgrListener.CHANGED_LOCATION, memberType, memberId); } return ErrorCode.OK; } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getMemberLocation(int, int) */ @Override public MemberLocation getMemberLocation(int memberType, int memberId) { ResultSet rs; MemberLocation result = null; try { findLocationPrepStmt.setInt(1, memberType); findLocationPrepStmt.setInt(2, memberId); rs = db.executePrepSelectResultSet(findLocationPrepStmt); /* if memberType/memberID is valid, fetch the x and y fields */ if (rs.next()) { result = new MemberLocation(); result.x = rs.getInt(1); result.y = rs.getInt(2); } rs.close(); } catch (SQLException e) { new FatalBuildStoreError("Error in SQL: " + e); } return result; } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getNeighboursOf(int, int, int) */ @Override public MemberDesc[] getNeighboursOf( int memberType, int memberId, int direction, boolean showFilters) { /* validate direction */ boolean lookLeft = false; boolean lookRight = false; if (direction == IPackageMemberMgr.NEIGHBOUR_LEFT) { lookLeft = true; } else if (direction == IPackageMemberMgr.NEIGHBOUR_RIGHT) { lookRight = true; } else if (direction == IPackageMemberMgr.NEIGHBOUR_ANY) { lookLeft = lookRight = true; } else { return null; } /* start with an empty list - we'll add to this as we go along */ List<MemberDesc> neighbours = new ArrayList<MemberDesc>(); /* is this member a file group? */ if (memberType == IPackageMemberMgr.TYPE_FILE_GROUP) { int groupType = fileGroupMgr.getGroupType(memberId); if (groupType == ErrorCode.NOT_FOUND) { return null; } if (lookLeft) { getNeighboursOfFileGroupLeft(neighbours, memberId, groupType, showFilters); } if (lookRight) { getNeighboursOfFileGroupRight(neighbours, memberId, groupType, showFilters); } } /* is this member an action? */ else if (memberType == IPackageMemberMgr.TYPE_ACTION) { if (!actionMgr.isActionValid(memberId)) { return null; } if (lookLeft) { getNeighboursOfActionLeft(neighbours, memberId, showFilters); } if (lookRight) { getNeighboursOfActionRight(neighbours, memberId); } } /* we don't yet support other types of package member */ else { return null; } /* * Convert the accumulated list of neighbours into an array, but first remove * duplicates. This is an O(n*n) algorithm, but with a small number of * neighbours it should be quick. */ List<MemberDesc> resultsNoDups = new ArrayList<MemberDesc>(); for (Iterator<MemberDesc> iterator = neighbours.iterator(); iterator.hasNext();) { MemberDesc newMember = (MemberDesc) iterator.next(); /* search to see if this neighbour is already in our "resultsNoDups" list */ boolean alreadyEntered = false; for (Iterator<MemberDesc> iterator2 = resultsNoDups.iterator(); iterator2.hasNext();) { MemberDesc existingMember = (MemberDesc) iterator2.next(); if ((newMember.memberType == existingMember.memberType) && (newMember.memberId == existingMember.memberId)) { alreadyEntered = true; break; } } if (!alreadyEntered) { resultsNoDups.add(newMember); } } return resultsNoDups.toArray(new MemberDesc[resultsNoDups.size()]); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getFilesInPackage(int) */ @Override public FileSet getFilesInPackage(int pkgId) { Integer results[] = null; try { findFilesInPackage1PrepStmt.setInt(1, pkgId); results = db.executePrepSelectIntegerColumn(findFilesInPackage1PrepStmt); } catch (SQLException e) { throw new FatalBuildStoreError("Unable to execute SQL statement", e); } /* convert to a FileSet */ return new FileSet(fileMgr, results); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getFilesInPackage(int, int) */ @Override public FileSet getFilesInPackage(int pkgId, int pkgScopeId) { Integer results[] = null; try { findFilesInPackage2PrepStmt.setInt(1, pkgId); findFilesInPackage2PrepStmt.setInt(2, pkgScopeId); results = db.executePrepSelectIntegerColumn(findFilesInPackage2PrepStmt); } catch (SQLException e) { throw new FatalBuildStoreError("Unable to execute SQL statement", e); } /* convert to a FileSet */ return new FileSet(fileMgr, results); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getFilesInPackage(java.lang.String) */ @Override public FileSet getFilesInPackage(String pkgSpec) { PackageDesc spec = parsePkgSpec(pkgSpec); /* the ID must not be invalid, else that's an error */ if ((spec.pkgId == ErrorCode.NOT_FOUND) || (spec.pkgScopeId == ErrorCode.NOT_FOUND)) { return null; } /* * If the scope ID isn't specified by the user, then scopeId == 0 (the * ID of the "None" scope). This indicates we should look for all paths * in the package, regardless of the scope. */ if (spec.pkgScopeId != 0) { return getFilesInPackage(spec.pkgId, spec.pkgScopeId); } else { return getFilesInPackage(spec.pkgId); } } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getFilesOutsidePackage(int) */ @Override public FileSet getFilesOutsidePackage(int pkgId) { Integer results[] = null; try { findFilesOutsidePackage1PrepStmt.setInt(1, pkgId); results = db.executePrepSelectIntegerColumn(findFilesOutsidePackage1PrepStmt); } catch (SQLException e) { throw new FatalBuildStoreError("Unable to execute SQL statement", e); } /* convert to a FileSet */ return new FileSet(fileMgr, results); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getFilesOutsidePackage(int, int) */ @Override public FileSet getFilesOutsidePackage(int pkgId, int pkgScopeId) { Integer results[] = null; try { findFilesOutsidePackage2PrepStmt.setInt(1, pkgId); findFilesOutsidePackage2PrepStmt.setInt(2, pkgScopeId); results = db.executePrepSelectIntegerColumn(findFilesOutsidePackage2PrepStmt); } catch (SQLException e) { throw new FatalBuildStoreError("Unable to execute SQL statement", e); } /* convert to a FileSet */ return new FileSet(fileMgr, results); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getFilesOutsidePackage(java.lang.String) */ @Override public FileSet getFilesOutsidePackage(String pkgSpec) { PackageDesc spec = parsePkgSpec(pkgSpec); /* the ID must not be invalid, else that's an error */ if ((spec.pkgId == ErrorCode.NOT_FOUND) || (spec.pkgScopeId == ErrorCode.NOT_FOUND)) { return null; } /* * The scope ID is optional, since it still allows us to * get the package's files. Note that scopeId == 0 implies * that the user didn't specify a /scope value. */ if (spec.pkgScopeId != 0) { return getFilesOutsidePackage(spec.pkgId, spec.pkgScopeId); } else { return getFilesOutsidePackage(spec.pkgId); } } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getActionsInPackage(int) */ @Override public ActionSet getActionsInPackage(int pkgId) { Integer results[] = null; try { findActionsInPackagePrepStmt.setInt(1, pkgId); results = db.executePrepSelectIntegerColumn(findActionsInPackagePrepStmt); } catch (SQLException e) { throw new FatalBuildStoreError("Unable to execute SQL statement", e); } return new ActionSet(actionMgr, results); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getActionsInPackage(java.lang.String) */ @Override public ActionSet getActionsInPackage(String pkgSpec) { /* translate the package's name to its ID */ int pkgId = pkgMgr.getId(pkgSpec); if (pkgId == ErrorCode.NOT_FOUND){ return null; } return getActionsInPackage(pkgId); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getActionsOutsidePackage(int) */ @Override public ActionSet getActionsOutsidePackage(int pkgId) { Integer results[] = null; try { findActionsOutsidePackagePrepStmt.setInt(1, pkgId); results = db.executePrepSelectIntegerColumn(findActionsOutsidePackagePrepStmt); } catch (SQLException e) { throw new FatalBuildStoreError("Unable to execute SQL statement", e); } return new ActionSet(actionMgr, results); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#getActionsOutsidePackage(java.lang.String) */ @Override public ActionSet getActionsOutsidePackage(String pkgSpec) { /* translate the package's name to its ID */ int pkgId = pkgMgr.getId(pkgSpec); if (pkgId == ErrorCode.NOT_FOUND){ return null; } return getActionsOutsidePackage(pkgId); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#addListener(com.buildml.model.IPackageMemberMgrListener) */ @Override public void addListener(IPackageMemberMgrListener listener) { listeners.add(listener); } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see com.buildml.model.IPackageMemberMgr#removeListener(com.buildml.model.IPackageMemberMgrListener) */ @Override public void removeListener(IPackageMemberMgrListener listener) { listeners.remove(listener); }; /*=====================================================================================* * PRIVATE METHODS *=====================================================================================*/ /** * Notify any registered listeners about our change in state. * @param pkgId The package that has changed. * @param how The way in which the package changed (see {@link IPackageMemberMgrListener}). * @param memberType The type of the member that has changed (e.g. TYPE_ACTION) * @param memberId The ID of the member (e.g. actionId or fileGroupId). */ private void notifyListeners(int pkgId, int how, int memberType, int memberId) { /* * Make a copy of the listeners list, otherwise a registered listener can't remove * itself from the list within the packageChangeNotification() method. */ IPackageMemberMgrListener listenerCopy[] = listeners.toArray(new IPackageMemberMgrListener[listeners.size()]); for (int i = 0; i < listenerCopy.length; i++) { listenerCopy[i].packageMemberChangeNotification(pkgId, how, memberType, memberId); } } /*-------------------------------------------------------------------------------------*/ /** * Helper function for GetNeighboursOf() to compute the left-side neighbours of an action. * * @param neighbours The List<MemberDesc> to add newly-found neighbours into. * @param actionId The ID of the action whose neighbours we are looking for. * @param showFilters True if we should return neighbouring filter file groups, or * false if filter groups should be skipped over. */ private void getNeighboursOfActionLeft(List<MemberDesc> neighbours, int actionId, boolean showFilters) { try { /* * Query the database for all file groups that are mentioned in our INPUT slots. */ findActionNeighbourPrepStmt.setInt(1, actionId); findActionNeighbourPrepStmt.setInt(2, ISlotTypes.SLOT_POS_INPUT); ResultSet rs = db.executePrepSelectResultSet(findActionNeighbourPrepStmt); while (rs.next()) { MemberDesc member = new MemberDesc(IPackageMemberMgr.TYPE_FILE_GROUP, Integer.valueOf(rs.getString(1)), rs.getInt(2), rs.getInt(3)); /* skip over filters, if necessary */ if (!showFilters && (fileGroupMgr.getGroupType(member.memberId) == IFileGroupMgr.FILTER_GROUP)) { member = getPredecessorOfFilter(member.memberId); } /* else add non-filter groups to our neighbour list */ if (member != null) { neighbours.add(member); } } rs.close(); } catch (SQLException e) { BuildStoreDB.throwSqlException(e); } } /*-------------------------------------------------------------------------------------*/ /** * Helper function for GetNeighboursOf() to compute the right-side neighbours of an action. * * @param neighbours The List<MemberDesc> to add newly-found neighbours into. * @param actionId The ID of the action whose neighbours we are looking for. */ private void getNeighboursOfActionRight(List<MemberDesc> neighbours, int actionId) { try { /* * Query the database for all file groups mentioned in our OUTPUT slots. */ findActionNeighbourPrepStmt.setInt(1, actionId); findActionNeighbourPrepStmt.setInt(2, ISlotTypes.SLOT_POS_OUTPUT); ResultSet rs = db.executePrepSelectResultSet(findActionNeighbourPrepStmt); while (rs.next()) { MemberDesc member = new MemberDesc(IPackageMemberMgr.TYPE_FILE_GROUP, Integer.valueOf(rs.getString(1)), rs.getInt(2), rs.getInt(3)); neighbours.add(member); } rs.close(); } catch (SQLException e) { BuildStoreDB.throwSqlException(e); } } /*-------------------------------------------------------------------------------------*/ /** * Helper function for GetNeighboursOf() to compute the left-side neighbours of a file group. * * @param neighbours The List<MemberDesc> to add newly-found neighbours into. * @param fileGroupId The ID of the file group whose neighbours we are looking for. * @param fileGroupType The type of this file group (e.g. MERGE_GROUP, etc). * @param showFilters True if we should return neighbouring filter file groups, or * false if filter groups should be skipped over. */ private void getNeighboursOfFileGroupLeft(List<MemberDesc> neighbours, int fileGroupId, int fileGroupType, boolean showFilters) { try { /* * To find left neighbours that are actions... search for output slots in this * package's actions that refer to our file group. = */ findFileGroupNeighbourPrepStmt.setInt(1, fileGroupId); findFileGroupNeighbourPrepStmt.setInt(2, ISlotTypes.SLOT_POS_OUTPUT); ResultSet rs = db.executePrepSelectResultSet(findFileGroupNeighbourPrepStmt); while (rs.next()) { MemberDesc member = new MemberDesc(IPackageMemberMgr.TYPE_ACTION, Integer.valueOf(rs.getString(1)), rs.getInt(2), rs.getInt(3)); neighbours.add(member); } rs.close(); /* * If this is a merge group, get all the members of this group (they will all * be file groups, since merge groups can only contain file groups). */ if (fileGroupType == IFileGroupMgr.MERGE_GROUP) { findMergeFileGroupMembersPrepStmt.setInt(1, fileGroupId); rs = db.executePrepSelectResultSet(findMergeFileGroupMembersPrepStmt); while (rs.next()) { MemberDesc member = new MemberDesc(IPackageMemberMgr.TYPE_FILE_GROUP, rs.getInt(1), rs.getInt(2), rs.getInt(3)); /* skip over filters, if necessary */ if (!showFilters && (fileGroupMgr.getGroupType(member.memberId) == IFileGroupMgr.FILTER_GROUP)) { member = getPredecessorOfFilter(member.memberId); } if (member != null) { neighbours.add(member); } } rs.close(); } /* * If this file group is itself a filter file group, the left neighbour is always the * predecessor. */ if (fileGroupType == IFileGroupMgr.FILTER_GROUP) { MemberDesc member = getPredecessorOfFilter(fileGroupId); if (member != null) { neighbours.add(member); } } } catch (SQLException e) { BuildStoreDB.throwSqlException(e); } } /*-------------------------------------------------------------------------------------*/ /** * Helper function for GetNeighboursOf() to compute the right-side neighbours of a file group. * * @param neighbours The List<MemberDesc> to add newly-found neighbours into. * @param fileGroupId The ID of the file group whose neighbours we are looking for. * @param fileGroupType The type of this file group (e.g. MERGE_GROUP, etc). * @param showFilters True if we should return neighbouring filter file groups, or * false if filter groups should be skipped over. */ private void getNeighboursOfFileGroupRight(List<MemberDesc> neighbours, int fileGroupId, int fileGroupType, boolean showFilters) { try { /* * To find right neighbours that are actions... search for input slots in this * package's actions that refer to our file group. = */ findFileGroupNeighbourPrepStmt.setInt(1, fileGroupId); findFileGroupNeighbourPrepStmt.setInt(2, ISlotTypes.SLOT_POS_INPUT); ResultSet rs = db.executePrepSelectResultSet(findFileGroupNeighbourPrepStmt); while (rs.next()) { MemberDesc member = new MemberDesc(IPackageMemberMgr.TYPE_ACTION, Integer.valueOf(rs.getString(1)), rs.getInt(2), rs.getInt(3)); neighbours.add(member); } rs.close(); /* * To find a right neighbour that's a merge group... look for * all occurrences of our file group ID in all merge groups. */ findMergeFileGroupsContainingFileGroup.setInt(1, fileGroupId); rs = db.executePrepSelectResultSet(findMergeFileGroupsContainingFileGroup); while (rs.next()) { MemberDesc member = new MemberDesc(IPackageMemberMgr.TYPE_FILE_GROUP, rs.getInt(1), rs.getInt(2), rs.getInt(3)); neighbours.add(member); } rs.close(); /* * When our right neighbour is a filter group... if we're a non-filter group, * search for any filter file groups that this file group is a predecessor of. * However, if showFilters is false, then we must instead return the right * neighbour of the filter, rather than the filter itself. */ if (fileGroupType != IFileGroupMgr.FILTER_GROUP) { /* get a list of all the filters */ List<MemberDesc> filters = new ArrayList<MemberDesc>(); findFiltersContainingFileGroup.setInt(1, fileGroupId); rs = db.executePrepSelectResultSet(findFiltersContainingFileGroup); while (rs.next()) { MemberDesc member = new MemberDesc(IPackageMemberMgr.TYPE_FILE_GROUP, rs.getInt(1), -1, -1); /* filter groups are never positioned */ filters.add(member); } rs.close(); /* if showFilters is true, we do want to see the filters returned */ if (showFilters) { neighbours.addAll(filters); } /* if we don't want to showFilters, return the right neighbours of each filter */ else { for (Iterator<MemberDesc> iterator = filters.iterator(); iterator.hasNext();) { MemberDesc memberDesc = (MemberDesc) iterator.next(); MemberDesc[] filterNeighbours = getNeighboursOf( IPackageMemberMgr.TYPE_FILE_GROUP, memberDesc.memberId, IPackageMemberMgr.NEIGHBOUR_RIGHT, false); neighbours.addAll(Arrays.asList(filterNeighbours)); } } } } catch (SQLException e) { BuildStoreDB.throwSqlException(e); } } /*-------------------------------------------------------------------------------------*/ /** * Helper method for returning the left-neighbour of a filter file group. * @param fileGroupId ID of the filter file group. * @return The corresponding MemberDesc of the predecessor, or null on error. */ private MemberDesc getPredecessorOfFilter(int fileGroupId) { int predId = fileGroupMgr.getPredId(fileGroupId); if (predId >= 0) { MemberLocation location = getMemberLocation( IPackageMemberMgr.TYPE_FILE_GROUP, predId); if (location != null) { MemberDesc member = new MemberDesc(IPackageMemberMgr.TYPE_FILE_GROUP, predId, location.x, location.y); return member; } } return null; } /*-------------------------------------------------------------------------------------*/ }