/**
* Copyright (c) 2011-2014, OpenIoT
*
* This file is part of OpenIoT.
*
* OpenIoT is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* OpenIoT is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenIoT. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: OpenIoT mailto: info@openiot.eu
*/
package org.openiot.cupus.artefact;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.openiot.cupus.common.Triplet;
import org.openiot.cupus.common.enums.Operator;
/**
* This class is a conjuction-of-predicates implementation of a boolean
* subscription. A predicate is represented by a Triplet object, an they are
* grouped by the attribute over which they are defined in a map. Each map entry
* is a list of predicated related to that attribute.
*
* @author Eugen
*/
public class TripletAnnouncement extends Announcement implements Serializable {
private static final long serialVersionUID = 1L;
private HashMap<String, Set<Triplet>> predicateMap = new HashMap<String, Set<Triplet>>();
/**
* This map is for internal use in matching only. It should be null outside
* of the broker/matcher to minimize the serialised size of the subscription
* for sending through networks. The keys are the identifiers(names) of
* string attributes that have predicates defined over them in this
* subscription, and the value is a triplet that holds the information about
* the lowest and highest index (BETWEEN) of the allowed values for the
* attribute that match the predicates over the attribute in this
* subscription. The information is used in the CdirBucket class to check
* for enclosement in an efficient way and it should be set by the
* Attributes class on initial check when the subscription enters the
* broker.
*/
public HashMap<String, Triplet> stringAttributeBorders = null;
public TripletAnnouncement(long validity, long startTime) {
super(validity, startTime);
}
public void addNumericalPredicate(String name) {
addPredicate(new Triplet(name, Double.NEGATIVE_INFINITY, Operator.GREATER_OR_EQUAL));
}
public void addNumericalPdredicate(String name, double minValue,
double maxValue) {
addPredicate(new Triplet(name, new Double[]{minValue, maxValue},
Operator.BETWEEN));
}
public void addNumericalPdredicate(String name, double value,
Operator operator) {
addPredicate(new Triplet(name, value, operator));
}
public void addTextualPdredicate(String name) {
addPredicate(new Triplet(name, "", Operator.CONTAINS_STRING));
}
public void addTextualPdredicate(String name, String value,
Operator operator) {
addPredicate(new Triplet(name, value, operator));
}
/**
* Adds a predicate (constraint) to this subscription.
*
* @param triplet (attribute(key), value, operator)
*/
private void addPredicate(Triplet triplet) {
Set<Triplet> set = predicateMap.get(triplet.getKey());
if (set != null) {
set.add(triplet);
} else {
set = new HashSet<Triplet>();
set.add(triplet);
predicateMap.put(triplet.getKey(), set);
}
}
public Set<String> attributes() {
return predicateMap.keySet();
}
public Set<Triplet> attributePredicates(String attribute) {
return predicateMap.get(attribute);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof TripletAnnouncement) {
TripletAnnouncement announcement = (TripletAnnouncement) other;
if (super.equals(announcement)) {
return true;
}
if (this.validity == announcement.validity
&& this.startTime == announcement.startTime) {
return predicateMap.equals(announcement.predicateMap);
} else {
return false;
}
} else {
return false;
}
}
@Override
public int hashCode() {
int hash = 5;
hash = 71 * hash + (predicateMap != null ? predicateMap.hashCode() : 0);
return hash;
}
/**
* Method for checking whether subscription covers other subscription.
*
* @param sub subscription to confront
*
*/
@Override
public boolean coversSubscription(Subscription sub) {
if (sub instanceof TripletSubscription) {
TripletSubscription tripSub = (TripletSubscription) sub;
for (String subAttribute : tripSub.attributes()) {
if (this.predicateMap.get(subAttribute) == null) {
return false;
} else {
Set<Triplet> subTriplets = tripSub.attributePredicates(subAttribute);
Set<Triplet> annTriplets = this.attributePredicates(subAttribute);
for (Triplet sT : subTriplets) {
boolean satisfaction = false;
for (Triplet aT : annTriplets) {
if (aT.partiallyCovers(sT)) {
satisfaction = true;
break;
}
}
if (!satisfaction) {
return false;
}
}
}
}
return true;
} else if (sub instanceof ActiveSubscription) {
return this.coversSubscription(((ActiveSubscription) sub)
.getSubscription());
} else if (sub instanceof MemorySubscription) {
return this.coversSubscription(((MemorySubscription) sub).getSubscription());
} else if (sub instanceof TripletTopKWSubscription) {
TripletTopKWSubscription tripTopKW = (TripletTopKWSubscription) sub;
for (Triplet subscriptionTriplet : tripTopKW.getData()) {
if (!this.attributes().contains(subscriptionTriplet.getKey())) {
return false;
}
}
return true;
}
return false;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder("( ");
for (String key : predicateMap.keySet()) {
for (Triplet triplet : predicateMap.get(key)) {
str.append(triplet.toString() + " , ");
}
}
str.replace(str.length() - 2, str.length(), ")"); // replacing last ", " with ")"
return str.toString();
}
}