package edu.usc.enl.dynamicmeasurement.model;
import java.util.Comparator;
/**
* Created with IntelliJ IDEA.
* User: Masoud
* Date: 1/22/13
* Time: 11:42 AM <br/>
* The main class that represents a prefix and its size
*/
public class WildcardPattern implements Cloneable, Comparable<WildcardPattern> {
public static final WeightComparator WEIGHT_COMPARATOR = new WeightComparator();
public static final WildcardPatternWildcardNumComparator WILDCARDNUM_COMPARATOR = new WildcardPatternWildcardNumComparator();
public static final char WILDCARD_CHAR = '_';
public static final char WILDCARD_FOLDER_CHAR = '=';
public static int TOTAL_LENGTH = 32;
/**
* the prefix
*/
protected long data;
/**
* Number of wildard numbers for the prefix. Thus the actual output is data < < wildcardNum
*/
protected int wildcardNum;
/**
* keeps track of the size this prefix . Merging this into the prefix class may not be the best design but is
* convenient.
*/
private double weight;
/**
* @param input must cover all bits of TOTAL_LENGTH bits.
* Multiple formats are supported. <ul>
* <li>Numeric formation e.g., 10.0.5.0/24</li>
* <li>bit pattern e.g., 111111001010101010_____. where the character for don't care (_ here)
* can be either WILDCARD_CHAR or WILDCARD_FOLDER_CHAR</li>
* </ul>
* @param weight
*/
public WildcardPattern(String input, double weight) {
if (input.matches("((\\d+)\\.){3}\\d+(/\\d+)?")) {
int dataInt;
int slashIndex = input.indexOf("/");
if (slashIndex >= 0) {
dataInt = toIPv4Address(input.substring(0, slashIndex));
wildcardNum = TOTAL_LENGTH - Integer.parseInt(input.substring(slashIndex + 1));
if (wildcardNum == TOTAL_LENGTH) {
dataInt = 0;
} else {
dataInt >>>= wildcardNum;
}
} else {
dataInt = toIPv4Address(input);
wildcardNum = 0;
}
data = dataInt & ((1l << 32) - 1);
} else {
int notWildcardsNum = input.indexOf(WILDCARD_CHAR);
if (notWildcardsNum < 0) {
notWildcardsNum = input.indexOf(WILDCARD_FOLDER_CHAR);
}
data = 0;
if (notWildcardsNum == 0) {
data = 0;
} else if (notWildcardsNum > 0) {
data = Long.parseLong(input.substring(0, notWildcardsNum), 2);
} else {
data = Long.parseLong(input, 2);
notWildcardsNum = TOTAL_LENGTH;
}
wildcardNum = TOTAL_LENGTH - notWildcardsNum;
}
this.weight = weight;
}
public WildcardPattern(long data, int wildcardNum, double weight) {
this.data = data;
this.wildcardNum = wildcardNum;
this.weight = weight;
}
public static void main(String[] args) {
int x = -1;
long y = x;
System.out.println(y);
System.out.println(y >>> 5);
System.out.println(y & ((1l << 32) - 1));
y = ((1l << 32) - 1) - 1232;
System.out.println((int) y);
TOTAL_LENGTH = 32;
System.out.println(new WildcardPattern("0.0.0.0", 0).toCIDRString());
System.out.println(new WildcardPattern("1.0.0.0", 0).toCIDRString());
System.out.println(new WildcardPattern("0.128.0.0", 0).toCIDRString());
System.out.println(new WildcardPattern("1.0.0.0/0", 0).toCIDRString());
System.out.println(new WildcardPattern("1.0.0.0/32", 0).toCIDRString());
System.out.println(new WildcardPattern("192.168.0.1/24", 0).toCIDRString());
}
/**
* GOT FROM FLOODLIGHT!!!
* Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
* returns the corresponding 32 bit integer.
*
* @param ipAddress
* @return
*/
public static int toIPv4Address(String ipAddress) {
if (ipAddress == null)
throw new IllegalArgumentException("Specified IPv4 address must" +
"contain 4 sets of numerical digits separated by periods");
String[] octets = ipAddress.split("\\.");
if (octets.length != 4)
throw new IllegalArgumentException("Specified IPv4 address must" +
"contain 4 sets of numerical digits separated by periods");
int result = 0;
for (int i = 0; i < 4; ++i) {
int oct = Integer.valueOf(octets[i]);
if (oct > 255 || oct < 0)
throw new IllegalArgumentException("Octet values in specified" +
" IPv4 address must be 0 <= value <= 255");
result |= oct << ((3 - i) * 8);
}
return result;
}
protected static String ipToString(long ip) {
return ((ip & 0xff000000) >>> 24)
+ "." + ((ip & 0x00ff0000) >> 16) + "."
+ ((ip & 0x0000ff00) >> 8) + "."
+ (ip & 0x000000ff);
}
public static String toStringNoWeight(long data, int wildcardNum, char c) {
char[] buf = new char[TOTAL_LENGTH];
int charPos = TOTAL_LENGTH - wildcardNum;
long d = data;
while (charPos > 0) {
buf[--charPos] = (d & 1) == 0 ? '0' : '1';
d >>>= 1;
}
for (int i = 0; i < wildcardNum; i++) {
buf[TOTAL_LENGTH - i - 1] = c;
}
return new String(buf);
}
public String toCIDRString() {
long ip = data << wildcardNum;
int prefix = TOTAL_LENGTH - wildcardNum;
String str;
if (prefix >= 32) {
str = ipToString(ip);
} else {
// use the negation of mask to fake endian magic
int mask = ~((1 << (32 - prefix)) - 1);
str = ipToString(ip & mask) + "/" + prefix;
}
return str;
}
/**
* Gets the parent prefix (check with canGoUp) method first
* Note that this will change the hashcode of the prefix. Thus make sure it is removed from any hashset
*/
public WildcardPattern goUp() {
data = (data >>> 1);
wildcardNum++;
return this;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
/**
* Make shure the number of wildcards are greater than 0
*
* @return
*/
public boolean canGoDown() {
return wildcardNum > 0;
}
// make sure it is removed from any hashset
public WildcardPattern goDown(boolean oneZero) throws InvalidWildCardValue {
if (!canGoDown()) {
throw new InvalidWildCardValue("Negative wildcardNum(" + wildcardNum + ")");
}
data = data << 1;
if (oneZero) {
data++;
}
wildcardNum--;
return this;
}
public long getData() {
return data;
}
public int getWildcardNum() {
return wildcardNum;
}
/**
* If this is not the root of the tree, the output will be the other child of the parent
*
* @return
*/
public WildcardPattern getSibling() {
WildcardPattern clone = clone();
clone.goUp();
try {
clone.goDown((data & 1) == 0);
} catch (InvalidWildCardValue invalidWildCardValue) {
invalidWildCardValue.printStackTrace();
}
clone.setWeight(0);
return clone;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof WildcardPattern)) return false;
WildcardPattern that = (WildcardPattern) o;
if (data != that.data) return false;
if (wildcardNum != that.wildcardNum) return false;
return true;
}
@Override
public int hashCode() {
int result = (int) (data ^ (data >>> 32));
result = 31 * result + wildcardNum;
return result;
}
@Override
public WildcardPattern clone() {
return new WildcardPattern(data, wildcardNum, weight);
}
/**
* @param d
* @return if this prefix matches d
*/
public boolean match(long d) {
return (d >>> wildcardNum) == data;
}
/**
* If prefix "pattern" is descendant of this.
*
* @param pattern
* @return
*/
public boolean match(WildcardPattern pattern) {
if (pattern.getWildcardNum() > wildcardNum) {
return false;
}
long d = pattern.getData() >>> (wildcardNum - pattern.getWildcardNum());
return data == d;
}
@Override
//InOrder comparison
public int compareTo(WildcardPattern o) {
if (wildcardNum > o.wildcardNum) {
long d2 = o.data >>> (wildcardNum - o.wildcardNum - 1);
if (data == d2 >>> 1) {//parent child
return (d2 & 1) == 0 ? 1 : -1;
}
return Long.compare(data, (d2 >>> 1));
} else if (wildcardNum < o.wildcardNum) {
long d1 = data >>> (o.wildcardNum - wildcardNum - 1);
if (o.data == d1 >>> 1) {//parent child
return (d1 & 1) == 0 ? -1 : 1;
}
return Long.compare((d1 >>> 1), o.data);
} else {
return Long.compare(data, o.data);
}
}
public boolean isLeft() {
return (data & 1) == 0;
}
public boolean isSibling(WildcardPattern pattern) {
return wildcardNum == pattern.wildcardNum && (data >>> 1) == (pattern.data >>> 1);
}
/**
* @return If there is any bit to be wildcarded
*/
public boolean canGoUp() {
return wildcardNum < TOTAL_LENGTH;
}
@Override
public String toString() {
return toStringNoWeight() + ": " + weight;
}
/**
* @param p
* @return the common pattern (common ancestor in the prefix tree)
*/
public WildcardPattern getCommonParent(WildcardPattern p) {
WildcardPattern pMax = wildcardNum > p.wildcardNum ? this : p;
WildcardPattern pMin = wildcardNum > p.wildcardNum ? p : this;
//make same level
long dMin = pMin.data >>> (pMax.wildcardNum - pMin.wildcardNum);
long dMax = pMax.data;
int w = pMax.wildcardNum;
while (w <= TOTAL_LENGTH) {
if (dMin == dMax) {
return new WildcardPattern(dMax, w, 0);
}
w++;
dMin >>>= 1;
dMax >>>= 1;
}
return new WildcardPattern(0, TOTAL_LENGTH, 0);
}
public WildcardPattern getCommonParent2(WildcardPattern p) { //BUG FOR 0 AND 010
long shiftedData = data << wildcardNum;
long uncommonDataMask = shiftedData ^ (p.data << p.wildcardNum);
if (uncommonDataMask == 0) {
//one is parent
WildcardPattern parent = (wildcardNum > p.wildcardNum ? this : p).clone();
parent.setWeight(0);
return parent;
} else {
long l = (Long.highestOneBit(uncommonDataMask) << 1) - 1;
int parentWildcardNum = Math.max(Math.max(Long.bitCount(l), wildcardNum), p.wildcardNum);
long parentData = (shiftedData & (~l)) >>> parentWildcardNum;
WildcardPattern wildcardPattern = new WildcardPattern(parentData, parentWildcardNum, 0);
// if (!wildcardPattern.equals(getCommonParent2(p))){
// System.out.println("Error");
// System.exit(1);
// }
return wildcardPattern;
}
}
public String toStringFolderName() {
return toStringNoWeight(data, wildcardNum, WILDCARD_FOLDER_CHAR);
}
public String toStringNoWeight() {
return toStringNoWeight(data, wildcardNum, WILDCARD_CHAR);
}
public boolean isChild(WildcardPattern child) {
return child.getWildcardNum() == wildcardNum - 1 && (child.getData() >>> 1) == data;
}
public String getLabel() {
return (isLeft() ? "0" : "1");
}
public static class WeightComparator implements Comparator<WildcardPattern> {
@Override
public int compare(WildcardPattern o1, WildcardPattern o2) {
int c = Double.compare(o1.weight, o2.weight);
if (c == 0) {
// System.out.println(o1+" vs "+o2+"="+-o1.compareTo(o2));
return -o1.compareTo(o2);
}
// System.out.println(o1+" vs "+o2+"="+-c);
return -c;
}
}
public static class WildcardPatternWildcardNumComparator implements Comparator<WildcardPattern> {
@Override
public int compare(WildcardPattern o1, WildcardPattern o2) { //sort to not process a children after its parent
int c = (o1.getWildcardNum() - o2.getWildcardNum());
if (c == 0) {
return o1.compareTo(o2);
}
return c;
}
}
public class InvalidWildCardValue extends Exception {
public InvalidWildCardValue(String message) {
super(message);
}
}
}