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

Fix #500. Implement checking of views to prevent injection of scopes #565

Merged
merged 1 commit into from
Oct 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ public Object call(Class<?> type) {
}

private static void handleInjection(View codeBehind, ResourceBundle resourceBundle, ContextImpl context, ObservableBooleanValue viewInSceneProperty) {
ViewLoaderReflectionUtils.checkScopesInView(codeBehind);

ResourceBundleInjector.injectResourceBundle(codeBehind, resourceBundle);

Consumer<ViewModel> newVmConsumer = viewModel -> {
Expand All @@ -379,6 +381,8 @@ private static void handleInjection(View codeBehind, ResourceBundle resourceBund

private static void handleInjection(View codeBehind, ResourceBundle resourceBundle, ViewModel viewModel,
ContextImpl context, ObservableBooleanValue viewInSceneProperty) {
ViewLoaderReflectionUtils.checkScopesInView(codeBehind);

ResourceBundleInjector.injectResourceBundle(codeBehind, resourceBundle);

if (viewModel != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,4 +470,20 @@ static void addSceneLifecycleHooks(ViewModel viewModel, ObservableBooleanValue v
}
}
}

/**
* This method is used to check if the given View instance has one or more fields that try to inject a scope
* with the {@link InjectScope} annotation.
* This is a violation of the mvvm-pattern and therefore this method will throw an exception with a message describing the
* error.
*/
static void checkScopesInView(View codeBehind) {
List<Field> scopeFields = ReflectionUtils.getFieldsWithAnnotation(codeBehind, InjectScope.class);

if(!scopeFields.isEmpty()) {
throw new IllegalStateException("The view class [" + codeBehind.getClass().getSimpleName() + "] tries to inject a Scope with " +
"@InjectScope. This would be a violation of the mvvm pattern. Scopes are only supported in ViewModels.");
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package de.saxsys.mvvmfx.scopes.scope_in_view;

import de.saxsys.mvvmfx.FxmlView;
import de.saxsys.mvvmfx.InjectViewModel;

public class RootView implements FxmlView<RootViewModel> {

@InjectViewModel
private RootViewModel viewModel;

public void initialize() {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package de.saxsys.mvvmfx.scopes.scope_in_view;

import de.saxsys.mvvmfx.ViewModel;

public class RootViewModel implements ViewModel {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package de.saxsys.mvvmfx.scopes.scope_in_view;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

import de.saxsys.mvvmfx.FluentViewLoader;
import de.saxsys.mvvmfx.testingutils.ExceptionUtils;

/**
* Scopes are only allowed in ViewModels. If a user tries to inject them in a View/CodeBehind then
* an exception should be thrown to notify the user about this error.
*/
public class ScopeInViewTest {

@Test
public void testDirectLoading() {
RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> {
FluentViewLoader.fxmlView(ScopeInViewTestView.class)
.providedScopes(new TestScope())
.load();
});

Throwable rootCause = ExceptionUtils.getRootCause(runtimeException);
assertThat(rootCause).isInstanceOf(IllegalStateException.class).hasMessageContaining("tries to inject a Scope");
}

/**
* This test case reproduces a separate branch in the viewloader logic that is used when
* a viewModel and a view instance is provided.
*/
@Test
public void testDirectLoadingWithExistingViewModel() {
RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> {
FluentViewLoader.fxmlView(ScopeInViewTestView.class)
.providedScopes(new TestScope())
.codeBehind(new ScopeInViewTestView())
.viewModel(new ScopeInViewTestViewModel())
.load();
});

Throwable rootCause = ExceptionUtils.getRootCause(runtimeException);
assertThat(rootCause).isInstanceOf(IllegalStateException.class).hasMessageContaining("tries to inject a Scope");
}


@Test
public void testSubviewLoading() {
RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> {
FluentViewLoader.fxmlView(RootView.class)
.providedScopes(new TestScope())
.load();
});


Throwable rootCause = ExceptionUtils.getRootCause(runtimeException);
assertThat(rootCause).isInstanceOf(IllegalStateException.class).hasMessageContaining("tries to inject a Scope");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.saxsys.mvvmfx.scopes.scope_in_view;

import de.saxsys.mvvmfx.FxmlView;
import de.saxsys.mvvmfx.InjectScope;
import de.saxsys.mvvmfx.InjectViewModel;

public class ScopeInViewTestView implements FxmlView<ScopeInViewTestViewModel> {

@InjectScope
private TestScope scope;

@InjectViewModel
private ScopeInViewTestViewModel viewModel;

public void initialize() {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.saxsys.mvvmfx.scopes.scope_in_view;

import de.saxsys.mvvmfx.ScopeProvider;
import de.saxsys.mvvmfx.ViewModel;

public class ScopeInViewTestViewModel implements ViewModel {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package de.saxsys.mvvmfx.scopes.scope_in_view;

import de.saxsys.mvvmfx.Scope;

public class TestScope implements Scope {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.AnchorPane?>


<AnchorPane fx:controller="de.saxsys.mvvmfx.scopes.scope_in_view.RootView" xmlns="http://javafx.com/javafx/8.0.60"
xmlns:fx="http://javafx.com/fxml/1">
<fx:include source="ScopeInViewTestView.fxml"/>
</AnchorPane>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.AnchorPane?>


<AnchorPane fx:controller="de.saxsys.mvvmfx.scopes.scope_in_view.ScopeInViewTestView" xmlns="http://javafx.com/javafx/8.0.60"
xmlns:fx="http://javafx.com/fxml/1">
</AnchorPane>