DevBoost / JaMoPP

JaMoPP can parse Java source and byte code into EMF-based models and vice versa. It preserves source formatting and can be used for code analysis and refactoring.
17 stars 18 forks source link

How to reference existing classes? #28

Closed AresEkb closed 8 years ago

AresEkb commented 9 years ago

Hi

I'm writing a QVTo transformation from UML into Java code. Here is a fragment:

    object cu : JAVA::containers::CompilationUnit {
        name := self.name;
        classifiers := c;
        imports += object JAVA::imports::ClassifierImport {
            classifier := object JAVA::classifiers::Class {
                name := 'ArrayList';
            };
            namespaces := Sequence{'java', 'util'};
        };
    };

The problem is that the new instance of JAVA::classifiers::Class is created. How can I reference existing java.util.ArrayList class?

I've found the following example (http://www.reuseware.org/index.php/EMFText_JaMoPP_Applications_ATL)

java!Package.allInstances()->any(p | p.name = 'java.lang').compilationUnits->collect(
            cu | cu.classifiers)->flatten()->any(c | c.name = 'LinkedList')

But in QVTo JAVA::containers::Package.allInstances() returns nothing.

And also I've found a one more example (org.emftext.language.java.examples/uml2java/uml2java.atl)

    typeRef1 : java!ClassifierReference (
        target <- umlProperty.correctType(true),
        typeArguments <- if (umlProperty.upper = 1) then
                Sequence{}
            else
                Sequence{typeArgument}
            endif
    ),

helper context uml!Element def: correctType(outerType : Boolean) : java!Classifier = 
     if (self.type = OclUndefined)  then OclUndefined else
     if (self.upper <> 1 and outerType)  then java!Commentable.allInstances()->first().getConcreteClassifier('java.util.List') else     
     if (self.type.name = 'String') then java!Commentable.allInstances()->first().getStringClass() else
     self.type 
     endif endif endif;

The target Java model is empty. It doesn't contain any instances.

Thanks!

AresEkb commented 9 years ago

I've added the following method into my QVTo blackbox library:

    public static org.emftext.language.java.classifiers.ConcreteClassifier createJavaClassifier(String fullQualifiedName) {
        return (org.emftext.language.java.classifiers.ConcreteClassifier)JavaClasspath.get().getClassifier(fullQualifiedName);
    }

It seems that it creates right references and the model is serialized well into xmi-file.

But when I try to save my model as *.java file I get the following code:

import java.util. //@classifiers[name='ArrayList'] ;
import java.util. //@classifiers[name='List'] ;

How to fix it?

jjohannes commented 9 years ago

The code of the example you are referring to can be found here: https://github.com/DevBoost/JaMoPP/tree/master/Examples/org.emftext.language.java.examples/uml2java

What you need to do is to add the existing classes from the Java classpath as another input model. Since most transformation engines expect a file of some sort of input, JaMoPP allows you to load a package-info.java file as input model and will automatically load all the classes in that package as well from the classpath. In the example, this was done for the ATL transformation like this:

https://github.com/DevBoost/JaMoPP/blob/master/Examples/org.emftext.language.java.examples/uml2java/build.xml#L5

AresEkb commented 8 years ago

Thanks a lot! It works.

I've created two trivial java-files:

package java.lang;

package java.util;

And pass them to my transformation as additional input models:

modeltype UML uses 'http://www.eclipse.org/uml2/2.1.0/UML';
modeltype JAVA uses 'http://www.emftext.org/java';
transformation ISO20022toJava(in m : UML, in lang : JAVA, in util : JAVA, out j : JAVA);

After that I can find the class I need:

lang.rootObjects()![JAVA::containers::Package].compilationUnits.classifiers
     ->selectOne(name = 'String')

Also there is even a simpler solution. I can call the getLibClass() method inside a mapping:

result.getLibClass('String')

Or just:

result.getStringClass()

Instead of result variable one can use any object inherited from JAVA::commons::Comentable. Also there are a lot of similar methods.

AresEkb commented 8 years ago

I can reference existing classes now. But I still have a serialization problem. When I execute my QVTo transformation inside Eclipse everithing works fine. But in stand-alone mode all references a serialized in java-file like this one:

//@classifiers[name='List']

I call this method in my code, but it seems that something else is needed to be initalized:

JaMoPPUtil.initialize();
jjohannes commented 8 years ago

This looks like the Java standard library (and potentially other libraries) from the classpath are not found. Usually, in standalone the standard library is registered automatically (in JavaClasspath.registerStdLib()).

When you run inside Eclipse, there is a JDT integration which takes care of most of these things. In standalone that is not used. The standard library location is instead resolved from system properties.

Issues might also occur, if you do not do everything (loading and serialisation) through one ResourceSetImpl. That is, because the JavaClasspath, which allows JaMoPP to resolve classes from other files, is attached to the ResourceSetImpl as an EMF Adapter.

Can you check your stand alone code if it behaves like this, or post it if you are not sure?

AresEkb commented 8 years ago

I've created the test project.

It behaves a little bit defferent from my main project.

When I execute QVTo transformation from Eclipse I get:

class SomeClass extends //@classifiers[name='String'] {
}

And in stand alone mode I get:

class SomeClass extends {
}

And I expect to get:

class SomeClass extends String {
}
jjohannes commented 8 years ago

In the test project, there is again the input library model(s) missing. In that case 'getStringClass()' just returns a proxy it can never resolve (and that what's JaMoPP fails to print correctly).

It works in standalone for me, if:

AresEkb commented 8 years ago

I've got it! I thought that methods like getStringClass() do all magic and I don't need input Java-models. I thought that I need them only If I find needed classes manually in the QVTo transformation. Thanks!