/******************************************************************************* | |
* Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* Gabor Bergmann - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.viatra2.gtasm.patternmatcher.incremental; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.LinkedHashSet; | |
import java.util.Map; | |
import java.util.Random; | |
import java.util.Set; | |
import org.eclipse.viatra2.core.IEntity; | |
import org.eclipse.viatra2.core.IModelElement; | |
import org.eclipse.viatra2.gtasm.patternmatcher.ExecutionMode; | |
import org.eclipse.viatra2.gtasm.patternmatcher.IMatching; | |
import org.eclipse.viatra2.gtasm.patternmatcher.ParameterMode; | |
import org.eclipse.viatra2.gtasm.patternmatcher.PatternCallSignature; | |
import org.eclipse.viatra2.gtasm.patternmatcher.Scope; | |
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.matcher.RetePatternMatcher; | |
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.Tuple; | |
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.TupleMask; | |
import org.eclipse.viatra2.gtasm.patternmatcher.patterns.IPatternMatcher; | |
/** | |
* @author Bergmann Gábor | |
* | |
*/ | |
public class ReteGTASMPatternMatcher implements IPatternMatcher { | |
RetePatternMatcher matcher; | |
Random random = null; | |
/** | |
* @param matcher the GTASM-ignorant RetePatternMatcher to wrap | |
*/ | |
public ReteGTASMPatternMatcher(RetePatternMatcher matcher) { | |
super(); | |
this.matcher = matcher; | |
} | |
/** | |
* @param inputMapping quantification indicated by org.eclipse.viatra2.gtasmmodel.gtasm.metamodel.asm.enums.ValueKind.UNDEF_LITERAL | |
*/ | |
public boolean match(Object[] inputMapping) { | |
boolean[] fixed = new boolean[inputMapping.length]; | |
for (int i = 0; i < inputMapping.length; ++i) { | |
fixed[i] = !org.eclipse.viatra2.gtasmmodel.gtasm.metamodel.asm.enums.ValueKind.UNDEF_LITERAL | |
.equals(inputMapping[i]); | |
} | |
return matcher.count(inputMapping, fixed) != 0; | |
} | |
public IMatching match(Object[] inputMapping, PatternCallSignature[] signature) { | |
boolean ignoreScope = isScopeIgnorable(signature); | |
if (!ignoreScope) // there needs to be scope checking, which is deadlock-prone to do in a matchfetcher! | |
{ | |
ArrayList<IMatching> result = matchAll(inputMapping, signature, null); | |
return result.isEmpty() ? null : result.iterator().next(); | |
} else | |
{ | |
boolean[] fixed = new boolean[inputMapping.length]; | |
for (int i = 0; i < inputMapping.length; ++i) { | |
fixed[i] = (signature[i].getParameterMode() == ParameterMode.INPUT); | |
} | |
// retrieving the projection | |
Tuple match = matcher.matchOne(inputMapping, fixed); | |
return match==null? null : new ReteMatching(match, matcher.getPosMapping()); | |
} | |
} | |
public ArrayList<IMatching> matchAll(Object[] inputMapping, PatternCallSignature[] signature, Integer[] quantificationOrder) { | |
// analyzing the execution mode (quantification, bound variables) | |
boolean[] fixed = new boolean[inputMapping.length]; | |
boolean[] quantified = new boolean[inputMapping.length]; | |
boolean hasExistential = false; // there are variables neither universally quantified not bound | |
boolean hasUniversal = false; | |
for (int i = 0; i < inputMapping.length; ++i) { | |
fixed[i] = (signature[i].getParameterMode() == ParameterMode.INPUT); | |
quantified[i] = (signature[i].getParameterMode() == ParameterMode.OUTPUT | |
&& signature[i].getExecutionMode() == ExecutionMode.MULTIPLE_RESULTS); | |
if (!fixed[i]) { | |
hasUniversal = hasUniversal || quantified[i]; | |
hasExistential = hasExistential || !quantified[i]; | |
} | |
} | |
boolean ignoreScope = isScopeIgnorable(signature); | |
// default to single matching | |
if (!hasUniversal && ignoreScope) { | |
Tuple match = matcher.matchOne(inputMapping, fixed); | |
return match==null? new ArrayList<IMatching>() : | |
new ArrayList<IMatching>(Collections.singleton(new ReteMatching(match, matcher.getPosMapping()))); | |
} | |
// retrieving the projection | |
Collection<Tuple> unscopedMatches = matcher.matchAll(inputMapping, fixed); | |
// processing matches, checking scopes | |
if (unscopedMatches == null) return new ArrayList<IMatching>(); | |
ArrayList<IMatching> matchings = new ArrayList<IMatching>(unscopedMatches.size()); | |
TupleMask quantifiedMask = null; | |
Set<Tuple> quantifiedProjections = null; | |
if (hasExistential) { | |
quantifiedMask = new TupleMask(quantified); | |
quantifiedProjections = new LinkedHashSet<Tuple>(); | |
} | |
for (Tuple ps : /* productionNode */unscopedMatches) { | |
//Tuple ps = boundary.unwrapTuple(um); | |
Tuple quantifiedPart = null; | |
if (hasExistential) { | |
quantifiedPart = quantifiedMask.transform(ps); | |
if (quantifiedProjections.contains(quantifiedPart)) continue; // not the first occurrence | |
} | |
boolean ok = true; | |
if (!ignoreScope) for (int k = 0; (k < ps.getSize()) && ok; k++) { | |
if (signature[k].getParameterMode() == ParameterMode.INPUT) { | |
// ok = ok && (inputMapping[k]==ps.elements[k]); | |
// should now be true | |
} else // ParameterMode.OUTPUT | |
{ | |
IEntity scopeParent = (IEntity) signature[k] | |
.getParameterScope().getParent(); | |
Integer containmentMode = signature[k] | |
.getParameterScope().getContainmentMode(); | |
if (containmentMode == Scope.BELOW) { | |
// ok = ok && | |
// scopeParent.getAllComponents().contains | |
// (ps.elements[k]); | |
ok = ok | |
&& ((IModelElement) ps.get(k)) | |
.isBelowNamespace(scopeParent); | |
} else /* case Scope.IN: */ | |
{ | |
ok = ok | |
&& scopeParent.equals(((IModelElement) ps | |
.get(k)).getNamespace()); | |
// note: getNamespace returns null instead of the | |
// (imaginary) modelspace root entity for top level | |
// elements; | |
// this is not a problem here as Scope.IN implies | |
// scopeParent != root. | |
} | |
} | |
} | |
if (ok) { | |
if (hasExistential) quantifiedProjections.add(quantifiedPart); | |
matchings.add(new ReteMatching(ps, matcher.getPosMapping())); | |
} | |
} | |
return matchings; | |
} | |
public IMatching matchRandomly(Object[] inputMapping, PatternCallSignature[] signature) { | |
if(random == null){ | |
random = new Random(); | |
} | |
// all unbounded parameters are set to return multiple results | |
for(int j = 0; j < signature.length; j++) | |
{ | |
// output parameters are in the front of the array | |
if(signature[j].getParameterMode() == ParameterMode.OUTPUT) | |
signature[j].setExecutionMode(ExecutionMode.MULTIPLE_RESULTS); | |
} | |
ArrayList<IMatching> allMatches = matchAll(inputMapping, signature, null); | |
if (allMatches == null || allMatches.isEmpty()) return null; | |
else return allMatches.get(random.nextInt(allMatches.size())); | |
} | |
boolean isScopeIgnorable(PatternCallSignature[] signature) { | |
for (PatternCallSignature sig : signature) { | |
if (sig.getParameterMode() == ParameterMode.OUTPUT) { | |
Scope scope = sig.getParameterScope(); | |
if (scope.getContainmentMode() != Scope.BELOW || scope.getParent() != Scope.DEFAULT_PARENT) | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Builds a mapping between parameter indices and scopes. | |
* @param signatures PatternCallSignature array, as present in an IPatternMatcher query | |
* @return a scope map suitable for ReteEngine.accessMatcherScoped() | |
*/ | |
public static Map<Integer, Scope> buildAdditionalScopeMap(PatternCallSignature[] signatures) { | |
Map<Integer, Scope> scopeMap = new HashMap<Integer, Scope>(); | |
for (int i=0; i<signatures.length; ++i) { | |
PatternCallSignature signature = signatures[i]; | |
if (signature.getParameterMode().equals(ParameterMode.OUTPUT) && | |
! signature.getParameterScope().getParent().equals(Scope.DEFAULT_PARENT)) | |
{ | |
scopeMap.put(i, signature.getParameterScope()); | |
} | |
} | |
return scopeMap; | |
} | |
} |