Alternative way of injecting own TokenMakers into RSTA

Post a reply

:D :) ;) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :!: :?: :idea: :arrow: :| :mrgreen: :geek: :ugeek:
BBCode is ON
[img] is ON
[flash] is OFF
[url] is ON
Smilies are ON
Topic review

Expand view Topic review: Alternative way of injecting own TokenMakers into RSTA

Re: Alternative way of injecting own TokenMakers into RSTA

Post by preditcon » Wed Oct 24, 2012 8:08 am

Just posting this as a followup. Turns out this problem was actually caused by the way we had our obfuscator set up (go figure).

The thing is - obfuscation consists out of two phases: class renaming and shrinking. While everything was ok with class renaming, shrinking was not properly set up. When you have code which is not directly called anywhere by your main class (does not appear in the method call graph), it will get removed during the shrinking process. This may leave you with classes which cannot be instantiated though a call from a library like yours (via reflection, service provider interfaces, etc.). And that's what was happening in our case. Our CustomXMLTokenMaker was actually found, it just couldn't be instantiated because the class did not even have a constructor. :D

Re: Alternative way of injecting own TokenMakers into RSTA

Post by robert » Wed May 23, 2012 12:49 pm

Excellent, I'm glad you're not completely stuck. You're right, I wasn't thinking that class.getName() would return the obfuscated name; it does appear that everything should work.

I probably don't need your custom class for testing if all you've done is add that method override. I might play around with it a little more to see if I can figure out what's going on, but as long as you're not stuck, it's not very high of a priority.

Re: Alternative way of injecting own TokenMakers into RSTA

Post by Guest » Wed May 23, 2012 7:27 am

Yes. When we obfuscate the package structure changes but this can be controlled at a per-class basis. You can for example specify that a certain class is to be ignored, leaving it in it's original form, including the package structure for it (the fully qualified class name is never changed). It doesn't work when my TokenMaker is left out in such a way or when it's obfuscated like the rest of the classes.

The thing that bothers me is that it shouldn't matter either way. When CustomXMLTokenMaker.class.getName() is called the new fully qualified class name is used for an obfuscated class. So instead of "", you now have something like "a.b.c.D" and the call actually looks like this: "D.class.getName()". This should not cause any problems during instantiation of this class...after all, I am able to create an instance of it by calling it's constructor whether the code is being obfuscated or not.

Your proposed workaround is exactly what I've been looking for and it works for me. I do not need the flexibilty of being able to change the style of RSTA however.

I can provide you with the code of my CustomXMLTokenMaker if you wish to test this yourself (it is just an extended XMLTokenMaker with a getInsertBreakAction() override to do some extra formatting - which was actually a suggestion of yours in one of my previous threads). We use yGuard for the obfuscation process.

Re: Alternative way of injecting own TokenMakers into RSTA

Post by robert » Tue May 22, 2012 12:24 pm

I'm a little unclear - when you obfuscate, are you also removing/changing the package structure? That's the most obvious culprit, but it sounds like you have this problem even when not obfuscating the CustomXMLTokenMaker class?

The easiest way around this is to set the TokenMaker directly on the RSyntaxDocument (not sure why I added this method, but it's there):

Code: Select all
RSyntaxTextArea textArea = new RSyntaxTextArea(...);
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
doc.setSyntaxStyle(new CustomXMLTokenMaker());

In other words, bypass the TokenMakerFactory altogether. If you want the highlighting for other languages of course, this is a little inflexible, though you could hack together something like this until we find a better solution:

Code: Select all
public static void setSyntaxStyle(RSyntaxTextArea textArea, String style) {
   if ("text/xmlNCC".equals(style)) {
      RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
      doc.setSyntaxStyle(new CustomXMLTokenMaker());
   else {

I cringe at the idea of suggesting that though. Let me know whether I'm correct about the package obfuscation.

Alternative way of injecting own TokenMakers into RSTA

Post by preditcon » Tue May 22, 2012 8:05 am

Just when I was almost completely sure that everything works and that we are ready for a new release of our product, I've been struck by a lightning bolt. My custom XML TokenMaker which extends org.fife.ui.rsyntaxtextarea.modes.XMLTokenMaker cannot be instantiated after the obfuscation phase of our build process.

I used the following way of adding my custom TokenMaker:
Code: Select all
String style = "text/xmlNCC";
String name = CustomXMLTokenMaker.class.getName();
AbstractTokenMakerFactory atmf =
        (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance();
atmf.putMapping(style, name);
and it works like a charm but only when our JARs are non-obfuscated. After obfuscation is done this code fails to instantiate my class even if it is left in it's original form. Note that we only obfuscate our code and all 3rd party JARs are left untouched.
Code: Select all
        at java.lang.Class.newInstance0(Unknown Source)
        at java.lang.Class.newInstance(Unknown Source)
        at org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory.getTokenMakerImpl(
        at org.fife.ui.rsyntaxtextarea.TokenMakerFactory.getTokenMaker(
        at org.fife.ui.rsyntaxtextarea.RSyntaxDocument.setSyntaxStyle(
        at org.fife.ui.rsyntaxtextarea.RSyntaxTextArea.setSyntaxEditingStyle(
I suspect some sort of a classpath problem but am unable to resolve it. Therefore I'd like to know whether any alternative ways of adding my own TokenMakers to RSTA exist (other than modifying your existing code and rolling our own version of RSTA JARs). Preferably in a way where I'm in charge of their instantiation.