/** * Given an actionId, move the action, and possibly all its predecessors to the new package. If * necessary, an output file group is created and also moved to the new package. So too are all * the files in that groups. * * @param actionId ID of the action to be moved. * @return ID of the output file group, or ErrorCode.NOT_FOUND if there's no output group. * @throws CanNotRefactorException If something goes wrong. */ private int moveActionToPackage(int actionId) throws CanNotRefactorException { /* first, check to see if this action is already imported this package - if so, we're done */ Object existingMap = actionCache.get(Integer.valueOf(actionId)); if (existingMap instanceof Integer) { return (Integer) existingMap; } /* validate that the action is atomic */ Integer children[] = actionMgr.getChildren(actionId); if (children.length != 0) { throw new CanNotRefactorException(Cause.ACTION_NOT_ATOMIC, actionId); } /* * The action is not yet imported, and it is atomic. Figure out its current package * and prepare to move it to the new package. */ int fileGroupId = ErrorCode.NOT_FOUND; PackageDesc desc = pkgMemberMgr.getPackageOfMember(IPackageMemberMgr.TYPE_ACTION, actionId); if (desc == null) { throw new FatalError("Can't retrieve action's current package"); } /* Create UndoOp for changing the action's package */ ActionUndoOp actionOp = new ActionUndoOp(buildStore, actionId); actionOp.recordPackageChange(desc.pkgId, destPkgId); multiOp.add(actionOp); /* Create a new file group containing all the files that this action generates */ Integer writtenFiles[] = actionMgr.getFilesAccessed(actionId, OperationType.OP_WRITE); if (writtenFiles.length > 0) { fileGroupId = createSourceFileGroup(Arrays.asList(writtenFiles)); /* Connect the "output" slot from the action to this new file group */ ActionUndoOp slotOp = new ActionUndoOp(buildStore, actionId); slotOp.recordSlotChange(IActionMgr.OUTPUT_SLOT_ID, null, fileGroupId); multiOp.add(slotOp); } /* compute/generate/move all the predecessor actions or file groups */ computeInputActions(actionId); /* * Store actionId/fileGroupId in our cache, to avoid doing this import again * and ending up with multiple output file groups when only one is required. */ actionCache.put(actionId, fileGroupId); /* return the ID of the output file group */ return fileGroupId; }
/** * For the specified action, identify (or generate) the necessary input file groups, which may * involve recursively moving predecessor actions into our destination package. * * @param actionId ID of the action to be recursively dealt with. * @throws CanNotRefactorException Something went wrong. */ private void computeInputActions(int actionId) throws CanNotRefactorException { /* compute the list of input files that are read by this action */ Integer readFiles[] = actionMgr.getFilesAccessed(actionId, OperationType.OP_READ); /* From this list of input files, we need to track the loose files, the bad files and the actions */ List<Integer> looseMembers = new ArrayList<Integer>(); List<Integer> badFiles = new ArrayList<Integer>(); List<Integer> actions = new ArrayList<Integer>(); /* for each file, figure out which action generates it, or perhaps it's a loose file? */ for (int i = 0; i < readFiles.length; i++) { int fileId = readFiles[i]; /* if any actions "modify" (read and then write) this file, that's an error */ Integer[] modifyingActions = actionMgr.getActionsThatAccess(fileId, OperationType.OP_MODIFIED); if (modifyingActions.length > 0) { badFiles.add(fileId); } else { /* figure out how this file is generated (if it is at all) */ Integer generatorActions[] = actionMgr.getActionsThatAccess(fileId, OperationType.OP_WRITE); int generatorLength = generatorActions.length; /* no generating action, so it's a "loose" file */ if (generatorLength == 0) { looseMembers.add(fileId); } /* With one generator action, record that action, if it's not already recorded */ else if (generatorLength == 1) { if (!actions.contains(generatorActions[0])) { actions.add(generatorActions[0]); } } /* files with multiple generators are a problem */ else { badFiles.add(fileId); } } } /* * Were there any files that were generated by multiple actions (or modified by an action). */ if (badFiles.size() != 0) { throw new CanNotRefactorException(Cause.FILE_IS_MODIFIED, badFiles.toArray(new Integer[0])); } /* * We now have a list of actions (with no duplicates) that are known to generate the files * that we need as input to the current action (actionId). Recursively deal with those * actions, and use their output file groups as our input (possibly using a merge file group * and filters). */ List<Integer> fileGroups = new ArrayList<Integer>(); /* If there are any loose files, create a file group and populate it with those files */ if (looseMembers.size() > 0) { int looseFileGroupId = createSourceFileGroup(looseMembers); fileGroups.add(looseFileGroupId); } /* * For each generating action, deal with it recursively, then record the output file group. * By the time this loop is done, we should have all the predecessor actions/groups scheduled * to be moved to destPkgId. The fileGroups list will contain the complete list of file * groups generated by these sub actions. */ for (int subActionId : actions) { int inputFileGroupId = moveActionToPackage(subActionId); if (inputFileGroupId != ErrorCode.NOT_FOUND) { if (!fileGroups.contains(inputFileGroupId)) { fileGroups.add(inputFileGroupId); } } } /* join the upstream file group(s) to this action's input slot, if there are any groups. */ int numUpstreamFileGroups = fileGroups.size(); if (numUpstreamFileGroups >= 1) { int inputFileGroupId; /* * a single input group - possibly with a filter inserted to eliminate any additional * files that are in the group (generated by the action), but aren't required by * the action. */ if (numUpstreamFileGroups == 1) { inputFileGroupId = createFilterIfNeeded(fileGroups.get(0), readFiles); } /* create a merge group */ else { inputFileGroupId = createMergeFileGroup(fileGroups, readFiles); } ActionUndoOp slotOp = new ActionUndoOp(buildStore, actionId); slotOp.recordSlotChange(IActionMgr.INPUT_SLOT_ID, null, inputFileGroupId); multiOp.add(slotOp); } }