Each UCOSP participant is asked to log all the steps they take as they make progress through the semester. This includes difficulties they faced (getting set up, 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.
Please record entries in reverse chronological order
Note to self: Never run grep
piped into sed
in the root directory of a git project unless you use either the `--exclude
flag to exclude specific files, or --include
to restrict them. Last night I completely wiped my Umple repo by trying to do a bulk-replace for a job that could have been done in a matter of minutes. Not my finest decision. Spent a good while cloning everything and setting up the remotes, then redoing my work on the branch (since my work was nuked). Thankfully most of my work has already been pushed, so not much was lost.
After working on extracting everything into a separate directory for parsing I came across a small problem (not necessarily an bug or issue). It seems extending from classes marked external
don't generate automatic constructors or call the super constructor. It's possible this is intended behavior since external
classes and interface are coming from Java, so they wouldn't have contextual information from Umple. It's also likely this is already thought-of and I just misunderstand how to handle externals.
At any rate, this lead me to re-evaluate where the Parser
class, and a few related components were located. I originally moved them to be part of cruise.umple.parser
namespace expecting they had a more important purpose to the overall RuleBasedParser
, but it turns out that only the Token
,Position`, and error handling classes were important. So I extracted those out, which not only helped my original problem, but also makes it more usable.
After I managed to get it working I issued a pull request and had it accepted sometime this morning.
Good news, finally. After working through the test-cases one-by-one to find the source of the issue, it turns out that part of the issue was coming from the handlers being assigned as non-static. Given the way that the RuleBasedParser
is constructed and contains static variables that are kept across instantiations, it is also important to keep the handlers static as well -- otherwise not assigning it somewhere may change the analyzer's data -- which is not good.
Another source of the issue was the way that I was passing linked files. My method expected the source file, along with the files to be linked with the main file. Now, this shouldn't itself be a problem -- except for the fact that in init(File file)
I call init(file, null)
-- effectively passing an empty list of linked files. This was giving -- depending on the case -- nullpointer exceptions along with index-out-of-bounds errors. This, obviously, is not desireable -- and thus I have since fixed it.
I need to clean up and squash some of my commits together to make them a little more coherent, but after that I will be pushing to origin
and will wait for the travis build. If all is good, I should be issuing a PR soon and then all that's left is extracting it to a separate project/directory/repository (depending on how it's handled) and then adding it to the build step.
It is such a relief that all is working well now. It was really intimidating to see a huge collection of errors like that.
I'm still working on the failing tests. For some reason, once I switched everything over from UmpleFile
to File
, every test began to fail for different reasons. Some were "NullPointer" exceptions, others were "Index out of bounds", or just simply "expected <some random thing>
". I'm not sure what exactly is the problem here, but it's quite annoying that this is occurring.
I'm beginning to suspect this has something to do with the static variables in RuleBasedParser
. The parser is created in many tests (and originally it had to be changed in tests in order to make it compile properly, since it originally expected an UmpleFile
instead of a File
), and it often is only constructed without ever reading the rules. My understanding is that it is doing this since the rules and everything are handled statically, meaning that by constructing a new one it still points to the one with generated rules from somewhere outside of the test-cases.
I feel like this may be part of the problem though. Perhaps the tests aren't being run in the right order, or perhaps the construction isn't quite working right; I'm not sure exactly what the cause is, but it's the only reason that makes sense to me.
Currently, Umple compiles anything properly -- including itself. It's strictly the test-cases that are dying a horrible, horrible death and making this even harder. I'm not sure why the original design was to create multiple RuleBasedParsers instead of just simply doing model.getRuleBasedParser()
or something. Right now, the static variables appear to be the cause of virtually all the problems. I've reverted back to a previous building version that still contained UmpleFile
s, and will work from here for now.
I'm very close to releasing a pull request that should remove all ties of umple from the parser, but I encountered a very difficult issue that has held me up for almost an hour.
In attempting to update the parser to take File
instead of UmpleFile
, I had to modify many different tests in order to make it work. However, one of the tests constructed a large quantity of java/php/ruby files that then caused a series of huge compilation issues at command line! I'm not sure which file in particular is the cause of this, or why the files weren't deleted, but this caused even further problems. This issue couldn't be simply fixed by using a previous Umple jar or even doing a first-build. Because it failed midway through the tests, the java files were attempting to be compiled -- but failing -- even in the first build. I had to reset back to a previous revision and manually delete the generated files (wrote a script to do it quickly). That was a pain that took away a decent amount of work.
I'll be working more on this throughout the day to hopefully get the PR in. Then it will be just figuring out where the parser will be moved to, and how it will be imported into the project.
I made sure to test that the changes so far work independently of Umple. I created a separate umple project that contains symlinks to all the umple files used in the Parser (e.g. RuleBasedParser.ump, etc), and a Master.ump file that brings them all together. Then I wrote a small script that simple compiles it with Umple, followed by with java into a separate directory.
As long as I can get all the tests to pass successfully, then this work should be mostly done.
I didn't do any work on Umple yesterday because it was my birthday and I had guests over, but I'm back to work today.
I finally came up with a way to get around the UmpleFile
and to pass and handle linked files. To do this, I created a new interface LinkedFileHandler
that requires users to implement String onFileLink( String input, File[] linkedFiles )
. This method takes in the input of the current file, along with the File objects for the linked files and then handles the linkage in this delegate handler. In Umple, this will be producing a bunch of use <filename>.ump
strings and appending it to input
before returning the value, but it can easily be changed for other people's uses.
I also managed to find a way to pass in the dependence on GrammarAnalyzer
to the ParserAction
by adding the analyzer to ParserDataPackage
. This way the required information is still accessible.
With all that done, I'm down to a few smaller, but manageable, problems:
1) I now need to find a way to pass in the linked files from the main. This requires me to pass the dependency all the way from UmpleModel
-> UmpleInternalParser
-> RuleBasedParser
. I'm thinking that I might be able to hackishly fix this by just doing a split on the linked string by doing getLinkedFiles().split("\nuse ")
or something to produce the string names from the UmpleFiles. At least for now. If I made changes to the constructor for UmpleModel
, it would require a massive change overall since many tests require the current constructor.
2) I need to make sure that all the pieces will work together at once. Currently, by design, it's been a pain to test things due to it building successfully but failing the next build (which Tim provided some helpful suggestions on going around). Still nonetheless, it has made testing to be quite difficult to ensure that it still provides the same output as the original work.
3) I may have to modify almost every test that includes the RuleBasedParser
due to the fact that I have removed the coupling from inside the class to be done externally through hooks now. This means after construction I have to do things like addParserAction(...)
on the RuleBasedParser
.
4) My final issue is just getting this all into a separate project/repository (however this may be handled) and having it import in correctly. After thoroughly reviewing the code I am nearly positive that there exists no more coupling between other classes after I finish these fixes -- but some things have snuck up on me in the past, meaning I can't be sure.
All of this is manageable. That said, I suspect I won't have this done by Monday necessarily, but shortly thereafter at least.
Every time I compile I risk the possibility that something has broken but compiles correctly, resulting in the next compile breaking everything. This is getting absolutely tedious, as it has resulted in many first-builds having to be done since subsequent builds won't work.
Every time I think I've found a solution to this problem, it causes further problems instead. It will compile successfully, pass the tests, and then fail on the next compile because something must have changed just enough to not be caught by the tests, but to die a horrible death on the next build. This is entirely caused by trying to remove the heavy and unnecessary integration of UmpleFile
all over the parser.
I was trying to make this class work without requiring any polymorphism, but the more i work with this the more I think about how much headache I could be saving if I did just decide to make GrammarAnalyzer
into a superclass, and have an UmpleGrammarAnalyzer
that implements it that will be used in Umple directly. My only dislike with this comes from the fact that it would require users of this library to now have to create their own analyzer class and have knowledge of how it works.
I might just have to do this though, since it will simplify many of the problems due to Umple's heavy integration. Part of the issue with doing it using methods and hooks like I was trying to do, is that the GrammarAnalyzer
currently needs to set up a ParserAction
in the constructor. And due too the cyclic dependency and construction of the parser, having RuleBasedParser
-> GrammarAnalyzer
-> RuleBasedParser
, it needs to have the GrammarAnalyzer
have the same ParserAction
constructed. This means it may not work nicely attempting to assign them with a method, since there could be various points where an assignment may be done.
I've spent a lot of time today working on removing the UmpleFile
dependency, and it has not been an easy task.
One change I am looking to make is to move the parser action for the useStatement
to be done from outside the class, but the current design forces the use of the UmpleFile
currently inside of it. I was hoping to use the ParserDataPackage
s 'getFileName()` method, but even this isn't helpful due to the fact that the file name is just the name and not the path. I could change it to remove that, but apparently it's something that is hoped to be refactored out.
I was also considering changing the data package to contain a File
object instead of a string filename, but this hasn't been particularly useful as it has resulted in a lot of errors from across many sections of Umple.
I'm also still working on getting the linked files into the RuleBasedParser
by other means instead of having to use getLinkedFiles
from UmpleFile
. It looks like one of the main classes actually keeps a list of filenames to be parsed, and so this may prove to be useful. This will require modifying the Model though to change what can be passed to it.
Still not 100% sure what to do for the linkage after that though. The current method just appends a series of use <filename>;
statements to the current file input, effectively shivving the recursive statements in. It's a hackish solution, but without better knowledge of the way in which the couples and everything are produced, I can't see how to change it from its current state. I might just make an overridable hook that will allow the user to specify how to add new files, but I'm not sure just yet.
I've spent quite a few hours going over the GrammarAnalyzer
code, and I think I finally understand what it's doing, and how to deal with it.
I've been concerned about the Analyzer
classes being constructed using reflection in the makeAnalyzer(...)
method, particularly because it tight quite tightly to cruise.umple.analysis
. After going through what the actual Analyzer
class is doing, however, it begins to make a bit more sense.
Analyzer
is just a superclass of all the different rules that need to be analyzed through specific means. Analyzers can implement up to two methods: prepare(Token token)
and analyze(Token token)
. prepare(...)
is called before analyze(...)
, acting almost like a constructor/initializer by giving time to initialize different data elements that will then be used in analyze(...)
. Alternatively, this could be used to sanitize/strip token input for consumption after. analyze(...)
, as the name suggests, is the actual analysis of the token. These analyzers are often called from outside for consumption, such as in the UmpleInternalParser
.
Analyzer
is simply used to construct other analyzers that inherit from it, constructing the appropriate ones by the rule names so that the rules get analyzed correctly.
So that's how the Analyzer
class alone works; now how does this all tie into GrammarAnalyzer
? Currently, the GrammarAnalyzer
constructs all analyzers by using reflection, attempting to instantiate it given the fully qualified classpath and the string rule-name to use. If it succeeds, then it's added to a running list of analyzers. If it fails because it doesn't exist, then it quitely ignores it and continues. Basically, at the moment, it brute-forces instantiating every type of analyzer for all rules in the grammar, even though only a few actually will exist.
Right now, this method is only working with cruise.umple.analysis.<some analyzer>Analyzer
-- but I plan to change that shortly. For the time being I will make the classpath assignable (setClasspath(String)
or something), but I would like to add to that in the future. I dislike using this reflection method because, though it's automated, it's also quite limited in that it requires the full path, and would need to be changed if ever the package name changes (instead of having a lot of it automated or successfully inferred).
It'd be much nicer if instead the user could manually assign which Analyzers exist for each string either individually, or in bulk. Still using java's reflection, this could be done with Class
objects and string identifiers, or using generics in a function and string identifiers. Something to the effect of:
void makeAnalyzer<T extends Analyzer>(String identifier){ // do something with identifier and T.getClass() }
That's just my thoughts on the matter. Might be a little more cumbersome to write for many classes (such as Umple's ~64 analyzers), but even that could be automated through other means -- for example by loading the data from a file.
Anyway, that was off-topic. Since this Analyzer
class has no real ties with cruise.umple.compiler
, I figured it was quite suiting to have it moved to cruise.umple.parser.analysis
instead. Now, any and all Analyzers must extend cruise.umple.parser.analysis.Analyzer
any time they wish to add new analyzers to their parser.
The other thing I figured out while working was how I can get around the use <umplefile>
statement. The GrammarAnalyzer
actually came pre-designed with the idea of ActionTokens; tokens that, when parsed, execute useful actions (ParserAction
). Currently, only one is being assigned in the constructor of RuleBasedParser
that handles "useStatement"s by reading the source file and parsing/analyzing it as well. I plan to take this functionality and make it work by use of a simple function (addAction or something), and this will be done in the construction of the RuleBasedParser somewhere in the model or compiler instead.
After that, all that should be left is removing UmpleModel. And now that I understand how the parser is automatically-parsing new files, I think I can solve this issue; I just need to figure out how. Hopefully after this I will be all set to bring this to a separate repo to compile and run as an independent project.
In My Opinion: Having had to pour through a lot of the RuleBasedParser
code, I really do have to say it is a wreck and could use an overhaul. Right now it has many static variables that get initialized only once, but the class is never handled like a singleton (which would at least indicate that it's a single-instantiated class). Instead other classes construct a RuleBasedParser
that refers to the same parser's grammar, but not to the same things like root tokens. Not only does this kill readability, but it's also created it's own form of spaghetti code.
GrammarAnalyzer
and RuleBasedParser
both pingpong off each other to produce results. RuleBasedParser
constructs a GrammarAnalyzer
which constructs a RuleBasedParser
(this one has the same grammar as the first, but different tokens) and handles dispatch of some of the RuleBasedParserThreads
. When a parser parses, it gets the parse result from the GrammarAnalyzer
and assigns the root token to its parser's root-token.
Most of the statics could have been eliminated by creating a copy constructor or reference-passing important variables like the grammer. Or, alternatively, the GrammarAnalyzer could have worked as an internal class that would have direct access to the important data that was required. Sadly, due to the current design and how often it's used, changing it would be a rather large feat.
This has made it a nightmare for readability and even more-so for maintenance. It may work and/or be efficient, but it comes at a painful cost. Not to mention this could minimize usefulness of the parser, since it can only be useful for a single grammar.
Moving forward I really strongly believe that this should, at some point, be overhauled in favor of a cleanly written one.
Apparently I missed a few emails from the forums where it was discussed that the jet generation was to be shut-down for good. After the last conference call I was under the impression that some of the Jet-related code was going to remain open and available for work while others were shut-down, but I guess this is not the case. Not entirely sure what my next course of action is with regards to the one fix -- but it looks like it will have to wait. I guess it's my fault for not keeping up with the emails from the forums (Google auto-sorts them into a 'forums' section and doesn't give me notifications when they arrive, so I often miss them). I've reached out to Vahdat about this, but I suspect it will just have to wait
Otherwise I've returned to the parser work. I've finally narrowed down the coupling to cruise.umple.compiler.UmpleFile
, which I'm working on removing, and cruise.umple.compiler.exception.UmpleCompilerException
which is used in the ErrorTypeSingleton
. Other than that I need to find a way to remove cruise.umple.analysis
from the RuleBasedParser
since it is heavily tied with it. Some things may be easy to extract, but others will likely be difficult.
Today I've been working quite heavily on fixing a few line comments that have been broken since the previous refactor with getRelativePath(...)
. The original refactor seems to have caused a lot more issues than intended, and this is in-part due to me attempting to achieve a syntax of getRelativePath(<lang>)
, as I was informed to do.
Given the results of this, it appears that having the syntax that simple may not actually be feasible after all, since the relative path is calculated multiple times for various elements -- for example between method declarations, class declarations, etc. Thus the only way that I can think for that syntax to be feasible is to do an obfuscated form of parameter-passing with a method. I originally used this since it worked properly for evaluating the @umplesource
tags, thus it didn't appear to be broken at all. However, in retrospect it caused many issues for individual line comments -- and not always in easy-to-find areas either.
While I've been doing this I've also taken the time to implement @umplesource
tags and line comments in the Java Interface generator. For some reason they were never implemented in interfaces before this. This may be due to the fact that UmpleInterface
never contained any associations with Comment
s, and because it was unable to acquire a relative path from a Position
, back with the way it was written before.
That's what I'm working on so far. After this I am going to be back to removing all traces of the compiler from the parser, which includes UmpleFile
. I think I'm finally getting close to completing this part of the project; it's required an awful lot more refactoring than I initially realized, and a lot of understanding of different components around the system.
1:00AM
I finally found the problem with the tests! The ErrorTypeSingleton
was causing problems between the two different test-cases. I'm not 100% sure on the specifics of how or why, but it appears that a call to ErrorTypeSingleton.getInstance().reset()
solved this. I found this out by looking through other JUnit files that made use of the singleton, and noticed that this was often the first call prior to constructing an UmpleModel
. I am guessing that, without resetting, it won't always properly throw exceptions.
The reason I think this is because the Collector's analyze(..)
method makes use of a try-catch block, where it only returns false
on a thrown exception (both failing tests were expecting false
but receiving true
instead). This was made harder to discover since it only catches a generic Exception
instead of specifying which Exception to look out for. It's complicated even further by the fact that the throwing methods don't have throws
declarations to easily find methods that throw Exceptions. Gah.
I'm going to guess the tests were able to successfully pass in the past by luck due to the order in which tests were executed. By moving the tests to a new directory, it must have also changed the order the tests were executed which resulted in this issue.
I'm just gonna tack this whole event on as another reason as to why I dislike Singletons. It took quite a few hours to find out, only to be fixed by a 1-liner.
Before I went to bed last night, I took one last stab at Umple and corrected most of my failing JUnit tests. It was actually a lot easier to fix than I had realized, as it turns out that the directory structure needed to match the package name. Some of the tests were renamed from cruise.umple.compiler
to cruise.umple.parser
accordingly, which meant the tests originally in cruise/umple/compiler
needed to be moved to a different directory of cruise/umple/parser
. I'm not sure why that made a difference, but it seemed to fix it.
All that's left now is some strange failing MetricsCollector
test. I'm not sure what's causing this one, as I didn't edit anything relating to it (short of just source imports), and the tests that are failing are fileNotFound
and analyze_unknownFile
-- both of which are expecting a result of false
from the method collector.analyze(...)
. Somehow, it's always returning true now even when the input is invalid -- so I need to find out why.
After this, I'll issue a pull request and implement the next few necessary changes, which are removing cruise.umple.compiler
from all parser-related files and moving them to a /parser
director. After that I can figure out what the cruise.umple.analysis
is for, and whether it's crucial to the parser. Hopefully this won't take as long though.
EDIT: 5:50PM
I can not for the life of me figure out this test failing issue. After moving some tests from cruise/umple/compiler/
to cruise/umple/parser
it causes the MetricsCollector
test to fail. But if the tests were kept in the old location, the parser tests fail -- but MetricsCollector
passes. I don't understand how the tests could be connected at all like this.
The other curious thing is that once (and only once), the testJava multipleDoActivitiesInNestedStateMachine
failed on me with the reason:
Index: 1, Size: 1
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.get(ArrayList.java:429)
at cruise.statemachine.test.CourseWMultiDo.getLog(CourseWMultiDo.java:59)
at cruise.statemachine.test.DoActivityTest.multipleDoActivitiesInNestedStateMachine(DoActivityTest.java:128)
I've done multiple compiles and tests since, and this one hasn't reoccurred. It's just strange since I never touched this file directly, or any of the files relating to it.
EDIT: 8:10PM
Tim emailed me a little while ago with information regarding the multipleDoActivitiesInNestedStateMachine
failure. Apparently it's a problem that occurs due to timing about 1% of the time it is run. I'm glad I'm not responsible for it!
I'm still trying to single out the problem regarding the other failing umple test.
I decided to start from basics and run the tests without any of the parser tests present, and the MetricsCollectionsTest
didn't fail. Then I tried running it with only the CoupleTest
, and it also didn't fail. Then, running it with CoupleTest
and ParserTest
caused a failure.
It looks like one of the parser tests being present in the parser/
directory is somehow causing the MetricsCollectionsTest
to fail for some reason. I have no idea why, but I guess the next little bit will be singling out the problematic file (or files) and isolating it from the rest. I'm hoping this is due to a test itself, and not due to some obscure error like the number of tests (or combination of tests) that cause this failure.
EDIT: 8:50PM
I've isolated the test that seems to be sparking the MetricsCollectionsTest
failure (although I still fail to understand why it's the problem). Any time ParserTest
is present in cruise/umple/parser
instead of cruise/umple/compiler
it causes 2 of the 10 metrics tests to fail.
I wonder if this has something to do with the order in which JUnit tests are executed?
The really weird thing that I've just discovered is that the sheer existence of a test named ParserTest
seems to cause it to fail, even if the entire file is commented out (except for the class declaration). This is insanely bizarre.
EDIT: 11:30PM
Apparently I was wrong about it failing as long as the class exists in the last edit. I wasn't aware that the tumple
script doesn't actually compile the tests, it just runs them. After many. many. many... tests I have discovered that the issue is somehow related to the ErrorTypeSingleton
tests in ParserTest
. It looks like any of the tests that invoke the singleton somehow cause the MetricsCollectorTest
to fail. Still no idea why it is, but at least I have a solid lead now.
It's just strange that the failing tests are relating to files not existing, not from any directly visible reference to the ErrorTypeSingleton
. I need to look into this even further.
Yesterday it took me under an hour to find the appropriate place to fix issue [#768], and yet -- due to a reinstall -- it took me many hours to get it to generate code with JET.
Background on my issue
To understand my problem, first it's best to know my current set-up. I have my Windows computer(s) that I do work on, but I use my linux server to compile the code. With the linux server I virtualize a filesystem that I mapped to my windows computers to make accessing/editing Umple code easier (the alternative is using another separate git repo that pushes to my linux server that then pushes to GitHub, but I didn't want to go that route). The main reason for this is I want to be able to compile all my code straight through Linux instead of through Windows, as the compile times jump from ~4.5 minutes to almost ~15 minutes.
I wanted to try something new with my setup, and that was to generate JET code from my Windows computer using a network mapped project (since I primarily avoid GUI on my linux server). So I started with Eclipse Mars on my Windows computer.
First I tried installing all the appropriate plugins needed to produce the JET code on Eclipse Mars. This straight-up refused to work, not installing a few necessary portions (even after following the answer on stackoverflow). This was to be expected, as the last time I used JET it didn't work properly on Mars for me -- and so I attempted to use Luna instead.
Luna proved to work a little bit better, with only some minor conflicts for installation. One somewhat major issue is that it never produced the expected "JET Settings" menu, meaning I could not properly set up the JET project. The other problem is that finding and dealing with projects over the network slowed down Eclipse to a grinding halt. This would take upwards of 10 minutes to produce meaningful data just so I could hit "next" to just continue the process.
And so I figured this wouldn't work. Instead, I decided to RDP over to my Linux server and just install Eclipse on that. I went through with the setup, and this time I even attempted using Eclipse Juno (the last one recommended to use with JET). I tried installing the appropriate plugins, and everything went through without complaining -- so I thought "yay, it's working". However, then I realized that -- according to Eclipse -- it had absolutely no plugins installed. Despite having all the appropriate *.jar files and having the update sites, the information claimed there were no plugins installed short of the defaults.
I looked into various methods to attempt to get around this, but I could not find what the issue was, and so I resolved to try Luna again, only on linux this time. This gave no different results for reasons that I could not fathom.
Eventually after all of the struggling, I went back into my network-mapped Eclipse set-up -- which is the first one that was closest to correct and tried running the clean
command to see if it would generate the jet files. It struggled and chugged along for over 10 minutes, but eventually it did produced the expected code. This came as a surprise to me, given that it doesn't give any editable options for JET settings in the menu.
This task took a lot longer than expected all because JET wasn't behaving as intended -- and for that I am excited to be moving to the Umple Template system.
EDIT 1
Man am I ever thankful for the javadocs, and the automatically generated @umplesource
tags, because I'd be so lost on this refactor without it. I've already had to go through changing 14 different files to change references from cruise.umple.compiler
to cruise.umple.parser
namespaces. Each time I clear up all the javac
compiler errors, it throws up 20+ new ones at me to fix -- and the javadocs have aided in locating each file.
Unlike other languages that stick to a "1-file-per-class" ideology, Umple allows multiple classes in multiple files -- which makes navigation and traversal quite difficult without auxiliary documentation. I understand the idea behind it, as it allows for a mixin-esque feature where a class is defined in more than one location -- but as far as maintainability goes, it's a feature I would rather not have since it complicates locating where classes exist.
As I've been changing the dependencies on the namespace, I'm also taking the liberty to decrease the scope of the dependency to just the required classes. Prior, most classes just had a generic depend cruise.umple.compiler.*
bringing in the entire compiler namespace. Often I'm finding that most classes only had that as a crude way to import just ParserResult
, Token
, or Position
-- and so I've narrowed the scope to just the requirements on most classes that I've had to refactor so far.
EDIT 2
I've been working at this for 5 hours straight, and have modified 41 files so far just changing the dependencies. So far there is no end in sight to the compiler errors. They keep coming, and each time I fix them it delivers new waves of them to me. I wish that initially the imports were handled explicitly instead of importing the entire namespace.
If all dependencies were done as depend cruise.umple.compiler.<specific class>
then this could have been sped along a lot faster by just doing a bulk rename from cruise.umple.compiler.<specific class>
to cruise.umple.parser.<specific class>
. Sadly this is not the case, and so I have to manually find each and every point of error, of which there are many.
This work has actually been kind of fun, albeit painful, as it has led me to almost all corners of Umple -- exposing me to parts that I hadn't seen before.
While on this magical journey I have noticed a few things that I wish to/need to get to as well.
ErrorMessage
and ErrorTypeSingleton
were originally part of cruise.umple.compiler
and are now part of cruise.umple.parser
. Neither location is particularly fitting, given that they are used in both namespaces. This probably belongs in a more utility-style namespace since it could be used just about anywhere. However, this change would require me duplicate the classes in cruise.umple.parser
to avoid the parser having dependency back to Umple (which is the main thing I'm working on).UmpleFile
really doesn't seem to have much practical use, it's a small wrapper around simple path methods. The one main difference that an UmpleFile
has that a Java File does not is the getLinkedFiles()
which seems to only return a string of use <umpfile>.ump; \n [ use <umpfile>.ump; ... ]
. This method only really gets used in one location so far as I can tell, short of the tests. Strangely, the method it is used in is titled AddExtraFiles
in title-case notation. This is only ever called once to set up the main *.ump
file to translate, and it gets called in init
. @umplesource
information. This doesn't seem to be related to my original refactoring work, as it never appeared for interfaces before I did that work. I think this may have something to do with the fact that UmpleInterface
, prior to the refactor, never kept any information relating to its source package -- meaning a relative path could not be established. At any rate, given that I've had to make extensive use of the JavaDocs, I'd like to implement this to make it easier to navigate the sea of *.ump
files. cruise.umple.parser
package that reference other Umple packages if I wish to properly separate this project.GrammarAnalyzer
uses elements from cruise.umple.analysis
. I'm still trying to figure out whether or not these classes are part of the Analysis stage or whether it would better belong in cruise.umple.parser.analysis
. There are quite a few poorly-chosen names that I've encountered that make deciphering the functionality to be quite difficult (for example, UmpleInternalParser
isn't a parser.)There is so much work to be done here! When I started isolating the Parser project, I had no idea how much work it'd entail since my initial overview made it appear rather separated. It wasn't until I began looking into the functionality of objects, and the namespacing that this became more evident.
EDIT 3
This will probably be my final edit of the day. I spent around 8 hours today working on the refactoring. I changed a total of 50 files (according to git), most of which were changing dependency statements. This task should have really been a lot faster, but took long for 3 reasons.
depend
statements tend to take in the entire namespace (e.g. depend cruise.umple.compiler.*
) instead of only importing specifics (cruise.umple.compiler.ParseResult
). If it were done via specifics, I could have had this done hours ago since I would have been able to run sed
or some other replacement program through all the files that import them.@umplesource
attribute to find where to begin looking. This issue became even more complicated due to my next point.javac
spat out errors to me, it didn't do it with any reasonable order. It would throw ~20 errors at a time, and often it would throw new errors from a class I already fixed a few iterations prior. I'm not sure why the errors occurred in this order, but it resulted in me often backtracking after I thought an issue was properly resolved. Given what I mentioned in point 2, it resulted in a lot of jumping around.I've updated my pull request that fixes the issue from yesterday. When I committed my changes yesterday it attempted to claim the entire JET-generated files were modified by me -- which isn't a good thing.
So today I found out what the issue was and pushed the changes back to origin to update my Pull Request. Turns out this issue is the result of my git server not properly stripping CRLF
to LF
. This was a frustrating endeavor that took a while to properly fix (resulting in a lot of soft-resets and re-commits). Now I'm just going to hope that it's accepted and I can continue on with other work.
Today has been a long day as a result of this. I have a few test cases that I need to fix (currently failing), but Umple at least compiles successfully -- which is a step forward.
My next few steps are to work on the following:
- correcting the currently-failing tests
- re-arranging some of the tests from umple/compiler
to umple/parser
(for tests strictly dealing with parser-related files).
- remove dependency to UmpleFile
from the parser. Ideally UmpleFile
should be removed in general, but this may be a huge change depending (lots of tests require this)
- Figure out why cruise.umple.analysis
is used in the GrammarParser
and see if it can be refactored out or removed.
For this past week I have been working on navigating files from the cruise.umple.compiler
namespace to the cruise.umple.parser
namespace, since a good portion of those symbols are crucial to the parser more-so than the compiler. This has resulted in the unintended side-effect of a lot of compiler errors resulting from "Ambiguous" references to the different classes. Apparently during the compilation stage javac
seems to think that certain classes exist in both namespaces simultaneously.
I originally thought that perhaps I missed a class that was defined in separate files (since Umple allows you to do that), and worried that it was defining the same symbol in 2 different namespaces -- however after a lot of grep
ping through the source, I feel I can rule that one out. I'm not entirely sure what's up with that one, though I'm still looking into this further. I'm wondering if perhaps there is some cache of files not being cleared, which is why it thinks there are two of them -- or perhaps that Umple is having some difficulty compiling itself due to the change in namespace during the self build. There are a number of possibilities here, so I have to try to narrow them down.
Aside from that, today my work was deviated for a small amount of time to fix bug [#768]. Apparently when I was moving getRelativePath
over to the UmpleClassifier
and changed the syntax of it, I forgot (in a couple places) to assign the sourceFilename to the classifier. This results in it coming up with the wrong line comments, pulling in the wrong filename.
Thankfully this doesn't seem to be too hard of a fix, as I've already narrowed down the issue (it seems to have come from a jet-file). That said, the fact that this slipped past tests worries me given that that means there aren't any tests that have verified that the // line
comments are accurate at all. It's possible that fixing this isolated case won't fix all issues relating to it that could spring up in different instances. I certainly hope this isn't the case, and that this is the only issue.
I've had a fever since Feb 28 that was getting progressively worse until just yesterday. I went to the nearby clinic out of concern for my health, and was diagnosed with pneumonia and put on antibiotics.
Until today, I've barely left my room or even been on my computer due to lack-of-focus from my fever -- meaning I haven't had much time to put toward the Umple project this week (or any other project for that matter).
I'm hoping to blitz some work in the following couple of days, but I'm not sure I will have a PR ready in time for the next conference call.
After my last post on the 22nd, I was contacted by Tim both by email and through the issue relating to the refactoring. Apparently the code is there for a cache system to help speed up repeated compilations, but for unknown reasons was set to never happen a few years ago.
At the request of Tim I ran some performance evaluations and found that, for some reason, the cache system actually performs approximately 3 to 4% slower than the non-cached version. Closer analysis suggests this may be due to the amount of string operations being done with the cached versions that is not done in the non-cached. There are a lot of split operations being done, which only get used once per function. This may be invoking the garbage collector due to the number of repeat calls, which would ultimately slow it down.
I hope, at some point, to get the chance to enhance it. With proper work, this could likely be more performant (though i don't know how much more comparative to the non-cached one). If it made use of a binary file instead of a text file, sorted the lines such that they can enhance branch prediction, and cut down on string operations it could increase performance a large amount while decreasing the file size.
Other than that, I've been looking into other parser coupling and came across something that I need to bring up. It appears that a lot of the classes used in the Parser belong to various different namespaces. For example, the ParseResult
class belongs to cruise.umple.compiler
and not cruise.umple.parser
. A lot of these things wind up being used in both the parser and the compiler which makes the decision to migrate it from one namespace to the other a little more difficult.
The other more difficult part of it is that many of the needed classes in cruise.umple.compiler
are there for the Parser
interface, of which the RuleBasedParser
mysteriously is not one of.
From this I can only think of two possible actions.
cruise.umple.compiler
to cruise.umple.parser
. This may bring some unnecessary classes that also depend on it though, which could bloat the project.cruise.umple.compiler
to cruise.umple.parser
. This is not ideal, but would serve the purposeNeither of these solutions seem all that great, and so I need to look into other, hopefully better alternatives. All I know for now is that separating the parser from the Umple project is looking to be more work than initially anticipated.
I managed to complete my first task a lot faster than anticipated by simply using linux commands. It was mostly a lot of changes in constructing a GrammarAnalyzer
/RuleBasedParser
/RuleBasedParserThread
by changing it from taking an UmpleModel
to not. This resulted in easy changes able to be done with grep -rl '<thing to change>' ./ | xargs sed -i 's/<thing to change>/<what to change to>/g'
. I was able to change almost everything before the conference call was even over, so that's a plus.
Everything compiled properly, passed Travis and appveyor, so I issued a pull request and it's since been merged.
So after that I started looking into a mysterious hardcoded file of "rules.grammar"
that's floating around in the RuleBasedParser
. It's really strange, since as far as I can tell it's not even used -- and the only path of execution automatically deletes the file. Here's a small snippet, starting at line 74 of GrammarParsing_Code.ump
.
boolean mustParse = true; <-- This never changes try { File rulesfile = new File(new File("cruise.umple").getAbsolutePath()+File.separator+"bin"+File.separator+"rules.grammar"); if(rulesfile.exists()&&rulesfile.canRead()) { reader = new BufferedReader(new FileReader(rulesfile)); } else { resourceStream = getClass().getResourceAsStream(File.separator+"rules.grammar"); if(resourceStream == null) { mustParse = true; <-- This is always true reader = null; } else { reader = new BufferedReader(new InputStreamReader(resourceStream)); } } if(mustParse || forceParse) <-- mustParse is always true, this is the only flow of execution { readGrammarFiles(); if(rulesfile.exists()&&rulesfile.canWrite()){ rulesfile.delete(); <-- Delete the file ? } // ... } else if(reader!=null) <-- Never reached { // ... }
mustParse
exists in only 3 places in this entire method. It exists in the declaration, beginning as true
. It changes to true
if the resource is not found, and then the if statement checks if it's true.
This means that the entire flow of execution will always hit that first if statement, and never reach anywhere else. Even more curiously, this file gets deleted in any instance that it exists, and everything continues on without ever making use of it. Why does it exist in the first place?
I suspect this is relic from previous code. A git blame
shows that these lines were written in late 2013, so it's possible that this entire execution series is entirely deprecated. Removing them entirely seems to cause no changes to any of the tests or functionality of Umple.
Will have to look further into this, and possibly consult with someone to make sure that it's alright to be removed -- but so far as I can tell, this code has no use.
Over the week I was reworking my previous pull-request to satisfy all participants in the discussion. It took a little work due to some NullPointerException
s that occurred regarding state machines (mentioned on Feb 17), but that was able to be taken care of by a fix suggested by Vahdat in his fix_3 branch.
After successfully changing the syntax from <position>.getRelativePath( <class/trait>, lang )
to <classifier>.getRelativePath( lang )
, I was ready for a pull request. Or more accurately, I would have been -- except I had to rebase onto the new master. This had strange consequences in that I began failing tests that my code never altered (part of the 'ECore' failing tests). Thankfully this was a known-issue, and when it was fixed the next day I was able to issue my pull request without any failing builds on Travis.
Right now I am working on 2 things.
UmpleModel
from RuleBasedParser
As far as I can tell, the UmpleModel
is only used for a call in getModel().setAnalyzer( getAnalyzer().getAnalyzerMap() )
-- which could be done from outside of the RuleBasedParser
. Stranger yet, this model gets passed to multiple other classes seemingly only to be passed back to this. The GrammarAnalyzer
requires the UmpleModel
so it can pass it to an RuleBasedParserThread
that uses the model to construct a RuleBasedParser
.
At least, this is how it appears at a first glance. I'm hoping I'm right about this, since it would be as simple as removing references to the model and extracting the getModel().setAnalyzer(..)
line to somewhere else.
umple_code.ump
(This one is less of a priority, and I primarily do it wherever I find the time.)
There are a lot of functions in umple_code.ump
, very few of which actually are documented.
In between my other work this week, primarily while waiting for responses on my Pull Request, I have been writing up javadoc information for each function in that file. It's not a particularly easy task, given that some of the method names don't describe exactly what they are doing -- for example, getAllTranslators()
doesn't just simply do a get, it actually calls newGenerator
for any presently unmapped language generators. Behavior that doesn't match the method name should be better documented to make it easier to understand for future maintainers.
A lot of this work has been trying to figure out what the methods are actually doing vs what the name of the method is. Most of them are adequately named, but just have certain behavioral things that should be noted -- such as a method returning silently instead of throwing an exception.
I will be issuing a pull request with all the documentation once I have documented all methods in the umple_code.ump
file -- but this may take a while, since it's not my top priority.
I keep forgetting to update this the past couple of days. This week I have been trying to work on my refactoring project of Umple, however I am not getting very far. My initial pull request was rejected for using static methods, and after I made some modifications and inquired further about a proper change, I didn't hear back for 3 days due to the long weekend.
My task was simple: remove the tight coupling between the Position
class and UmpleClass
/UmpleTrait
. The getRelativePath
method in Position
takes one of the latter two classes and returns a string representing the relative path between the position of the class/trait and the current position token. This is logically an easy fix, as a Position
token really has no reason to have knowledge about an UmpleClass
or UmpleTrait
, and so the method shouldn't exist in that class.
My original suggestion was to extract that method into a ParserUtil
class using static methods. This would change the call from positon.getRelativePath( umpleclass, "Java")
to ParserUtil.getRelativePath( umpleClass, position, "Java" );
. I chose this notation as it acts more similarly to a C# extension method, wherein a method can be defined from outside the class statically, but is treated as a method that is part of the class. This doesn't violate OO principles since encapsulation is still kept, and methods are still treated as existing as part of the class (and thus are not functionally free functions, even though they are under-the-hood). I guess this is not considered the case in Java.
This was rejected due to it being a code-smell (use of public static functions), and also since I could modify the method to accept all descendents of UmpleClassifier
instead of UmpleClass
/UmpleTrait
. I was also suggested to use a mixin class instead of using a static class.
Since then I have fixed the former and have been aiming toward accomplishing the latter, but am now met with the problem that state machines will often produce null UmpleClassifier
s. This, of course, leads to <classifier>.getRelativePosition(...)
causing a NullPointerException anywhere that the state machine is tested, resulting in java tests failing, and the compileJava
build step to fail as well.
The original implementation of getRelativePath(...)
was designed to accept null UmpleClassifier
values, and in the case that it was null it would instead return the full path to the position token's file.
As a result, it's not trivial to fix this issue in the state machines. It would result in a series of nested if statements that look something like the following:
String filename = p.getFilename(); String path = null; if( sm.getUmpleClass() == null ){ if( filename == null ){ path = ""; }else{ path = Paths.get(filename).getFileName().toString(); } }else{ path = sm.getUmpleClass().getRelativePath( ... ); }
This leaves an awful lot of work on the caller rather than the callee, and so it doesn't feel like a clean solution to me.
One of my alternative suggestions was to wrap the dependencies in another class, say UmplePath
that accepts as parameters the required arguments for the functions. This would result in an OO-proper calling notation like this new UmplePath( uClass, position, "Java" ).getRelativePath();
. It would be easier to keep the null-checks in the method, and the class could even be used for alternative purposes, such as finding the relative difference between two UmpleFile
s. This suggestion wasn't even acknowledged, so I'm not entirely sure where this stands.
I regret that we can't come to a more agreed upon solution to the problem, as it's debilitating my progress and jeopardizing my ability to finish my project. I hope to see some resolution in the future, as I'd prefer a lot more progress to show for my efforts.
I've been looking into the RuleBasedParser
for a little bit now to try to understand how it works so that I can easily extract it later on, and I've noticed a few other things that also need to be done.
UmpleFile
is often passed to RuleBasedParser
or related classes. This class seems to be an adapter around what a java File
already does, with the one exceptional case that it contains a list of strings that amount to a bunch of Umple's use <file>.ump
statements. This can probably be refactored out entirely, or alternatively be made to extend Java's File
class so that it can be interoperable with regular files. If it extends File
, then most areas that use UmpleFile
in the parser can be changed to just File
to reduce the coupling. It would also be a good transition to completely removing it from the systemRuleBasedParser
doesn't just return a parse tree, it performs a grammar analysis in the same stage. I'm not sure if I'm confusing this with the same analyzer that I will be working with after this project or not, but if it is the same one then that will complicate extracting the parser. This Analyzer also hard-codes a few of the grammar files in Umple, which I need to change to use injected strings so that it can be more generic.Position
often have calls to UmpleClass
or UmpleTrait
. This one may be tricky to get around. Some of these are utility methods like getRelativePath
-- and so they can likely be extracted into a helper/utility class instead.The coupling with traits/classes shouldn't be too hard to fix from what I've encountered so far, I'm more concerned with the deep-tie between parser and GrammarAnalyzer
. I am hoping this won't be in need of a complex refactoring, as I don't want to hurt any performance.
I finally know what I'll be doing for the rest of the semester (or rather, I finalized it on Wednesday). To help prepare for the eventual shift to a plugin architecture, I will be doing generic refactoring around the Umple codebase to help clean up unnecessary coupling, among other things.
As part of this, I will be extracting the RuleBasedParser
, and all the related files, into a self-contained module either in the root of the project or perhaps in a new Github project. In the case of the latter, it will require modifying the Ant build to bring in the source at build-time.
I will also be working on extracting the Analyzer class into more self-contained classes -- a project that has already been started by another student in the past, though not completed. This may take a little time to try to find out what he was doing initially.
For the time being I've been pouring over the codebase and looking for ways to reduce coupling for the RuleBasedParser
. It's quite interesting since I have almost no idea why it is so tightly tied to the the UmpleModel
. It looks like it accepts an UmpleModel
strictly to used model.setAnalyzer( getAnalyzer() )
later on -- which could be done outside the class entirely. Other than that, there are a few strange locations that pass on the UmpleModel
to other classes in the parser, such as the RuleBasedParserThread
, which only seems to use it to construct yet another class, etc, etc.
As far as I can tell, the model serves next to no purpose. I hope to have this all removed by mid-week if possible.
After that I will also need to look into extracting the error messaging system. All error messages are currently going through a central class in the core, and so I need to find a way to attempt to reduce this coupling as well.
I'm a little more concerned about the actual parse tree produced however. It looks like the tree contains a lot more than just tokens; but rather a lot of Position
elements and Comments
, which then reference a lot of classes like UmpleClass
, UmpleTrait
, etc. This is making me think this may be a larger undertaking than I initially suspected.
I've had quite a few challenges posed this week. What started as a suggestion for a plugin system has resulted in a debate among previous contributors, with suggestions that lead in various directions.
I was originally expecting to be researching into topics brought up in issue [#706] (originally discussed in email), but now have multiple different directions to look into. Andrew recommends splitting code into different repos that then get pulled together at build time instead of a plugin architecture. Tim suggests starting by working on refactoring the Analyzer into different classes and files in order to prepare for a shift to a plugin-environment. Vahdat recommends a similar action of refactoring the core component of Umple into different layers for better preparation, and to start with a system more feasibly ported to a plugin like the Analyzer (which is more consistent across versions).
All are great suggestions, but are somewhat deviating in different directions, which is a little overwhelming.
Before this conversation took place I was working on a Java project that simply implemented a plugin architecture and loaded them at runtime; just something I could use as a starting-guide for integrating into Umple (though this will have to be put on the side for now).
I didn't get it fully working, but it was in the form of:
Where Plugin
is the interface that all Plugins would extend from. It gives the functions for loading/unloading plugins such as onEnable()
, onDisable()
, onLoad()
, and onUnload()
. It also requires that features for permissions, configuration, and description data be fulfilled in the form of getPermissions()
, getPluginConfig()
, and getPluginDescription()
.
UmplePlugin
is an abstract class that fulfills this interface by providing a concrete definition. The class is abstract so that it cannot be directly instantiated. Any plugin designed would extend from this plugin.
PluginDescription
is a small class used to contain descriptive information about a Plugin. It contains details like the name, version number, a list of dependencies, a list of requested permissions, and -- most importantly -- the plugin entry-point. The latter of which is necessary to actually instantiate the jar, because otherwise it would be impossible to know which class extends UmplePlugin
.
PluginConfig
a generic method for handling configuration for a plugin. I haven't fully fleshed this out, but a standardized pattern for local configuration files would be helpful -- and this would be the main method for providing access to it.
PluginPermissions
is a small class that contains a collection of requested Java permissions that will be passed to the Umple core. These permissions are in the form of Java's security package, and may be modified from a generate configuration file.
PluginLoader
is the generic interface that loads the plugin file itself. It requires that the following methods be defined:
Plugin loadPlugin( File file )
void enablePlugin( Plugin p )
void disablePlugin( Plugin p )
UmplePluginLoader
extends the PluginLoader
. The method loadPlugin( File file )
will, given a File
, open up the resource of the jar and look for a plugin manifest (perhaps something like plugin.xml
). It will first load this file to produce a PluginDescription
. If successful, it would use this description to instantiate an instance of Plugin
from the jar.
UmplePlugin
would, when instantiated, somehow gain an instance of the current UmpleModel
. How I can manage this without requiring it explicitly passed during its construction is making this hard though. I'm considering UmplePlugin
to have a setModel(...)
method that is called after successful instantiation of a plugin, which would also mean that UmplePlugin
would contain an instance of UmpleModel
. This would give any UmplePlugin
support for retrieving the current model.
Listeners could also solve the above issue of passing the model, as it would be possible for listeners that require the model to pass it as a parameter. For example, onGenerate( UmpleModel model, ... )
would pass the instance of model to a method onGenerate
that would be written by a Listener
class. This would likely be the preferred method, and it also guarantees that the UmpleModel
would be fully instantiated before being passed to the Plugin
Listener
would be a generic interface for listeners. The UmpleModel
would contain a list of Listeners
that it stores for various purposes. Listeners can be added through a method registerListener( Listener )
, where the signature takes a Listener for a specific purpose.
All descendants of the Listener
will be either abstract or interface classes, as they will give a concrete requirement for implementation. This could be simple like a CommandListener
which operates at runtime, requiring onCommand( String command, ... )
to be satisfied -- and would register a new command-line option to the Umple Main.
The Listener
classes would be the primary hook, and could be added just about anywhere throughout Umple for future integration.
That's the main part of what I was working on this week. I've also looked into other things mentioned by Tim (outlined in the issues page), but I haven't found solutions to all problems.
All that said: I'm not 100% sure where to go from here. I've received a few different views that could have me starting in completely different directions, making it hard to know where to begin.
Not a huge update this week. After the code sprint I have had to focus on a couple school assignments, a job-fair, and preparing for an upcoming interview. Overall, this has been a hectic-week as a result.
In between all that work, I have mostly been looking into a replacement issue I can work on while the Jet Java generation issue is resolved. The issues that keep interesting me most are either already assigned, java-generation (which requires jet), or seem particularly easy. I might just take some of the smaller very-easy tasks like the wiki cleanup/update/fixes just to power-through a bunch until I can return to my originally assigned one.
Another Umple-project I've been looking into is the feasibility of a plugin-framework (which I still need request permission to work on). One problem that I am worried about occurring is the same one that occurred when I was trying to solve the issue relating to the case-sensitive names. When I was attempting to read class files from within the jar, this would only work when run from command-line, but junit testing failed -- a problem I suspect is due to a Java security issue.
I need to run some tests to see whether or not a plugin system would fail once it reaches Junit, because if it does then this whole idea may have to be approached differently. It is possible that Junit may disallow the jar that it is testing from itself executing another external jar not found in the classpath, and this would likely be a permission-related issue if it were the case.
Started the day with the pull-request being accepted. Began looking into Issue [#295] relating to Java generation. I spent most of the time setting up the environment, and installing the Jet plugin for Eclipse. The first install on Eclipse Mars didn't work properly -- resulting in it showing as installed, but not giving me context options in the project preferences. As a result I had to download an older version of Eclipse (Luna), which I managed to get working.
It took a little bit for me to get used to working with the generation stuff, so mostly it was reading over the wiki and getting some help from Morgan who had already begun to use Jet. After a little while it became apparent that there was a strange Java generation issue that could not be avoided. The generated jet code on GitHub did not match the code actually generated using Jet, likely resulting from a previous user either making changes to the generated code directly, or not testing the Jet code. Since the full build only copies the jet-generated code to the appropriate directories, it wasn't discovered until now.
I spent a little time looking into this issue, though Morgan was doing a lot more on it than I was. As a result of this though, my current assigned issue will have to be put on hold. Until then, I will likely just assign myself another issue.
I really enjoyed this whole Code Sprint. It was nice to actually meet my teammates in person, and speak with them 1-on-1 without any feedback, network issues, etc.. Though I didn't write as much code as I originally had hoped to do over this weekend, I did get the chance to become acquainted with a lot of the umple core model, and a little bit of the parser and generators. I have a far better understanding now than I did before of its overall structure, which should certainly aid in future work. I also learned important things like issuing pull-requests through github, and updating the remotes in a git repository through command-line (Thanks to Victoria for that one!).
Vahdat mentioned that we would be allowed to develop a new feature/add onto existing features in an area we are comfortable with. This has got me excited and thinking about possible tasks.
One thing that I have considered is that Umple's current structure lends itself quite nicely to a pluggable interface, allowing plugins to provide additional functionality outside of the original scope. My thought is that it could be used as a new way to provide generators to the core compiler without requiring as much interdependency (For example, Umple_Code.ump
has methods that pertain to translating to comments of all possible languages; a functionality that may be better encapsulated in a module). If this transition were done properly, it would require additional build steps to construct individual jars for each plugin at build time -- but may also decrease time for quickbuild options for people attempting to construct their own generators (since only the jar would have to be constructed).
My current thought for a general interface is the following:
With a structure like this, the compiler can poll for all plugins at runtime -- loading each jar file. It would search the root of the jar for a manifest file that specifies the entrypoint for the jar (e.g. the class that extends UmplePlugin) and attempt to construct it by passing it the UmpleModel (necessary for parsing .ump
files). If successful, the UmplePlugin would automatically load a configuration file (also in the jar) specifying key details (the name of the plugin, version, the type of plugin functionality it provides, etc) to set it up.
All that's left is to attempt to cast it to the expected plugin type specified in the configuration. If the cast is successful, then it can be stored in a master list that will have pre-set plugin hooks called at specific areas throughout the system.
Policies are also able to be manually assigned (using Java 7+), so that executing external jars are sandboxed within a set of rules.
By extending it this way, it would allow for other organizations that may wish to use Umple to add their own features, perhaps with support for UmpleOnline, without having to clone the repo and work with intricate system knowledge; instead they can work straight from a Java jar API (generated with ant) to make development faster.
This idea would not strictly be limited to generators; my thought is to develop a generic API that only requires UmplePlugin
to be extended in order to develop new functionality. Perhaps adding new hooks at various stages of the build process, using an event system (or possibly Java traits?) to pass messages to the various hooks.
Though this idea is a very large-scale idea for such a short time, my thought is to start small; build a (working) plugin API, and attempt to migrate one of the less-used generators to the new structure as a proof-of-concept.
Of course, this is just an idea. I haven't posed this to Tim yet, so I need to get around to that during the next hangouts call.
JUnit tests continued to fail using the method I wrote yesterday, in which I traverse Umple.jar's structure, searching for all class files.
As a result I explored a different approach and wrote a small resource parser in 10 minutes that was capable of extracting the generate
token. This was capable of satisfying Issue [#602] in the JUnit tests, and so I made a pull request before noon.
I later assigned myself Issue [#592], and began working on a solution. The fix is simple enough; change the comments from =begin
=end
block-style syntax to #
inline (common ruby) syntax. I constructed a small StringBuilder in Umple_Code.ump
and append #
at the start of every new line (indented for the internal comments).
A small annoyance kept occurring where the comment text was 2 characters after the comment symbol, which took a little while to trace. I had to find the Generator code, which I then followed to Jet to try to change before realizing that the code made a call to Comment.format(...)
in UmpleCode
which forced a 2 space delimiter.
All there is to work on is the test cases, which immediately failed since they test against the previous commenting-format.
One thing I noticed is the previous author of the code forced test-cases that could not compile correctly if ran. All test cases compared against the =begin
and =end
delimiters as indented by 2 spaces, which cannot be run by ruby (it must be at the start of the line for block-syntax to work).
I have a lot of test-cases to correct with the new comments, and then I should be good to issue a pull request. My target is to have the pull-request issued by tonight.
Update Issued a pull request after a successful build. It took a little while to properly correct the test-cases to get the formatting accurate.
Once this pull request is accepted, I'd like to make a small change to the main -h
/--help
instruction and auto-generate languages based on my revision to Issue [#602].
Today I spent hours working on solving Issue [#602]. The solution would have been a lot simpler had I started looking in a better area first.
The majority of the time was spent attempting to extract tokens from the RuleBasedParser
. I first began by trying to grab the parser and read a list of generates that were read from umple_core.grammar
; however this proved to not be useful, as the parser is constructed after the generator is selected with newGenerator()
.
I then attempted to construct a second parser that was used strictly to acquire the appropriate generation languages, and this led to other problems. It appears as though Umple can only have one RuleBasedParser
at a time, otherwise it will cause other build steps to fail.
As a result of this, I had to look into other approaches. I spoke to Vahdat for help regarding this, and he suggested 2 possibilities: build a custom parser to read the .grammar
file, or attempt to read all .class
files and compare against that. Given the way newGenerator()
works by substituting the language string into {0}.Generator.class
, I found that to be the most effective way of solving the problem.
A little while later I had a working solution, which walked the files existing in the .jar
file, extracting only ones that were in cruise/umple/compiler/
and ending in Generator.class
. Finally, a solution that works. Mostly.
Somewhere down the line, this change caused a few JUnit tests to crash (not just fail). This problem took a lot of searching to discover where it was coming from. It turns out, if you use System.exit()
early on a JUnit test expecting a result, it treats it as an unhandled crash.
Last on the list is writing a few test cases to ensure that this is working properly. I intend to have this done tomorrow morning, and from there I can issue my first pull request.
The current test cases I have written seem to be failing, as the runtime-generated list of valid strings doesn't appear to populate if run through JUnit (go figure). I need to figure out how to successfully make a call while still making the test meaningful.
Ultimately, this problem has taken a lot longer than I was originally anticipating. I spent far too long looking in the wrong direction for a solution, and I am thankful for Vahdat's suggestion.
I checked in at the Chelsea Hotel around 11pm in preparation for the Code Sprint this weekend (sadly I couldn't make the Meet & Greet at U of T).
While waiting around in my hotel room, I decided to quickly look into the JUnit issues that are happening with Umple when compiling on Windows (as discussed with Vahdat during our last video conference).
The issue of cruise.umple.UmpleConsoleMainTest
failing was simple, a few JUnit test cases relied on the Unix-style line endings \n
instead of the system-specific System.getProperty("line.separator")
. Changing that solved the issue quickly.
The second issue of cruise.umple.compiler.UmpleImportTest
seems a little more complicated, as the data is being read from two different files. Glancing over the test files doesn't show any glaring issues, and so I will need to look more in-depth into this in the future.
Side note: Compiling Umple on Windows is killer. It takes around 12 minutes, as opposed to ~3 minutes on Linux.
On the topic of Issue [#602]: I need to look further into Umple to provide a decent fix for the issue. It's simple in nature, and has many naive solutions, but I want to provide one that is generic and scalable if ever future languages are added. My current attention is on the languages supplied in umple_core.grammar
, which is made accessible by iterating through model.getGenerates()
-- only model
doesn't seem to be initialized until a while after main.
I spent a couple hours searching through the Umple source code to get a feel for it, and to understand how compiler parameters are parsed from command line. I did quite a lot of grep
ing through the different files to follow the flow of execution relating to parameter handling and code generation, which I need for Issue [#602].
I discovered that all (command line) compiler parameters use an Optional
object from a library that simulates POSIX's getopts() functionality in an object-oriented fashion. I also found that parameters to the argument -g
or --generate
all are sent to an object, <[inline_block>3](https://github.com/umple/umple/blob/master/cruise.umple/src/Umple.ump#999, that is used as a means to hold the target language. This object is used in the method newGenerator(...)
by providing the language string to it, which it then uses to to construct the appropriate compiler at runtime using the fully-qualified path.
This appears to be why all the parameters to the generate argument require a specific case, because otherwise it will not refer to the appropriate object due to case-sensitivity.
Since GenerateTarget
seems to be a common denominator for selecting the language, I have suggested the possibility of making the modification in this class instead of in the main. This could (if I understand it properly) correct the case-problem in a generic manner that will support any manner of entry point, not just the console compiler (since UmplePlayground also makes a call to GenerateTarget). Given my still limited knowledge of Umple's code-base, this could easily not be the case and so I am waiting for a response back before pursuing it officially.
A small modification made to Umple.ump
adding before constructor
to GenerateTarget
did appear to solve the error in testing, however it caused an unspecified failure in a JUnit test (Something I need to look into).
I have been struggling since late January 8th to properly get Umple compiling on my Linux server running OpenSuse 13.2. Every time I would try, I would encounter one problem after another. Initially it was ant configuration issues, such as improperly set JAVA_HOME
, ANT_HOME
, and even ANT_OPTS
variables (the latter of which resulted in various out-of-memory exceptions from Java since I was only allocating 256M). At one point, I needed to created a symbolic link pointing from /etc/lib/java-1.8.0
to /etc/lib/java
(since the original /etc/lib/java-1.8.0
was, strangely, created as an empty directory).
Some of the required libraries weren't brought in with Ant as dependencies, which required me to either find a working update site for zypper
, or find a working rpm.
After various package installations, updates, and configuration I finally got compilation working... almost. For some reason, Umple would compile properly (and even be runnable, generating correct code), yet somehow it would always fail all junit tests immediately. Strangely, Umple worked better on my Windows computer than on OpenSuse. Searching online yielded various known complications with some versions of Java and xerces-j2 which result in warnings and exceptions being thrown in junit tests, which ultimately result in all tests failing. The primary fixes were to reconfigure the project itself, which I am not (yet) equipped to do, or to remove xerces from the classpath, which I couldn't do without generating other errors.
For this reason, I decided to follow advice from previous UCOSP participants and migrate everything to Ubuntu. I'm not particularly partial on Ubuntu given the way they manage files (which differs from more-common Unix directory structures), but I figured having a better-maintained distro with a larger support community would be beneficial in the long-run.
It took a little getting used to, especially following symbol-link chains that Ubuntu loves to do (for example, /usr/bin/java -> /etc/alternatives/java -> /usr/lib/jvm/java-8-oracle/jre/bin/java
instead of simply /usr/lib/java -> /usr/bin/java-1.8
like other distros), but I quickly managed to set it up.
Running the ant build commands resulted in a quick and easy build without any errors or failures, so that's already a major step ahead from using OpenSuse. It's a shame the fix was installing a whole different OS, but I figured it was about time I upgraded anyway.
php -S localhost:8001
runs a server successfully, but accessing the page just throws an error. I think it isn't able to find an index.php file and as a result isn't running properly. Need to look into this further._{namespace(s)}_BEGIN
and _{namespace(s)}_END
for scoping namespaces. On a single-deep namespace, this is senseless, but on multi-layers it expands to multiple namespaces (which ultimately saves on code bloat while still remaining readable)Overall, I'm quite excited to work on this project.