package com.sap.ap.metamodel.eventhandlers;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import com.sap.tc.moin.globalmodellistener.GlobalEventListener;
import com.sap.tc.moin.globalmodellistener.GlobalEventListenerRegistry;
import com.sap.tc.moin.repository.Connection;
import com.sap.tc.moin.repository.MRI;
import com.sap.tc.moin.repository.events.EventChain;
import com.sap.tc.moin.repository.events.EventListener;
import com.sap.tc.moin.repository.events.UpdateListener;
import com.sap.tc.moin.repository.events.filter.AndFilter;
import com.sap.tc.moin.repository.events.filter.AssociationFilter;
import com.sap.tc.moin.repository.events.filter.ClassFilter;
import com.sap.tc.moin.repository.events.filter.EventFilter;
import com.sap.tc.moin.repository.events.filter.EventTypeFilter;
import com.sap.tc.moin.repository.events.filter.InstanceFilter;
import com.sap.tc.moin.repository.events.type.ChangeEvent;
import com.sap.tc.moin.repository.events.type.ElementDeleteEvent;
import com.sap.tc.moin.repository.events.type.LinkAddEvent;
import com.sap.tc.moin.repository.mmi.model.Association;
import com.sap.tc.moin.repository.mmi.reflect.RefClass;
import data.classes.AElementsOfTypeClazz;
import data.classes.AOwnedTypeDefinitionOwnerTypedElement;
import data.classes.ActualObjectParameter;
import data.classes.ClassTypeDefinition;
import data.classes.Parameter;
import data.classes.SapClass;
import dataaccess.expressions.This;
import dataaccess.expressions.VariableExpression;
/**
* Due to the current limitations of the Furcas/TCS approach we cannot create the actual object
* parameters for the {@link ClassTypeDefinition} that is attached to a {@link This} expression,
* mostly because creating a number of elements determined dynamically from the cardinality of some
* other feature is not supported at this time. As a workaround, we install this global event
* listener which will get notified whenever a {@link This} expressions has its owned type
* definition set. This handler will determine the formal object parameters of the class and create
* {@link VariableExpression}s, one for each formal object parameter, and set them as the actual
* object parameters for the class type definition used by the {@link This} expression. It does so
* in the scope of the connection that caused the change event.
*
* @author Axel Uhl (D043530)
*
*/
public class ThisTypeDefinitionSetter implements GlobalEventListener, UpdateListener {
Logger log = Logger.getLogger(ThisTypeDefinitionSetter.class.getName());
/**
* The OSGi bundle context; will be initialized when {@link #getFilters} is called.
*/
private BundleContext context;
private ServiceReference globalEventListenerRegistryRef;
@Override
public void notifyUpdate(EventChain events) {
for (ChangeEvent event : events.getEvents()) {
if (event instanceof LinkAddEvent) {
LinkAddEvent lre = (LinkAddEvent) event;
MRI thisMri = lre.getSecondLinkEndMri();
Connection conn = event.getEventTriggerConnection();
This thiz = (This) conn.getElement(thisMri.getLri());
ClassTypeDefinition ctd = (ClassTypeDefinition) conn.getElement(lre
.getFirstLinkEndMri().getLri());
constructActualObjectParams(thiz, ctd, conn);
}
}
}
private void constructActualObjectParams(This thiz, ClassTypeDefinition ctd, Connection conn) {
if (ctd == null) {
log.severe("This expression's class type definition doesn't exist yet."+
" Unable to establish actual object parameters");
} else {
if (ctd.getClazz() == null) {
registerForClassToBeSetOnClassTypeDefinition(ctd, conn);
} else {
setActualObjectParameters(ctd, ctd.getClazz(), conn);
}
}
}
private void setActualObjectParameters(ClassTypeDefinition ctd, SapClass clazz, Connection conn) {
RefClass vec = conn
.getClass(VariableExpression.CLASS_DESCRIPTOR);
RefClass aopc = conn
.getClass(ActualObjectParameter.CLASS_DESCRIPTOR);
for (Parameter param : clazz.getFormalObjectParameters()) {
VariableExpression ve = (VariableExpression) vec.refCreateInstance();
ve.setVariable(param);
ActualObjectParameter aop = (ActualObjectParameter) aopc.refCreateInstance();
aop.setFormalObjectParameter(param);
aop.setValue(ve);
ctd.getObjectParameters().add(aop);
}
}
/**
* When the class type definition of the {@link This} expression doesn't yet have a
* {@link SapClass} set, we have to wait for this connection to be made or the class type
* definition being deleted. In both events, those registrations will be canceled again.
* If a valid link shows up, the setting of the actual object parameters on the
* class type definition will be triggered.
* @param ctd
*/
private void registerForClassToBeSetOnClassTypeDefinition(ClassTypeDefinition ctd, Connection conn) {
GlobalEventListenerRegistry registry = (GlobalEventListenerRegistry) context.getService(globalEventListenerRegistryRef);
HashMap<EventFilter, Map<ListenerType, EventListener>> listenerFilters = new HashMap<EventFilter, Map<ListenerType, EventListener>>();
Map<ListenerType, EventListener> ctdDeletedListeners = new HashMap<ListenerType, EventListener>();
ctdDeletedListeners.put(ListenerType.UPDATE, new UpdateListener() {
@Override
public void notifyUpdate(EventChain events) {
// TODO remove listener with yet to be defined API on GlobalEventListenerRegistry
}
});
listenerFilters.put(new AndFilter(new InstanceFilter(ctd), new EventTypeFilter(ElementDeleteEvent.class)),
ctdDeletedListeners);
Map<ListenerType, EventListener> ctdReceivedClassListeners = new HashMap<ListenerType, EventListener>();
ctdReceivedClassListeners.put(ListenerType.UPDATE, new UpdateListener() {
@Override
public void notifyUpdate(EventChain events) {
for (ChangeEvent event : events.getEvents()) {
if (event instanceof LinkAddEvent) {
LinkAddEvent linkAddEvent = (LinkAddEvent) event;
Connection conn = event.getEventTriggerConnection();
ClassTypeDefinition ctd = (ClassTypeDefinition) conn
.getElement(linkAddEvent.getFirstLinkEndMri());
SapClass clazz = (SapClass) conn.getElement(linkAddEvent
.getSecondLinkEndMri());
setActualObjectParameters(ctd, clazz, event.getEventTriggerConnection());
// TODO remove listener with yet to be defined API on
// GlobalEventListenerRegistry
}
}
}
});
listenerFilters.put(new AndFilter(new InstanceFilter(ctd), new AssociationFilter(
conn.getAssociation(AElementsOfTypeClazz.ASSOCIATION_DESCRIPTOR).refMetaObject()),
new EventTypeFilter(LinkAddEvent.class)),
ctdReceivedClassListeners);
registry.addFilters(listenerFilters);
}
@Override
public Map<EventFilter, Map<ListenerType, EventListener>> getFilters(Connection connection, BundleContext context) {
this.context = context;
globalEventListenerRegistryRef = context.getServiceReference(GlobalEventListenerRegistry.class.getName());
HashMap<EventFilter, Map<ListenerType, EventListener>> result = new HashMap<EventFilter, Map<ListenerType, EventListener>>();
Association ownedTDAssoc = connection.getAssociation(
AOwnedTypeDefinitionOwnerTypedElement.ASSOCIATION_DESCRIPTOR).refMetaObject();
EventFilter assocFilter = new AssociationFilter(ownedTDAssoc);
EventFilter thisTypeFilter = new ClassFilter(connection.getClass(
This.CLASS_DESCRIPTOR).refMetaObject(),
/* includeSubclasses */ true);
EventFilter andFilter = new AndFilter(assocFilter, thisTypeFilter, new EventTypeFilter(LinkAddEvent.class));
Map<ListenerType, EventListener> listenerForType = new HashMap<ListenerType, EventListener>();
listenerForType.put(ListenerType.UPDATE, this);
result.put(andFilter, listenerForType);
return result;
}
}