/* * Copyright 2003-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.newTypesystem.rules; import gnu.trove.THashSet; import jetbrains.mps.languageScope.LanguageScope; import jetbrains.mps.smodel.NodeReadAccessCasterInEditor; import org.jetbrains.mps.openapi.language.SAbstractConcept; import org.jetbrains.mps.openapi.language.SConcept; import org.jetbrains.mps.openapi.model.SNode; import jetbrains.mps.util.Computable; import jetbrains.mps.util.Pair; import jetbrains.mps.util.Triplet; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * User: fyodor * Date: 8/31/12 */ public abstract class DoubleTermRules<K> { private ConcurrentHashMap<Object, Set<K>> myCachedRules = new ConcurrentHashMap<Object, Set<K>>(); public Set<K> lookupRules(SNode leftTerm, SNode rightTerm) { final LanguageScope langScope = LanguageScope.getCurrent(); final SAbstractConcept leftConcept = leftTerm.getConcept(); final SAbstractConcept rightConcept = rightTerm.getConcept(); final Object compoundKey = new Triplet<Object, SAbstractConcept, SAbstractConcept>(langScope, leftConcept, rightConcept); Set<K> cachedRules = myCachedRules.get(compoundKey); if (cachedRules != null) return cachedRules; return NodeReadAccessCasterInEditor.runReadTransparentAction(new Computable<Set<K>>() { @Override public Set<K> compute() { Set<K> computedRules = computeRules(leftConcept, rightConcept, langScope); myCachedRules.put(compoundKey, computedRules); return computedRules; } }); } public void purgeCache() { myCachedRules.clear(); } private Set<K> computeRules(SAbstractConcept leftConcept, SAbstractConcept rightConcept, LanguageScope langScope) { THashSet<K> result = new THashSet<K>(); LinkedList<Pair<SAbstractConcept, SAbstractConcept>> queue = new LinkedList<Pair<SAbstractConcept, SAbstractConcept>>(); queue.add(new Pair<SAbstractConcept, SAbstractConcept>(leftConcept, rightConcept)); for (SConcept leftSuperConcept : allSuperConcepts(leftConcept)) { queue.add(new Pair<SAbstractConcept, SAbstractConcept>(leftSuperConcept, rightConcept)); } while (!queue.isEmpty()) { Pair<SAbstractConcept, SAbstractConcept> nextConceptPair = queue.remove(); for (K applicableRule : allForConceptPair(nextConceptPair.o1, nextConceptPair.o2, langScope)) { result.add(applicableRule); } for (SConcept rightSuperConcept : allSuperConcepts(nextConceptPair.o2)) { queue.add(new Pair<SAbstractConcept, SAbstractConcept>(nextConceptPair.o1, rightSuperConcept)); } } return Collections.unmodifiableSet(result); } // all super-concepts provided supplied concept is not an interface; including BaseConcept private Iterable<SConcept> allSuperConcepts(SAbstractConcept concept) { // I have no idea why interface concepts are not considered - computeRules(iface1, iface2) would // look for applicable rules anyway (queue is initialized regardless of concept kind), just won't // traverse concept hierarchy like it does for computeRules(concept1, concept2). if (!(concept instanceof SConcept)) { return Collections.emptyList(); } // not sure there's much sense in BaseConcept among return values, left as it used to be. SConcept c = ((SConcept) concept).getSuperConcept(); ArrayList<SConcept> rv = new ArrayList<SConcept>(); while (c != null) { rv.add(c); c = c.getSuperConcept(); } return rv; } abstract protected Iterable<K> allForConceptPair(SAbstractConcept leftConcept, SAbstractConcept rightConcept, LanguageScope langScope); }