/*******************************************************************************
* Copyright (c) 2012 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 java.util.HashMap;
import com.buildml.model.IBuildStore;
import com.buildml.model.IPackageMgr;
/**
* Represents a set of packages. Each package (and it's various scopes) can
* be either be a member of this set, or not a member. Objects of this class are
* typically used for recording which packages should be displayed/filtered.
*
* @author "Peter Smith <psmith@arapiki.com>"
*/
public class PackageSet implements Cloneable {
/*=====================================================================================*
* FIELDS/TYPES
*=====================================================================================*/
/**
* The BuildStore this PackageSet belongs to.
*/
private IBuildStore buildStore;
/**
* The Packages object containing the packages/scopes that this set can have as
* members.
*/
private IPackageMgr pkgMgr = null;
/**
* The default membership state for packages/scopes that have not explicitly been
* added to, or removed from this set.
*/
private boolean defaultState = false;
/**
* The map that records the membership state. The "key" is the package's ID, and the
* "value" is a bitmap of scopes that are set (that is, bit 0 = None, bit 1 = Private, etc).
* If a package is not explicitly stored in this map, it's membership status defaults
* to "defaultState".
*/
private HashMap<Integer, Integer> membershipMap;
/*=====================================================================================*
* PUBLIC METHODS
*=====================================================================================*/
/**
* Create a new PackageSet object. This records a subset of all possible packages
* from the specified buildStore. Objects of this type are typically used for selecting
* or filtering a list of packages.
*
* @param buildStore The BuildStore that contains the packages.
*/
public PackageSet(IBuildStore buildStore) {
this.buildStore = buildStore;
pkgMgr = buildStore.getPackageMgr();
/* Create a mapping of package IDs to a scope-ID bitmap */
membershipMap = new HashMap<Integer, Integer>();
}
/*=====================================================================================*
* PUBLIC METHODS
*=====================================================================================*/
/**
* @return The BuildStore associated with this PackageSet.
*/
public IBuildStore getBuildStore() {
return buildStore;
}
/*-------------------------------------------------------------------------------------*/
/**
* When a package has not explicitly been added or removed from this set then
* a default state (true or false) is used to determine whether the member should
* be considered as a member. This is useful for when a set should contain "everything
* except for these packages".
*
* @param defaultState The default state of packages/scopes that are not explicitly
* added or removed.
*/
public void setDefault(boolean defaultState) {
this.defaultState = defaultState;
}
/*-------------------------------------------------------------------------------------*/
/**
* Add the specified package into the set. Given that a scope ID isn't specified,
* all scopes will be added.
*
* @param pkgId The ID of the package to be added.
*/
public void add(int pkgId) {
/*
* By adding -1 as the key, we're adding all scopes to the bitmap. This "put"
* may overwrite an existing value for this key.
*/
membershipMap.put(Integer.valueOf(pkgId), Integer.valueOf(-1));
}
/*-------------------------------------------------------------------------------------*/
/**
* Add the specified package/scope into this set.
* @param pkgId The package's ID.
* @param scopeId The scope's ID.
*/
public void add(int pkgId, int scopeId) {
Integer pkgIdInteger = Integer.valueOf(pkgId);
int existingBitMap = 0;
/* fetch the existing bitmap value (if it exists) */
Object value = membershipMap.get(pkgIdInteger);
if (value != null) {
existingBitMap = (Integer)value;
}
/* merge in the new scopeId bit */
existingBitMap |= (1 << scopeId);
/* write it back to the map */
membershipMap.put(pkgIdInteger, Integer.valueOf(existingBitMap));
}
/*-------------------------------------------------------------------------------------*/
/**
* Remove the specified package from the set.
* @param pkgId The package's ID.
*/
public void remove(int pkgId) {
/*
* Set the whole entry to be 0, which removes all scopes. Note that this isn't
* the same as removing the map entry, since removing a package from a set
* that has setDefault(true) will cause the package to explicitly not be in
* the set, as opposed to being in the set by default.
*/
membershipMap.put(Integer.valueOf(pkgId), Integer.valueOf(0));
}
/*-------------------------------------------------------------------------------------*/
/**
* Remove the specified package/scope from this set.
* @param pkgId The package's ID.
* @param scopeId The scope's ID.
*/
public void remove(int pkgId, int scopeId) {
Integer pkgIdInteger = Integer.valueOf(pkgId);
int existingBitMap = 0;
/* fetch the existing bitmap value (if it exists) */
Object value = membershipMap.get(pkgIdInteger);
if (value != null) {
existingBitMap = (Integer)value;
}
/*
* if it doesn't exist, figure out its default state. defaultState of true
* means that all scopes are set (-1 in the bitmap).
*/
else {
existingBitMap = defaultState ? -1 : 0;
}
/* merge in the new scopeId bit */
existingBitMap &= ~(1 << scopeId);
/* write it back to the map */
membershipMap.put(pkgIdInteger, Integer.valueOf(existingBitMap));
}
/*-------------------------------------------------------------------------------------*/
/**
* Determine whether the specified package is a member of this set. Given that the
* scope ID is not specified, a package is a member if any of its scopes are members.
* @param pkgId The package's ID.
* @return true if the package is a member, else false.
*/
public boolean isMember(int pkgId) {
Object value = membershipMap.get(Integer.valueOf(pkgId));
if (value == null) {
/* package not register, return default setting */
return defaultState;
}
/*
* For the 1-parameter isMember function, we simply look at whether there are
* any scopes registered for this package.
*/
int scopeMap = (Integer)value;
return (scopeMap != 0);
}
/*-------------------------------------------------------------------------------------*/
/**
* Determine whether the specified package/scope combination is a member of this set.
* @param pkgId The package's ID.
* @param scopeId The scope's ID.
* @return true if the package/scope is a member, else false.
*/
public boolean isMember(int pkgId, int scopeId) {
Object value = membershipMap.get(Integer.valueOf(pkgId));
if (value == null) {
/* package not register, return default setting */
return defaultState;
}
/* look within the bit-map to see if the scope's bit is set */
int scopeMap = (Integer)value;
return ((scopeMap & (1 << scopeId)) != 0);
}
/*-------------------------------------------------------------------------------------*/
/**
* Implement the standard Object clone() method for PackageSet, but perform a deep
* copy, rather than a shallow copy.
*/
@SuppressWarnings("unchecked")
public Object clone() throws CloneNotSupportedException {
PackageSet newCs = (PackageSet)super.clone();
newCs.buildStore = this.buildStore;
newCs.defaultState = this.defaultState;
newCs.membershipMap = (HashMap<Integer, Integer>)this.membershipMap.clone();
return newCs;
}
/*-------------------------------------------------------------------------------------*/
}