All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.saxsys.mvvmfx.internal.viewloader.JavaViewLoader Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright 2013 Alexander Casall, Manuel Mauky
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package de.saxsys.mvvmfx.internal.viewloader;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
import javafx.scene.Parent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.saxsys.mvvmfx.ViewModel;
import de.saxsys.mvvmfx.ViewTuple;

/**
 * This viewLoader is used to load views that are implementing {@link de.saxsys.mvvmfx.JavaView}.
 *
 * @author manuel.mauky
 */
public class JavaViewLoader {
	private static final Logger LOG = LoggerFactory.getLogger(JavaViewLoader.class);
	
	
	private static final String NAMING_CONVENTION_RESOURCES_IDENTIFIER = "resources";
	private static final String NAMING_CONVENTION_INITIALIZE_IDENTIFIER = "initialize";
	
	/**
	 * Loads the java written view of the given type and injects the ViewModel for this view. 
* If the given view type implements the {@link javafx.fxml.Initializable} interface the "initialize" method of this * interface will be invoked. When this is not the case an implicit initialization will be done that is working * similar to the way the {@link javafx.fxml.FXMLLoader} is working.
* When there is a public no-args method named "initialize" is available this method will be * called. When there is a public field of type {@link java.util.ResourceBundle} named "resources" * is available this field will get the provided ResourceBundle injected.
* The "initialize" method (whether from the {@link javafx.fxml.Initializable} interface or implicit) will be * invoked after the viewModel was injected. This way the user can create bindings to the viewModel * in the initialize method. * * @param viewType * class of the view. * @param resourceBundle * optional ResourceBundle that will be injected into the view. * @param viewModel * the viewModel instance that is used to load the view. * @param * the generic type of the view. * @param * the generic type of the viewModel. * * @return a fully loaded and initialized instance of the view. */ public , ViewModelType extends ViewModel> ViewTuple loadJavaViewTuple( Class viewType, ResourceBundle resourceBundle, ViewModelType viewModel) { DependencyInjector injectionFacade = DependencyInjector.getInstance(); final ViewType view = injectionFacade.getInstanceOf(viewType); if (!(view instanceof Parent)) { throw new IllegalArgumentException("Can not load java view! The view class has to extend from " + Parent.class.getName() + " or one of it's subclasses"); } if (viewModel == null) { viewModel = ReflectionUtils.createViewModel(view); } ReflectionUtils.injectViewModel(view, viewModel); if (view instanceof Initializable) { Initializable initializable = (Initializable) view; initializable.initialize(null, resourceBundle); } else { injectResourceBundle(view, resourceBundle); callInitialize(view); } return new ViewTuple<>(view, (Parent) view, viewModel); } /** * This method is trying to invoke the initialize method of the given view by reflection. This is done to meet the * conventions of the {@link javafx.fxml.FXMLLoader}. The conventions say that when there is a * public no-args method called "initialize" and the class does not implement the * {@link javafx.fxml.Initializable} interface, the initialize method will be invoked.
* This method is package scoped for better testability. * * @param view * the view instance of which the initialize method will be invoked. * @param * the generic type of the view. */ void callInitialize(View view) { try { final Method initializeMethod = view.getClass().getMethod(NAMING_CONVENTION_INITIALIZE_IDENTIFIER); initializeMethod.invoke(view); } catch (NoSuchMethodException e) { // This exception means that there is no initialize method declared. // While it's possible that the user has no such method by design, // normally and in most cases you need an initialize method in your view (either with Initialize interface // or implicit). // So it's likely that the user has misspelled the method name or uses a wrong naming convention. // For this reason we give the user the log message. LOG.debug("There is no '{}' method declared at the view {}", NAMING_CONVENTION_INITIALIZE_IDENTIFIER, view); } catch (InvocationTargetException e) { LOG.warn("The '{}' method of the view {} has thrown an exception!", NAMING_CONVENTION_INITIALIZE_IDENTIFIER, view); Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else { throw new RuntimeException(cause); } } catch (IllegalAccessException e) { LOG.warn("Can't invoke the '{}' method of the view {} because it is not accessible", NAMING_CONVENTION_INITIALIZE_IDENTIFIER, view); } } /** * Injects the given ResourceBundle into the given view using reflection. This is done to meet the conventions of * the {@link javafx.fxml.FXMLLoader}. The resourceBundle is only injected when there is a public * field of the type {@link java.util.ResourceBundle} named "resources".
* This method is package scoped for better testability. * * @param view * the view instance that gets the resourceBundle injected. * @param resourceBundle * the resourceBundle instance that will be injected. * @param * the generic type of the view. */ void injectResourceBundle(View view, ResourceBundle resourceBundle) { try { Field resourcesField = view.getClass().getField(NAMING_CONVENTION_RESOURCES_IDENTIFIER); if (resourcesField.getType().isAssignableFrom(ResourceBundle.class)) { resourcesField.set(view, resourceBundle); } } catch (NoSuchFieldException e) { // This exception means that there is no field for the ResourceBundle. // This is no exceptional case but is normal when you don't need a resourceBundle in a specific view. // Therefore it's save to silently catch the exception. } catch (IllegalAccessException e) { LOG.warn("Can't inject the ResourceBundle into the view {} because the field isn't accessible", view); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy