/*******************************************************************************
* 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.types;
import com.buildml.model.IBuildStore;
import com.buildml.model.IFileMgr;
import com.buildml.model.IPackageMemberMgr;
import com.buildml.model.IPackageMgr;
import com.buildml.model.IReportMgr;
import com.buildml.utils.errors.ErrorCode;
import com.buildml.utils.types.IntegerTreeSet;
/**
* Implements an unordered set of file IDs. This is used in numerous places
* where a set of files must be passed as a single data value.
*
* The FileSet data type is different from a regular set, since the parent/child
* relationship between entries is maintained.
*
* @author "Peter Smith <psmith@arapiki.com>"
*/
public class FileSet extends IntegerTreeSet {
/*=====================================================================================*
* TYPES/FIELDS
*=====================================================================================*/
/**
* The FileMgr manager object containing the files referenced in this FileSet.
*/
private IFileMgr fileMgr;
/*=====================================================================================*
* CONSTRUCTORS
*=====================================================================================*/
/**
* Creates a new FileSet and initializes it to empty.
*
* @param fileMgr The FileMgr manager object that owns the files in the FileSet.
*/
public FileSet(IFileMgr fileMgr) {
/* most of the functionality is provided by the IntegerTreeSet class */
super();
/* except we also need to record our FileMgr object */
this.fileMgr = fileMgr;
}
/*-------------------------------------------------------------------------------------*/
/**
* Creates a new FileSet and initializes it from an array of path ID values.
*
* @param fileMgr The FileMgr manager object that owns the files in the FileSet.
* @param paths The IDs of the paths to be added to the FileSet.
*/
public FileSet(IFileMgr fileMgr, Integer paths[]) {
super(paths);
this.fileMgr = fileMgr;
}
/*=====================================================================================*
* PUBLIC METHODS
*=====================================================================================*/
/**
* Given zero or more text-based path name specifications, populate the FileSet with
* the relevant files.
*
* @param pathArgs Each pathArg String can be one of the following:
* <ol>
* <li>An absolute path name (starting with /), either a directory name or a file name. If the
* path is a directory, all files and directories below that point in the tree are added.</li>
* <li>A path name starting with a \@root - the same rules apply as for #1.</li>
* <li>A single file name, with one or more wildcard (*) characters. All files that match
* the name are added, no matter what their directory.</li>
* <li>A package spec, starting with %pkg, or the complement of a package, starting
* with %not-pkg.</li>
* </ol>
* @return ErrorCode.OK on success, or ErrorCode.BAD_PATH if an invalid path name was
* provided.
*/
public int populateWithPaths(String [] pathArgs) {
IBuildStore buildStore = fileMgr.getBuildStore();
IPackageMgr pkgMgr = buildStore.getPackageMgr();
IPackageMemberMgr pkgMemberMgr = buildStore.getPackageMemberMgr();
IReportMgr reportMgr = buildStore.getReportMgr();
/* for each path provided as input... */
for (int i = 0; i < pathArgs.length; i++) {
String thisPath = pathArgs[i];
/*
* First, check for "commands" that have the syntax: "%<name>/".
*/
if (thisPath.startsWith("%")){
/*
* Figure out what the "name" is. It must be terminated by a '/',
* which is then followed by the command's argument(s).
*/
int slashIndex = thisPath.indexOf('/');
if (slashIndex == -1) { /* there must be a / */
return ErrorCode.BAD_PATH;
}
String commandName = thisPath.substring(1, slashIndex);
String commandArgs = thisPath.substring(slashIndex + 1);
if (commandName.equals("p") || commandName.equals("pkg")){
FileSet results = pkgMemberMgr.getFilesInPackage(commandArgs);
if (results == null) {
return ErrorCode.BAD_PATH;
}
mergeSet(results);
}
else if (commandName.equals("np") || (commandName.equals("not-pkg"))){
FileSet results = pkgMemberMgr.getFilesOutsidePackage(commandArgs);
if (results == null) {
return ErrorCode.BAD_PATH;
}
mergeSet(results);
}
/* else, it's a bad command name */
else {
return ErrorCode.BAD_PATH;
}
}
/*
* Next check case where a single file name (possibly with wildcards) is used.
* This implies there are no '/' or '@' characters in the path.
*/
else if ((thisPath.indexOf('/') == -1) && (thisPath.indexOf('@') == -1)){
/* run a report to get all files that match this regexp */
FileSet results = reportMgr.reportFilesThatMatchName(thisPath);
/*
* Merge these results into this FileSet (we update the same file set
* for each user-supplied path).
*/
mergeSet(results);
}
/* else add files/directories by name. Look up the path and add its children recursively */
else {
int pathId = fileMgr.getPath(thisPath);
if (pathId == ErrorCode.BAD_PATH) {
return ErrorCode.BAD_PATH;
}
/* add this path to the FileSet, and all its children too */
populateWithPathsHelper(pathId);
}
}
return ErrorCode.OK;
}
/*-------------------------------------------------------------------------------------*/
/**
* Merge the content of a second FileSet into this FileSet.
*
* @param second The second FileSet.
*/
public void mergeSet(FileSet second) {
/* ensure the FileMgrs are the same for both FileSets */
if (fileMgr != second.fileMgr) {
return;
}
super.mergeSet(second);
}
/*-------------------------------------------------------------------------------------*/
/**
* Given a path ID, return the ID of that path's parent.
*
* @param id The ID of the path whose parent we wish to determine.
* @return The ID of the path's parent.
*/
@Override
public int getParent(int id) {
return fileMgr.getParentPath(id);
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.utils.types.IntegerTreeSet#isValid(int)
*/
@Override
public boolean isValid(int id) {
return !fileMgr.isPathTrashed(id);
}
/*-------------------------------------------------------------------------------------*/
/**
* Given a path ID, return the array of children of the path.
* @param id The ID of the path whose children we wish to determine.
*/
@Override
public Integer[] getChildren(int id) {
return fileMgr.getChildPaths(id);
}
/*=====================================================================================*
* PRIVATE METHODS
*=====================================================================================*/
/**
* Helper function for populateWithPaths. Recursively add a path and its children to
* this FileSet.
*
* @param pathId The ID of the path to be added.
*/
private void populateWithPathsHelper(int pathId) {
add(pathId);
/* now add all the children of this path */
Integer children [] = fileMgr.getChildPaths(pathId);
for (int i = 0; i < children.length; i++) {
populateWithPathsHelper(children[i]);
}
}
/*-------------------------------------------------------------------------------------*/
/**
* @return the maximum number of files that can be represented in this FileSet
* (numbered 0 to getMaxIdNumber() - 1).
*/
protected int getMaxIdNumber()
{
return IFileMgr.MAX_FILES;
}
/*-------------------------------------------------------------------------------------*/
}