/**
*
*/
package org.openiaml.model.drools;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.openiaml.model.model.BuiltinOperation;
import org.openiaml.model.model.ECARule;
import org.openiaml.model.model.GeneratedElement;
import org.openiaml.model.model.GeneratesElements;
import org.openiaml.model.model.NamedElement;
import org.openiaml.model.model.Parameter;
import org.openiaml.model.model.Scope;
import org.openiaml.model.model.Value;
import org.openiaml.model.model.Wire;
import org.openiaml.model.model.components.LoginHandler;
import org.openiaml.model.model.domain.DomainAttribute;
import org.openiaml.model.model.domain.DomainSource;
import org.openiaml.model.model.domain.DomainType;
import org.openiaml.model.model.domain.SchemaEdge;
import org.openiaml.model.model.scopes.Session;
import org.openiaml.model.model.users.Role;
import org.openiaml.model.model.visual.Label;
import org.openiaml.model.model.wires.AutocompleteWire;
import org.openiaml.model.model.wires.ExtendsEdge;
import org.openiaml.model.model.wires.SyncWire;
/**
* Helper functions for Drools rules.
*
* This is necessary because:
*
* <ul>
* <li>The same rules are used in multiple files (such as {@link #connects(WireEdge, Object, Object)}.</li>
* <li>Function names cannot be shared across multiple rule files (so each rule file needs its own package).</li>
* </ul>
*
* @author jmwright
*
*/
public class DroolsHelperFunctions {
/**
* True if the given bidirectional {@model Wire} connects
* the source object to the target object, or the target
* object to the source object.
*
* @param wire the wire to investigate
* @param source the source object
* @param target the target object
* @return true only if the wire connects the two objects bidirectionally
*/
public boolean connects(Wire wire, Object source, Object target) {
if (wire == null)
throw new NullPointerException("Wire '" + wire + "' = null");
if (wire.getFrom() == null)
throw new NullPointerException("Wire '" + wire + "'.from = null");
if (wire.getTo() == null)
throw new NullPointerException("Wire '" + wire + "'.to = null");
return (wire.getFrom().equals(source) && wire.getTo().equals(target)) ||
(wire.getFrom().equals(target) && wire.getTo().equals(source));
}
/**
* Is <code>target</code> generated by <code>by</code>?
*
* @param by
* @param target
* @return
*/
public boolean generatedBy(GeneratesElements by, GeneratedElement target) {
return target.getGeneratedBy().contains(by);
}
public boolean loginAttributeMatches(Value p, DomainAttribute a) {
return p.getName() != null && p.getName().equals("current " + a.getName());
}
public boolean loginHandlerScopeMatches(Scope target, LoginHandler handler) {
return target.getName() != null && target.getName().equals(handler.getName() + " login");
}
/**
* Does the given LoginHandler[instance] have incoming ParameterWires from
* attributes contained by the given DomainSchema?
*
* @param handler
* @param dobj
* @return
*/
public boolean hasIncomingParameterEdgesFrom(LoginHandler handler, DomainType dobj) {
for (Parameter edge : handler.getInParameterEdges()) {
if (edge.getParameterValue() instanceof DomainAttribute) {
if (dobj.equals(((DomainAttribute) edge.getParameterValue()).eContainer())) {
// found one
return true;
}
}
}
return false;
}
/**
* Check that the given attribute is not a primary key, or a
* derived foreign key.
*
* @param attribute
* @return
*/
public boolean notPrimaryKey(DomainAttribute attribute) {
return !attribute.getName().contains("generated primary key");
}
/**
* What is the last chained operation for the given {@link BuiltinOperation}?
*/
public BuiltinOperation lastChainedOperation(BuiltinOperation op) {
for (ECARule action : op.getListeners()) {
if (action.getTarget() instanceof BuiltinOperation) {
return lastChainedOperation((BuiltinOperation) action.getTarget());
}
}
return op;
}
/**
* Get the query string for selecting this user.
*
* We can't rely on using only attributes inferred to exist in the
* current role, because they may not have been inferred yet (they
* would be inferred in a later step).
*
* Also iterates over extends so we get parent attributes.
*/
public String getUserQueryString(Role role) {
String q = "";
for (EStructuralFeature feature : role.getEStructuralFeatures()) {
if (!(feature instanceof DomainAttribute))
continue;
DomainAttribute attribute = (DomainAttribute) feature;
// ignore primary keys
if (notPrimaryKey(attribute)) {
// ignore attributes which are extensions of other attributes;
// these attributes will be selected later
if (attribute.getOutExtendsEdges().isEmpty()) {
// add this attribute as a query
if (!q.isEmpty()) {
q += " and ";
}
q += attribute.getName() + " = :" + attribute.getName();
}
}
}
// get all parents
for (ExtendsEdge w : role.getOutExtendsEdges()) {
if (w.getTo() instanceof Role) {
String q2 = getUserQueryString((Role) w.getTo());
if (!q2.isEmpty()) {
if (q.isEmpty()) {
q = q2;
} else {
q += " and " + q2;
}
}
}
}
return q;
}
public String getQueryString(LoginHandler login_handler, DomainType dobj) {
String q = "";
for (Parameter wire : login_handler.getInParameterEdges()) {
if (wire.getParameterValue() instanceof DomainAttribute &&
dobj.equals(wire.getParameterValue().eContainer())) {
// add this attribute as a query
DomainAttribute attribute = (DomainAttribute) wire.getParameterValue();
if (notPrimaryKey(attribute)) {
if (!q.isEmpty()) {
q += " and ";
}
q += attribute.getName() + " = :" + attribute.getName();
}
}
}
return q;
}
public boolean nameMatches(NamedElement e1, NamedElement e2) {
return nameMatches(e1.getName(), e2.getName());
}
public boolean nameMatches(ENamedElement e1, NamedElement e2) {
return nameMatches(e1.getName(), e2.getName());
}
public boolean nameMatches(NamedElement e1, ENamedElement e2) {
return nameMatches(e1.getName(), e2.getName());
}
public boolean nameMatches(ENamedElement e1, ENamedElement e2) {
return nameMatches(e1.getName(), e2.getName());
}
public boolean nameMatches(String e1, String e2) {
return e1.toLowerCase().equals(e2.toLowerCase());
}
/**
* True if the given uni-directional {@model SyncWire} connects
* the source object to the target object.
*
* @param wire the wire to investigate
* @param source the source object
* @param target the target object
* @return true only if the wire connects the two objects
*/
public boolean connectsSync1(SyncWire wire, Object source, Object target) {
if (wire.getFrom() == null)
throw new NullPointerException("Wire '" + wire + "'.from = null");
if (wire.getTo() == null)
throw new NullPointerException("Wire '" + wire + "'.to = null");
return wire.getFrom().equals(source) && wire.getTo().equals(target);
}
public Session containingSession(EObject e) {
if (e.eContainer() == null) {
return null;
}
if (e.eContainer() instanceof Session) {
return (Session) e.eContainer();
}
return containingSession(e.eContainer());
}
public boolean containingSessionEquals(EObject e, Session s) {
Session actual = containingSession(e);
return s.equals(actual);
}
public boolean debug(Object o) {
System.out.println(o);
return true;
}
/**
* For a DomainAttribute, this returns true if it eventually
* extends a DomainAttribute which is a generated primary key.
*/
public boolean notExtendingGeneratedPrimaryKey(DomainAttribute attr) {
if (attr.isPrimaryKey() && attr.isIsGenerated()) {
return false;
}
for (ExtendsEdge w : attr.getOutExtendsEdges()) {
if (w.getTo() instanceof DomainAttribute) {
if (!notExtendingGeneratedPrimaryKey((DomainAttribute) w.getTo())) {
// we are extending a generated primary key
return false;
}
}
}
return true;
}
/**
* For a DomainAttribute, this returns true if it eventually
* extends a DomainAttribute which is a primary key.
*/
public boolean notExtendingPrimaryKey(DomainAttribute attr) {
if (attr.isPrimaryKey()) {
return false;
}
for (ExtendsEdge w : attr.getOutExtendsEdges()) {
if (w.getTo() instanceof DomainAttribute) {
if (!notExtendingPrimaryKey((DomainAttribute) w.getTo())) {
// we are extending a generated primary key
return false;
}
}
}
return true;
}
/**
* Does the given session directly or indirectly contain the given object?
*
* @param session the session to check against
* @param prop the object to investigate
* @return true if the given session contains the given property to some degree
*/
public boolean sessionContains(Session session, EObject prop) {
EObject obj = prop;
int i = 0;
while (i < 1000) {
i++;
if (obj == null) {
// we ran out of hierarchy
return false;
} else if (obj.equals(session)) {
// 'prop' is contained directly or indirectly by the given session
return true;
} else {
// recurse up the containment
obj = obj.eContainer();
}
}
throw new RuntimeException("Possible infinite loop detected in containment hierarchy: " + prop);
}
/**
* Get a DomainSource that will be duplicated, in order to provide for the
* given Role.
*
* @param root_role
* @return
*/
public DomainSource getOriginalDomainSource(Role root_role) {
for (ExtendsEdge ex : root_role.getInExtendsEdges()) {
if (ex.getFrom() instanceof DomainType) {
DomainType schema = (DomainType) ex.getFrom();
for (SchemaEdge se : schema.getInSchemas()) {
// return the first one found
return se.getFrom();
}
}
}
// couldn't find any
throw new IllegalArgumentException("Role " + root_role + " did not have any extended edges that provide a DomainSource");
}
/**
* Perform the same as the codegen <code>safeNameString</code>; i.e.
* replace all <code>[^A-Za-z0-9]</code> characters with an underscore.
*
* @param s
* @return
*/
public String safeNameString(String s) {
return s.replaceAll("[^A-Za-z0-9]", "_");
}
public String getEntryGateFrameNameForProvides(Label label) {
return "Provide " + label.getName();
}
public String getAutocompleteInputName(DomainAttribute attribute) {
return "Search by " + attribute.getName();
}
public String getAutocompleteIteratorQuery(AutocompleteWire wire) {
return "matches(" + wire.getMatch().getName() + ", :" + wire.getMatch().getName() + ")";
}
/**
* Are the two given XSD data types equal?
* The definition by {@link XSDSimpleTypeDefinition#equals(Object)} is extended
* to also check for {@link XSDSimpleTypeDefinition#getURI() URI} equality.
*/
public static boolean equalDataTypes(XSDSimpleTypeDefinition def1,
XSDSimpleTypeDefinition def2) {
return def1.equals(def2) ||
(def1 != null && def2 != null && def1.getURI().equals(def2.getURI()));
}
/**
* Returns <code>true</code> if the given element was generated by the source element.
*
* @param element given element
* @param source source element
* @return
*/
public static boolean isGeneratedBy(GeneratedElement element, GeneratesElements source) {
return element.getGeneratedBy().contains(source);
}
}