Skip to content

[#1668] API types to simplify work with launch configuration attributes #1669

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ruspl-afed
Copy link
Contributor

@ruspl-afed ruspl-afed commented Dec 19, 2024

  • identify launch attribute
  • connect it with preference metadata (to supply defaults/label/description)
  • read attribute from configuration
  • write attribute to configuration working copy
  • demonstrate usage for ExternalTools

@eclipse-platform-bot
Copy link
Contributor

eclipse-platform-bot commented Dec 19, 2024

This pull request changes some projects for the first time in this development cycle.
Therefore the following files need a version increment:

ant/org.eclipse.ant.launching/META-INF/MANIFEST.MF
ant/org.eclipse.ant.ui/META-INF/MANIFEST.MF
debug/org.eclipse.ui.externaltools/META-INF/MANIFEST.MF

An additional commit containing all the necessary changes was pushed to the top of this PR's branch. To obtain these changes (for example if you want to push more changes) either fetch from your fork or apply the git patch.

Git patch
From 9d9fc2199cde44fd761137da4d6184cafbefd898 Mon Sep 17 00:00:00 2001
From: Eclipse Platform Bot <[email protected]>
Date: Mon, 10 Mar 2025 11:24:15 +0000
Subject: [PATCH] Version bump(s) for 4.36 stream


diff --git a/ant/org.eclipse.ant.launching/META-INF/MANIFEST.MF b/ant/org.eclipse.ant.launching/META-INF/MANIFEST.MF
index 701a5b50f8..9c9a10f1a4 100644
--- a/ant/org.eclipse.ant.launching/META-INF/MANIFEST.MF
+++ b/ant/org.eclipse.ant.launching/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
 Bundle-Localization: plugin
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.ant.launching;singleton:=true
-Bundle-Version: 1.4.700.qualifier
+Bundle-Version: 1.4.800.qualifier
 Bundle-Activator: org.eclipse.ant.internal.launching.AntLaunching
 Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)",
  org.eclipse.debug.core;bundle-version="[3.12.0,4.0.0)",
diff --git a/ant/org.eclipse.ant.ui/META-INF/MANIFEST.MF b/ant/org.eclipse.ant.ui/META-INF/MANIFEST.MF
index 14d67d91bc..d9d8d92c9a 100644
--- a/ant/org.eclipse.ant.ui/META-INF/MANIFEST.MF
+++ b/ant/org.eclipse.ant.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.ant.ui; singleton:=true
-Bundle-Version: 3.10.0.qualifier
+Bundle-Version: 3.10.100.qualifier
 Bundle-Activator: org.eclipse.ant.internal.ui.AntUIPlugin
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/debug/org.eclipse.ui.externaltools/META-INF/MANIFEST.MF b/debug/org.eclipse.ui.externaltools/META-INF/MANIFEST.MF
index 3d7bc00b7c..40216b2a14 100644
--- a/debug/org.eclipse.ui.externaltools/META-INF/MANIFEST.MF
+++ b/debug/org.eclipse.ui.externaltools/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %Plugin.name
 Bundle-SymbolicName: org.eclipse.ui.externaltools; singleton:=true
-Bundle-Version: 3.6.500.qualifier
+Bundle-Version: 3.6.600.qualifier
 Bundle-Activator: org.eclipse.ui.externaltools.internal.model.ExternalToolsPlugin
 Bundle-Vendor: %Plugin.providerName
 Bundle-Localization: plugin
-- 
2.48.1

Further information are available in Common Build Issues - Missing version increments.

Copy link
Contributor

github-actions bot commented Dec 19, 2024

Test Results

 1 947 files   1 947 suites   1h 37m 35s ⏱️
 4 720 tests  4 696 ✅  24 💤 0 ❌
14 160 runs  13 993 ✅ 167 💤 0 ❌

Results for commit e1b3b60.

♻️ This comment has been updated with latest results.

@mickaelistria
Copy link
Contributor

As mentioned in the call, some example using this API proposal would be helpful. There is probably some content in org.eclipse.ui.externaltools/External Tools Base/org/eclipse/ui/externaltools/internal/launchConfigurations you can tweak to leverage this API.

@ruspl-afed ruspl-afed force-pushed the 1668 branch 3 times, most recently from ceac906 to 0eaee71 Compare January 30, 2025 11:38
@ruspl-afed
Copy link
Contributor Author

I'm not sure why it is is failing @HannesWell

11:59:41.178 [ERROR] Failed to execute goal org.eclipse.tycho:tycho-apitools-plugin:4.0.11-SNAPSHOT:verify (verify) on project org.eclipse.debug.core: 
Execute ApiApplication failed: InvocationTargetException: EmptyStackException -> [Help 1]

@HannesWell
Copy link
Member

I'm not sure why it is is failing @HannesWell

11:59:41.178 [ERROR] Failed to execute goal org.eclipse.tycho:tycho-apitools-plugin:4.0.11-SNAPSHOT:verify (verify) on project org.eclipse.debug.core: 
Execute ApiApplication failed: InvocationTargetException: EmptyStackException -> [Help 1]

It looks like your change is breaking API-tools^^
I have replayed the Jenkins build the -e set to get an more informative stack-trace:
https://ci.eclipse.org/platform/job/eclipse.platform/job/PR-1669/7/

@ruspl-afed
Copy link
Contributor Author

Thank you @HannesWell for your assistance.

It looks like your change is breaking API-tools^^

WOW! But that wasn't part of my plan. It looks like this issue was already reported. Interesting, may be I can have a deeper look to it soon.

And still interested in your feedback regarding PR content.

@ruspl-afed
Copy link
Contributor Author

After supporting record for API Tools everything is green @HannesWell

@ruspl-afed
Copy link
Contributor Author

ok, a few more days for discussion and I'm going to merge it

@HannesWell
Copy link
Member

After supporting record for API Tools everything is green @HannesWell

Thank you for that fix/enhancement of API-tools! It's much appreciated.

And still interested in your feedback regarding PR content.

Sorry for not responding. It has been some busy days recently. I have not forgotten this, just didn't have the time for it.
I try my best to have look at it in the next days.

Copy link
Member

@HannesWell HannesWell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a more detailed look at it and understand the goal and see the benefits from this: Better type-safety and more convenient read and writes.
Nevertheless I'm not sure that these benefits really carry the weight of the change, because currently the new API surface is very big.
Below I made some suggestion to make the API more compact, maybe we can then achieve a sufficiently got relation of advantages to API surface.

With these suggestions we would end up with just the LaunchAttributeIdentity and LaunchAttributeDefined types. Maybe we could even 'inline' the former into the latter and would then still get benefits mentioned above.
And eventually just having an ILaunchAttribute interface added with a few static factories to create them is something that could indeed be a in my opinion suitable addition.

Comment on lines 50 to 52
if (String.class.equals(type)) {
return type.cast(configuration.getAttribute(id, String.class.cast(value.get())));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we have this if-chain in the constructor and then encapsulate the action into a lambda and just execute it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not so sure

@ruspl-afed ruspl-afed force-pushed the 1668 branch 3 times, most recently from aebee8c to a278c9a Compare February 8, 2025 13:00
@ruspl-afed
Copy link
Contributor Author

Thank you for your feedback @HannesWell

PR was reimplemented according to your comments.
I'm not sure why we are fighting for reducing number of API types with adding more API methods and more implementation code the the interface compilation unit, but the result looks much more closer to the old good Eclipse 2.0 style of Debug subsystem.

@HannesWell
Copy link
Member

HannesWell commented Feb 9, 2025

I'm not sure why we are fighting for reducing number of API types with adding more API methods and more implementation code the the interface compilation unit, but the result looks much more closer to the old good Eclipse 2.0 style of Debug subsystem.

I don't consider it as a fight, more as a search for (ideally) the best solution. Of course what is best is also a matter of opinion, but at least my personal opinion is that in general I like more compact APIs better. However can you please explain to me what the advantage is of defining in types what could be methods respectively why exactly this is Eclipse-2.0 style and why it's bad (I haven't been around back then)?
With the suggestions made we could still define typed launch-attributes using LaunchAttribute(-Defined/Definition) as static constant and read/write with it from/to a LaunchConfiguration in a more convenient way, couldn't we?

But of course others can have other opinions about it and I invite everyone to share theirs.

@ruspl-afed
Copy link
Contributor Author

You are right @HannesWell , it's definitely a matter of personal opinion. For me the old good Eclipse 2.0 style means a lot of static definitions, favoring of primitive types in API and a lot of setters to reuse one instance as long as one can. All this was actual 25 years ago for ancient JVMs. Nowadays we have modern JVM and very dynamic environment where non-replaceable static definition can cause problems and saving memory due to mutability of objects does not justify the cost of (much more complex) code support.

And even if I've stopped naming interfaces with "I" for new code and prefer simple immutable objects with very simple interfaces, I've done a lot following your comments to make this API improvement better suited to the surrounding code.

Now the question is: what else should be improved in this PR? I still can see your "change requested" veto but your latest comment doesn't content any direct change requests. Please clarify.

@ruspl-afed
Copy link
Contributor Author

Hello @HannesWell
What else do you think should be changed in this PR?

Copy link
Member

@HannesWell HannesWell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right @HannesWell , it's definitely a matter of personal opinion. For me the old good Eclipse 2.0 style means a lot of static definitions, favoring of primitive types in API and a lot of setters to reuse one instance as long as one can. All this was actual 25 years ago for ancient JVMs. Nowadays we have modern JVM and very dynamic environment where non-replaceable static definition can cause problems and saving memory due to mutability of objects does not justify the cost of (much more complex) code support.

My concern is not about a few objects more or less, I look at these new types from a consumer-perspective and how difficult a new API is to understand and learn. And there my experience/opinion is that 'distributed' types are more difficult and having it compact makes the capabilities easier to discover (as an IMO bad example: try to configure a JFace resource manager properly to be disposed with a Control and a child of the global registry).
As far as I know the problematic part of the Eclipse 2.0 style you referring to is that there tend to be only one large class that provided almost everything as static methods that don't allow dynamics, e.g. the Platform class in eclipse.runtime.
But at least from my perspective we are here talking about static factories on types (classes or interfaces), which I don't consider old-style. In fact I have the impression that's often the preferred way over constructors and many are added in Eclipse. From the top of my head I remember e.g. ILog or IPath or in the JDK there is Path.of() or the brand-new Classfile-API also makes heavy use of static factory methods AFAIK.

And even if I've stopped naming interfaces with "I" for new code and prefer simple immutable objects with very simple interfaces

Personally I'm also not fully convinced that it's best to prefix all interfaces I. But in the end Eclipse is an existing code-base and when adding elements considering how new code fits into the existing code-base is IMO also an item to consider. Of course that doesn't make it simpler. Immutability is also something I think has many advantages and I try to achieve. For simple interfaces on the other hand, I would say it depends and that's IMO where the work starts and were the quality of an API is measured, if the 'right' size is found.

Now the question is: what else should be improved in this PR? I still can see your "change requested" veto but your latest comment doesn't content any direct change requests. Please clarify.

Sorry, I simply oversaw that you made changes. I was very busy of getting rid of the JXPath dependency in platform.ui.
I have now made another pass, please see the comments below.

@ruspl-afed ruspl-afed force-pushed the 1668 branch 5 times, most recently from 1d54c43 to 502eedc Compare February 12, 2025 15:45
Copy link
Member

@HannesWell HannesWell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update.
I didn't had the time for a detailed review. I still think we should handle the case where the default value depends on other attributes, because IIRC we have this case multiple times in PDE and I would like to have this covered. But I'll provide a specific example for that.

I'll be on vacation now, but when back I'll come back to this. Sorry for the further delay.

@ruspl-afed ruspl-afed force-pushed the 1668 branch 2 times, most recently from 146cae3 to 0d99094 Compare March 14, 2025 06:24
@ruspl-afed ruspl-afed force-pushed the 1668 branch 3 times, most recently from 1c2827e to 425af03 Compare July 20, 2025 07:09
@ruspl-afed
Copy link
Contributor Author

@HannesWell I changed it to a state that we discussed on a meeting:

  1. remove ILaunchAttributeIdentity
  2. minimize API surface

@ruspl-afed
Copy link
Contributor Author

@HannesWell do you have more comments on this one?

@HannesWell
Copy link
Member

@HannesWell do you have more comments on this one?

I didn't had the time yet to look into this, but definitely want to.
Try to respond until the end of this week and will also look how to use this for PDE.

* identify launch attribute
* connect it with preference metadata (to supply
defaults/label/description)
* read attribute from configuration
* write attribute to configuration working copy
* demonstrate usage for ExternalTools
@HannesWell
Copy link
Member

@HannesWell do you have more comments on this one?

I didn't had the time yet to look into this, but definitely want to. Try to respond until the end of this week and will also look how to use this for PDE.

I only had the time for a short look at this change and in general I think this looks very good now. But I'd like to do an in depth review and try my best to do it tomorrow or Wednesday.

Copy link
Member

@HannesWell HannesWell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the update.

Conceptually I'm really happy with it now and for the API I just have one suggestion about the name of the probe method. And we should consider to have more factories to avoid verbosity in simple cases.

Everything else are just minor internal implementation details.

catch (CoreException e) {
// do nothing
}
fCurrentLocation = IExternalToolConstants.LAUNCH_ATTRIBUTE_LOCATION.probe(configuration);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before fCurrentLocation was left unchanged, if getAttribute() threw. But as the code below also handles empty optional, I think this change is fine.

*/
String UI_PLUGIN_ID = "org.eclipse.ui.externaltools"; //$NON-NLS-1$;
String UI_PLUGIN_ID = "org.eclipse.ui.externaltools"; //$NON-NLS-1$ ;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please remove the unnecessary trailing semicolon here and below?

Suggested change
String UI_PLUGIN_ID = "org.eclipse.ui.externaltools"; //$NON-NLS-1$ ;
String UI_PLUGIN_ID = "org.eclipse.ui.externaltools"; //$NON-NLS-1$

Comment on lines +33 to +40

public static String LaunchAttributeArguments_name;


public static String LaunchAttributeLocation_name;


public static String LaunchAttributeWorkingDirectory_name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid excessive new lines.

Suggested change
public static String LaunchAttributeArguments_name;
public static String LaunchAttributeLocation_name;
public static String LaunchAttributeWorkingDirectory_name;
public static String LaunchAttributeArguments_name;
public static String LaunchAttributeLocation_name;
public static String LaunchAttributeWorkingDirectory_name;

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation and others.
* Copyright (c) 2000, 2025 IBM Corporation and others.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot see any relevant change in this class, beside white-space changes.
If that's correct, please leave this class untouched.

* must not be <code>null</code>
* @return the {@link Optional} with attribute value
*/
Optional<V> probe(ILaunchConfiguration configuration);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to admit that I found the method name probe a bit unusual (and I'm not even sure it fit's perfectly here).
What about just naming this method to the ordinary name get()? With read one could somehow expect that it could fail.

Suggested change
Optional<V> probe(ILaunchConfiguration configuration);
Optional<V> get(ILaunchConfiguration configuration);

Comment on lines +81 to +82
* @param type the value type of the attribute, must not be
* <code>null</code>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of ``nullI generally would just use{@code null}`.

Suggested change
* @param type the value type of the attribute, must not be
* <code>null</code>
* @param type the type of the attribute value, must not be {@code null}

Comment on lines +83 to +85
* @param value the {@link Function} to calculate of default value of the
* attribute from {@link ILaunchConfiguration}, must not be
* <code>null</code>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe name this parameter defaultValue?

Suggested change
* @param value the {@link Function} to calculate of default value of the
* attribute from {@link ILaunchConfiguration}, must not be
* <code>null</code>
* @param defaultValue the {@link Function} to calculate the default value of the
* attribute for a given {@link ILaunchConfiguration}, must not be
* {@code null}.

As it's mentioned very often that an attribute may not be null, what about stating that once in the interface description and only mention the cases where null is allowed (I assume nowhere?)

Comment on lines +89 to +91
static <V> ILaunchAttribute<V> of(String id, Class<V> type, Function<ILaunchConfiguration, V> value, String name) {
return new ConsiderStringVariables<>(id, type, value, name, name);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As many callers just specify lc -> null as default value, what do you think about providing two more overloads for convenience for attributes that have a fixed default and no default:

  • of(String id, Class<V> type, V defaultValue, String name)
  • of(String id, Class<V> type, String name)

They can then just call the main of method with a corresponding lambda.

Comment on lines +79 to +93
if (String.class.equals(type)) {
working.setAttribute(id, String.class.cast(value));
} else if (Integer.class.equals(type)) {
working.setAttribute(id, Integer.class.cast(value).intValue());
} else if (Boolean.class.equals(type)) {
working.setAttribute(id, Boolean.class.cast(value).booleanValue());
} else if (List.class.equals(type)) {
working.setAttribute(id, List.class.cast(value));
} else if (Map.class.equals(type)) {
working.setAttribute(id, Map.class.cast(value));
} else if (Set.class.equals(type)) {
working.setAttribute(id, Set.class.cast(value));
} else {
working.setAttribute(id, value);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the distinction for the actual type actually necessary? AFAICT all special methods delegate to the object accepting variant any ways?

Suggested change
if (String.class.equals(type)) {
working.setAttribute(id, String.class.cast(value));
} else if (Integer.class.equals(type)) {
working.setAttribute(id, Integer.class.cast(value).intValue());
} else if (Boolean.class.equals(type)) {
working.setAttribute(id, Boolean.class.cast(value).booleanValue());
} else if (List.class.equals(type)) {
working.setAttribute(id, List.class.cast(value));
} else if (Map.class.equals(type)) {
working.setAttribute(id, Map.class.cast(value));
} else if (Set.class.equals(type)) {
working.setAttribute(id, Set.class.cast(value));
} else {
working.setAttribute(id, value);
}
working.setAttribute(id, value);

Similarly, although I haven't checked it in detail, the readInternal method maybe can be simplified too?

*
* @see IStringVariableManager
*/
record ConsiderStringVariables<V>(String id, Class<V> type, Function<ILaunchConfiguration, V> defaults, String name, String description) implements ILaunchAttribute<V> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name and description seem unused atm.

Suggested change
record ConsiderStringVariables<V>(String id, Class<V> type, Function<ILaunchConfiguration, V> defaults, String name, String description) implements ILaunchAttribute<V> {
record ConsiderStringVariables<V>(String id, Class<V> type, Function<ILaunchConfiguration, V> defaultValue) implements ILaunchAttribute<V> {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants