/**
* Copyright 2014 SAP AG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.aim.description.restrictions;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.lpe.common.util.LpeStringUtils;
/**
* This class represents a restriction to a given scope.
*
* @author Henning Schulz
*
*/
public class Restriction {
private static final int HASH_PRIME = 31;
private final Set<String> packageIncludes;
private final Set<String> packageExcludes;
private final Set<Integer> modifierIncludes;
private final Set<Integer> modifierExcludes;
private double granularity = 1.0;
/**
* Constructor. Sets all sets to empty ones.
*/
@JsonCreator
public Restriction() {
this.packageIncludes = new HashSet<>();
this.packageExcludes = new HashSet<>();
this.modifierExcludes = new HashSet<>();
this.modifierIncludes = new HashSet<>();
}
/**
* Includes the given package.
*
* @param packageName
* package to be included
*/
public void addPackageInclude(String packageName) {
packageIncludes.add(packageName);
}
/**
* @return the packageIncludes
*/
public Set<String> getPackageIncludes() {
return packageIncludes;
}
/**
* Excludes the given package.
*
* @param packageName
* package to be excluded
*/
public void addPackageExclude(String packageName) {
packageExcludes.add(packageName);
}
/**
* @return the packageExcludes
*/
public Set<String> getPackageExcludes() {
return packageExcludes;
}
/**
* Includes all methods having the given modifier.
*
* @param modifier
* modifier of the methods to be included
*/
public void addModifierInclude(int modifier) {
modifierIncludes.add(modifier);
}
/**
* @return the modifierIncludes
*/
public Set<Integer> getModifierIncludes() {
return modifierIncludes;
}
/**
* Excludes all methods having the given modifier.
*
* @param modifier
* modifier of the methods to be excluded
*/
public void addModifierExclude(int modifier) {
modifierExcludes.add(modifier);
}
/**
* @return the modifierExcludes
*/
public Set<Integer> getModifierExcludes() {
return modifierExcludes;
}
/**
* Sets the granularity. Note is has to be between 0 to 1.
*
* @param granularity
* granularity to be set
*/
public void setGranularity(double granularity) {
if (granularity > 1 || granularity < 0) {
throw new IllegalArgumentException("The granularity has to be between 0 to 1!");
}
this.granularity = granularity;
}
/**
* @return the granularity
*/
public double getGranularity() {
return granularity;
}
/**
* Checks whether given entity is excluded from instrumentation.
*
* @param entityName
* full qualified name of the entity (class, package, interface,
* etc. ) to check
* @return true, if entity shell be excluded from instrumentation
*/
@JsonIgnore
public boolean isExcluded(String entityName) {
if (getPackageIncludes().isEmpty()) {
for (String excl : getPackageExcludes()) {
if (LpeStringUtils.patternMatches(entityName, excl)) {
return true;
}
}
return false;
} else {
boolean found = false;
for (String incl : getPackageIncludes()) {
if (LpeStringUtils.patternMatches(entityName, incl)) {
found = true;
break;
}
}
if (!found) {
return true;
}
for (String excl : getPackageExcludes()) {
if (LpeStringUtils.patternMatches(entityName, excl)) {
return true;
}
}
return false;
}
}
/**
* Checks whether restriction is empty.
*
* @return true if no restrictions have been defined
*/
@JsonIgnore
public boolean isEmpty() {
return packageExcludes.isEmpty() && packageIncludes.isEmpty() && modifierExcludes.isEmpty()
&& modifierIncludes.isEmpty();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
boolean trailingComma = false;
for (String include : packageIncludes) {
builder.append("+");
builder.append(include);
builder.append(", ");
trailingComma = true;
}
for (int mod : modifierIncludes) {
builder.append("+\"");
builder.append(Modifier.toString(mod));
builder.append("\" methods, ");
trailingComma = true;
}
for (String exclude : packageExcludes) {
builder.append("-");
builder.append(exclude);
builder.append(", ");
trailingComma = true;
}
for (int mod : modifierExcludes) {
builder.append("-\"");
builder.append(Modifier.toString(mod));
builder.append("\" methods, ");
trailingComma = true;
}
if (trailingComma) {
builder.deleteCharAt(builder.length() - 1);
builder.deleteCharAt(builder.length() - 1);
}
return builder.toString();
}
/**
* Returns all included modifiers as one integer (the modifiers linked with
* OR).
*
* @return all included modifiers
*/
@JsonIgnore
public int getModifiersOrLinked() {
int thisModifiers = 0;
for (int includedModifier : getModifierIncludes()) {
thisModifiers |= includedModifier;
}
for (int excludedModifier : getModifierExcludes()) {
thisModifiers &= ~excludedModifier;
}
return thisModifiers;
}
/**
* Checks whether the given set of modifiers is excluded. THis method checks
* whether the passed set of modifiers covers all modifiers included in the
* restrictions.
*
* @param modifiers
* binary representation of a set of modifiers
* @return true, if given set is excluded by the restrictions
*/
public boolean modifierSetExcluded(int modifiers) {
if (!hasModifierRestrictions()) {
return false;
}
int thisModifiers = getModifiersOrLinked();
return (thisModifiers & modifiers) != thisModifiers;
}
/**
* Checks whether this restriction object has modifier restrictions.
*
* @return true, if any modifier restrictions have been defined
*/
public boolean hasModifierRestrictions() {
return !getModifierIncludes().isEmpty() || !getModifierExcludes().isEmpty();
}
/**
* Returns the merge of this restriction and the specified one. The
* granularity is not overwritten.
*
* @param other
* Restriction to be merged with
* @return merge of this restriction and {@code other}
*/
public Restriction mergeWith(Restriction other) {
Restriction result = new Restriction();
result.getModifierExcludes().addAll(this.getModifierExcludes());
result.getModifierExcludes().addAll(other.getModifierExcludes());
result.getModifierIncludes().addAll(this.getModifierIncludes());
result.getModifierIncludes().addAll(other.getModifierIncludes());
result.getPackageExcludes().addAll(this.getPackageExcludes());
result.getPackageExcludes().addAll(other.getPackageExcludes());
result.getPackageIncludes().addAll(this.getPackageIncludes());
result.getPackageIncludes().addAll(other.getPackageIncludes());
result.setGranularity(this.getGranularity());
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!obj.getClass().equals(this.getClass())) {
return false;
}
Restriction other = (Restriction) obj;
return this.getPackageExcludes().equals(other.getPackageExcludes())
&& this.getPackageIncludes().equals(other.getPackageIncludes())
&& this.getModifiersOrLinked() == other.getModifiersOrLinked();
}
@Override
public int hashCode() {
int hash = 1;
hash = hash * HASH_PRIME + getModifiersOrLinked();
hash = hash * HASH_PRIME + getPackageExcludes().hashCode();
hash = hash * HASH_PRIME + getPackageIncludes().hashCode();
return hash;
}
}