/** * Copyright 2010 JBoss Inc * * 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 org.drools.spi; import java.util.HashMap; import java.util.Map; import java.util.Stack; import org.drools.base.ClassObjectType; import org.drools.rule.Declaration; import org.drools.rule.GroupElement; import org.drools.rule.Package; import org.drools.rule.Pattern; import org.drools.rule.Rule; import org.drools.rule.RuleConditionElement; import org.drools.spi.InternalReadAccessor; /** * A class capable of resolving a declaration in the current build context * * @author etirelli */ public class DeclarationScopeResolver { private static final Stack<RuleConditionElement> EMPTY_STACK = new Stack<RuleConditionElement>(); private Map<String, Class< ? >>[] maps; private Stack<RuleConditionElement> buildStack; private Package pkg; public DeclarationScopeResolver(final Map<String, Class< ? >>[] maps) { this( maps, EMPTY_STACK ); } public DeclarationScopeResolver(final Map<String, Class< ? >>[] maps, final Stack<RuleConditionElement> buildStack) { this.maps = maps; if ( buildStack == null ) { this.buildStack = EMPTY_STACK; } else { this.buildStack = buildStack; } } public void setPackage(Package pkg) { this.pkg = pkg; } private Declaration getExtendedDeclaration(Rule rule, String identifier) { if ( rule.getLhs().getInnerDeclarations().containsKey( identifier ) ) { return (Declaration) rule.getLhs().getInnerDeclarations().get( identifier ); } else if ( null != rule.getParent() ) { return getExtendedDeclaration( rule.getParent(), identifier ); } return null; } private HashMap<String, Declaration> getAllExtendedDeclaration(Rule rule, HashMap<String, Declaration> dec) { dec.putAll( ((RuleConditionElement) rule.getLhs()).getInnerDeclarations() ); if ( null != rule.getParent() ) { return getAllExtendedDeclaration( rule.getParent(), dec ); } return dec; } public Declaration getDeclaration(final Rule rule, final String identifier) { // it may be a local bound variable for ( int i = this.buildStack.size() - 1; i >= 0; i-- ) { final Declaration declaration = (Declaration) ((RuleConditionElement) this.buildStack.get( i )).getInnerDeclarations().get( identifier ); if ( declaration != null ) { return declaration; } } // look at parent rules if ( rule != null && rule.getParent() != null ) { // recursive algorithm for each parent // -> lhs.getInnerDeclarations() Declaration parentDeclaration = getExtendedDeclaration( rule.getParent(), identifier ); if ( null != parentDeclaration ) { return parentDeclaration; } } // it may be a global or something for ( int i = 0, length = this.maps.length; i < length; i++ ) { if ( this.maps[i].containsKey( (identifier) ) ) { if ( pkg != null ) { Class<?> cls = (Class<?>) this.maps[i].get( identifier ); ClassObjectType classObjectType = new ClassObjectType( cls ); Declaration declaration = null; final Pattern dummy = new Pattern( 0, classObjectType ); GlobalExtractor globalExtractor = new GlobalExtractor( identifier, classObjectType ); declaration = new Declaration( identifier, globalExtractor, dummy ); // make sure dummy and globalExtractor are wired up to correct ClassObjectType // and set as targets for rewiring pkg.getClassFieldAccessorStore().getClassObjectType( classObjectType, dummy ); pkg.getClassFieldAccessorStore().getClassObjectType( classObjectType, globalExtractor ); return declaration; } else { throw new UnsupportedOperationException( "This shoudln't happen outside of PackageBuilder" ); } } } return null; } public boolean available(Rule rule, final String name) { for ( int i = this.buildStack.size() - 1; i >= 0; i-- ) { final Declaration declaration = (Declaration) ((RuleConditionElement) this.buildStack.get( i )).getInnerDeclarations().get( name ); if ( declaration != null ) { return true; } } for ( int i = 0, length = this.maps.length; i < length; i++ ) { if ( this.maps[i].containsKey( (name) ) ) { return true; } } // look at parent rules if ( rule != null && rule.getParent() != null ) { // recursive algorithm for each parent // -> lhs.getInnerDeclarations() Declaration parentDeclaration = getExtendedDeclaration( rule.getParent(), name ); if ( null != parentDeclaration ) { return true; } } return false; } public boolean isDuplicated(Rule rule, final String name) { for ( int i = 0, length = this.maps.length; i < length; i++ ) { if ( this.maps[i].containsKey( (name) ) ) { return true; } } for ( int i = this.buildStack.size() - 1; i >= 0; i-- ) { final RuleConditionElement rce = (RuleConditionElement) this.buildStack.get( i ); final Declaration declaration = (Declaration) rce.getInnerDeclarations().get( name ); if ( declaration != null ) { if ( (rce instanceof GroupElement) && ((GroupElement) rce).isOr() ) { // if it is an OR and it is duplicated, we can stop looking for duplication now // as it is a separate logical branch return false; } return true; } } // look at parent rules if ( rule != null && rule.getParent() != null ) { // recursive algorithm for each parent // -> lhs.getInnerDeclarations() Declaration parentDeclaration = getExtendedDeclaration( rule.getParent(), name ); if ( null != parentDeclaration ) { return true; } } return false; } /** * Return all declarations scoped to the current * RuleConditionElement in the build stack * * @return */ public Map<String, Declaration> getDeclarations(Rule rule) { final Map<String, Declaration> declarations = new HashMap<String, Declaration>(); for ( int i = 0; i < this.buildStack.size(); i++ ) { // this may be optimized in the future to only re-add elements at // scope breaks, like "NOT" and "EXISTS" declarations.putAll( ((RuleConditionElement) this.buildStack.get( i )).getInnerDeclarations() ); } if ( null != rule.getParent() ) { return getAllExtendedDeclaration( rule.getParent(), (HashMap<String, Declaration>) declarations ); } return declarations; } public Map<String,Class<?>> getDeclarationClasses(Rule rule) { final Map<String, Declaration> declarations = getDeclarations( rule ); final Map<String, Class<?>> classes = new HashMap<String, Class<?>>(); for ( Map.Entry<String, Declaration> decl : declarations.entrySet() ) { InternalReadAccessor ira = decl.getValue().getExtractor(); if( ira != null ) classes.put( decl.getKey(), ira.getExtractToClass() ); } return classes; } public Pattern findPatternByIndex(int index) { if ( !this.buildStack.isEmpty() ) { return findPatternInNestedElements( index, (RuleConditionElement) this.buildStack.get( 0 ) ); } return null; } private Pattern findPatternInNestedElements(final int index, final RuleConditionElement rce) { for ( RuleConditionElement element : rce.getNestedElements() ) { if ( element instanceof Pattern ) { Pattern p = (Pattern) element; if ( p.getIndex() == index ) { return p; } } else if ( !element.isPatternScopeDelimiter() ) { Pattern p = findPatternInNestedElements( index, element ); if ( p != null ) { return p; } } } return null; } }