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())); } }
protected int compareTypeDeclarations( TypeDeclaration oldDeclaration, TypeDeclaration newDeclaration) throws IncompatibleClassChangeError { // different formats -> incompatible if (!oldDeclaration.getFormat().equals(newDeclaration.getFormat())) { throw new IncompatibleClassChangeError( "Type Declaration " + newDeclaration.getTypeName() + " has a different" + " format that its previous definition: " + newDeclaration.getFormat() + "!=" + oldDeclaration.getFormat()); } // different superclasses -> Incompatible (TODO: check for hierarchy) if (!oldDeclaration .getTypeClassDef() .getSuperClass() .equals(newDeclaration.getTypeClassDef().getSuperClass())) { if (oldDeclaration.getNature() == TypeDeclaration.Nature.DEFINITION && newDeclaration.getNature() == TypeDeclaration.Nature.DECLARATION && Object.class.getName().equals(newDeclaration.getTypeClassDef().getSuperClass())) { // actually do nothing. The new declaration just recalls the previous definition, probably // to extend it. } else { throw new IncompatibleClassChangeError( "Type Declaration " + newDeclaration.getTypeName() + " has a different" + " superclass that its previous definition: " + newDeclaration.getTypeClassDef().getSuperClass() + " != " + oldDeclaration.getTypeClassDef().getSuperClass()); } } // different duration -> Incompatible if (!nullSafeEqualityComparison( oldDeclaration.getDurationAttribute(), newDeclaration.getDurationAttribute())) { throw new IncompatibleClassChangeError( "Type Declaration " + newDeclaration.getTypeName() + " has a different" + " duration: " + newDeclaration.getDurationAttribute() + " != " + oldDeclaration.getDurationAttribute()); } // //different masks -> incompatible if (newDeclaration.getNature().equals(TypeDeclaration.Nature.DEFINITION)) { if (oldDeclaration.getSetMask() != newDeclaration.getSetMask()) { throw new IncompatibleClassChangeError( "Type Declaration " + newDeclaration.getTypeName() + " is incompatible with" + " the previous definition: " + newDeclaration + " != " + oldDeclaration); } } // TODO: further comparison? // Field comparison List<FactField> oldFields = oldDeclaration.getTypeClassDef().getFields(); Map<String, FactField> newFieldsMap = new HashMap<String, FactField>(); for (FactField factField : newDeclaration.getTypeClassDef().getFields()) { newFieldsMap.put(factField.getName(), factField); } // each of the fields in the old definition that are also present in the // new definition must have the same type. If not -> Incompatible boolean allFieldsInOldDeclarationAreStillPresent = true; for (FactField oldFactField : oldFields) { FactField newFactField = newFieldsMap.get(oldFactField.getName()); if (newFactField != null) { // we can't use newFactField.getType() since it throws a NPE at this point. String newFactType = ((FieldDefinition) newFactField).getTypeName(); if (!newFactType.equals(((FieldDefinition) oldFactField).getTypeName())) { throw new IncompatibleClassChangeError( "Type Declaration " + newDeclaration.getTypeName() + "." + newFactField.getName() + " has a different" + " type that its previous definition: " + newFactType + " != " + oldFactField.getType().getCanonicalName()); } } else { allFieldsInOldDeclarationAreStillPresent = false; } } // If the old declaration has less fields than the new declaration, oldDefinition < // newDefinition if (oldFields.size() < newFieldsMap.size()) { return -1; } // If the old declaration has more fields than the new declaration, oldDefinition > // newDefinition if (oldFields.size() > newFieldsMap.size()) { return 1; } // If the old declaration has the same fields as the new declaration, // and all the fieds present in the old declaration are also present in // the new declaration, then they are considered "equal", otherwise // they are incompatible if (allFieldsInOldDeclarationAreStillPresent) { return 0; } // Both declarations have the same number of fields, but not all the // fields in the old declaration are present in the new declaration. throw new IncompatibleClassChangeError( newDeclaration.getTypeName() + " introduces" + " fields that are not present in its previous version."); }