Supporting multiple versions of Eclipse - eclipse

I have an Eclipse plugin and I am aiming for 3.1 or 3.2 as a minimum version to support. The problem is that some of my code only works in version 3.5 and above (see my other question: Is there an alternative to CaretListener in Eclipse?).
Now that I have code that works in the older versions and different code that works in the newer versions, is there a way that I can call the newer code only if my plugin is running in version 3.5 or above and then revert to the old code if running anything older?
As a test, I've created two plugins that have the same class within it (just doing slightly different things). I have marked the org.eclipse.ui dependency as a minimum of 3.5 in one plugin and 3.1 as a minimum in the other but I can't get the one that relies on 3.5 to be ignored in older versions...
Can anyone help?
Thanks,
Alan

You could use org.eclipse.core.runtime.Platform to get the org.eclipse.ui Bundle and check the version.
Version ui = Platform.getBundle("org.eclipse.ui").getVersion();
// then do something with that
Register MyListener if >=3.5, and OldMyListener otherwise.
EDIT:
Right, the above is only good for capturing differences in runtime behaviour.
Eclipse supports a couple of tricks for only loading some classes.
The easiest from a development point of view is the trick that #ShiDoiSi mentioned.
Bundle myBundle = org.osgi.framework.FrameworkUtil.getBundle(this.class);
Version ui = Platform.getBundle("org.eclipse.ui").getVersion();
Version cutOff = new Version(3,5,0);
final Executable processListener;
if (ui.compareTo(cutOff)<0) {
Class pc = myBundle.loadClass("my.pkg.OldListenerProcess");
processListener = (Executable) pc.newInstance();
} else {
Class pc = myBundle.loadClass("my.pkg.ListenerProcess");
processListener = (Executable) pc.newInstance();
}
processListener.execute(targetObject);
Another option that uses more of the eclipse framework would be defining your own extension point so that contributions from other bundles can decide which version to use. Basically it's the same pattern as above, except the version checking is done by the dependency ranges on the plugin the contributes the Executable to run. Depend on org.eclipse.ui [0.0.0,3.5.0) for the old way, and simply specifying org.eclipse.ui 3.5.0 (that's an open ended range on 3.5.0) for the current way. Then you can read your extension and instantiate the class provided.
If you were creating extra plugins for this (a little heavy weight for the 2 differences) you could define a command in your main plugin, and have the extra plugins provide the equivalent handler. The plugins would still have to have dependency ranges so that only one would load for the <3.5 or >=3.5 case. Then using the command API you could execute the command (and the correct handler would run).
ICommandService cmdS
= (ICommandService) workbenchWindow.getService(ICommandService.class);
Command process = cmdS.getCommand("my.pkg.ListenerProcess");
ParameterizedCommand cmd = new ParameterizedCommand(process, null);
IHandlerService handlerS
= (IHandlerService) workbenchWindow.getService(IHandlerService.class);
IEvaluationContext ctx = handlerS.createContextSnapshot(false);
ctx.addVariable("toAddListener", targetObject);
handlerS.executeCommandInContext(cmd, null, ctx);
Then your implementation of handler would use HandlerUtil.getVariable(event, "toAddListener") to extract the object you need from the ExecutionEvent.

It sounds to me that you should be supplying two plugins, one supporting version 3.1 to "just below" 3.5, and the other one from 3.5 upwards. So you don't really get to choose, it's basically the Eclipse plugin layer choosing the right one based on your version ranges.
Or if you provide just the compiled class files, then of course you could load the required class dynamically based on testing the version of the running Eclipse.

Related

Strategies for supporting multiple TYPO3 versions in extensions

I am looking for potential pitfalls or best practices when supporting multiple TYPO3 major versions with extensions. There are some things to consider.
I noticed several extensions support several TYPO3 major version in one version, e.g.
extension version 1.0.0 supports 9.5.* and 10.4.*
However, this way, you can't really get rid of things as soon as they are deprecated. (For example, a function may be deprecated in 10 and there is a replacement function, but this is not available in 9, so in order to support both you use the deprecated function).
This has the disadvantage, that the extension scanner will point out lots of things which are just deprecated. I am a huge fan of the extension scanner and getting rid of deprecations as soon as possible.
When I created my extension migrate2composer I used a separate version branch 8.7. But this is more work if I fix bugs because it needs to be backported.
What is a good strategy and are there ways to keep the workload minimal?
One possibility to support multiple versions and already use the new functionality can be found in the bootstrap_package:
/***************
* Make the extension configuration accessible
*/
if (class_exists(\TYPO3\CMS\Core\Configuration\ExtensionConfiguration::class)) {
$extensionConfiguration = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
\TYPO3\CMS\Core\Configuration\ExtensionConfiguration::class
);
$bootstrapPackageConfiguration = $extensionConfiguration->get('bootstrap_package');
} else {
// Fallback for CMS8
// #extensionScannerIgnoreLine
$bootstrapPackageConfiguration = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['bootstrap_package'];
if (!is_array($bootstrapPackageConfiguration)) {
$bootstrapPackageConfiguration = unserialize($bootstrapPackageConfiguration);
}
}
The line // #extensionScannerIgnoreLine will make the extension scanner ignore the following line and will not clutter up the report with this, see Extension scanner documentation.
Thanks goes to Simon Gilli for pointing this out ...
I release a unique version for each TYPO3 LTS version to be able to throw out old stuff. This also facilitates automated testing.
I am like you using different branches to manage that and cherry-pick commits between them.
It's a little bit more work but can be facilitated with helper scripts. I have written some sentences about our reasoning here https://docs.typo3.org/p/dmind/cookieman/master/en-us/Contributors/Index.html#branches
I've heard from some users being confused by the versioning scheme, so in hindsight for the next time I would choose to stick with 1 major version = 1 TYPO3 LTS.
E.g. starting with
v1 - TYPO3 v9
v2 - TYPO3 v10
v3 - TYPO3 v11
And then if our extension has breaking changes, continue with the next free major version, maybe removing the "new feature" support for an older LTS.
v4 - TYPO3 v10
v5 - TYPO3 v11
It is also a compromise however. This way you cannot say that feature X is "in version > 4.1" but maybe the composer scheme will be better understood in the future so you could say "^4.1 || ^5.1".

`#babel/runtime` and `#babel/plugin-transform-runtime` versions

Are #babel/runtime and #babel/plugin-transform-runtime supposed to be on the same version (e.g. both 7.2.0 exactly)? Or can I (as a library author) specify #babel/runtime dependency as ^7.0.0, whilst having the latest #babel/plugin-transform-runtime?
I'm aware that during the beta versions of Babel 7, there was a breaking change in beta.56 (see https://stackoverflow.com/a/51686837/2148762), but I'm guessing this should no longer be the case with the current stable version?
The reason I ask this is I'd ideally want the helpers from #babel/runtime to be shared across different packages, and to me leaving the version range open seems like a good idea. But at the same time, I'm not sure how low I should go (^7.0.0 or ^7.2.0), and whether there's an implicit contract between #babel/runtime and #babel/plugin-transform-runtime with regards to version numbers.
By default, #babel/plugin-transform-runtime is only allowed to output references to #babel/runtime that work on ^7.0.0 because it does not know what version you'd otherwise want to use, and doing anything else would cause lots of issues for users. This means that what you want to do is safe. The downside of this is that, if we add new helpers moving forward, your code would not be able to take advantage of the #babel/runtime version of them (because you might still be using a #babel/runtime version that doesn't have them.
Users can specify the version in the arguments for the transform, if you want to specifically make use of helpers that may have been added to Babel since 7.0.0, e.g.
{
"plugins": [
["#babel/plugin-transform-runtime", { version: "^7.2.0" }],
]
}
would then require you to have "#babel/runtime": "^7.2.0" in your package.json.
For instance, since support for the newer decorators proposal didn't land until Babel 7.1.5, if you use transform-runtime and non-legacy decorators, the decorators helper will still be inserted into every file where you use decorators, instead of importing it from #babel/runtime. To get the shared helper, you need to specify version: "^7.1.5" in your options for transform-runtime.
Can I (as a library author) specify #babel/runtime dependency as ^7.0.0, whilst having the latest #babel/plugin-transform-runtime?
Yes, this is safe.
I'm guessing this should no longer be the case with the current stable version?
Correct, that issue was because people failed to take the beta versioning into account.

babel-preset-env runtime version check

babel-preset-env allows to automatically determine the needed polyfills and transformation but how does it check at runtime if the target versions specified at compile time are really available.
Let say I target safari 10 using
"targets": {
"safari": 10
}
but the code is run on an older version of safari.
How does babel-preset-env recognize this or what are the typical ways to deal with this common problem?
babel-preset-env does not carry out any runtime checks. It enables only the transforms that are required for your specified environment. You're effectively setting your minimum supported environment, and opting out of any transforms that would be required to support lower versions.
For example, if all of the browser versions specified in my config supported ES2015 classes, Babel would not transform classes into an ES5-supported form, since it's not necessary to do so!

How to compare files programmatically in eclipse?

I am developing an eclipse plugin that runs code violation checker on the difference of two versions of a file. Right now I am using diff.exe to get the difference between the two files. But as diff.exe is an extrenal app, I realized that its better to use eclipse built-in compare tool to get the file difference.
So I used org.eclipse.compare and reached up to this point:
public static List<Patch> compare(String old, String recent) {
try{
IRangeComparator left = new TokenComparator(old); //what exactly to be passed in this constructor, a file path, a literal value or something else?
IRangeComparator right = new TokenComparator(recent);
RangeDifference[] diffs = RangeDifferencer.findDifferences(left, right); // This line is throwing NPE
//..
// Process RangeDifferences into Collection of Patch collection
//..
}catch(Exception e){}
//Returns a collection of file differences.
return null;
}
Now the problem is I am not sure what exactly to be passed in the constructor TokenComparator(String). The document says this constructor Creates a TokenComparator for the given string. But it is not written what exactly to be passed in this constructor, a file path, a literal value or something else? When I'm passing a file path or a string literal I am getting NullPointerException on the next line of finding differences.
java.lang.NullPointerException
at org.eclipse.compare.internal.core.LCS.isCappingDisabled(LCS.java:98)
at org.eclipse.compare.internal.core.LCS.longestCommonSubsequence(LCS.java:55)
at org.eclipse.compare.rangedifferencer.RangeComparatorLCS.longestCommonSubsequence(RangeComparatorLCS.java:186)
at org.eclipse.compare.rangedifferencer.RangeComparatorLCS.findDifferences(RangeComparatorLCS.java:31)
at org.eclipse.compare.rangedifferencer.RangeDifferencer.findDifferences(RangeDifferencer.java:98)
at org.eclipse.compare.rangedifferencer.RangeDifferencer.findDifferences(RangeDifferencer.java:82)
at org.eclipse.compare.rangedifferencer.RangeDifferencer.findDifferences(RangeDifferencer.java:67)
at com.dassault_systemes.eclipseplugin.codemonview.util.CodeMonDiff.compare(CodeMonDiff.java:48)
at com.dassault_systemes.eclipseplugin.codemonview.util.CodeMonDiff.main(CodeMonDiff.java:56)
Someone please tell what is right way to proceed.
If the question is What value the token comparators constructor takes then the answer is it takes the input string to compare. Specified in javadoc here http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fcompare%2Fcontentmergeviewer%2FTokenComparator.html
TokenComparator(String text)
Creates a TokenComparator for the given string.
And the null pointer yo are getting is because in function isCappingDisabled it tries to open the compare plugin which seems to be null. You seem to be missing a direct dependency to the plugin "org.eclipse.compare.core"
The org.eclipse.compare plugin was never meant to be used in standalone : many of its functionalities require a running instance of Eclipse. Furthermore, it mixes core and UI code within the same plugin, which will lead to unexpected behavior if you are not very careful about what you use and what dependencies are actually available in your environment.
You mentionned that you were developping an Eclipse plugin. However, the NPE you get indicates that you are not running your code as an Eclipse plugin, but rather as a standard Java program. In an Eclipse environment, ComparePlugin.getDefault() cannot return null : the plugin needs to be started for that call to return anything but null.... and the mere loading of the ComparePlugin class within Eclipse is enough to start it.
The answer will be a choice :
You need your code to run as a standalone Java program out of Eclipse. In such an event, you cannot use org.eclipse.compare and diff.exe is probably your best choice (or you could switch to an implementation of diff that was implemented in Java in order to be independent of the platform).
You do not need your program to work in a standalone environment, only as an Eclipse plugin. In this case, you can keep the code you're using. However, when you run your code, you have to launch it as a new "Eclipse application" instead of "Java Application". You might want to look at a tutorial on how to develop Eclipse plugins for this, This simple tutorial from Lars Vogel shows how to run a new Eclipse Application to test an Hello World plugin. You will need a similar code, with a menu entry to launch your plugin somewhere (right-click on a file then select "check violations" in your case?).

Gwt i18n > generating properties files

I'm using GWT in my stuff, and I would like to make it,
international, so I use GWT constants method.
I have a java file with defaults, and I now need to make properties files.
In a remember, there is a special thing to do (or done automagically) to generate
a kind of template where all constants are generated with empty labels for other langages.
Did I dream this ?
(using eclipse indigo to develop webapp with gwt but not gae)
[edit:]
this was not a dream, it's i18ncreator:
http://code.google.com/intl/fr-FR/webtoolkit/doc/latest/RefCommandLineTools.html#i18nCreator
but I can't make it working on windows :-(
[edit again ]
due to this issue : http://code.google.com/p/google-web-toolkit/issues/detail?id=5113
recommended solution is use i18ncreator in gwt 1.7 (!)
you should see the page on locales in GWT
I had the same issue. I was looking all over the place for the answer but could not find an answer; either in the docs or on stackoverflow.
So I asked in the GWT gitter channel and was told to use the compiler argument
-extra <destination-folder-name>
to generate the .properties files from the Interface files.
Steps in eclipse:
Select project you want to compile
[right click] -> Google -> GWT Compile
In the window that opens, open the Advanded options.
Add the following additional compiler argument -extra <destination-folder-name>
Compile
This should generate the *.properties files in the /destination-folder-name.
NOTE: This only generates the .properties files. It does not actually compile the application with all the locales for deploy.
Move the MyInterfaceExtension_*.properties to be right beside the MyInterfaceExtension.java file.
Make copies for each locale i.e. MyInterfaceExtension_fr_CA.properties, MyInterfaceExtension_fr_FR.properties, etc..
Translate them
Then run the compilation process again with out the -extra <destination-folder-name> option. Because it is not needed anymore.
This will compile with all the locales you enabled. You can now deploy the app the usual way.
Quick Tips:
When compiling for the first time in order to generate the .properties file, I commented out the locales in the module definition file so that the compiler will not sit there and compile again and again for every browser and every locale
i.e. supported_browser_count x enabled_locale_count = 5 browsers x 3 locales = 15 compilation Permutations, which is going to increase your compilation time.
Because, all I needed was that one *_en.properties file.
For the second compilation, after you copied and translated the properties files for each locale, you have to enable all the locales you want to support and compile.
Credits:
github #niloc132 : Colin Alworth
github #ibaca : Ignacio Baca Moreno-Torres
For helping me with this.
For my project, I used the i18n-Creator
http://code.google.com/webtoolkit/doc/latest/DevGuideI18n.html#DevGuidePropertiesFiles
It kind of does the opposite of what you are asking for. With the i18n-creator, you create the properties files for the various locales and run the script that is generated with the i18n-creator, and it will generate the constants interface.
I haven't heard yet of this feature in Eclipse but IntelliJ IDEA has this feature, you just create the Constants Interface class and the properties file. If you add a method in the class file it will warn you to add the property or the other way around. HTH.