/**
   * ********************************************************************************************************************************************************************
   * 1) Process the TypeDeclaration Descriptors Resolve names Normalize field descriptors
   * ********************************************************************************************************************************************************************
   */
  public void processTypeDeclarations(
      Collection<? extends PackageDescr> packageDescrs,
      Collection<AbstractClassTypeDeclarationDescr> unsortedDescrs,
      List<TypeDefinition> unresolvedTypes,
      Map<String, AbstractClassTypeDeclarationDescr> unprocesseableDescrs) {

    // init package to ensure type resolvers are available
    for (PackageDescr packageDescr : packageDescrs) {
      if (kbuilder.getPackageRegistry(packageDescr.getName()) == null) {
        kbuilder.createPackageRegistry(packageDescr);
      }
    }

    setResourcesInDescriptors(packageDescrs);

    // ensure all names are fully qualified before continuing
    typeDeclarationNameResolver.resolveTypes(packageDescrs, unresolvedTypes);

    // create "implicit" packages
    for (PackageDescr packageDescr : packageDescrs) {
      normalizeForeignPackages(packageDescr);
    }

    // merge "duplicate" definitions and declarations
    unsortedDescrs = compactDefinitionsAndDeclarations(unsortedDescrs, unprocesseableDescrs);

    // now sort declarations by mutual dependencies
    ClassHierarchyManager classHierarchyManager =
        new ClassHierarchyManager(unsortedDescrs, kbuilder);

    for (AbstractClassTypeDeclarationDescr typeDescr :
        classHierarchyManager.getSortedDescriptors()) {
      PackageRegistry pkgRegistry = kbuilder.getPackageRegistry(typeDescr.getNamespace());
      createBean(
          typeDescr, pkgRegistry, classHierarchyManager, unresolvedTypes, unprocesseableDescrs);
    }

    for (AbstractClassTypeDeclarationDescr typeDescr :
        classHierarchyManager.getSortedDescriptors()) {
      if (!unprocesseableDescrs.containsKey(typeDescr.getType().getFullName())) {
        PackageRegistry pkgRegistry = kbuilder.getPackageRegistry(typeDescr.getNamespace());
        typeDeclarationConfigurator.wireFieldAccessors(
            pkgRegistry,
            typeDescr,
            pkgRegistry.getPackage().getTypeDeclaration(typeDescr.getType().getName()));
      }
    }
  }
  private void setupTest(String consequence, Map<String, Object> namedConsequences) {
    builder = new MVELConsequenceBuilder();

    InternalKnowledgePackage pkg = new KnowledgePackageImpl("org.drools.compiler.test");
    pkg.addImport(new ImportDeclaration(Cheese.class.getCanonicalName()));

    KnowledgeBuilderConfigurationImpl conf = new KnowledgeBuilderConfigurationImpl();
    KnowledgeBuilderImpl pkgBuilder = new KnowledgeBuilderImpl(pkg, conf);

    ruleDescr = new RuleDescr("test consequence builder");
    ruleDescr.setConsequence(consequence);
    ruleDescr.addAttribute(new AttributeDescr("dialect", "mvel"));

    for (Entry<String, Object> entry : namedConsequences.entrySet()) {
      ruleDescr.addNamedConsequences(entry.getKey(), entry.getValue());
    }

    RuleImpl rule = new RuleImpl(ruleDescr.getName());
    rule.addPattern(new Pattern(0, new ClassObjectType(Cheese.class), "$cheese"));

    rule.addPattern(new Pattern(0, new ClassObjectType(Map.class), "$map"));

    PackageRegistry pkgRegistry = pkgBuilder.getPackageRegistry(pkg.getName());
    DialectCompiletimeRegistry reg =
        pkgBuilder.getPackageRegistry(pkg.getName()).getDialectCompiletimeRegistry();
    context =
        new RuleBuildContext(
            pkgBuilder, ruleDescr, reg, pkg, reg.getDialect(pkgRegistry.getDialect()));
    context.getBuildStack().push(rule.getLhs());

    context.getDialect().getConsequenceBuilder().build(context, RuleImpl.DEFAULT_CONSEQUENCE_NAME);
    for (String name : namedConsequences.keySet()) {
      context.getDialect().getConsequenceBuilder().build(context, name);
    }

    context.getDialect().addRule(context);
    pkgRegistry.getPackage().addRule(context.getRule());
    pkgBuilder.compileAll();
    pkgBuilder.reloadAll();
    if (pkgBuilder.hasErrors()) {
      fail(pkgBuilder.getErrors().toString());
    }
  }
  @Test
  public void testImperativeCodeError() throws Exception {
    InternalKnowledgePackage pkg = new KnowledgePackageImpl("pkg1");
    final RuleDescr ruleDescr = new RuleDescr("rule 1");
    ruleDescr.setConsequence("if (cheese.price == 10) { cheese.price = 5; }");

    Properties properties = new Properties();
    properties.setProperty("drools.dialect.default", "mvel");
    KnowledgeBuilderConfigurationImpl cfg1 = new KnowledgeBuilderConfigurationImpl(properties);

    KnowledgeBuilderImpl pkgBuilder = new KnowledgeBuilderImpl(pkg, cfg1);
    final KnowledgeBuilderConfigurationImpl conf = pkgBuilder.getBuilderConfiguration();
    PackageRegistry pkgRegistry = pkgBuilder.getPackageRegistry(pkg.getName());
    DialectCompiletimeRegistry dialectRegistry =
        pkgBuilder.getPackageRegistry(pkg.getName()).getDialectCompiletimeRegistry();
    MVELDialect mvelDialect = (MVELDialect) dialectRegistry.getDialect(pkgRegistry.getDialect());

    final InstrumentedBuildContent context =
        new InstrumentedBuildContent(pkgBuilder, ruleDescr, dialectRegistry, pkg, mvelDialect);

    final InstrumentedDeclarationScopeResolver declarationResolver =
        new InstrumentedDeclarationScopeResolver();

    final ObjectType cheeseObjeectType = new ClassObjectType(Cheese.class);

    final Pattern pattern = new Pattern(0, cheeseObjeectType);

    final PatternExtractor extractor = new PatternExtractor(cheeseObjeectType);

    final Declaration declaration = new Declaration("cheese", extractor, pattern);
    final Map<String, Declaration> map = new HashMap<String, Declaration>();
    map.put("cheese", declaration);
    declarationResolver.setDeclarations(map);
    context.setDeclarationResolver(declarationResolver);

    final MVELConsequenceBuilder builder = new MVELConsequenceBuilder();
    builder.build(context, RuleImpl.DEFAULT_CONSEQUENCE_NAME);

    InternalKnowledgeBase kBase = (InternalKnowledgeBase) KnowledgeBaseFactory.newKnowledgeBase();
    StatefulKnowledgeSessionImpl ksession =
        (StatefulKnowledgeSessionImpl) kBase.newStatefulKnowledgeSession();

    final Cheese cheddar = new Cheese("cheddar", 10);
    final InternalFactHandle f0 = (InternalFactHandle) ksession.insert(cheddar);
    final LeftTupleImpl tuple = new LeftTupleImpl(f0, null, true);

    final AgendaItem item = new AgendaItemImpl(0, tuple, 10, null, null, null);
    final DefaultKnowledgeHelper kbHelper = new DefaultKnowledgeHelper(ksession);
    kbHelper.setActivation(item);
    try {
      ((MVELConsequence) context.getRule().getConsequence())
          .compile(
              (MVELDialectRuntimeData)
                  pkgBuilder
                      .getPackageRegistry(pkg.getName())
                      .getDialectRuntimeRegistry()
                      .getDialectData("mvel"));
      context.getRule().getConsequence().evaluate(kbHelper, ksession);
      fail("should throw an exception, as 'if' is not allowed");
    } catch (Exception e) {
    }

    assertEquals(10, cheddar.getPrice());
  }
  protected void updateTraitInformation(
      AbstractClassTypeDeclarationDescr typeDescr,
      TypeDeclaration type,
      ClassDefinition def,
      PackageRegistry pkgRegistry) {
    if (typeDescr.hasAnnotation(Traitable.class)
        || (!type.getKind().equals(TypeDeclaration.Kind.TRAIT)
            && kbuilder.getPackageRegistry().containsKey(def.getSuperClass())
            && kbuilder
                .getPackageRegistry(def.getSuperClass())
                .getTraitRegistry()
                .getTraitables()
                .containsKey(def.getSuperClass()))) {
      // traitable
      if (type.isNovel()) {
        try {
          PackageRegistry reg = kbuilder.getPackageRegistry(typeDescr.getNamespace());
          String availableName = typeDescr.getType().getFullName();
          Class<?> resolvedType = reg.getTypeResolver().resolveType(availableName);
          updateTraitDefinition(type, resolvedType, false);
        } catch (ClassNotFoundException cnfe) {
          // we already know the class exists
        }
      }
      pkgRegistry.getTraitRegistry().addTraitable(def);
    } else if (type.getKind().equals(TypeDeclaration.Kind.TRAIT)
        || typeDescr.hasAnnotation(Trait.class)) {
      // trait
      if (!type.isNovel()) {
        try {
          PackageRegistry reg = kbuilder.getPackageRegistry(typeDescr.getNamespace());
          String availableName = typeDescr.getType().getFullName();
          Class<?> resolvedType = reg.getTypeResolver().resolveType(availableName);
          if (!Thing.class.isAssignableFrom(resolvedType)) {
            if (!resolvedType.isInterface()) {
              kbuilder.addBuilderResult(
                  new TypeDeclarationError(
                      typeDescr,
                      "Unable to redeclare concrete class "
                          + resolvedType.getName()
                          + " as a trait."));
              return;
            }
            updateTraitDefinition(type, resolvedType, false);

            String target = typeDescr.getTypeName() + TraitFactory.SUFFIX;
            TypeDeclarationDescr tempDescr = new TypeDeclarationDescr();
            tempDescr.setNamespace(typeDescr.getNamespace());
            tempDescr.setFields(typeDescr.getFields());
            tempDescr.setType(target, typeDescr.getNamespace());
            tempDescr.setTrait(true);
            tempDescr.addSuperType(typeDescr.getType());
            tempDescr.setResource(type.getResource());
            TypeDeclaration tempDeclr = new TypeDeclaration(target);
            tempDeclr.setKind(TypeDeclaration.Kind.TRAIT);
            tempDeclr.setTypesafe(type.isTypesafe());
            tempDeclr.setNovel(true);
            tempDeclr.setTypeClassName(tempDescr.getType().getFullName());
            tempDeclr.setResource(type.getResource());

            ClassDefinition tempDef = new ClassDefinition(target);
            tempDef.setClassName(tempDescr.getType().getFullName());
            tempDef.setTraitable(false);
            for (FieldDefinition fld : def.getFieldsDefinitions()) {
              tempDef.addField(fld);
            }
            tempDef.setInterfaces(def.getInterfaces());
            tempDef.setSuperClass(def.getClassName());
            tempDef.setDefinedClass(resolvedType);
            tempDef.setAbstrakt(true);
            tempDeclr.setTypeClassDef(tempDef);

            declaredClassBuilder.generateBeanFromDefinition(
                tempDescr, tempDeclr, pkgRegistry, tempDef);
            try {
              Class<?> clazz =
                  pkgRegistry.getTypeResolver().resolveType(tempDescr.getType().getFullName());
              tempDeclr.setTypeClass(clazz);

              pkgRegistry
                  .getTraitRegistry()
                  .addTrait(tempDef.getClassName().replace(TraitFactory.SUFFIX, ""), tempDef);

            } catch (ClassNotFoundException cnfe) {
              kbuilder.addBuilderResult(
                  new TypeDeclarationError(
                      typeDescr,
                      "Internal Trait extension Class '"
                          + target
                          + "' could not be generated correctly'"));
            } finally {
              pkgRegistry.getPackage().addTypeDeclaration(tempDeclr);
            }

          } else {
            updateTraitDefinition(type, resolvedType, true);
            pkgRegistry.getTraitRegistry().addTrait(def);
          }
        } catch (ClassNotFoundException cnfe) {
          // we already know the class exists
        }
      } else {
        if (def.getClassName().endsWith(TraitFactory.SUFFIX)) {
          pkgRegistry
              .getTraitRegistry()
              .addTrait(def.getClassName().replace(TraitFactory.SUFFIX, ""), def);
        } else {
          pkgRegistry.getTraitRegistry().addTrait(def);
        }
      }
    }
  }
  protected void createBean(
      AbstractClassTypeDeclarationDescr typeDescr,
      PackageRegistry pkgRegistry,
      ClassHierarchyManager hierarchyManager,
      List<TypeDefinition> unresolvedTypes,
      Map<String, AbstractClassTypeDeclarationDescr> unprocesseableDescrs) {

    // descriptor needs fields inherited from superclass
    if (typeDescr instanceof TypeDeclarationDescr) {
      hierarchyManager.inheritFields(
          pkgRegistry,
          typeDescr,
          hierarchyManager.getSortedDescriptors(),
          unresolvedTypes,
          unprocesseableDescrs);
    }

    TypeDeclaration type = typeDeclarationFactory.processTypeDeclaration(pkgRegistry, typeDescr);
    boolean success = !kbuilder.hasErrors();

    try {
      // the type declaration is generated in any case (to be used by subclasses, if any)
      // the actual class will be generated only if needed
      ClassDefinition def = null;
      if (success) {
        def =
            classDefinitionFactory.generateDeclaredBean(
                typeDescr, type, pkgRegistry, unresolvedTypes, unprocesseableDescrs);

        // now use the definition to compare redeclarations, if any
        // this has to be done after the classDef has been generated
        if (!type.isNovel()) {
          typeDeclarationFactory.checkRedeclaration(typeDescr, type, pkgRegistry);
        }
      }
      success = (def != null) && (!kbuilder.hasErrors());

      if (success) {
        updateTraitInformation(typeDescr, type, def, pkgRegistry);
      }
      success = !kbuilder.hasErrors();

      if (success) {
        declaredClassBuilder.generateBeanFromDefinition(typeDescr, type, pkgRegistry, def);
      }
      success = !kbuilder.hasErrors();

      if (success) {
        Class<?> clazz =
            pkgRegistry.getTypeResolver().resolveType(typeDescr.getType().getFullName());
        type.setTypeClass(clazz);
        type.setValid(true);
      } else {
        unprocesseableDescrs.put(typeDescr.getType().getFullName(), typeDescr);
        type.setValid(false);
      }

      typeDeclarationConfigurator.finalize(
          type, typeDescr, pkgRegistry, kbuilder.getPackageRegistry(), hierarchyManager);

    } catch (final ClassNotFoundException e) {
      unprocesseableDescrs.put(typeDescr.getType().getFullName(), typeDescr);
      kbuilder.addBuilderResult(
          new TypeDeclarationError(
              typeDescr,
              "Class '"
                  + type.getTypeClassName()
                  + "' not found for type declaration of '"
                  + type.getTypeName()
                  + "'"));
    }

    if (!success) {
      unresolvedTypes.add(new TypeDefinition(type, typeDescr));
    } else {
      registerGeneratedType(typeDescr);
    }
  }
  protected void checkRedeclaration(
      AbstractClassTypeDeclarationDescr typeDescr,
      TypeDeclaration type,
      PackageRegistry pkgRegistry) {
    TypeDeclaration previousTypeDeclaration =
        kbuilder
            .getPackageRegistry(typeDescr.getNamespace())
            .getPackage()
            .getTypeDeclaration(typeDescr.getTypeName());

    try {
      // if there is no previous declaration, then the original declaration was a POJO
      // to the behavior previous these changes
      if (previousTypeDeclaration == null) {
        // new declarations of a POJO can't declare new fields,
        // except if the POJO was previously generated/compiled and saved into the kjar
        Class<?> existingDeclarationClass =
            TypeDeclarationUtils.getExistingDeclarationClass(typeDescr, pkgRegistry);
        if (!kbuilder.getBuilderConfiguration().isPreCompiled()
            && !GeneratedFact.class.isAssignableFrom(existingDeclarationClass)
            && !type.getTypeClassDef().getFields().isEmpty()) {
          try {
            Class existingClass =
                pkgRegistry
                    .getPackage()
                    .getTypeResolver()
                    .resolveType(typeDescr.getType().getFullName());
            ClassFieldInspector cfi = new ClassFieldInspector(existingClass);

            int fieldCount = 0;
            for (String existingFieldName : cfi.getFieldTypesField().keySet()) {
              if (!cfi.isNonGetter(existingFieldName)
                  && !"class".equals(existingFieldName)
                  && cfi.getSetterMethods().containsKey(existingFieldName)
                  && cfi.getGetterMethods().containsKey(existingFieldName)) {
                if (!typeDescr.getFields().containsKey(existingFieldName)) {
                  type.setValid(false);
                  kbuilder.addBuilderResult(
                      new TypeDeclarationError(
                          typeDescr,
                          "New declaration of "
                              + typeDescr.getType().getFullName()
                              + " does not include field "
                              + existingFieldName));
                } else {
                  String fldType = cfi.getFieldTypes().get(existingFieldName).getName();
                  fldType =
                      TypeDeclarationUtils.toBuildableType(fldType, kbuilder.getRootClassLoader());
                  TypeFieldDescr declaredField = typeDescr.getFields().get(existingFieldName);
                  if (!fldType.equals(
                      type.getTypeClassDef().getField(existingFieldName).getTypeName())) {
                    type.setValid(false);
                    kbuilder.addBuilderResult(
                        new TypeDeclarationError(
                            typeDescr,
                            "New declaration of "
                                + typeDescr.getType().getFullName()
                                + " redeclared field "
                                + existingFieldName
                                + " : \n"
                                + "existing : "
                                + fldType
                                + " vs declared : "
                                + declaredField.getPattern().getObjectType()));
                  } else {
                    fieldCount++;
                  }
                }
              }
            }

            if (fieldCount != typeDescr.getFields().size()) {
              kbuilder.addBuilderResult(reportDeclarationDiff(cfi, typeDescr));
            }
          } catch (IOException e) {
            e.printStackTrace();
            type.setValid(false);
            kbuilder.addBuilderResult(
                new TypeDeclarationError(
                    typeDescr,
                    "Unable to redeclare "
                        + typeDescr.getType().getFullName()
                        + " : "
                        + e.getMessage()));
          } catch (ClassNotFoundException e) {
            type.setValid(false);
            kbuilder.addBuilderResult(
                new TypeDeclarationError(
                    typeDescr,
                    "Unable to redeclare "
                        + typeDescr.getType().getFullName()
                        + " : "
                        + e.getMessage()));
          }
        }
      } else {

        int typeComparisonResult = this.compareTypeDeclarations(previousTypeDeclaration, type);

        if (typeComparisonResult < 0) {
          // oldDeclaration is "less" than newDeclaration -> error
          kbuilder.addBuilderResult(
              new TypeDeclarationError(
                  typeDescr,
                  typeDescr.getType().getFullName()
                      + " declares more fields than the already existing version"));
          type.setValid(false);
        } else if (typeComparisonResult > 0 && !type.getTypeClassDef().getFields().isEmpty()) {
          // oldDeclaration is "grater" than newDeclaration -> error
          kbuilder.addBuilderResult(
              new TypeDeclarationError(
                  typeDescr,
                  typeDescr.getType().getFullName()
                      + " declares less fields than the already existing version"));
          type.setValid(false);
        }

        // if they are "equal" -> no problem

        // in the case of a declaration, we need to copy all the
        // fields present in the previous declaration
        if (type.getNature() == TypeDeclaration.Nature.DECLARATION) {
          mergeTypeDeclarations(previousTypeDeclaration, type);
        }
      }

    } catch (IncompatibleClassChangeError error) {
      // if the types are incompatible -> error
      kbuilder.addBuilderResult(new TypeDeclarationError(typeDescr, error.getMessage()));
    }
  }