/**
* 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 TripletSubscription extends Subscription implements Serializable,
Comparable<TripletSubscription> {
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;
/**
* Should not be used, created only for WS methods
*/
public TripletSubscription() {
super();
}
public TripletSubscription(long validity, long startTime) {
super(validity, startTime);
}
/**
* Adds a predicate (constraint) to this subscription.
*
* @param triplet (attribute(key), value, operator)
*/
public 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 TripletSubscription) {
TripletSubscription subscription = (TripletSubscription) other;
if (super.equals(subscription)) {
return true;
}
if (this.validity == subscription.validity
&& this.startTime == subscription.startTime) {
return predicateMap.equals(subscription.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 attr : this.attributes()) {
for (Triplet tempTriplet1 : this.attributePredicates(attr)) {
boolean satisfaction = false;
Set<Triplet> attrPreds = tripSub.attributePredicates(attr);
if (attrPreds == null) {
return false;
}
for (Triplet tempTriplet2 : attrPreds) { // only the
// predicates
// over attr are
// needed...
if (tempTriplet1.covers(tempTriplet2)) {
satisfaction = true;
break;
}
}
if (!satisfaction) {
return false;
}
}
}
return true;
} else if (sub instanceof ActiveSubscription) {
return this.coversSubscription(((ActiveSubscription) sub)
.getSubscription());
}
return false;
}
/**
* Method for checking whether subscription covers publication.
*
* @param pub publication
*
*/
@Override
public boolean coversPublication(Publication pub) {
if (pub instanceof HashtablePublication) {
HashtablePublication hashPub = (HashtablePublication) pub;
for (String attr : this.attributes()) { // for each attribute in the
// subscription...
Object value = hashPub.getProperties().get(attr);
if (value == null) {
return false;
}
Triplet tempPubTriplet = new Triplet(attr, value,
Operator.EQUAL);
for (Triplet tempSubTriplet : this.attributePredicates(attr)) { // ...for
// each
// triplet
// over
// the
// attribute
if (!tempSubTriplet.covers(tempPubTriplet)) {
return false;
}
}
}
return true;
} else if (pub instanceof ActivePublication) {
return this.coversPublication(((ActivePublication) pub)
.getPublication());
}
return false;
}
@Override
public int compareTo(TripletSubscription other) {
if (this.equals(other)) {
return 0;
} else if (this.coversSubscription(other)) {
return 1;
} else if (other.coversSubscription(this)) {
return -1;
} else {
return -2;
}
}
@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();
}
}