package gov.nasa.jpl.mbee.mdk.ocl;
import com.nomagic.magicdraw.uml2.util.UML2ModelUtil;
import com.nomagic.uml2.ext.jmi.helpers.StereotypesHelper;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Property;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Slot;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.ValueSpecification;
import com.nomagic.uml2.ext.magicdraw.mdprofiles.Stereotype;
import gov.nasa.jpl.mbee.mdk.docgen.DocGenUtils;
import gov.nasa.jpl.mbee.mdk.emf.EmfUtils;
import gov.nasa.jpl.mbee.mdk.util.CollectionAdder;
import gov.nasa.jpl.mbee.mdk.util.Utils;
import gov.nasa.jpl.mbee.mdk.util.Utils.AvailableAttribute;
import gov.nasa.jpl.mbee.mdk.util.Utils2;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* A CallOperation implementing a blackbox function extension of the OCL library
* that accesses (gets) data in some specified relation to some specified
* object.
*/
public class GetCallOperation implements CallOperation {
public enum CallReturnType {
SELF, NAME, TYPE, VALUE, MEMBER, RELATIONSHIP, OWNER, DEFAULT
}
private boolean collect = true; // TODO
public boolean filter = true; // REVIEW
// --
// should
// always
// (collect
// ==
// !filter)?
public boolean onlyOneForAll = false;
public boolean onlyOnePer = false;
public int recursionDepth = 1;
// List handling
public boolean mustFlatten = false;
public boolean mayFlatten = false;
public boolean flattenIfSizeOne = false;
public boolean flattenToNullIfEmpty = false;
public int defaultFlattenDepth = 1;
public boolean nullOk = true;
public Class<?> unflattenedCollectionType = ArrayList.class;
public boolean asElement = true;
public boolean asEObject = true;
public boolean asObject = true;
public boolean asCollection = true;
public boolean useName = true;
public boolean useType = true;
public boolean useValue = true;
private boolean matchNull = true; // TODO
private boolean activityEdgeIsRelationship = true; // TODO
/**
* Always filter on these; i.e. collected elements should match all Objects
* in alwaysFilter.
*/
public Object[] alwaysFilter = null;
public CallReturnType resultType = CallReturnType.SELF;
public GetCallOperation(CallReturnType opType, boolean onlyOneForAll, boolean onlyOnePer) {
super();
this.resultType = opType;
this.onlyOneForAll = onlyOneForAll;
this.onlyOnePer = onlyOnePer;
}
public GetCallOperation() {
super();
}
/*
* (non-Javadoc)
*
* @see gov.nasa.jpl.mbee.mdk.ocl.CallOperation#callOperation(java.lang.Object,
* java.lang.Object[])
*/
@Override
public Object callOperation(Object source, Object[] args) {
CollectionAdder adder = new CollectionAdder(mustFlatten, mayFlatten, flattenIfSizeOne,
flattenToNullIfEmpty, defaultFlattenDepth, nullOk, onlyOneForAll, unflattenedCollectionType);
List<Object> resultList = new ArrayList<Object>();
if (source == null) {
return resultList;
}
Object[] filterArgs = Utils2.join(alwaysFilter, args);
if (filter) {
filter = !Utils2.isNullOrEmpty(filterArgs);
}
Element elem = (source instanceof Element ? (Element) source : null);
Collection<?> coll = (source instanceof Collection ? (Collection<?>) source : null);
Object objectToAdd = null;
boolean loop = coll != null && asCollection && recursionDepth > 0;
boolean filterAlreadyUsed = false;
// boolean doingAdd = true;
switch (resultType) {
case SELF:
objectToAdd = source;
// filter = false;
// if ( EmfUtils.matches( source, useName, useType, args ) ) {
// objectToAdd = source;
// // } else {
// // doingAdd = false;
// }
// added = adder.add( source, resultList );
break;
case OWNER:
if (loop) {
objectToAdd = source;
}
else {
if (!(source instanceof Element)) {
objectToAdd = null;
}
else {
List<Element> owners = new ArrayList<Element>();
Element owner = ((Element) source).getOwner();
while (owner != null) {
owners.add(owner);
if (onlyOneForAll || onlyOnePer) {
break;
}
owner = owner.getOwner();
}
objectToAdd = owners;
}
}
break;
case NAME:
// if ( onlyOnePer )
if (loop) {
objectToAdd = source;
}
else {
objectToAdd = EmfUtils.getName(source);
}
// if ( filter && !EmfUtils.matches( objectToAdd, useName,
// useType, args ) ) {
// objectToAdd = null;
// }
// added = adder.add( name, resultList );
break;
case TYPE: // TODO -- use asElement, asEObject, asElement!! Need to
// pass thru to EmfUtils?
if (loop) {
objectToAdd = source;
}
else {
if ((onlyOnePer || onlyOneForAll) && Utils2.isNullOrEmpty(filterArgs)) {
// objectToAdd = EmfUtils.getTypeName( source );
objectToAdd = EmfUtils.getType(source);
if (!Utils2.isNullOrEmpty(objectToAdd)) {
break;
}
}
else {
objectToAdd = EmfUtils.getTypes(source);
}
if (asElement && elem != null) {
// Stereotypes
List<Stereotype> sTypes = StereotypesHelper.getStereotypes(elem);
if (!Utils2.isNullOrEmpty(sTypes) && objectToAdd instanceof Collection) {
Collection<Object> c = (Collection<Object>) objectToAdd;
for (Stereotype s : sTypes) {
if (!c.contains(s)) {
c.add(s);
}
if ((onlyOnePer || onlyOneForAll) && c.size() > 0
&& Utils2.isNullOrEmpty(filterArgs)) {
break;
}
}
if ((onlyOnePer || onlyOneForAll) && c.size() > 0
&& Utils2.isNullOrEmpty(filterArgs)) {
break;
}
}
else {
List<Object> list = Utils2.newList(sTypes.toArray());
if (objectToAdd != null) {
list.add(0, objectToAdd);
}
objectToAdd = list;
if ((onlyOnePer || onlyOneForAll) && list.size() > 0
&& Utils2.isNullOrEmpty(filterArgs)) {
break;
}
}
// Metaclasses -- TODO -- !!!!
// elem.m
// StereotypesHelper.getM
}
}
break;
case VALUE:
if (loop) {
objectToAdd = source;
}
else {
objectToAdd = null;
// If arguments were passed, then treat them as names of properties in source.
if (source instanceof Element && !Utils2.isNullOrEmpty(args)) {
List<Object> objects = new ArrayList<Object>();
for (Object arg : args) {
Property prop = null;
List<Object> propVals = null;
if (arg instanceof String) {
// TODO -- REVIEW -- should this be addAll or add?
propVals = Utils.getElementPropertyValues((Element) source,
(String) arg,
true);
}
else if (arg instanceof Property) {
prop = (Property) arg;
propVals = Utils.getElementPropertyValues((Element) source,
prop,
true);
}
if (!Utils2.isNullOrEmpty(propVals)) {
filterAlreadyUsed = true;
objects.addAll(propVals);
}
}
if (!objects.isEmpty()) {
objectToAdd = objects;
}
}
boolean one = !filter && (onlyOneForAll || (asCollection && coll != null && onlyOnePer));
// If the source is a Property or slot, get its value
if (Utils2.isNullOrEmpty(objectToAdd)
&& (source instanceof Property || source instanceof Slot)) {
objectToAdd =
Utils.getElementAttribute((Element) source,
AvailableAttribute.Value);
}
/*if ( Utils2.isNullOrEmpty( objectToAdd )
&& source instanceof ElementValue ) {
objectToAdd = ((ElementValue) source).getElement();
}*/
if (Utils2.isNullOrEmpty(objectToAdd)
&& source instanceof ValueSpecification) {
objectToAdd =
DocGenUtils.getLiteralValue(source, true);
}
// Handle onlyOne.
if (!Utils2.isNullOrEmpty(objectToAdd)) {
if (one && objectToAdd instanceof Collection) {
Object first = ((Collection<?>) objectToAdd).iterator().next();
objectToAdd = Utils2.newList(first);
}
}
else {
// Last resort -- try to find a member that looks like it would return a value
// boolean one = (onlyOneForAll || (asCollection && coll != null && onlyOnePer))
// && Utils2.isNullOrEmpty(filterArgs);
objectToAdd = EmfUtils.getValues(source, null, true, true, one, false, null);
}
if (Utils2.isNullOrEmpty(objectToAdd) &&
Utils2.isNullOrEmpty(args)) {
objectToAdd = source;
}
}
break;
case MEMBER:
if (loop) {
objectToAdd = source;
}
else {
if (asElement && elem != null) {
ArrayList<Element> members = new ArrayList<Element>();
if (elem.getOwnedElement() != null) {
members.addAll(elem.getOwnedElement());
}
members.addAll(Utils.getSlots(elem));
objectToAdd = members;
// } else if ( coll != null && !asCollection ) {
// objectToAdd = source;
}
else if (asEObject && source instanceof EObject) {
EList<EObject> elist = ((EObject) source).eContents();
objectToAdd = elist;
}
else if (asObject) {
objectToAdd = EmfUtils.getFieldValues(source, false);
// objectToAdd = EmfUtils.getMemberValues( source, null,
// true, false, onlyOnePer || onlyOneForAll,
// (String[])null );
}
}
break;
case RELATIONSHIP:
if (!loop) {
if (asElement && elem != null) {
objectToAdd = EmfUtils.getRelationships(elem);
}
else {
// REVIEW -- TODO -- asEObject???!
// REVIEW -- TODO -- ActivityEdge?
// REVIEW -- TODO -- complain???!
}
}
else {
objectToAdd = source;
}
break;
case DEFAULT:
if (!loop) {
if (asElement && elem != null && elem instanceof Property) {
objectToAdd = UML2ModelUtil.getDefault((Property) elem);
}
else {
// REVIEW -- TODO -- asEObject???!
// REVIEW -- TODO -- ActivityEdge?
// REVIEW -- TODO -- complain???!
}
}
else {
objectToAdd = source;
}
break;
default:
}
boolean isCollection = objectToAdd instanceof Collection;
if (loop) {
ArrayList<Object> list = new ArrayList<Object>();
--adder.defaultFlattenDepth;
--recursionDepth;
for (Object o : coll) {
Object result = callOperation(o, filterArgs);
adder.add(result, list);
}
++recursionDepth;
++adder.defaultFlattenDepth;
objectToAdd = list;
}
else {
// TODO -- apply filter while collecting above for efficiency in
// case returning only one!
// REVIEW -- this todo above may already be done
if (filter && !filterAlreadyUsed) {
if (!Utils2.isNullOrEmpty(args)) {
objectToAdd = EmfUtils.collectOrFilter(adder, objectToAdd, !filter,
(onlyOneForAll || (isCollection && onlyOnePer)), useName, useType, useValue,
asObject, args);
}
if (!Utils2.isNullOrEmpty(alwaysFilter)) {
objectToAdd = EmfUtils.collectOrFilter(adder, objectToAdd, false,
(onlyOneForAll || (isCollection && onlyOnePer)), useName, useType, useValue,
asObject, alwaysFilter);
}
}
}
if (objectToAdd instanceof Collection) {
objectToAdd = adder.fix((Collection<?>) objectToAdd);
}
return objectToAdd;
}
}