Menu

FieldDeclaration - add attribute to a class

Developers
2011-05-27
2012-10-08
  • Andrea Poli

    Andrea Poli - 2011-05-27

    Hi, I'm writing a method which converts java static blocks inside a class
    declaration into a new static method. The body of this method is composed of:
    (1) an if statement which checks if the method has been already called
    (2) pieces of code of static blocks

    I have to add a bool static attribute in order to write (1).

    To add a static attribute I perform the following steps:
    1. creating a FieldDeclaration using createFieldDeclaration(TypeReference, Identifier) of factory class

    ProgramFactory factory = clazz.getFactory();
    String boolTypeName = clazz.getProgramModelInfo().getServiceConfiguration().ge
    tNameInfo().getBooleanType().getName();
    TypeReference boolType =
    factory.createTypeReference(factory.createIdentifier(boolTypeName));

    FieldDeclaration flag = factory.createFieldDeclaration(boolType, new
    Identifier("static_initializer_flag"));

    1. adding STATIC and PROTECTED to the FieldDeclarationSpecifier

    ASTList<DeclarationSpecifier> decs = new
    ASTArrayList<DeclarationSpecifier>();
    decs.add(factory.createStatic());
    decs.add(factory.createProtected());
    flag.setDeclarationSpecifiers(decs);

    1. adding FieldDeclaration to class members

    clazz.addVariableToScope(flag.getVariables().get(0));
    clazz.getMembers().add(flag);
    clazz.makeParentRoleValid();

    or

    Transformation.doAttach(flag, clazz, 0);

    1. updating the model

    ChangeHistory changeHistory =
    clazz.getProgramModelInfo().getServiceConfiguration().getChangeHistory();

    changeHistory.attached(flag);
    changeHistory.commit();
    changeHistory.updateModel();

    clazz is the ClassDeclaration related to the class where I want to add the
    static attribute.

    When the program executes I receive this execption when it tries to perform
    step (4):

    Unexpected error: java.lang.NullPointerException. End.
    java.lang.NullPointerException
    at recoder.service.DefaultSourceInfo.getVariable(DefaultSourceInfo.java:1656)
    at recoder.service.DefaultSourceInfo.getField(DefaultSourceInfo.java:1756)
    at recoder.service.DefaultSourceInfo.getVariable(DefaultSourceInfo.java:1796)
    at recoder.service.DefaultCrossReferenceSourceInfo.analyzeReferences(DefaultCr
    ossReferenceSourceInfo.java:599)
    at recoder.service.DefaultCrossReferenceSourceInfo.reset(DefaultCrossReference
    SourceInfo.java:705)
    at recoder.service.DefaultCrossReferenceSourceInfo.modelChanged(DefaultCrossRe
    ferenceSourceInfo.java:214)
    at recoder.service.ChangeHistory.updateModel(ChangeHistory.java:500)
    at ....IClassMethodFactory$StaticBlockAggregator.createMethod(IClassMethodFact
    ory.java:147)

    I'm doing something wrong but I don't understand what :-) Thanks.

     
  • Tobias Gutzmann

    Tobias Gutzmann - 2011-05-30

    Hej,

    it looks to me that parent links are not set. All program elements need to
    have a link to their parent NonTerminalProgramElement set.
    Four ways of doing it:
    1. call "makeParentRoleValid" on everything where you add (I recommend that)
    2. call set(Parent|StatementContainer) etc. for each element that has a new parent (not recommend, tedious and methods setting the parents have different names in different classes)
    3. call CompilationUnit.makeAllParentRolesValid() - this will set the parent links for the entire AST. Good for testing if that's the case, but not necessarily the most performant way.
    4. use Transformation.(doAttach|doReplace) - they should take care of setting proper parent links.

    Regards,
    Tobias

     
  • Andrea Poli

    Andrea Poli - 2011-06-01

    I created a subclass, XALTransformation, of recoder.kit.Transformation which
    implements the following methods:

        public void attach(FieldDeclaration field, ClassDeclaration clazz){
            if (field == null || clazz == null)
                throw new IllegalArgumentException("field and clazz can't be null");
            this.attach(field, clazz, 0);
        }
    
        public void attach(MethodDeclaration method, ClassDeclaration clazz){
            if (method == null || clazz == null)
                throw new IllegalArgumentException("field and clazz can't be null");
    
            this.attach(method, clazz, 0);
        }
    

    Then I wrote the following code to add a method to a class:

                    /* other code ... */
                    body.makeAllParentRolesValid();
    
            MethodDeclaration md = factory.createMethodDeclaration(modifiers, returnType, id, parameters, exceptions, body);
    
            md.setProgramModelInfo(clazz.getProgramModelInfo());
            md.setFactory((JavaProgramFactory) factory);
            md.makeAllParentRolesValid();
    
            XALTransformation t = new XALTransformation((CrossReferenceServiceConfiguration) clazz.getProgramModelInfo().getServiceConfiguration());
    
            t.attach(md, clazz);
    
            ChangeHistory changeHistory = clazz.getProgramModelInfo().getServiceConfiguration().getChangeHistory();
    
            try{
                changeHistory.begin(t);
                t.execute();
                changeHistory.updateModel();
                changeHistory.commit();
            }catch(Exception e){
                changeHistory.rollback(t);
                this.log(e, md.getName());
                return null;
            }
    

    I added only an If and some CopyAssignment statement to method body. Every
    time an If or a CopyAssignment is created the method makeAllParentRolesValid()
    is called on it.

    Using this code I can't still add methods to certain classes receiving an
    error during updateModel call.

    Thanks a lot :-)

     
  • Andrea Poli

    Andrea Poli - 2011-06-07

    I found that the error related to the model update depends on the following
    TypingException:

    inner type has type arguments, but the outer does not.

    which is thrown after the following statement has been analyzed:

    for (Map.Entry<String, Object> e : namedList.entrySet()) { ...
    

    namedList is correctly declared previously:

    protected Map<String, Object> namedList = new LinkedHashMap<String, Object>();
    

    The TypingException is thrown inside the checkRawTypeOk method,
    recoder.service.SemanticsChecker.

                        if (!trType.isStatic() && rpType instanceof ErasedType) {
                            errorHandler.reportError(
                                    new TypingException(
                                            "inner type has type arguments, but the outer does not.",null));
                        }
    
     
  • Tobias Gutzmann

    Tobias Gutzmann - 2011-06-12

    It's a bug. Fixed in SVN / next release.

     
  • Andrea Poli

    Andrea Poli - 2011-06-12

    Thanks. :-)

     

Log in to post a comment.