/** * Insert the specified file groups into a newly created merge file group. * * @param subFileGroups The sub file groups to be added. * @param filesNeeded An array of files that are actually needed by the action that uses them. * @return The ID of the newly-created merge file group. */ private int createMergeFileGroup(List<Integer> subFileGroups, Integer[] filesNeeded) { /* * Create the new fileGroup. Even though we won't populate it until the multiOp * is executed, we must still allocate a new fileGroup ID number. */ int fileGroupId = fileGroupMgr.newMergeGroup(destPkgId); if (fileGroupId < 0) { throw new FatalError("Unable to create new merge file group"); } /* * Determine whether any filters are required (since we don't necessarily need all * the files that appear in the subFileGroups. */ List<Integer> filteredSubFileGroups = new ArrayList<Integer>(); for (int subFileGroup : subFileGroups) { filteredSubFileGroups.add(createFilterIfNeeded(subFileGroup, filesNeeded)); } FileGroupUndoOp op = new FileGroupUndoOp(buildStore, fileGroupId); op.recordMembershipChange(new ArrayList<Integer>(), filteredSubFileGroups); multiOp.add(op); return fileGroupId; }
/** * @param inputFileGroupId The group ID we're planning to attach to an action's input. * @param filesNeeded The files that are actually required by the action. * @return If all files in the fileGroup are required, return inputFileGroupId, else return a * filterID (which depends on inputFileGroupId) that restricts the input to only those files * that are required. */ private int createFilterIfNeeded(int inputFileGroupId, Integer[] filesNeeded) { /* * Look through all of the files in the file group, and learn which of them are needed. * This is an O(n*n), but for relatively small file groups (not 1000s of items), this is OK. */ List<Integer> fileGroupMembers = fileGroupCache.get(inputFileGroupId); if (fileGroupMembers == null) { throw new FatalError("Can't find members for fileGroup with ID: " + inputFileGroupId); } List<Integer> neededFileGroupMembers = new ArrayList<Integer>(); for (int pathId : fileGroupMembers) { for (int i = 0; i < filesNeeded.length; i++) { if (pathId == filesNeeded[i]) { neededFileGroupMembers.add(pathId); break; } } } /* * If the set of needed members is smaller than the set of total members, we must add * a filter so that only the required files are kept. */ if (neededFileGroupMembers.size() < fileGroupMembers.size()) { int filterGroupId = fileGroupMgr.newFilterGroup(destPkgId, inputFileGroupId); if (filterGroupId < 0) { throw new FatalError("Unable to create new filter file group"); } /* * Populate the filter with patterns that match the needed paths. Note that * the file is not (yet) in the destination package, so we just pretend its * there. */ List<String> neededPatterns = new ArrayList<String>(); for (int pathId : neededFileGroupMembers) { String pathString = fileMgr.getPathName(pathId, destPkgId); if (pathString == null) { throw new FatalError("Invalid pathId in file group"); } neededPatterns.add("ia:" + pathString); } /* schedule the population of the filter group with patterns */ FileGroupUndoOp op = new FileGroupUndoOp(buildStore, filterGroupId); op.recordMembershipChange(new ArrayList<String>(), neededPatterns); multiOp.add(op); return filterGroupId; } /* else, no filter required - return the original file group */ else { return inputFileGroupId; } }
/** * Given a list of file IDs, create a new source file group and schedule the members to be added * to it (by appending to the multiOp). In addition to moving the file group into the destination * package, all the individual files are also moved. If any files are not within the source root, * throw a {@link CanNotRefactorException} with cause code of PATH_OUT_OF_RANGE. * * @param members A list of file IDs to be added to the file group. * @return The ID of the newly-created file group. * @throws CanNotRefactorException Something went wrong during the refactoring. */ private int createSourceFileGroup(List<Integer> members) throws CanNotRefactorException { /* * Create the new fileGroup. Even though we won't populate it until the multiOp * is executed, we must still allocate a new fileGroup ID number. */ int fileGroupId = fileGroupMgr.newSourceGroup(destPkgId); if (fileGroupId < 0) { throw new FatalError("Unable to create new file group"); } FileGroupUndoOp op = new FileGroupUndoOp(buildStore, fileGroupId); op.recordMembershipChange(new ArrayList<Integer>(), members); multiOp.add(op); /* * Move all the files into the destination package, using FileUndoOps. Before * a file can be moved, we must ensure that it's within the source root of the package. */ List<Integer> filesOutOfRange = new ArrayList<Integer>(); /* * For each loose file, validate if it's within the package roots, and if so, * schedule an UndoOp to make the necessary change. If not, throw an exception. */ for (int pathId : members) { PackageDesc oldDesc = pkgMemberMgr.getPackageOfMember(IPackageMemberMgr.TYPE_FILE, pathId); if (oldDesc == null) { throw new FatalError("Can't find pathId"); } /* * Check that this path is within the source root. If so, schedule it to be move to * the destination package. */ if (fileMgr.isAncestorOf(pkgRootId, pathId)) { FileUndoOp pkgChangeOp = new FileUndoOp(buildStore, pathId); pkgChangeOp.recordChangePackage( oldDesc.pkgId, oldDesc.pkgScopeId, destPkgId, IPackageMemberMgr.SCOPE_PRIVATE); multiOp.add(pkgChangeOp); } /* * Else, record this pathID as being out of range. We'll report an exception * once we've collected the complete list of invalid paths. */ else { filesOutOfRange.add(pathId); } } /* * If any files were out of range, throw an exception. */ if (filesOutOfRange.size() > 0) { throw new CanNotRefactorException( Cause.PATH_OUT_OF_RANGE, filesOutOfRange.toArray(new Integer[0])); } /* update the cache, with the members that the file group will contain (once the multiOp is executed) */ fileGroupCache.put(fileGroupId, members); return fileGroupId; }
/** * Validate the list of members that is input into moveMembersToPackage(). There are many possible * errors, including invalid member types, or undefined action, file, or fileGroup ID numbers. * Errors are report via exceptions. * * @param members The list of MemberDesc to be validated. * @throws CanNotRefactorException The reason why the members list is invalid. */ private void validateMembersList(List<MemberDesc> members) throws CanNotRefactorException { IFileMgr fileMgr = buildStore.getFileMgr(); IFileGroupMgr fileGroupMgr = buildStore.getFileGroupMgr(); IActionMgr actionMgr = buildStore.getActionMgr(); /* can't be null! */ if (members == null) { throw new CanNotRefactorException(Cause.INVALID_MEMBER, -1); } /* keep a lists of invalid "things" - we can provide the whole list in the exception */ List<Integer> invalidFiles = new ArrayList<Integer>(); List<Integer> invalidFileGroups = new ArrayList<Integer>(); List<Integer> invalidActions = new ArrayList<Integer>(); /* * Scan the list of members, validating their type, and whether each ID is valid. * Invalid entries are logged, and will be reported as exceptions once we've seen * all the members. */ for (MemberDesc member : members) { int id = member.memberId; switch (member.memberType) { case IPackageMemberMgr.TYPE_FILE: PathType pathType = fileMgr.getPathType(id); if (pathType == PathType.TYPE_INVALID) { invalidFiles.add(id); } break; case IPackageMemberMgr.TYPE_FILE_GROUP: if (fileGroupMgr.getGroupType(id) == ErrorCode.NOT_FOUND) { invalidFileGroups.add(id); } break; case IPackageMemberMgr.TYPE_ACTION: if (!actionMgr.isActionValid(id)) { invalidActions.add(id); } break; default: throw new CanNotRefactorException(Cause.INVALID_MEMBER, member.memberType); } } /* Thrown exceptions if we found anything invalid */ if (!invalidFiles.isEmpty()) { throw new CanNotRefactorException(Cause.INVALID_PATH, invalidFiles.toArray(new Integer[0])); } if (!invalidActions.isEmpty()) { throw new CanNotRefactorException( Cause.INVALID_ACTION, invalidActions.toArray(new Integer[0])); } if (!invalidFileGroups.isEmpty()) { throw new CanNotRefactorException( Cause.INVALID_FILE_GROUP, invalidFileGroups.toArray(new Integer[0])); } }