package com.sap.mi.textual.parsing.textblocks.reference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.stream.EventFilter;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate;
import com.sap.furcas.metamodel.FURCAS.TCS.ConcreteSyntax;
import com.sap.furcas.metamodel.FURCAS.TCS.FilterPArg;
import com.sap.furcas.metamodel.FURCAS.TCS.ForeachPredicatePropertyInit;
import com.sap.furcas.metamodel.FURCAS.TCS.InjectorAction;
import com.sap.furcas.metamodel.FURCAS.TCS.InjectorActionsBlock;
import com.sap.furcas.metamodel.FURCAS.TCS.LookupPropertyInit;
import com.sap.furcas.metamodel.FURCAS.TCS.PredicateSemantic;
import com.sap.furcas.metamodel.FURCAS.TCS.Property;
import com.sap.furcas.metamodel.FURCAS.TCS.QueryPArg;
import com.sap.furcas.metamodel.FURCAS.TCS.Template;
import com.sap.furcas.runtime.common.exceptions.ModelAdapterException;
import com.sap.furcas.runtime.common.interfaces.IRuleName;
import com.sap.furcas.runtime.common.util.ContextAndForeachHelper;
import com.sap.furcas.runtime.parser.impl.DelayedReference;
import com.sap.furcas.runtime.parser.impl.ObservableInjectingParser;
import com.sap.furcas.runtime.tcs.PropertyArgumentUtil;
/**
* This registry is responsible to setup and register all {@link IAExpressionInvalidationChangeListener}
* required to correctly observer and incrementally maintain all references of a given ConcreteSyntax
*
*/
public class IncrementalReferenceEvaluationRegistry {
private final Map<ConcreteSyntax, ObservableInjectingParser> parsersBySyntax = new HashMap<ConcreteSyntax, ObservableInjectingParser>();
private final Set<ConcreteSyntax> registeredSytaxes = new HashSet<ConcreteSyntax>();
private final Map<DelayedReference, String[]> delayedReference2RegistrationNames = new HashMap<DelayedReference, String[]>();
private final Map<DelayedReference, Map<EventFilter, Map<ListenerType, EventListener>>> delayedReference2ReEvaluationListener = new HashMap<DelayedReference, Map<EventFilter, Map<ListenerType, EventListener>>>();
private final Map<String, DelayedReference> registration2DelayedReference = new HashMap<String, DelayedReference>();
private final BundleContext context;
private final ServiceReference globalEventListenerRegistryRef;
private ReferenceResolver resolver;
public IncrementalReferenceEvaluationRegistry(ServiceReference globalEventListenerRegistryRef, BundleContext context) {
this.context = context;
this.globalEventListenerRegistryRef = globalEventListenerRegistryRef;
}
public void registerReferenceForIncrementalEvaluation(ConcreteSyntax cs, ResourceSet connection,
EPackage outermostPackageOfMetamodel, ObservableInjectingParser parser, IRuleName ruleNameBuilder,
IProgressMonitor monitor) {
if (!registeredSytaxes.contains(cs)) {
parsersBySyntax.put(cs, parser);
Collection<EPackage> packagesForLookup = new ArrayList<EPackage>();
packagesForLookup.addAll(AdapterEcoreHelper.getImportedRefPackages(outermostPackageOfMetamodel));
packagesForLookup.add(outermostPackageOfMetamodel);
EClass elementClass = AdapterEcoreHelper.getReflectElement(connection);
String mqlQuery = "select instance \n" + "from TCS::InjectorAction as instance";
MQLResultSet resultSet = connection.getMQLProcessor().execute(mqlQuery,
connection.getMQLProcessor().getInclusivePartitionScopeProvider(cs.get___Partition().getPri()));
mqlQuery = "select instance \n" + "from TCS::Property as instance";
MQLResultSet resultSetQueries = connection.getMQLProcessor().execute(mqlQuery,
connection.getMQLProcessor().getInclusivePartitionScopeProvider(cs.get___Partition().getPri()));
try {
monitor.beginTask("Registering OCL expressions for Impact Analysis for syntax: " + cs.getName(), resultSet
.getSize()
+ resultSetQueries.getSize());
monitor.subTask("PropertyInits");
for (EObject result : resultSet.getRefObjects("instance")) {
monitor.worked(1);
InjectorAction injectorActionBase = (InjectorAction) result;
// check if metamodel element is present
if (injectorActionBase.getInjectorActionsBlock().getParentTemplate().getMetaReference() == null) {
throw new RuntimeException("Cannot resolve reference to metamodel for template:\n"
+ injectorActionBase.getInjectorActionsBlock().getParentTemplate().get___Mri() + "\n"
+ "Make sure the metamodel is correctly referenced and loaded.");
}
if (injectorActionBase instanceof LookupPropertyInit) {
registerLookupPropertyInitForIA(cs, connection, packagesForLookup, elementClass, injectorActionBase);
} else if (injectorActionBase instanceof ForeachPredicatePropertyInit) {
registerForEachPropertyInitForIA(cs, connection, packagesForLookup, elementClass, injectorActionBase,
ruleNameBuilder);
}
}
monitor.subTask("Property Queries");
for (EObject result : resultSetQueries.getRefObjects("instance")) {
monitor.worked(1);
Property property = (Property) result;
registerPropertyQueryForIA(connection, packagesForLookup, elementClass, property, cs);
}
} finally {
// monitor.done();
}
registeredSytaxes.add(cs);
}
}
@SuppressWarnings("restriction")
private void registerForEachPropertyInitForIA(ConcreteSyntax cs, ResourceSet connection,
Collection<EPackage> packagesForLookup, EClass elementClass, InjectorAction injectorActionBase,
IRuleName ruleNameFinder) {
ForeachPredicatePropertyInit foreachPredicatePropertyInit = (ForeachPredicatePropertyInit) injectorActionBase;
InjectorActionsBlock block = foreachPredicatePropertyInit.getInjectorActionsBlock();
Template template = block.getParentTemplate();
String query = foreachPredicatePropertyInit.getValue();
try {
List<com.sap.furcas.runtime.parser.impl.PredicateSemantic> list = new ArrayList<com.sap.furcas.runtime.parser.impl.PredicateSemantic>();
String mode = template instanceof ClassTemplate ? ((ClassTemplate) template).getMode() : null;
for (PredicateSemantic next : foreachPredicatePropertyInit.getPredicateSemantic()) {
String localMode = mode;
if (next.getMode() != null) {
localMode = next.getMode();
}
if (next.getWhen() != null) {
list.add(new com.sap.furcas.runtime.parser.impl.PredicateSemantic(next.getWhen().toString(), ruleNameFinder.getRuleName(
next.getAs(), localMode)));
} else {
list.add(new com.sap.furcas.runtime.parser.impl.PredicateSemantic(null, ruleNameFinder.getRuleName(next.getAs(),
localMode)));
}
}
boolean hasContext = false;
if (block.getParentTemplate() instanceof ClassTemplate) {
hasContext = ((ClassTemplate) block.getParentTemplate()).isIsContext();
}
DelayedReference ref = new DelayedReference(null, null, DelayedReference.TYPE_SEMANTIC_PREDICATE, null,
foreachPredicatePropertyInit.getPropertyReference().getStrucfeature().getName(), query,
foreachPredicatePropertyInit.getMode(), list, ruleNameFinder, null, hasContext,
/*
* isOptional: ForEach is always considered optional as
* error reporting will be done based on metamodel
* constraints.
*/true);
ref.setQueryElement(foreachPredicatePropertyInit);
ref.setGenericReference(true);
// now replace any #context parts within the query with self
// and use the context element type for registration if it is
// used here,
registerOclExpressionsOfForeachConstruct(connection, packagesForLookup, elementClass, foreachPredicatePropertyInit,
template, ref);
} catch (OclManagerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ModelAdapterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MoinLocalizedBaseRuntimeException ex) {
System.err.println("Failed to register at IA: " + foreachPredicatePropertyInit.getValue() + "\n" + ex.getMessage());
ex.printStackTrace();
}
}
private void registerLookupPropertyInitForIA(ConcreteSyntax cs, ResourceSet connection,
Collection<EPackage> packagesForLookup, EClass elementClass, InjectorAction injectorActionBase) {
LookupPropertyInit injectorAction = (LookupPropertyInit) injectorActionBase;
Template template = ((InjectorActionsBlock) injectorAction.refImmediateComposite()).getParentTemplate();
String query = injectorAction.getValue();
try {
EObject parsingContext = ContextAndForeachHelper.getParsingContext(connection, query, template, packagesForLookup,
elementClass);
DelayedReference ref = new DelayedReference(null, null, null, injectorAction.getPropertyReference().getStrucfeature()
.getName(), null, null, query, false, null);
// now replace any #context parts within the query with self
// and use the context element type for registration if it is
// used here,
query = AdapterEcoreHelper.prepareOclQuery(query, null);
if (query != null) {
ref.setQueryElement(injectorAction);
ref.setGenericReference(true);
String name = "<genericReference>" + injectorAction.refMofId();
OclExpressionRegistration registration = (OclExpressionRegistration) connection.getOclRegistryService()
.getFreestyleRegistry().getRegistration(name);
if (registration != null) {
connection.getOclRegistryService().getFreestyleRegistry().deleteRegistration(name);
}
registration = connection.getOclRegistryService().getFreestyleRegistry().createExpressionRegistration(name,
query, OclRegistrationSeverity.Info, new String[] { "TCS Property Init" }, parsingContext,
packagesForLookup.toArray(new EPackage[] {}));
Map<EventFilter, Map<ListenerType, EventListener>> reEvaluationListener = createReEvaluationListener(ref,
registration);
GlobalEventListenerRegistry registry = (GlobalEventListenerRegistry) context
.getService(globalEventListenerRegistryRef);
registry.addFilters(reEvaluationListener);
registration2DelayedReference.put(registration.getName(), ref);
delayedReference2RegistrationNames.put(ref, new String[] { registration.getName() });
delayedReference2ReEvaluationListener.put(ref, reEvaluationListener);
}
} catch (OclManagerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ModelAdapterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MoinLocalizedBaseRuntimeException ex) {
System.err.println("Failed to register at IA: " + injectorAction.getValue() + "\n" + ex.getMessage());
ex.printStackTrace();
} catch (RuntimeException ex) {
System.err.println("Failed to register at IA: " + injectorAction.getValue() + "\n" + ex.getMessage());
}
}
/**
* Combine the <tt>foreach</tt> and all <tt>when</tt> expressions into one
* event listener. For each expression a separate
* {@link OclExpressionRegistration} is created. Their event filters are
* combined into one filter by using an {@link OrFilter}. Therefore, events
* that affect any of the <tt>foreach</tt> or the <tt>when</tt> expressions
* are delivered to the <tt>ref</tt> reference only once. The same listener
* instance is registered for the combined event filter.
*/
private void registerOclExpressionsOfForeachConstruct(ResourceSet connection, Collection<EPackage> packagesForLookup,
EClass elementClass, ForeachPredicatePropertyInit foreachPredicatePropertyInit, Template template,
DelayedReference ref) throws ModelAdapterException, OclManagerException {
Set<OclExpressionRegistration> registrations = new HashSet<OclExpressionRegistration>();
Map<String, String> mofIdToOclQueryPairs = new HashMap<String, String>();
if (foreachPredicatePropertyInit.getValue() != null) {
mofIdToOclQueryPairs.put(foreachPredicatePropertyInit.refMofId(), foreachPredicatePropertyInit.getValue());
}
for (tcs.PredicateSemantic whenClause : foreachPredicatePropertyInit.getPredicatesemantic()) {
if (whenClause.getWhen() != null) {
mofIdToOclQueryPairs.put(whenClause.refMofId(), whenClause.getWhen());
}
}
List<String> registrationNames = new ArrayList<String>();
for (String mofId : mofIdToOclQueryPairs.keySet()) {
String oclRegistrationName = "<genericReference>" + mofId;
OclExpressionRegistration registration = (OclExpressionRegistration) connection.getOclRegistryService()
.getFreestyleRegistry().getRegistration(oclRegistrationName);
if (registration != null) {
connection.getOclRegistryService().getFreestyleRegistry().deleteRegistration(oclRegistrationName);
}
EObject parsingContext = ContextAndForeachHelper.getParsingContext(connection, mofIdToOclQueryPairs.get(mofId),
template, packagesForLookup, elementClass);
String preparedQuery = AdapterEcoreHelper.prepareOclQuery(mofIdToOclQueryPairs.get(mofId), null);
registration = connection.getOclRegistryService().getFreestyleRegistry().createExpressionRegistration(
oclRegistrationName, preparedQuery, OclRegistrationSeverity.Info, new String[] { "TCS Property Init" },
parsingContext, packagesForLookup.toArray(new EPackage[] {}));
registrations.add(registration);
registration2DelayedReference.put(oclRegistrationName, ref);
registrationNames.add(oclRegistrationName);
}
delayedReference2RegistrationNames.put(ref, registrationNames.toArray(new String[0]));
Map<EventFilter, Map<ListenerType, EventListener>> reEvaluationListener = createReEvaluationListener(ref, registrations
.toArray(new OclExpressionRegistration[0]));
GlobalEventListenerRegistry registry = (GlobalEventListenerRegistry) context.getService(globalEventListenerRegistryRef);
registry.addFilters(reEvaluationListener);
delayedReference2ReEvaluationListener.put(ref, reEvaluationListener);
}
private void registerPropertyQueryForIA(ResourceSet connection, Collection<EPackage> packagesForLookup,
EClass elementClass, Property property, ConcreteSyntax cs) {
Template template = property.getParentTemplate();
if (template != null && template.getMetaReference() instanceof EClass) {
// TODO what about StructureTypes?
QueryPArg qarg = PropertyArgumentUtil.getLookupScopePArg(property);
// TODO still needed? if not, delete
// RefersToParg refersToArg = TcsUtil.getRefersToParg(property);
FilterPArg filter = PropertyArgumentUtil.getFilterPArg(property);
if (qarg != null) {
try {
String query = qarg.getQuery();
if (filter != null) {
query += filter.getFilter();
}
EObject parsingContext = ContextAndForeachHelper.getParsingContext(connection, query, template,
packagesForLookup, elementClass);
DelayedReference ref = new DelayedReference(null, null, null, property.getPropertyReference()
.getStrucfeature().getName(), null, null, query, false, null);
query = AdapterEcoreHelper.prepareOclQuery(query, GlobalDelayedReferenceResolver.TEMPORARY_QUERY_PARAM_REPLACEMENT);
if (query != null) {
ref.setQueryElement(property);
ref.setGenericReference(true);
String name = "<genericReference>" + property.refMofId();
OclExpressionRegistration registration = (OclExpressionRegistration) connection.getOclRegistryService()
.getFreestyleRegistry().getRegistration(name);
if (registration != null) {
connection.getOclRegistryService().getFreestyleRegistry().deleteRegistration(name);
}
registration = connection.getOclRegistryService().getFreestyleRegistry().createExpressionRegistration(
name, query, OclRegistrationSeverity.Info, new String[] { "TCS Property Query" }, parsingContext,
packagesForLookup.toArray(new EPackage[] {}));
Map<EventFilter, Map<ListenerType, EventListener>> reEvaluationListener = createReEvaluationListener(ref,
registration);
GlobalEventListenerRegistry registry = (GlobalEventListenerRegistry) context
.getService(globalEventListenerRegistryRef);
registry.addFilters(reEvaluationListener);
registration2DelayedReference.put(registration.getName(), ref);
delayedReference2RegistrationNames.put(ref, new String[] { registration.getName() });
delayedReference2ReEvaluationListener.put(ref, reEvaluationListener);
}
} catch (OclManagerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ModelAdapterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MoinLocalizedBaseRuntimeException ex) {
System.err.println("Failed to register at IA: " + qarg.getQuery() + "\n" + ex.getMessage());
ex.printStackTrace();
}
}
}
}
/**
* Fetches the event filters for <tt>registrations</tt> (see
* {@link OclExpressionRegistration#getEventFilter(boolean)}) and combines
* them into one {@link OrFilter}. An event filter map suitable for use with
* {@link GlobalEventListenerRegistry#addFilters(Map)} is created with a new
* {@link IAExpressionInvalidationChangeListener} listener object for the
* <tt>ref</tt> reference.
*/
private Map<EventFilter, Map<ListenerType, EventListener>> createReEvaluationListener(DelayedReference ref,
OclExpressionRegistration... registrations) {
IAExpressionInvalidationChangeListener listener = new IAExpressionInvalidationChangeListener(resolver, ref, registrations);
Map<EventFilter, Map<ListenerType, EventListener>> map = new HashMap<EventFilter, Map<ListenerType, EventListener>>();
Set<EventFilter> filters = new HashSet<EventFilter>();
for (OclExpressionRegistration registration : registrations) {
// notifyNewContextElement can be set to false because element
// creation is handled by the
// parser itself, evaluating all properties initially that cna be
// evaluated at that point.
filters.add(registration.getEventFilter(/* notifyNewContextElement */false));
}
OrFilter filter = new OrFilter(filters.toArray(new EventFilter[0]));
Map<ListenerType, EventListener> listenerForType = new HashMap<ListenerType, EventListener>();
listenerForType.put(ListenerType.UPDATE, listener);
listenerForType.put(ListenerType.POST_CHANGE, listener);
map.put(filter, listenerForType);
return map;
}
private Set<IAExpressionInvalidationChangeListener> getAllListeners() {
Set<IAExpressionInvalidationChangeListener> result = new HashSet<IAExpressionInvalidationChangeListener>();
for (Map<EventFilter, Map<ListenerType, EventListener>> m : delayedReference2ReEvaluationListener.values()) {
for (Map<ListenerType, EventListener> ltel : m.values()) {
for (EventListener listener : ltel.values()) {
result.add((IAExpressionInvalidationChangeListener) listener);
}
}
}
return result;
}
void removeRegistration(DelayedReference unresolvedRef) {
GlobalEventListenerRegistry registry = (GlobalEventListenerRegistry) context.getService(globalEventListenerRegistryRef);
String[] regs = delayedReference2RegistrationNames.remove(unresolvedRef);
for (String reg : regs) {
registration2DelayedReference.remove(reg);
}
Map<EventFilter, Map<ListenerType, EventListener>> filter = delayedReference2ReEvaluationListener.remove(unresolvedRef);
if (filter != null) {
registry.removeFilters(filter);
}
}
ObservableInjectingParser getParser(ConcreteSyntax concretesyntax) {
return parsersBySyntax.get(concretesyntax);
}
/**
* Set the resolver that shall be passed on to and used by the created listeners.
* @param resolver
*/
void setResolver(ReferenceResolver resolver) {
this.resolver = resolver;
}
public String getDebugInfo(ResourceSet conn) {
StringBuilder result = new StringBuilder();
for (IAExpressionInvalidationChangeListener listener : getAllListeners()) {
result.append(listener.getDebugInfo(conn));
}
return result.toString();
}
public String getDebugInfoAsCsv(ResourceSet conn) {
Statistics oclIaStatistics = Statistics.getInstance();
StringBuilder result = new StringBuilder();
result.append(oclIaStatistics.getCsvHeader());
for (IAExpressionInvalidationChangeListener listener : getAllListeners()) {
result.append(listener.getDebugInfoAsCsv(conn));
}
return result.toString();
}
}