All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.drools.compiler.builder.impl.ClassHierarchyManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *      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.compiler.builder.impl;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.drools.compiler.compiler.PackageRegistry;
import org.drools.compiler.compiler.TypeDeclarationError;
import org.drools.util.TypeResolver;
import org.drools.base.base.ClassFieldInspector;
import org.drools.base.base.CoreComponentsBuilder;
import org.drools.base.definitions.InternalKnowledgePackage;
import org.drools.base.factmodel.ClassDefinition;
import org.drools.base.factmodel.FieldDefinition;
import org.drools.base.factmodel.traits.Alias;
import org.drools.base.rule.TypeDeclaration;
import org.drools.drl.ast.descr.AbstractClassTypeDeclarationDescr;
import org.drools.drl.ast.descr.AnnotationDescr;
import org.drools.drl.ast.descr.PatternDescr;
import org.drools.drl.ast.descr.QualifiedName;
import org.drools.drl.ast.descr.TypeDeclarationDescr;
import org.drools.drl.ast.descr.TypeFieldDescr;
import org.kie.api.definition.type.Key;
import org.kie.api.io.Resource;

import static org.drools.compiler.rule.builder.util.AnnotationFactory.getTypedAnnotation;

public class ClassHierarchyManager {

    protected final TypeDeclarationContext tdContext;

    protected List sortedDescriptors;
    protected Map> taxonomy;

    public ClassHierarchyManager(Collection unsortedDescrs, TypeDeclarationContext tdContext, BuildResultCollector results) {
        this.tdContext = tdContext;
        this.sortedDescriptors = sortByHierarchy(unsortedDescrs, tdContext, results);
    }

    public List getSortedDescriptors() {
        return sortedDescriptors;
    }

    /**
     * Utility method to sort declared beans. Linearizes the hierarchy,
     * i.e.generates a sequence of declaration such that, if Sub is subclass of
     * Sup, then the index of Sub will be > than the index of Sup in the
     * resulting collection. This ensures that superclasses are processed before
     * their subclasses
     */
    protected List sortByHierarchy(Collection unsortedDescrs, TypeDeclarationContext tdContext, BuildResultCollector results) {

        taxonomy = new HashMap<>();
        Map cache = new HashMap<>();

        for (AbstractClassTypeDeclarationDescr tdescr : unsortedDescrs) {
            cache.put(tdescr.getType(), tdescr);
        }

        for (AbstractClassTypeDeclarationDescr tdescr : unsortedDescrs) {
            QualifiedName name = tdescr.getType();

            Collection supers = taxonomy.get(name);
            if (supers == null) {
                supers = new ArrayList<>();
                taxonomy.put(name, supers);
            } else {
                results.addBuilderResult(new TypeDeclarationError(tdescr,
                                                                   "Found duplicate declaration for type " + tdescr.getType()));
            }

            boolean circular = false;
            for (QualifiedName sup : tdescr.getSuperTypes()) {
                if (!Object.class.getName().equals(name.getFullName())) {
                    if (!hasCircularDependency(tdescr.getType(), sup, taxonomy)) {
                        if (cache.containsKey(sup)) {
                            supers.add(sup);
                        }
                    } else {
                        circular = true;
                        results.addBuilderResult(new TypeDeclarationError(tdescr,
                                                                           "Found circular dependency for type " + tdescr.getTypeName()));
                        break;
                    }
                }
            }
            if (circular) {
                tdescr.getSuperTypes().clear();
            }
        }

        for (AbstractClassTypeDeclarationDescr tdescr : unsortedDescrs) {
            for (TypeFieldDescr field : tdescr.getFields().values()) {
                QualifiedName name = tdescr.getType();
                QualifiedName typeName = new QualifiedName(field.getPattern().getGenericType().getRawType());
                if (!hasCircularDependency(name, typeName, taxonomy)) {
                    if (cache.containsKey(typeName)) {
                        taxonomy.get(name).add(typeName);
                    }
                } else {
                    field.setRecursive(true);
                }
            }
        }

        List sorted = new HierarchySorter().sort(taxonomy);
        ArrayList list = new ArrayList(sorted.size());
        for (QualifiedName name : sorted) {
            list.add(cache.get(name));
        }

        return list;
    }

    protected static boolean hasCircularDependency(QualifiedName name,
                                                   QualifiedName typeName,
                                                   Map> taxonomy) {
        if (name.equals(typeName)) {
            return true;
        }
        Collection parents = taxonomy.get(typeName);
        if (parents != null) {
            if (parents.contains(name)) {
                return true;
            } else {
                for (QualifiedName ancestor : parents) {
                    if (hasCircularDependency(name, ancestor, taxonomy)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public void inheritFields(PackageRegistry pkgRegistry,
                              AbstractClassTypeDeclarationDescr typeDescr,
                              BuildResultCollector results,
                              Map unprocessableDescrs) {
        TypeDeclarationDescr tDescr = (TypeDeclarationDescr) typeDescr;
        boolean isNovel = TypeDeclarationUtils.isNovelClass(typeDescr, pkgRegistry);
        boolean inferFields = !isNovel && typeDescr.getFields().isEmpty();

        mergeInheritedFields(tDescr, unprocessableDescrs, pkgRegistry.getTypeResolver(), results);

        if (inferFields) {
            // not novel, but only an empty declaration was provided.
            // after inheriting the fields from supertypes, now we fill in the locally declared fields
            Class existingClass = TypeDeclarationUtils.getExistingDeclarationClass(typeDescr, pkgRegistry);
            buildDescrsFromFields(existingClass, tDescr, tDescr.getFields());
        }
    }

    private static void buildDescrsFromFields(Class klass, TypeDeclarationDescr typeDescr,
                                              Map fieldMap) {
        ClassFieldInspector inspector;
        try {
            inspector = CoreComponentsBuilder.get().createClassFieldInspector(klass);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        for (String name : inspector.getGetterMethods().keySet()) {
            // classFieldAccessor requires both getter and setter
            if (inspector.getSetterMethods().containsKey(name)) {
                if (!inspector.isNonGetter(name) && !"class".equals(name)) {
                    Resource resource = typeDescr.getResource();
                    PatternDescr patternDescr = new PatternDescr(inspector.getFieldType(name).getName());
                    patternDescr.setResource(resource);
                    TypeFieldDescr inheritedFlDescr = new TypeFieldDescr(name, patternDescr);
                    inheritedFlDescr.setResource(resource);
                    inheritedFlDescr.setInherited(!Modifier.isAbstract(inspector.getGetterMethods().get(name).getModifiers()));

                    if (!fieldMap.containsKey(inheritedFlDescr.getFieldName()))
                        fieldMap.put(inheritedFlDescr.getFieldName(),
                                     inheritedFlDescr);
                }
            }
        }
    }

    /**
     * In order to build a declared class, the fields inherited from its
     * superclass(es) are added to its declaration. Inherited descriptors are
     * marked as such to distinguish them from native ones. Various scenarioes
     * are possible. (i) The superclass has been declared in the DRL as well :
     * the fields are cloned as inherited (ii) The superclass is imported
     * (external), but some of its fields have been tagged with metadata (iii)
     * The superclass is imported.
     * 

* The search for field descriptors is carried out in the order. (i) and * (ii+iii) are mutually exclusive. The search is as such: (i) The * superclass' declared fields are used to build the base class additional * fields (iii) The superclass is inspected to discover its (public) fields, * from which descriptors are generated (ii) Both (i) and (iii) are applied, * but the declared fields override the inspected ones * * @param typeDescr The base class descriptor, to be completed with the inherited * fields descriptors */ protected void mergeInheritedFields(TypeDeclarationDescr typeDescr, Map unprocessableDescrs, TypeResolver typeResolver, BuildResultCollector results) { if (typeDescr.getSuperTypes().isEmpty()) { return; } for (int j = typeDescr.getSuperTypes().size() - 1; j >= 0; j--) { QualifiedName qname = typeDescr.getSuperTypes().get(j); String simpleSuperTypeName = qname.getName(); String superTypePackageName = qname.getNamespace(); String fullSuper = qname.getFullName(); mergeFields(simpleSuperTypeName, superTypePackageName, fullSuper, typeDescr, unprocessableDescrs, typeResolver, results); } } protected void mergeFields(String simpleSuperTypeName, String superTypePackageName, String fullSuper, TypeDeclarationDescr typeDescr, Map unprocessableDescrs, TypeResolver resolver, BuildResultCollector results) { Map fieldMap = new LinkedHashMap<>(); PackageRegistry registry = tdContext.getPackageRegistry(superTypePackageName); InternalKnowledgePackage pack = null; if (registry != null) { pack = registry.getPackage(); } if (unprocessableDescrs.containsKey(fullSuper)) { unprocessableDescrs.put(typeDescr.getType().getFullName(), typeDescr); return; } // if a class is declared in DRL, its package can't be null? The default package is replaced by "defaultpkg" boolean isSuperClassTagged = false; boolean isSuperClassDeclared = true; //in the same package, or in a previous one if (pack != null) { // look for the supertype declaration in available packages TypeDeclaration superTypeDeclaration = pack.getTypeDeclaration(simpleSuperTypeName); if (superTypeDeclaration != null && superTypeDeclaration.getTypeClassDef() != null) { ClassDefinition classDef = superTypeDeclaration.getTypeClassDef(); // inherit fields for (org.kie.api.definition.type.FactField fld : classDef.getFields()) { TypeFieldDescr inheritedFlDescr = buildInheritedFieldDescrFromDefinition(fld, typeDescr); fieldMap.put(inheritedFlDescr.getFieldName(), inheritedFlDescr); } // new classes are already distinguished from tagged external classes isSuperClassTagged = !superTypeDeclaration.isNovel(); } } else { isSuperClassDeclared = false; } // look for the class externally if (!isSuperClassDeclared || isSuperClassTagged) { try { Class superKlass; if (registry != null) { superKlass = registry.getTypeResolver().resolveType(fullSuper); } else { // if the supertype has not been declared, and we have got so far, it means that this class is not novel superKlass = resolver.resolveType(fullSuper); } buildDescrsFromFields(superKlass, typeDescr, fieldMap); } catch (ClassNotFoundException cnfe) { unprocessableDescrs.put(typeDescr.getType().getFullName(), typeDescr); return; } } // finally, locally declared fields are merged. The map swap ensures that super-fields are added in order, before the subclass' ones // notice that it is not possible to override a field changing its type for (String fieldName : typeDescr.getFields().keySet()) { if (fieldMap.containsKey(fieldName)) { String type1 = fieldMap.get(fieldName).getPattern().getObjectType(); String type2 = typeDescr.getFields().get(fieldName).getPattern().getObjectType(); if (type2.lastIndexOf(".") < 0) { try { TypeResolver typeResolver = tdContext.getPackageRegistry(typeDescr.getNamespace()).getTypeResolver(); type1 = typeResolver.resolveType(type1).getName(); type2 = typeResolver.resolveType(type2).getName(); // now that we are at it... this will be needed later anyway fieldMap.get(fieldName).getPattern().setObjectType(type1); typeDescr.getFields().get(fieldName).getPattern().setObjectType(type2); } catch (ClassNotFoundException cnfe) { // will fail later } } boolean clash = !type1.equals(type2); TypeFieldDescr overriding = null; if (clash) { // this may still be an override using a subclass of the original type try { Class sup = resolver.resolveType(type1); Class loc = resolver.resolveType(type2); if (sup.isAssignableFrom(loc)) { clash = false; // mark as non inherited so that a new field is actually built overriding = fieldMap.get(fieldName); } } catch (ClassNotFoundException cnfe) { // not much to do } } if (clash) { results.addBuilderResult(new TypeDeclarationError(typeDescr, "Cannot redeclare field '" + fieldName + " from " + type1 + " to " + type2)); typeDescr.setType(null, null); return; } else { String initVal = fieldMap.get(fieldName).getInitExpr(); TypeFieldDescr fd = typeDescr.getFields().get(fieldName); if (fd.getInitExpr() == null) { fd.setInitExpr(initVal); } fd.setInherited(fieldMap.get(fieldName).isInherited()); fd.setOverriding(overriding); for (String key : fieldMap.get(fieldName).getAnnotationNames()) { if (fd.getAnnotation(key) == null) { fd.addAnnotation(fieldMap.get(fieldName).getAnnotation(key)); } } if (fd.getIndex() < 0) { fd.setIndex(fieldMap.get(fieldName).getIndex()); } } } fieldMap.put(fieldName, typeDescr.getFields().get(fieldName)); } typeDescr.setFields(fieldMap); } protected TypeFieldDescr buildInheritedFieldDescrFromDefinition(org.kie.api.definition.type.FactField fld, TypeDeclarationDescr typeDescr) { TypeFieldDescr inheritedFldDescr = new TypeFieldDescr(); inheritedFldDescr.setFieldName(fld.getName()); inheritedFldDescr.setResource(typeDescr.getResource()); PatternDescr fldType = new PatternDescr(); fldType.setObjectType(((FieldDefinition) fld).getTypeName()); inheritedFldDescr.setPattern(fldType); // also sets resource for PatternDescr fldType if (fld.isKey()) { AnnotationDescr keyAnnotation = new AnnotationDescr(Key.class.getCanonicalName()); keyAnnotation.setFullyQualifiedName(Key.class.getCanonicalName()); keyAnnotation.setResource(typeDescr.getResource()); inheritedFldDescr.addAnnotation(keyAnnotation); } inheritedFldDescr.setIndex(((FieldDefinition) fld).getDeclIndex()); inheritedFldDescr.setInherited(true); String initExprOverride = ((FieldDefinition) fld).getInitExpr(); int overrideCount = 0; // only @aliasing local fields may override defaults. for (TypeFieldDescr localField : typeDescr.getFields().values()) { Alias alias = getTypedAnnotation(localField, Alias.class); if (alias != null && fld.getName().equals(alias.value().replaceAll("\"", "")) && localField.getInitExpr() != null) { overrideCount++; initExprOverride = localField.getInitExpr(); } } if (overrideCount > 1) { // however, only one is allowed initExprOverride = null; } inheritedFldDescr.setInitExpr(initExprOverride); return inheritedFldDescr; } public void addDeclarationToPackagePreservingOrder(TypeDeclaration type, AbstractClassTypeDeclarationDescr typeDescr, InternalKnowledgePackage tgtPackage, Map pkgRegistryMap) { Collection parents = taxonomy.get(new QualifiedName(type.getFullName())); int index = getSortedDescriptors().indexOf(typeDescr); if (parents != null && !parents.isEmpty()) { for (QualifiedName parentName : parents) { String nameSpace = parentName.getNamespace(); String name = parentName.getName(); PackageRegistry parentPkgRegistry = pkgRegistryMap.get(nameSpace); if (parentPkgRegistry != null) { TypeDeclaration parentDeclaration = parentPkgRegistry.getPackage().getTypeDeclaration(name); if (parentDeclaration != null && parentDeclaration.getNature() == TypeDeclaration.Nature.DEFINITION) { index = Math.max(index, parentDeclaration.getOrder()); } } } } type.setOrder(index + 1); tgtPackage.addTypeDeclaration(type); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy