Skip to content
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

[WIP] Refactoring and cleanup #49

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open

Conversation

Squareys
Copy link
Contributor

@Squareys Squareys commented Sep 6, 2016

Hello everybody!

This is @dietzc's work-in-progress which I will be doing a polishing pass on.

TODOs:

  • Documentation
  • Batch together WIP commits with some description of what was done
  • Repair all FIXMEs
  • Proper Logging / Exception handling (which is part of FIXMEs)
  • Proper MissingValue Handling
  • Unit and Workflow-Tests where suitable
  • Split scripting away, open PR in knip-scripting and move it there.
  • Remove temp ScijavaTestCommand and Script directory, implement a replacement

Regards,
Jonathan.

This is a work-in-progress pull request and not meant to be merged before [WIP] is removed from the title!

public SettingsModelType getSettingsModelTypeFor(final ModuleItem<?> item) {

// FIXME why do I need this check in case of scripts?... Why should you
// not?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dietzc ? :)

Copy link
Member

Choose a reason for hiding this comment

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

I think the question here was/is why I need the item.getWidgetStyle()!=null check.

Copy link
Contributor Author

@Squareys Squareys Sep 12, 2016

Choose a reason for hiding this comment

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

(Context independent description of the problem:)

null is default for getWidgetStyle() in DefaultMutableModuleItem, see here. It is used as ModuleItems for ScriptInfo, => more proof, and is only set if a matching key exists in the parameter annotation in the script.

In the @Parameter annotation style() is default "", though, which is why getWidgetStyle() is never null for CommandModule (also see this line ).

@ctrueden This is a slight inconsistency in Scijava. What is your oppinion about this? There seems to be no documentation about the getWidgetStyle() method possibly returning null, so I guess the widget style should probably be initialized with "" for DefaultMutableModuleItem?

Copy link
Member

Choose a reason for hiding this comment

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

This is a slight inconsistency in Scijava. What is your oppinion about this? There seems to be no documentation about the getWidgetStyle() method possibly returning null, so I guess the widget style should probably be initialized with "" for DefaultMutableModuleItem?

I agree this is inconsistent. However, it goes far beyond just widget style. Every attribute of the @Parameter annotation has non-null defaults, because Java annotations do not allow null values whatsoever. However, that is inconsistent with SJC behavior in general, where null—not ""—is used to indicate no/default value/behavior.

I suppose my preferred solution to this conundrum is to change the PluginInfo class to translate empty strings into nulls, as perverse as that seems...

Fixes the related FIXMEs.

Signed-off-by: Squareys <[email protected]>
We handle the single row output using the "enableManualPush" flag and a
final flush() method which pushes a row only if manual push is not enabled.

Manual push is enabled for modules with a output listener parameter.

Signed-off-by: Squareys <[email protected]>
If `.createInstancesOfType(PreprocessorPlugin.class);` does not produce
results, the returned list will be empty, but not null.

Signed-off-by: Squareys <[email protected]>
module.resolveInput(entry.getKey());
}

// FIXME: do we need them all?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dietzc Does it matter?

Copy link
Member

Choose a reason for hiding this comment

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

depends how costly the preprocessors are. As we've no put the preprocessing logic outside the run logic it seems to be fine to take all preprocessors. However, there are preprocessors which are really useless in case of headless execution.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since they are only executed once per Node Execution, I doubt this will impact performance. What's your oppinion?

I think it is probably not worth the effort bisecting the preprocessors to find out which we need and which we don't. (Aka. premature opt... we can always revisit this if we see this is turning into a problem)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dietzc Are you ok with that? Then I would remove the FIXME.

Copy link
Contributor Author

@Squareys Squareys Sep 13, 2016

Choose a reason for hiding this comment

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

I removed the FIXME. If you object, I will add it back in.

  • Comment seen.

Example: input type is "MyMultiOutputListener" (my subclass of
MultiOutputListener), then the check would suceed, since I can assign this
type to MultiOutputListener.

When I try to assign NodeModuleOutputChangedListener, it would throw an
IllegalArgumentException.

If we would just simply switch to testing whether the item type is
assignable from MultiOutputListener, it could also be Object, which would
also resolve Object inputs with our logic here, which is not intended.

A simple check for the class being exactly MultiOutputListener.class is
therefore suficient.

Signed-off-by: Squareys <[email protected]>
* if <code>true</code>, signals that the module handles
* pushing rows.
*/
public void enableManualPush(final boolean b) {
Copy link
Member

Choose a reason for hiding this comment

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

uh, this is really state-driven now. can we avoid this method, e.g. by determining in the constructor if manual or not?! just asking, not saying it's nicer.

Copy link
Contributor Author

@Squareys Squareys Sep 12, 2016

Choose a reason for hiding this comment

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

Well, since it is only called immediately after the constructor, I can put it into the constructor, makes sense :)

@Squareys Squareys mentioned this pull request Sep 13, 2016
@Squareys
Copy link
Contributor Author

@dietzc I had this idea to split NodeModule and DefaultNodeModuleService into Scripting- and Command- to handle the slight differences (primarily "result" output module item atm). This could even be extended to more module info types than just ScriptInfo, CommandInfo. Possibly ImageJ Plugins?

My current attempt will not work, though, since the appropriate Service cannot be chosen depending on the module info type. Instead, I would introduce a new singleton plugin type which allows adding support for more ModuleInfo types in DefaultNodeModuleService (which will then handle those plugins). It would basically define two methods createNodeModule() and createOutputColumnSpecs() (possibly Class<? extends ModuleInfo) getModuleInfoType()).

Does that sound sane?

@ctrueden
Copy link
Member

ctrueden commented Sep 13, 2016 via email

@dietzc
Copy link
Member

dietzc commented Sep 13, 2016

@ctrueden each Script outputs a result String for logging errors etc. I think @gab1one and you have already discussed this issue and also solved it? The reason why we have to treat the result output in a a special way, is that in KNIME we create the output-spec of the resulting table (especially how many columns, which types etc) before we execute the node. However, the result is a special thing in scripting which is not part of the ModuleInfo we get after parsing the script. That's why we have to treat the result in a special way.

Either, we handle the result on our side or on scijava-side. I'd prefer to do it in scijava and if you have suggestions how to do it, I'm sure @Squareys can help you. It would be great if the abstraction layer we use in our code is ModuleInfo rather than having special cases for different types of ModuleInfo.

One related issue: scijava/scijava-common#225

@ctrueden
Copy link
Member

ctrueden commented Sep 13, 2016

Ok great, I thought he was talking about the Object result which SciJava adds to scripts with no declared outputs.

@dietzc
Copy link
Member

dietzc commented Sep 13, 2016

Actually, this one might be problematic, too. Is it also added to the ScriptInfo?

Signed-off-by: Squareys <[email protected]>
@dietzc
Copy link
Member

dietzc commented Sep 14, 2016

After discussing this with @ctrueden in person we figured that we still should special case the result output of a ScriptInfo if it was added by the framework. Especially, as we want to support n-to-0 nodes. However, we don't need two implementations in this case., I think a if(info instanceof ScriptInfo) is fine.

@ctrueden
Copy link
Member

ctrueden commented Sep 14, 2016

More specifically, I would recommend:

boolean hasSyntheticResult = info instanceof ScriptInfo &&
    ((ScriptInfo) info).isReturnValueAppended();

@Squareys Squareys force-pushed the refactoring-and-cleanup branch 2 times, most recently from 56398f7 to 255e9fc Compare September 26, 2016 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants