UCOSPLogQuinlanJung

Kevin Brightwell edited this page Aug 26, 2015 · 1 revision
Clone this wiki locally

Each UCOSP participant is asked to log all their accomplishments as well as the difficulties they have getting set up to understand Umple and other experiences they have. Difficulties might include understanding how to model in Umple, using UmpleOnline, code that is difficult to figure out, and development tools that are hard to figure out, or don't seem to work properly. The more log entries added the more we can better get a grip on the usability issues with Umple, and the more we can help future new contributors come up to speed.

Log entries in reverse chronological order

April 8

Hooray! I have finished implemented preconditions with all their tests.

This was the first extension of the umple project where i had to edit jet files (which was the solution to April1's problems). I had a lot of trouble getting it to compile -- im pretty bad with eclipse and i had my configurations all wrong when i was trying to get the jet templates to compile. Eventually, i got them working.

After i had gotten the jet stuff to work, everything just fell into place. Had to make two commits though -- i wanted to make sure the java precondiitons were working for sure and the build passed before working on the other stuff just in case.

Its exam season for me now, so I think this will be my last UCOSP log.

April 1

I started trying to implement the preconditions today, but I ran into a pretty big obstacle. Here is the problem:

Consider the following test.ump code:

class Client {
  Integer age;

int someMethod(Integer arg) {
 [pre: age > 5]
 // rest of stuff that we don't interpret
}
}

In order to inject code into the beginning of someMethod, I assumed the following call would be able to do it:

CodeInjection before = new CodeInjection("before", "someMethod", "ALL THE PRECONDITION CODE", aClass);
before.setIsInternal(true);
aClass.addCodeInjection(before);

However, this does not work. Upon further inspection, it appears that all the methods in code injections is called using getApplicableCodeInjections() in UmpleClass. This method is then called in JavaClassGenerator, where every code injectable method is inserted depending on which jet template is used. Thats as far as ive gotten, more troubleshooting to come.

March 29

So i started working on the pre/post conditions today. They are both present in the grammar, but not added to any parent rule. Unfortunately, once I added them to a parent rule, they still did not parse, so had to fix those definitions. Eventually, I made the following changes to the grammar and made sure they all parse successfully.


precondition : [ [name]? pre: [[constraint]] ]
postcondition : [ [name]? post: [[constraint]] ]

methodBody- : ( [[precondition]] )* [**code] ( [[postcondition]] )*
concreteMethodDeclaration :  [type] [[methodDeclarator]] ([[codeLang]] [[codeLangs]])? { [[methodBody]] } ( [[moreCode]] )*

Now that we've got it parsing, here is how I think the pre/post conditions should be implemented. Suppose we have something like this:

class Client {
  Integer minAge;
  Integer age;

int someMethod(Integer arg) {
 [pre: arg > 5]
 // rest of stuff that we don't interpret
 [post: result < 10]
}

The precondition should check if arg>5 at the beginning of the method. The postcondition should check if result < 10 at the end of the method. If not it should throw an Exception, or return null.

Oh wait... while writing this log, I just realized I'll need to somehow get the postcondition to inject code before any return statement. Thats going to be tricky.

March 25

Committed the changes for negation operator on constraints with tests. Will now tackle post and pre conditions for constraints. They are currently in grammar (and I think they parse), but they are not implemented yet.

March 24

Tinkered around with the umple grammar, and got the ! operator to parse eventually. Couple issues with the token processing -- say for example, we wanted to parse constraint [! (age<3)]

After parsing the ! token, I wanted to create a dummy token with which i could add all the remaining subtokens (age<3), so I could pass it into the constraintAnalysis method again. Problem was, everytime when you call addSubToken(s), if the parent token isnt the same, it deletes s from the original parent token. Because I had code that was also iterating through the list (ie)

for (Token s : subTokensList){
      dummyToken.addSubToken(s); // deletes s from subTokensList
}

I was getting concurrent access exceptions. I was confused for a really long time, because I didnt realize addSubTokens had this behaviour, but the fix i used was just to make another instance of the list to iterate over.

I should have the patch committed plus tests for the ! operator by tomorrow morning.

March 23

Tried to implement the ! operator, but was encountering some really weird grammar issues. Have written all the code injection stuff, but need to figure out how to actually get the grammar to parse.

The grammar I came up with:

negativeConstraint : ( [[notOp]] OPEN_ROUND_BRACKET ([[constraintBody]]) CLOSE_ROUND_BRACKET) | ( [[notOp]] [[constraintBody]])

notOp- : [=notOp:~|!|not]

Made a constraint like: [! 18<age] get properly parsed.

But something like: [18<age] is getting parsed as a negativeConstraint

Geoff had grammar that did parse, that i used as a stepping off point.

There are also other problems with constraints (ie), constraints with brackets arent getting properly parsed. A constraint like [(age<4) && (weight<3)] doesnt get properly parsed as a "constraint". Was trying to fix that as well, but weird grammar is weird.

March 17

Today was the most nerve wracking experience ever -- i committed my first change. Fortunately, the build passed. So now, the basic boolean operators are implemented along with their tests. Awesome.

March 16

Hurrah! Finally was able to extend the constraints to include && and ||. Had a lot of trouble with the grammar part, but with some help from geoff, we got the grammar to pickup on the linking operators (| ||, &&), rather than just dumping to extraCode. After some tinkering, it was only a matter of time until i got the && and || to get generated in the code for php, cpp , rb and java.

Gonna try to commit this patch myself. But first things first -- gotta finish writing the tests and read up the committing instructions. Should be done all that by tomorrow.

March 12

This past week was quite interesting. Geoff overhauled the entire constraints system, and the changes he made were very impressive. It took quite a while for me to go through all the changes to figure out what was going on (not his fault. his code was super readable, just that there was a lot to go through). What was really good though was that when i looked at how he was changing the code, there were a lot of "aha" moments -- things that i was originally confused on how i would implement suddenly made more sense when i looked at some of the things he added to the constraints.

For extending stuff in Issue 187 (on Google Code) , I was tinkering around with the grammar and parsing dummy .ump files in an attempt to add more to the operator part of the umple system. Im having quite a few problems:

  1. For the longest time, I was trying to figure out why my constraints werent even getting parsed (ie) the template tests seemed to be passing, yet the dummy files i was trying to parse via command line were getting ignored. It is interesting to note that spaces that were originally irrelevant in constraints now matter.

(ie)[age<3] wont match any grammar rules whereas when you add spaces, [age < 3]gets parsed correctly

  1. Im trying to add extra grammar rules, but am not having much success. All the constraints now get dumped into [ExtraCode].

For example, one of the things i am trying to change are the constraintBody such that we can have the && operators.

Before we had

constraintBody- :  [[stringExpr]] | [[boolExpr]] | [[genExpr]] | [[numExpr]] | [loneBoolean] ( [ [index] ] )? 

Which I wanted to change to

logicalAndOp- : [=logicalAndOp:AND|&&] 
constraintExpr- :  [[stringExpr]] | [[boolExpr]] | [[genExpr]] | [[numExpr]] | [loneBoolean] ( [ [index] ] )? 
constraintBody- :  [[constraintExpr]] ([[logicalAndOp]] [[constraintExpr]])*

But when I inspect the tokens, the constraint just gets dumped to ExtraCode. In the change i made above, i am assuming the * has the same effect as the regex * , which means the expression in parenthesis can be present 0 or more times.

March 4 ish

I think the constructor patch was committed around this time.

Not only did the patch solve the constructor issue in the different language, but it also solved some of the code generation problems from before. Originally, the code generated in ruby and php werent compilable code (some places were missing parenthesis, etc). So fixed that and wrote the full template tests for everything. (rewrote a bunch of my tests that werent as robust from feb 18th)

March 2

Pretty much done the constructor issue. Originally, I had the fix where all the code was placed in the generators, but felt that there should be a more efficient way to do this. Tim pointed out that Geoff had done some work with multilingual code injections -- it would be ideal if i could use his implementations such that I can have a code injection in one place (instead of writing it in 5 different language generators).

After I had emailed Geoff, who was super helpful in explaining how his code injections worked, I spent a really long time trying to implement this, but was relatively unsuccessful. Here are the issues I encountered:

What I wanted to do was start with an .ump file with a constraint, (ie)

class student_mod
{
  Integer age;
  [(18>age)]
 }

then turn it into code with the same effects as

before constructor {
    if (!18>aAge)
    {
      throw new RuntimeException("Please provide a valid query");
    }
  }

The before/after code was mainly in analyzeInjectionCode() in the UmpleInternalParser. In order to generate the appropriate constraint code i felt like i had 2 options:

  1. Modify the token tree such that we have a codeinjection token where the invariant token used to be. This way, we can pass it into analyzeinjectioncode().

Result: I attempted this, but later felt that it probably wasnt a very good idea, since it goes against a lot of design principles to re-modify the token tree in the middle of a parse.

  1. Use the methods called by analyzeinjectioncode() to parse the constraint myself.

Result: I attempted this -- methods such as setCode, which allow you to put inject code for a specific language, made this option seem very promising. Partway through this implementation I realized that I needed to use the translate() method, which was responsible for fetching the names of function parameters, attributes, function names, etc. Since this method was only available in the generators , i'd wind up witht he same problem again because i'd have to write the code in 5 different places.

Conclusion: I'm not sure if its doable to fix the constructor problem without putting code in all the different generators.

For the original fix (code in the different generators), ill have the full patch with tests out by tomorrow.

February 22

Before extending the constraints, Tim made me aware of the fact that no code is generated in an object's constructor despite the presence of a constraint. I decided to work on this before doing anything else first.

After looking at the way code injections were done in Umple, as well as the translate method, I thought I had come up with a way to fix this problem. I was able to find where the code was being injected in the setMethods, so I thought I could just add the same code injection to the constructor. Unfortunately, no code gets placed in the constructor, and I am not quite sure why.

I am attempting to figure out why this might be the case. In the meantime, I'll shoot Tim an email with the changes I made and see if he can give me any advice about this.

February 18

Umple Tests Finished writing the tests, with one issue though:

For issue 366 (on Google Code), part of the fix was to make sure that a constraint such as [age<maxAge] generated code such that the values of age and maxAge would be restricted. Before I fixed it, it still generated valid code, but the values of maxAge were not restricted.

I am not sure how I would write a test (besides check for a successful parse) to make sure that the values of both attributes are being restricted. I have seen the number of classes, attributes and interfaces being checked (using umple model), but I'm not certain how to test for two attributes being constrained.

Extending OCL Constraints As for figuring out what to extend, I'm evaluating the three possible options:

  1. implement the preconditions/postconditions. They seem to exist already in the grammar, but i dont think they are being carried over to the generated code,

  2. Implement more operators (for single objects). Currently, only a few boolean operators are allowed (ie) <, =, >, and !=. Im thinking of implementing more operators (ie) implies, xor, not, and, as well as the ability for the user to write constraints like age + maxAge > 8.

  3. Implement aggregate operators such as isEmpty, size, avg, sum , etc. For attributes where they are sets or collections, be able to constrain their size/properties (ie) employees->size >= 50.

February 17

Finally finished overhauling the code, have yet to write the tests though.

As for improving the umple grammar, the OCL invariants (ie) [x<8] are now pretty robust. Im thinking that I can start implementing new features for the OCL constraints, but Im not too sure where to start. I've been reading http://www.iro.umontreal.ca/~sahraouh/qaoose01/OCL_standard.pdf , an article about OCL specifications to figure out what i should tackle next.

February 11-17

I spent several days overhauling the entire constraints system, and trying to figure out how I can better improve the grammar.

For the longest time, I was trying to differentiate between a primitive and an actual variable encoded in a String (ie) "7" vs "x". If I am able to do this, I can solve pretty much all the child issues of 187 as well as make the code way more efficient and easier to upgrade. I have all the restructuring done, except for this check, which I have been relatively unsuccessful at figuring out how to implement.

I spent quite a bit of time trying to make a regex function to do this until I realized there was already a function that existed in the token class, isValidIdentifier() to do this. Oh well.

February 6

In this entry, I will cover the following items: Issue 365 (on Google Code) and debugging methods

A.) Issue 365 (on Google Code) (master issue: Issue 187 (on Google Code))
Background:
This week, I was assigned 3 issues revolving around improving the constraints in the Umple compiler. Apparently, some work had been done on constraints before. In order to better understand how the constraints were being implemented, I decided to tackle Issue 365 (on Google Code), an issue that seemed relatively straightforward. This would let me better understand the entire system so i can later work on 187.

Issue:

If there is a constraint with a non existent attribute, parsing fails but no error is raised.
(ie) '['nonExistentAttribute > 12']'

After some tinkering and System.out.println's, I have figured out that most of the constraint processing is in UmpleInternalParser's analyzeInvariant method. There, I can take all the constraintVals and run a check on them.

In the above example, "nonExistentAttribute" and "12" would be the constraintVals. The problem now, would be to implement a good way to distinguish between a primitive type and a variable. For instance, we want to be able to identify that 12 is a primitive value and not check if it an existing attribute. On the other hand, we want to identify nonExistentAttribute as an attribute, and later check if it actually exists.

However, the only way i can think to differentiate whether the constraintVal is a primitive type or not is to attempt to convert it into an int, float, double, etc. If all of them fail the parsing, then I would be able to conclude that the constraintVal is an attribute. This is where I can check for the existence of this attribute.

I'm not too sure if this is the best way to approach this problem, so I will check with Tim first before I attempt to implement the fix.


B.) A better way to debug

Currently, to test all my changes, I am doing a full build to see the results of what I have just coded. This was ok for the simpler issues, but now its starting to take a bit too long. Ideally, I was looking for a way where I could change the java source code and compile it into umple.jar without running any tests.

After some tinkering and lots of help from Geoff (in addition to using his makefile), I realized that if you called compile and package, it will compile all the code from the .java file into umple.jar. This method takes about 12 seconds, compared to 2minutes for the full build.

This is advantageous because once I am sure my changes work, I can just apply them to the corresponding .ump file. It is relatively hard for me to read code in the .ump file because the eclispe ide doesnt highlight the code.

January 24

Issue 237 (on Google Code): You cannot declare an association between a Class and Interface

Desired Solution: Allow the Class-Interface association, only if there exists an '->'

Fix:1. in verifyClassesInUse(), I ran a check through all Classes (and Interfaces) referenced in associations. If they weren't a class AND an interface, I raised an error. This way, classes and interfaces can be referenced in an association.

2. in postTokenClassAnalysis(), I called a helper function I made, checkClassInterfaceAssociations(), which basically checks for the '->' in the class-interface association.

Discussion About My Implementation Approach: For Fix (1.), the problem I was having was that if you allow for Class-Interface Associations, they end up in the Umple Model's list of associations. A lot of the methods called in UmpleInternalParser assumed that there were only class-class associations. As a result, there were a lot of for-loops that would iterate over associations, extract the UmpleClass from it, and call UmpleClass specific functions.

The problem here is that now, we also have UmpleInterfaces being extracted, and you cannot call the UmpleClass functions on them. In my mind, there were 3 possible solutions:

1. Remove the Interface-Class Association for the UmpleModel list of associations, so when they are iterated, we wont have the problem. (Bad idea though, especially if you want to work with this kind of association in the future.)

2. Rewrite the methods that are causing the errors in UmpleInternalParser, such that they take type UmpleClassifier (superclass of interface and class). Theoretically, this sounds like a good idea, but a lot of the UmpleClass methods being called dont belong in UmpleInterface, so it wouldnt make sense to write the interface version of these methods. I could have also differentiated them in the for loop by using if-else statements, but the code would've gotten very messy/unreadable.

3. Every time the associations in umplemodel were being iterated, and the classes were being extracted to call the class specific functions, I did a check every iteration. This check would determine whether the association was an interface-class one. If it was, I would "continue", aborting the current iteration and continuing on to the next one.

I thought (3.) was the best compromise because it maintains readability of the code and allows for future developers to continue using the class-interface association.

Long Term Issues for Current Developers: I have noticed that people who have been working on UmpleInternalParser have been making the assumption that only Classes can be present in a association, which is clearly no longer the case. If they continue to make that assumption, and attempt to extract 2 umpleclasses out of every association to call the umpleclass functions, we will get a lot of nullpointerexceptions. Perhaps make a notice that interfaces can now exist in an association?

January 20

During the sprint, I had a lot of help from tim and group members to clear up a lot of questions i had about umple, good svn practices, making patches , etc.

I was able to finish issue #344 (on Google Code) and start on issue #237 (on Google Code).

In the end, issue #344 (on Google Code) required a lot of refactoring for interfaces and classes. The issue was labelled as very easy (an estimated 3-4 lines to add/fix), but ended up requiring much more work. Here is what I did:

1. Made a super class, UmpleClassifier, that the interface and class both extended.

2. The ramifications of (1.) meant that the interface now had a field for tokens, which allowed me to pass in the position of interface items into error messages.

3. I had to make 3-4 interface specific methods to initialize the token fields.

addUnlinkedExtends --------> unlinkedExtends ------> init by addExtendsTo

setExtendsToken unlinkedExtendsTokens

The figure above are the fields and methods involved in the token initialization process for classes. I had to make the interface version for all the fields and methods shown above.

4. I thoroughly tested the method i made to detect interface cycles

5. I learned how to update the en.error document to change the error messages.

January 15

I started working on my first issue, #344 today. I have a problem regarding my issue.

Background: For Umple Classes, the method that checks for cycles is checkExtendsForCycles() in UmpleInternalParser_CodeClass.ump. Now, the issue here is that I cannot directly use this method to check the UmpleInterface cycles because it was written to take UmpleClasses as parameters and return values.

What I am trying to do:

  1. I am trying to write a different set of functions for the UmpleInterface, basically a rewritten version of checkExtendsForCycle(...) and recursiveCycleCheck(...).

  2. I have been successful with recursiveCycleCheck -- because Interfaces can have multiple "parents", I needed to rewrite it so that it recursed over lists of Interfaces. (Side effect is bad function runtime though).

    What I have trouble doing:

I am having trouble rewriting the last bit of checkExtendsForCycle. In particular, it is the bit that generates the ErrorMessage that I have trouble with. For the UmpleClass, there is a method, getExtendsToken(), that is called to differentiate and initialize the ErrorMessages. However, there does not seem to be an equivalent for this in UmpleInterface. Now I am kind of stuck.


January 13

After I posted my log on January 10, Geoffrey and Timothy were really helpful and cleared up a lot of questions I had with umple. Most of my prior knowledge of compilers basically came from this article that I read. So if any future reader chances upon any of my log entries, and suspects I might not be grasping certain concepts, I would really appreciate it if they emailed me to clarify some of the things I might not be getting!

Today, I tried to figure out how the command line compiler worked. I started off looking at UmpleConsoleMain and was looking at all the method calls and what they did. I eventually wound up looking at the parse method in UmpleInternalParser.

At first, I didnt understand what the Rule object was, until I realized they were the rules that were parsed from the .grammar files using the innerParser upon initialization of the UmpleInternalParser.

So then, I decided to look at the grammar files. But upon seeing them, I realized I should probably learn some EBNF in order for it to make more sense as well as Grammar notation on the umple site. i got through most of the EBNF wikipedia article and about half of the umple grammar articles until i decided to call it quits and resume tomorrow morning.

January 10

Development set up: Had a couple problems doing development setup.
1. I didnt check out the entire trunk (just the cruise.umple and the other "core" projects) leading to much confusion, as all the subsequent steps didnt work

2. After checking out the trunk, none of my Junit tests (run through Eclipse) passed. The solution was to do a complete build from command line using ant and then refresh everything in eclipse.

3. The tests in BuilderTest.java kept failing. The problem was that my working directory contained a space (ie) Google Drive and when one of the methods tried to execute an ant command, it stopped parsing at the space of "Google Drive". It took me some time to figure this out, after playing around with the code and looking at the dist/qa/index.html

General education: Read through a lot of the architecture stuff. I'm not sure if I'm understanding this properly, but this is what I think Umple does at a high level:

1. We start with code containing xtext or jet template files

2. Using the umple compiler, it goes through the tokenizer, then the parser

3. The appropriate code generators are invoked to generate the resulting source code.

When I look at the src-gen folders, in cruise.umple im assuming that everything to do with compiling the code with xtext is in src-gen-umple and everything to do with the jet template files are in src-gen-jet. What i dont really understand is how the src-gen-jet can just contain the code generators (wouldnt the template files need to be tokenized and parsed?)

TODO'S for next week:
1. Keep reading more on the Umple project until I have a more solid understanding of it
2. Take a look at issues and see which ones would be a good idea to get started on.

January 5

Problem 0a: Had to brush up on UML syntax (was a long time since I had looked at that stuff), but once I did, I was able to complete it with the help of the UmpleOnline tool. Afterwards, I successfully compiled it with the compiler via command line without any issues.

Installation: Installed everything successfully. Had some trouble with Ant, but got it resolved eventually. Completed up to step 4 of this tutorial to make sure I didnt miss anything and Ant was indeed working as it should.

TODO's for next week:
1. Familiarize myself with the core components of the Umple compiler
2. Work through the additional setup, as outlined in the email (completion by jan 15th)