/*******************************************************************************
 * Copyright 2015 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.utils.validation;

import javafx.beans.binding.Bindings;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;

import java.util.Collection;
import java.util.Optional;

/**
 * This class represents the state of a {@link Validator}.
 *

 * This class is reactive, which means that it's values will represent the current validation status. When the
 * validation status changes the observable lists for the messages will be updated automatically.
 *
 *
 * @author manuel.mauky
 */
public class ValidationStatus {

	private ListProperty<ValidationMessage> messages = new SimpleListProperty<>(FXCollections.observableArrayList());

	private ObservableList<ValidationMessage> unmodifiableMessages = FXCollections.unmodifiableObservableList(messages);
	private ObservableList<ValidationMessage> errorMessages = new FilteredList<>(unmodifiableMessages, message -> message.getSeverity().equals(Severity.ERROR));
	private ObservableList<ValidationMessage> warningMessages = new FilteredList<>(unmodifiableMessages, message -> message.getSeverity().equals(Severity.WARNING));

	/**
	 * Intended for subclasses used by special validator implementations.
	 * Such subclasses can access the message list with writing accesses (unlike the {@link #getMessages()}
	 * which is read-only.
	 *
	 * An example for a subclass is {@link CompositeValidationStatus}.
	 */
	protected ObservableList<ValidationMessage> getMessagesInternal() {
		return messages;
	}

	void addMessage(ValidationMessage message) {
		getMessagesInternal().add(message);
	}

	void addMessage(Collection<ValidationMessage> messages) {
		getMessagesInternal().addAll(messages);
	}

	void removeMessage(ValidationMessage message) {
		getMessagesInternal().remove(message);
	}

	void removeMessage(Collection<? extends ValidationMessage> messages) {
		getMessagesInternal().removeAll(messages);
	}

	void clearMessages() {
		getMessagesInternal().clear();
	}

	/**
	 * @return an unmodifiable observable list of all messages.
	 */
	public ObservableList<ValidationMessage> getMessages() {
		return unmodifiableMessages;
	}

	/**
	 * @return an unmodifiable observable list of all messages of severity {@link Severity#ERROR}.
	 */
	public ObservableList<ValidationMessage> getErrorMessages() {
		return errorMessages;
	}

	/**
	 * @return an unmodifiable observable list of all messages of severity {@link Severity#WARNING}.
	 */
	public ObservableList<ValidationMessage> getWarningMessages() {
		return warningMessages;
	}

	/**
	 * @return true if there are no validation messages present.
	 */
	public ReadOnlyBooleanProperty validProperty() {
		return messages.emptyProperty();
	}

	public boolean isValid() {
		return validProperty().get();
	}

	/**
	 * Returns the message with the highest priority using the following algorithm: - if there are messages with
	 * {@link Severity#ERROR}, take the first one. - otherwise, if there are messages with {@link Severity#WARNING},
	 * take the first one. - otherwise, an empty Optional is returned.
	 *
	 * @return an Optional containing the ValidationMessage or an empty Optional.
	 */
	public Optional<ValidationMessage> getHighestMessage() {
		final Optional<ValidationMessage> error = getMessages().stream()
				.filter(message -> message.getSeverity().equals(Severity.ERROR))
				.findFirst();

		if (error.isPresent()) {
			return error;
		} else {
			final Optional<ValidationMessage> warning = getMessages().stream()
					.filter(message -> message.getSeverity().equals(Severity.WARNING))
					.findFirst();

			return warning;
		}
	}

}


/*******************************************************************************
 * Copyright 2015 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 eu.lestard.doc.Internal;
import sun.util.ResourceBundleEnumeration;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListResourceBundle;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;

/**
 * @author manuel.mauky
 */
@Internal
public class ResourceBundleManager {

	private static final ResourceBundleManager SINGLETON = new ResourceBundleManager();
	private ResourceBundle globalResourceBundle;

	ResourceBundleManager() {
	}

	/**
	 * Represents an empty ResourceBundle that is used when no actual resourceBundle was provided by the user.
	 */
	public static final ResourceBundle EMPTY_RESOURCE_BUNDLE = new ListResourceBundle() {
		@Override protected Object[][] getContents() {
			return new Object[0][];
		}
	};

	public static ResourceBundleManager getInstance() {
		return SINGLETON;
	}

	public void setGlobalResourceBundle(ResourceBundle resourceBundle) {
		this.globalResourceBundle = resourceBundle;
	}

	public ResourceBundle getGlobalResourceBundle() {
		return globalResourceBundle;
	}

	/**
	 * Merges the provided ResourceBundle with the global one (if any).
	 *
	 * The global resourceBundle has a lower priority then the provided one. If there is the same key defined in the
	 * global and the provided resourceBundle, the value from the provided resourceBundle will be used.
	 *
	 * @param resourceBundle
	 *            a resourceBundle that will be merged.
	 * @return the merged resourceBundle or null, if there is no global resource bundle and not given resource bundle.
	 */
	public ResourceBundle mergeWithGlobal(ResourceBundle resourceBundle) {
		if (globalResourceBundle == null) {
			if (resourceBundle == null) {
				return EMPTY_RESOURCE_BUNDLE;
			} else {
				return new ResourceBundleWrapper(resourceBundle);
			}
		} else {
			if (resourceBundle == null) {
				return new ResourceBundleWrapper(globalResourceBundle);
			} else {
				return merge(resourceBundle, globalResourceBundle);
			}
		}
	}

	private static ResourceBundle reduce(List<ResourceBundle> bundles) {
		return bundles.stream()
				.filter(Objects::nonNull)
				.reduce(EMPTY_RESOURCE_BUNDLE, (a, b) -> merge(b, a));
	}

	/**
	 * Merges the list of ResourceBundles with the global one (if any).
	 *

	 * The global resourceBundle has a lower priority then the provided ones. If there is the same key defined in the
	 * global and in one of the provided resourceBundles, the value from the provided resourceBundle will be used.
	 *

	 * The order of resourceBundles in the list defines the priority for resourceBundles.
	 * ResourceBundles at the start of the list have a lower priority compared to bundles
	 * at the end of the list. This means that the last resourceBundle will overwrite values from previous resourceBundles
	 * (including the global resourceBundle (if any)).	 *
	 *
	 * @param bundles
	 *            a list of resourceBundles that will be merged. null is accepted.
	 * @return the merged resourceBundle.
	 */
	public ResourceBundle mergeListWithGlobal(List<ResourceBundle> bundles) {
		if (globalResourceBundle == null) {
			if (bundles == null) {
				return EMPTY_RESOURCE_BUNDLE;
			} else {
				return reduce(bundles);
			}
		} else {
			if (bundles == null) {
				return new ResourceBundleWrapper(globalResourceBundle);
			} else {
				final List<ResourceBundle> resourceBundles = new ArrayList<>();
				resourceBundles.add(globalResourceBundle);
				resourceBundles.addAll(bundles);
				return reduce(resourceBundles);
			}
		}
	}

	private static ResourceBundle merge(ResourceBundle highPriority, ResourceBundle lowPriority) {
		return new MergedResourceBundle(highPriority, lowPriority);
	}

	private static class MergedResourceBundle extends ResourceBundle {
		private ResourceBundle highPriority;
		private ResourceBundle lowPriority;

		public MergedResourceBundle(ResourceBundle highPriority, ResourceBundle lowPriority) {
			Objects.nonNull(highPriority);
			Objects.nonNull(lowPriority);
			this.highPriority = highPriority;
			this.lowPriority = lowPriority;
		}

		@Override
		protected Object handleGetObject(String key) {
			if (highPriority.containsKey(key)) {
				return highPriority.getObject(key);
			}

			if (lowPriority.containsKey(key)) {
				return lowPriority.getObject(key);
			}

			return null;
		}

		@Override
		public Enumeration<String> getKeys() {
			return new MergedEnumeration(highPriority.keySet(), lowPriority.keySet());
		}
	}

	private static class MergedEnumeration implements Enumeration<String> {

		final Iterator<String> iterator;

		public MergedEnumeration(Set<String> highPriority, Set<String> lowPriority) {
			Set<String> allElements = new HashSet<>();
			allElements.addAll(highPriority);
			allElements.addAll(lowPriority);

			iterator = allElements.iterator();
		}

		@Override
		public boolean hasMoreElements() {
			return iterator.hasNext();
		}

		@Override
		public String nextElement() {
			return iterator.next();
		}
	}

	/**
	 * This wrapper is needed due to a bug in FXMLLoader (https://javafx-jira.kenai.com/browse/RT-33764).
	 *
	 * With this class we make sure that there is a classLoader defined for this class.
	 */
	private static class ResourceBundleWrapper extends ResourceBundle {
		private final ResourceBundle bundle;

		ResourceBundleWrapper(ResourceBundle bundle) {
			this.bundle = bundle;
		}

		@Override
		protected Object handleGetObject(String key) {
			return bundle.getObject(key);
		}

		@Override
		public Enumeration<String> getKeys() {
			return bundle.getKeys();
		}

		@Override
		public boolean containsKey(String key) {
			return bundle.containsKey(key);
		}

		@Override
		public Locale getLocale() {
			return bundle.getLocale();
		}

		@Override
		public Set<String> keySet() {
			return bundle.keySet();
		}
	}
}


/*******************************************************************************
 * 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 javafx.util.Callback;

/**
 * This class handles the dependency injection for the mvvmFX framework.
 *
 * The main reason for this class is to make it possible for the user to use her own dependency injection
 * mechanism/framework. The user can define how instances should be retrieved by setting an callback that returns an
 * instance for a given class type (see {@link DependencyInjector#setCustomInjector}.
 *
 * @author manuel.mauky
 */
public class DependencyInjector {

	private Callback<Class<?>, Object> customInjector;

	private static DependencyInjector singleton = new DependencyInjector();

	DependencyInjector() {
	}

	public static DependencyInjector getInstance() {
		return singleton;
	}

	/**
	 * Define a custom injector that is used to retrieve instances. This can be used as a bridge to you dependency
	 * injection framework.
	 *
	 * The callback has to return an instance for the given class type. This is same way as it is done in the
	 * {@link javafx.fxml.FXMLLoader#setControllerFactory(javafx.util.Callback)} method.
	 *
	 * @param callback
	 *            the callback that returns instances of a specific class type.
	 */
	public void setCustomInjector(Callback<Class<?>, Object> callback) {
		this.customInjector = callback;
	}

	/**
	 * Returns an instance of the given type. When there is a custom injector defined (See:
	 * {@link #setCustomInjector(javafx.util.Callback)}) then this injector is used. Otherwise a new instance of the
	 * desired type is created. This is done by a call to {@link Class#newInstance()} which means that all constraints
	 * of the newInstance method are also need to be satisfied.
	 *
	 * @param type
	 * @param <T>
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T getInstanceOf(Class<? extends T> type) {
		if (isCustomInjectorDefined()) {
			return (T) customInjector.call(type);
		} else {
			try {
				// use default creation
				return type.newInstance();
			} catch (InstantiationException | IllegalAccessException e) {
				throw new RuntimeException("Can't create instance of type " + type.getName() +
						". Make sure that the class has a public no-arg constructor.", e);
			}
		}
	}

	/**
	 * See {@link #setCustomInjector(javafx.util.Callback)} for more details.
	 *
	 * @return the defined custom injector if any
	 */
	Callback<Class<?>, Object> getCustomInjector() {
		return customInjector;
	}

	/**
	 * @return true when a custom injector is defined, otherwise false.
	 */
	boolean isCustomInjectorDefined() {
		return customInjector != null;
	}

}


/*******************************************************************************
 * 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.utils.itemlist;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import javafx.beans.property.ListProperty;
import javafx.beans.property.ReadOnlyListProperty;
import javafx.beans.property.ReadOnlyListWrapper;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.WeakListChangeListener;

/**
 * Binds an {@link javafx.collections.ObservableList} that contains elements of {@link TargetType} to another
 * {@link javafx.collections.ObservableList} that contains elements of {@link SourceType}. This is different to the
 * normal list binding offered by JavaFX where the lists have to contain elements of the same type.
 *
 * @param <SourceType>
 *            the generic type of the source list.
 * @param <TargetType>
 *            the generic type of the target list.
 */
public class ListTransformation<SourceType, TargetType> {

	// Converter
	private final Function<SourceType, TargetType> function;

	// The two lists - List which was provided and the TargetType representation of
	// the list
	private ReadOnlyListWrapper<TargetType> viewModelList = new ReadOnlyListWrapper<>(
			FXCollections.<TargetType> observableArrayList());
	private ListProperty<SourceType> sourceList = new SimpleListProperty<>();

	// Reference to the listener to use it by a wrapped listChangeListener
	private ListChangeListener<SourceType> listChangeListener;

	/**
	 * Creates a {@link ListTransformation} by a given list of items and a function.
	 *
	 * @param modelList
	 *            which should be transformed for the UI
	 * @param function
	 *            which is used for transformation
	 */
	public ListTransformation(ObservableList<SourceType> modelList,
			final Function<SourceType, TargetType> function) {
		this.function = function;
		initListEvents();
		this.modelListProperty().set(modelList);
	}

	/**
	 * Creates a {@link ListTransformation} by with a given function.
	 *
	 * @param function
	 *            which is used for transformation
	 */
	public ListTransformation(final Function<SourceType, TargetType> function) {
		this(FXCollections.observableArrayList(), function);
	}

	// If the list changed we want the recreate the targetType representation
	private void initListEvents() {
		this.listChangeListener = new ListChangeListener<SourceType>() {
			@Override
			public void onChanged(
					Change<? extends SourceType> listEvent) {

				// We have to stage delete events, because if we process them
				// separately, there will be unwanted ChangeEvents on the
				// targetList
				List<TargetType> deleteStaging = new ArrayList<>();

				while (listEvent.next()) {
					if (listEvent.wasUpdated()) {
						processUpdateEvent(listEvent);
					} else if (listEvent.wasReplaced()) {
						processReplaceEvent(listEvent, deleteStaging);
					} else if (listEvent.wasAdded()) {
						processAddEvent(listEvent);
					} else if (listEvent.wasRemoved()) {
						processRemoveEvent(listEvent, deleteStaging);
					}
				}

				// Process the staged elements
				processStagingLists(deleteStaging);
			}
		};
		modelListProperty().addListener(
				new WeakListChangeListener<>(listChangeListener));

	}

	/**
	 * Maps an add event of the model list to new elements of the {@link #viewModelList}.
	 *
	 * @param listEvent
	 *            to analyze
	 */
	private void processAddEvent(
			ListChangeListener.Change<? extends SourceType> listEvent) {

		final List<TargetType> toAdd = new ArrayList<>();
		for (int index = listEvent.getFrom(); index < listEvent.getTo(); index++) {
			final SourceType item = listEvent.getList().get(index);
			toAdd.add(function.apply(item));
		}
		viewModelList.addAll(listEvent.getFrom(), toAdd);
	}

	/**
	 * Maps an remove event of the model list to new elements of the {@link #viewModelList}.
	 *
	 * @param listEvent
	 *            to process
	 * @param deleteStaging
	 *            for staging the delete events
	 */
	private void processRemoveEvent(
			ListChangeListener.Change<? extends SourceType> listEvent,
			List<TargetType> deleteStaging) {
		for (int i = 0; i < listEvent.getRemovedSize(); i++) {
			deleteStaging.add(viewModelList.get(listEvent.getFrom() + i));
		}
	}

	/**
	 * Maps an update event of the model list to new elements of the {@link #viewModelList}.
	 *
	 * @param listEvent
	 *            to process
	 */
	private void processUpdateEvent(ListChangeListener.Change<? extends SourceType> listEvent) {
		for (int i = listEvent.getFrom(); i < listEvent.getTo(); i++) {
			SourceType item = listEvent.getList().get(i);
			viewModelList.set(i, ListTransformation.this.function.apply(item));
		}
	}

	/**
	 * Maps an replace event of the model list to new elements of the {@link #viewModelList}.
	 *
	 * @param listEvent
	 *            to process
	 */
	private void processReplaceEvent(
			ListChangeListener.Change<? extends SourceType> listEvent, List<TargetType> deletedStaging) {
		processRemoveEvent(listEvent, deletedStaging);
		processStagingLists(deletedStaging);
		processAddEvent(listEvent);
	}

	/**
	 * Process staging events.
	 *
	 * @param deleteStaging
	 *            to process
	 */
	private void processStagingLists(List<TargetType> deleteStaging) {
		viewModelList.removeAll(deleteStaging);
		deleteStaging.clear();
	}

	/**
	 * @deprecated Please use {@link #sourceListProperty()} instead.
	 * @return List of elements which should be transformed.
	 */
	@Deprecated
	public ListProperty<SourceType> modelListProperty() {
		return sourceList;
	}

	/**
	 * Set the model list that should be synchronized with the target list.
	 * @deprecated Please use {@link #setSourceList(ObservableList)} instead.
	 *
	 * @param modelList
	 *            the source list
	 */
	@Deprecated
	public void setModelList(ObservableList<SourceType> modelList) {
		this.sourceList.set(modelList);
	}

	/**
	 * @deprecated Please use {@link #getSourceList()} instead.
	 * @return the model list that should be synchronized with the target list.
	 */
	@Deprecated
	public ObservableList<SourceType> getModelList() {
		return sourceList.get();
	}

	/**
	 *
	 * @return List of elements which should be transformed.
	 */
	public ListProperty<SourceType> sourceListProperty() {
		return sourceList;
	}

	/**
	 * Set the source list that should be synchronized with the target list.
	 *
	 * @param sourceList
	 *            the source list
	 */
	public void setSourceList(ObservableList<SourceType> sourceList) {
		this.sourceList.set(sourceList);
	}

	/**
	 * @return the source list that should be synchronized with the target list.
	 */
	public ObservableList<SourceType> getSourceList() {
		return sourceList.get();
	}

	/**
	 * @return {@link TargetType} representation of {@link #modelListProperty()}.
	 */
	public ReadOnlyListProperty<TargetType> targetListProperty() {
		return viewModelList.getReadOnlyProperty();
	}

	/**
	 * @return {@link TargetType} representation of {@link #modelListProperty()}.
	 */
	public ObservableList<TargetType> getTargetList() {
		return viewModelList.getReadOnlyProperty().get();
	}

}


package de.saxsys.mvvmfx.examples.welcome.model;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Singleton;

/**
 * Service class for providing some dummy data.
 *
 * @author sialcasa
 */
@Singleton
public class Repository {

	List<Person> persons = new ArrayList<Person>();

	/**
	 * Creates the Repo.
	 */
	public Repository() {
		persons.add(new Person("Alexander", "Casall", Gender.MALE));
		persons.add(new Person("Bernd", "Grams", Gender.MALE));
		persons.add(new Person("Anna", "Schulze", Gender.FEMALE));
		persons.add(new Person("Andy", "Mueller", Gender.NOT_SPECIFIED));
	}

	/**
	 * @return available {@link Person}s
	 */
	public List<Person> getPersons() {
		return persons;
	}

	/**
	 * Gets a Person.s
	 *
	 * @param id of the person
	 * @return person
	 */
	public Person getPersonById(final int id) {
		for (Person person : persons) {
			if (id == person.getId()) {
				return person;
			}
		}
		return null;
	}

}


package de.saxsys.mvvmfx.internal.viewloader;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import javafx.fxml.JavaFXBuilderFactory;
import javafx.util.Builder;
import javafx.util.BuilderFactory;

/**
 * A {@link BuilderFactory} that can manage multiple custom builder factories.
 *

 * This class is part of the internal API of mvvmFX and may be subject to changes.
 * Don't use this class directly. Instead add new builder factories by using public API:
 * {@link de.saxsys.mvvmfx.MvvmFX#addGlobalBuilderFactory(BuilderFactory)}.
 */
public class GlobalBuilderFactory implements BuilderFactory {

	private List<BuilderFactory> factories = new ArrayList<>();

	private static final GlobalBuilderFactory SINGLETON = new GlobalBuilderFactory();

	private BuilderFactory defaultBuilderFactory = new JavaFXBuilderFactory();

	private GlobalBuilderFactory() {
	}

	public static GlobalBuilderFactory getInstance() {
		return SINGLETON;
	}

	@Override
	public Builder<?> getBuilder(Class<?> type) {
		// if no factories are defined yet, use the default factory.
		// this is an optimization for performance reasons to prevent unnecessary iterator handling
		// for a very common usage scenario
		if(factories.isEmpty()) {
			return defaultBuilderFactory.getBuilder(type);
		}

		Builder<?> builder = null;

		final ListIterator<BuilderFactory> listIterator = factories.listIterator(factories.size());

		// iterate builder list in reverse order
		while(listIterator.hasPrevious() && builder == null) {
			final BuilderFactory factory = listIterator.previous();

			builder = factory.getBuilder(type);
		}

		// if no custom builderFactory is suitable for this type, use the default one.
		if(builder == null) {
			builder = defaultBuilderFactory.getBuilder(type);
		}

		return builder;
	}

	/**
	 * Create a new BuilderFactory instance that contains all custom factories of this instance
	 * combined with the list of factories passed as argument to this method.
	 *

	 * This instance of the builderFactory is not changed by this method.
	 */
	public BuilderFactory mergeWith(List<BuilderFactory> factories) {
		GlobalBuilderFactory factory = new GlobalBuilderFactory();

		factory.factories.addAll(factories);

		return factory;
	}

	public void addBuilderFactory(BuilderFactory factory) {
		this.factories.add(factory);
	}
}


package de.saxsys.mvvmfx.internal;
/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the "License").  You may not use this file except
 * in compliance with the License.
 *
 * You can obtain a copy of the license at
 * glassfish/bootstrap/legal/CDDLv1.0.txt or
 * https://glassfish.dev.java.net/public/CDDLv1.0.html.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * HEADER in each file and include the License file at
 * glassfish/bootstrap/legal/CDDLv1.0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your
 * own identifying information: Portions Copyright [yyyy]
 * [name of copyright owner]
 */

/*
 * Copyright 2005 The Apache Software Foundation.
 *
 * 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.
 */

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * A WeakValueHashMap is implemented as a HashMap that maps keys to WeakValues. Because we don't have access to the
 * innards of the HashMap, we have to wrap/unwrap value objects with WeakValues on every operation. Fortunately
 * WeakValues are small, short-lived objects, so the added allocation overhead is tolerable. This implementaton directly
 * extends java.util.HashMap.
 *
 * @author Markus Fuchs
 * @see java.util.HashMap
 * @see java.lang.ref.WeakReference
 */

public class WeakValueHashMap extends HashMap {

	/* Reference queue for cleared WeakValues */
	private final ReferenceQueue queue = new ReferenceQueue();

	/**
	 * Returns the number of key-value mappings in this map.
	 *

	 *
	 * @return the number of key-value mappings in this map.
	 */
	@Override
	public int size() {
		// delegate to entrySet, as super.size() also counts WeakValues
		return entrySet().size();
	}

	/**
	 * Returns <tt>true</tt> if this map contains no key-value mappings.
	 *

	 *
	 * @return <tt>true</tt> if this map contains no key-value mappings.
	 */
	@Override
	public boolean isEmpty() {
		return size() == 0;
	}

	/**
	 * Returns <tt>true</tt> if this map contains a mapping for the specified key.
	 *

	 *
	 * @param key
	 *            key whose presence in this map is to be tested
	 * @return <tt>true</tt> if this map contains a mapping for the specified key.
	 */
	@Override
	public boolean containsKey(Object key) {
		// need to clean up gc'ed values before invoking super method
		processQueue();
		return super.containsKey(key);
	}

	/**
	 * Returns <tt>true</tt> if this map maps one or more keys to the specified value.
	 *

	 *
	 * @param value
	 *            value whose presence in this map is to be tested
	 * @return <tt>true</tt> if this map maps one or more keys to this value.
	 */
	@Override
	public boolean containsValue(Object value) {
		return super.containsValue(WeakValue.create(value));
	}

	/**
	 * Gets the value for the given key.
	 *

	 *
	 * @param key
	 *            key whose associated value, if any, is to be returned
	 * @return the value to which this map maps the specified key.
	 */
	@Override
	public Object get(Object key) {
		// We don't need to remove garbage collected values here;
		// if they are garbage collected, the get() method returns null;
		// the next put() call with the same key removes the old value
		// automatically so that it can be completely garbage collected
		return getReferenceObject((WeakReference) super.get(key));
	}

	/**
	 * Puts a new (key,value) into the map.
	 *

	 *
	 * @param key
	 *            key with which the specified value is to be associated.
	 * @param value
	 *            value to be associated with the specified key.
	 * @return previous value associated with specified key, or null if there was no mapping for key or the value has
	 *         been garbage collected by the garbage collector.
	 */
	@Override
	public Object put(Object key, Object value) {
		// If the map already contains an equivalent key, the new key
		// of a (key, value) pair is NOT stored in the map but the new
		// value only. But as the key is strongly referenced by the
		// map, it can not be removed from the garbage collector, even
		// if the key becomes weakly reachable due to the old
		// value. So, it isn't necessary to remove all garbage
		// collected values with their keys from the map before the
		// new entry is made. We only clean up here to distribute
		// clean up calls on different operations.
		processQueue();

		WeakValue oldValue = (WeakValue) super.put(key, WeakValue.create(key, value, queue));
		return getReferenceObject(oldValue);
	}

	/**
	 * Removes key and value for the given key.
	 *

	 *
	 * @param key
	 *            key whose mapping is to be removed from the map.
	 * @return previous value associated with specified key, or null if there was no mapping for key or the value has
	 *         been garbage collected by the garbage collector.
	 */
	@Override
	public Object remove(Object key) {
		return getReferenceObject((WeakReference) super.remove(key));
	}

	/**
	 * A convenience method to return the object held by the weak reference or null if it does not exist.
	 */
	private final Object getReferenceObject(WeakReference ref) {
		return (ref == null) ? null : ref.get();
	}

	/**
	 * Removes all garbage collected values with their keys from the map. Since we don't know how much the
	 * ReferenceQueue.poll() operation costs, we should not call it every map operation.
	 */
	private void processQueue() {
		WeakValue wv = null;

		while ((wv = (WeakValue) this.queue.poll()) != null) {
			// "super" is not really necessary but use it
			// to be on the safe side
			super.remove(wv.key);
		}
	}

	/* -- Helper classes -- */

	/**
	 * We need this special class to keep the backward reference from the value to the key, so that we are able to
	 * remove the key if the value is garbage collected.
	 */
	private static class WeakValue extends WeakReference {
		/**
		 * It's the same as the key in the map. We need the key to remove the value if it is garbage collected.
		 */
		private Object key;

		private WeakValue(Object value) {
			super(value);
		}

		/**
		 * Creates a new weak reference without adding it to a ReferenceQueue.
		 */
		private static WeakValue create(Object value) {
			if (value == null)
				return null;
			else
				return new WeakValue(value);
		}

		private WeakValue(Object key, Object value, ReferenceQueue queue) {
			super(value, queue);
			this.key = key;
		}

		/**
		 * Creates a new weak reference and adds it to the given queue.
		 */
		private static WeakValue create(Object key, Object value,
				ReferenceQueue queue) {
			if (value == null)
				return null;
			else
				return new WeakValue(key, value, queue);
		}

		/**
		 * A WeakValue is equal to another WeakValue iff they both refer to objects that are, in turn, equal according
		 * to their own equals methods.
		 */
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;

			if (!(obj instanceof WeakValue))
				return false;

			Object ref1 = this.get();
			Object ref2 = ((WeakValue) obj).get();

			if (ref1 == ref2)
				return true;

			if ((ref1 == null) || (ref2 == null))
				return false;

			return ref1.equals(ref2);
		}

		/**
		 *
		 */
		@Override
		public int hashCode() {
			Object ref = this.get();

			return (ref == null) ? 0 : ref.hashCode();
		}
	}

	/**
	 * Internal class for entries. This class wraps/unwraps the values of the Entry objects returned from the underlying
	 * map.
	 */
	private class Entry implements Map.Entry {
		private final Map.Entry ent;
		private Object value; /*
								 * Strong reference to value, so that the GC will leave it alone as long as this Entry
								 * exists
								 */

		Entry(Map.Entry ent, Object value) {
			this.ent = ent;
			this.value = value;
		}

		@Override
		public Object getKey() {
			return ent.getKey();
		}

		@Override
		public Object getValue() {
			return value;
		}

		@Override
		public Object setValue(Object value) {
			// This call changes the map. Please see the comment on
			// the put method for the correctness remark.
			Object oldValue = this.value;
			this.value = value;
			ent.setValue(WeakValue.create(getKey(), value, queue));
			return oldValue;
		}

		private boolean valEquals(Object o1, Object o2) {
			return (o1 == null) ? (o2 == null) : o1.equals(o2);
		}

		@Override
		public boolean equals(Object o) {
			if (!(o instanceof Map.Entry))
				return false;
			Map.Entry e = (Map.Entry) o;
			return (valEquals(ent.getKey(), e.getKey())
					&& valEquals(value, e.getValue()));
		}

		@Override
		public int hashCode() {
			Object k;
			return ((((k = ent.getKey()) == null) ? 0 : k.hashCode())
					^ ((value == null) ? 0 : value.hashCode()));
		}

	}

	/**
	 * Internal class for entry sets to unwrap/wrap WeakValues stored in the map.
	 */
	private class EntrySet extends AbstractSet {

		@Override
		public Iterator iterator() {
			// remove garbage collected elements
			processQueue();

			return new Iterator() {
				Iterator hashIterator = hashEntrySet.iterator();
				Entry next = null;

				@Override
				public boolean hasNext() {
					if (hashIterator.hasNext()) {
						// since we removed garbage collected elements,
						// we can simply return the next entry.
						Map.Entry ent = (Map.Entry) hashIterator.next();
						WeakValue wv = (WeakValue) ent.getValue();
						Object v = (wv == null) ? null : wv.get();
						next = new Entry(ent, v);
						return true;
					}
					return false;
				}

				@Override
				public Object next() {
					if ((next == null) && !hasNext())
						throw new NoSuchElementException();
					Entry e = next;
					next = null;
					return e;
				}

				@Override
				public void remove() {
					hashIterator.remove();
				}

			};
		}

		@Override
		public boolean isEmpty() {
			return !(iterator().hasNext());
		}

		@Override
		public int size() {
			int j = 0;
			for (Iterator i = iterator(); i.hasNext(); i.next())
				j++;
			return j;
		}

		@Override
		public boolean remove(Object o) {
			if (!(o instanceof Map.Entry))
				return false;
			Map.Entry e = (Map.Entry) o;
			Object ek = e.getKey();
			Object ev = e.getValue();
			Object hv = WeakValueHashMap.this.get(ek);
			if (hv == null) {
				// if the map's value is null, we have to check, if the
				// entry's value is null and the map contains the key
				if ((ev == null) && WeakValueHashMap.this.containsKey(ek)) {
					WeakValueHashMap.this.remove(ek);
					return true;
				} else {
					return false;
				}
				// otherwise, simply compare the values
			} else if (hv.equals(ev)) {
				WeakValueHashMap.this.remove(ek);
				return true;
			}

			return false;
		}

		@Override
		public int hashCode() {
			int h = 0;
			for (Iterator i = hashEntrySet.iterator(); i.hasNext();) {
				Map.Entry ent = (Map.Entry) i.next();
				Object k;
				WeakValue wv = (WeakValue) ent.getValue();
				if (wv == null)
					continue;
				h += ((((k = ent.getKey()) == null) ? 0 : k.hashCode())
						^ wv.hashCode());
			}
			return h;
		}

	}

	// internal helper variable, because we can't access
	// entrySet from the superclass inside the EntrySet class
	private Set hashEntrySet = null;
	// stores the EntrySet instance
	private Set entrySet = null;

	/**
	 * Returns a Set view of the mappings in this map.
	 *

	 *
	 * @return a Set view of the mappings in this map.
	 */
	@Override
	public Set entrySet() {
		if (entrySet == null) {
			hashEntrySet = super.entrySet();
			entrySet = new EntrySet();
		}
		return entrySet;
	}

	// stores the value collection
	private transient Collection values = null;

	/**
	 * Returns a Collection view of the values contained in this map.
	 *

	 *
	 * @return a Collection view of the values contained in this map.
	 */
	@Override
	public Collection values() {
		// delegates to entrySet, because super method returns
		// WeakValues instead of value objects
		if (values == null) {
			values = new AbstractCollection() {
				@Override
				public Iterator iterator() {
					return new Iterator() {
						private final Iterator i = entrySet().iterator();

						@Override
						public boolean hasNext() {
							return i.hasNext();
						}

						@Override
						public Object next() {
							return ((Entry) i.next()).getValue();
						}

						@Override
						public void remove() {
							i.remove();
						}
					};
				}

				@Override
				public int size() {
					return WeakValueHashMap.this.size();
				}

				@Override
				public boolean contains(Object v) {
					return WeakValueHashMap.this.containsValue(v);
				}
			};
		}
		return values;
	}

}


/*******************************************************************************
 * Copyright 2015 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.utils.validation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * This class is used as {@link ValidationStatus} for {@link CompositeValidator}.
 *
 * In contrast to the basic {@link ValidationStatus} this class not only tracks
 * {@link ValidationMessage} alone but also keeps track of the {@link Validator}s that
 * the messages belong to. This is needed to be able to remove only those messages for
 * a specific validator.
 *
 * @author manuel.mauky
 */
class CompositeValidationStatus extends ValidationStatus {

	/**
	 * The CompositeValidator needs to be able to add and remove {@link ValidationMessage}s for specific validators only.
	 *
	 * Because {@link ValidationMessage} is an immutable value type (overrides equals/hashCode for value equality)
	 * two message instances will be considered to be "equal" when they have the same values even if they are belonging
	 * to different validators. Simply putting the messages into the message list ({@link #getMessagesInternal()})
	 * doesn't work because if a message is removed for one validator, messages with the same values for other validators would be removed too.
	 *

	 * For this reason we need a special logic here.
	 * To get this working we will maintain a Map that keeps track of all messages for each validator.
	 * Instead of using the actual instances of validator as key and messages as values we will use {@link System#identityHashCode(Object)}
	 * for both. This way we can distinguish between different instances of {@link ValidationMessage} even if
	 * they are considered to be "equal" by equals/hashCode methods.
	 * A second benefit of using identityHashCode is that it minimizes the changes of memory leaks because no references to
	 * actual objects are stored. This is especially important for the validator instance.
	 *

	 *
	 * Key: {@link System#identityHashCode(Object)} of the validator
	 * Values: A list of {@link System#identityHashCode(Object)} of the validation messages.
	 */
	private Map<Integer, List<Integer>> validatorToMessagesMap = new HashMap<>();

	/**
	 * This class is package private and only used in the {@link CompositeValidator}.
	 * For this use case adding and removing messages is only done in combination with a validator instance.
	 * For this reason the normal methods to add/remove messages are overridden ad no-op methods.
	 */
	@Override
	void addMessage(ValidationMessage message) {
	}

	@Override
	void addMessage(Collection<ValidationMessage> messages) {
	}

	@Override
	void removeMessage(ValidationMessage message) {
	}

	@Override
	void removeMessage(Collection<? extends ValidationMessage> messages) {
	}

	@Override
	void clearMessages() {
	}

	/**
	 * Add a list of validation messages for the specified validator.
	 */
	void addMessage(Validator validator, List<? extends ValidationMessage> messages) {
		if(messages.isEmpty()) {
			return;
		}

		final int validatorHash = System.identityHashCode(validator);

		if(!validatorToMessagesMap.containsKey(validatorHash)){
			validatorToMessagesMap.put(validatorHash, new ArrayList<>());
		}

		final List<Integer> messageHashesOfThisValidator = validatorToMessagesMap.get(validatorHash);

		// add the hashCodes of the messages to the internal map
		messages.stream()
				.map(System::identityHashCode)
				.forEach(messageHashesOfThisValidator::add);

		// add the actual messages to the message list so that they are accessible by the user.
		getMessagesInternal().addAll(messages);
	}

	/*
	 Remove all given messages for the given validator.
	 */
	void removeMessage(final Validator validator, final List<? extends ValidationMessage> messages) {
		if(messages.isEmpty()) {
			return;
		}

		final int validatorHash = System.identityHashCode(validator);

		// if the validator is unknown by the map we haven't stored any messages for it yet that could be removed
		if(validatorToMessagesMap.containsKey(validatorHash)){
			final List<Integer> messageHashesOfThisValidator = validatorToMessagesMap.get(validatorHash);

			final List<Integer> hashesOfMessagesToRemove = messages.stream()
					.filter(m -> { // only those messages that are stored for this validator
						int hash = System.identityHashCode(m);
						return messageHashesOfThisValidator.contains(hash);
					})
					.map(System::identityHashCode) // we only need the hashCode here
					.collect(Collectors.toList());

			// only remove those messages that we have the hashCode stored
			getMessagesInternal().removeIf(message -> {
				int hash = System.identityHashCode(message);
				return hashesOfMessagesToRemove.contains(hash);
			});

			// we need to cleanup our internal map
			messageHashesOfThisValidator.removeAll(hashesOfMessagesToRemove);

			if(messageHashesOfThisValidator.isEmpty()) {
				validatorToMessagesMap.remove(validatorHash);
			}
		}
	}

	/*
	 * Remove all messages for this particular validator.
	 */
	void removeMessage(final Validator validator) {
		final int validatorHash = System.identityHashCode(validator);

		if(validatorToMessagesMap.containsKey(validatorHash)){

			final List<Integer> messageHashesOfThisValidator = validatorToMessagesMap.get(validatorHash);

			getMessagesInternal().removeIf(message -> {
				int hash = System.identityHashCode(message);

				return messageHashesOfThisValidator.contains(hash);
			});

			validatorToMessagesMap.remove(validatorHash);
		}
	}

}


package de.saxsys.mvvmfx.examples.welcome.viewmodel.personwelcome;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

import javax.inject.Inject;

import de.saxsys.mvvmfx.examples.welcome.model.Gender;
import de.saxsys.mvvmfx.examples.welcome.model.Person;
import de.saxsys.mvvmfx.examples.welcome.model.Repository;
import de.saxsys.mvvmfx.ViewModel;

/**
 * ViewModel for a welcome view for a person. It provides the data which should
 * be visualized in the frontend. The tests for it can be written first. Have a
 * look on PersonWelcomeViewModelTest.
 *
 * @author alexander.casall
 *
 */
public class PersonWelcomeViewModel implements ViewModel {

	private final Repository repository;

	// Properties which are used by the view.
	private final StringProperty welcomeString = new SimpleStringProperty();

	private Person person;

	/**
	 * Create a {@link PersonWelcomeViewModel}.
	 *
	 * @param repository repo which is used
	 */
	@Inject
	public PersonWelcomeViewModel(Repository repository) {
		this.repository = repository;
	}

	/**
	 * Provides the the concated string.
	 *
	 * @return
	 */
	public StringProperty welcomeStringProperty() {
		return welcomeString;
	}

	/**
	 * Set Person id for the screen
	 *
	 * @param personId for the screen
	 */
	public void setPersonId(int personId) {
		person = repository.getPersonById(personId);

		StringBinding salutationBinding
				= Bindings.when(person.genderProperty().isEqualTo(Gender.NOT_SPECIFIED))
				.then("Herr/Frau/* ")
				.otherwise(
						Bindings.when(person.genderProperty().isEqualTo(Gender.MALE))
						.then("Herr ").otherwise("Frau "));

		welcomeString.unbind();
		welcomeString.bind(Bindings.concat("Willkommen ", salutationBinding,
				person.firstNameProperty(), " ",
				person.lastNameProperty()));
	}

	/**
	 * Returns the id of the displayed person.
	 *
	 * @return id
	 */
	public int getPersonId() {
		return person.getId();
	}

}


/*******************************************************************************
 * 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.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.List;
import java.util.ResourceBundle;

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

import de.saxsys.mvvmfx.Context;
import de.saxsys.mvvmfx.Scope;
import de.saxsys.mvvmfx.ViewModel;
import de.saxsys.mvvmfx.ViewTuple;
import de.saxsys.mvvmfx.internal.ContextImpl;
import javafx.fxml.Initializable;
import javafx.scene.Parent;

/**
 * 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 existingViewModel
     *            the viewModel instance that is used to load the view.
     * @param <ViewType>
     *            the generic type of the view.
     * @param <ViewModelType>
     *            the generic type of the viewModel.
     *
     * @return a fully loaded and initialized instance of the view.
     */
    public <ViewType extends View<? extends ViewModelType>, ViewModelType extends ViewModel> ViewTuple<ViewType, ViewModelType> loadJavaViewTuple(
            Class<? extends ViewType> viewType, ResourceBundle resourceBundle, final ViewModelType existingViewModel,
            ViewType codeBehind, Context parentContext, Collection<Scope> providedScopes) {

        // FIXME Woanders hin?!
        ContextImpl context = ViewLoaderScopeUtils.prepareContext(parentContext, providedScopes);
        ////////////////////////////

        DependencyInjector injectionFacade = DependencyInjector.getInstance();

        final ViewType view = codeBehind == null ? injectionFacade.getInstanceOf(viewType) : codeBehind;

        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");
        }

        ViewModelType viewModel = null;

        // when no viewmodel was provided by the user...
        if (existingViewModel == null) {
            // ... we create a new one (if possible)
            viewModel = ViewLoaderReflectionUtils.createViewModel(view);
        } else {
            viewModel = existingViewModel;
        }

        ResourceBundleInjector.injectResourceBundle(view, resourceBundle);

        // if no ViewModel is available...
        if (viewModel == null) {
            // we need to check if the user is trying to inject a viewModel.

            final List<Field> viewModelFields = ViewLoaderReflectionUtils.getViewModelFields(viewType);

            if (!viewModelFields.isEmpty()) {
                throw new RuntimeException("The given view of type <" + view.getClass()
                        + "> has no generic viewModel type declared but tries to inject a viewModel.");
            }

        } else {
            ResourceBundleInjector.injectResourceBundle(viewModel, resourceBundle);

            // if the user has provided an existing ViewModel, we will not
            // (re-)initialize this existing instance
            ViewLoaderReflectionUtils.createAndInjectScopes(viewModel, context);
            if (existingViewModel == null) {
                ViewLoaderReflectionUtils.initializeViewModel(viewModel);
            }

            ViewLoaderReflectionUtils.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 with the simple name "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 <ViewModelType>
     *            the generic type of the view.
     */
    <ViewModelType extends ViewModel> void callInitialize(View<? extends ViewModelType> view) {
        try {
            final Method initializeMethod = view.getClass().getMethod(NAMING_CONVENTION_INITIALIZE_IDENTIFIER);

            AccessController.doPrivileged((PrivilegedAction) () -> {
                try {
                    return initializeMethod.invoke(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);
                }
                return null;
            });

        } 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);
        }
    }

    /**
     * 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 <ViewModelType>
     *            the generic type of the view.
     */
    <ViewModelType extends ViewModel> void injectResourceBundle(View<? extends ViewModelType> 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);
        }

    }

}


package de.saxsys.mvvmfx.examples.contacts.ui.detail;

import static eu.lestard.advanced_bindings.api.ObjectBindings.map;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;

import javax.inject.Inject;

import de.saxsys.mvvmfx.InjectScope;
import de.saxsys.mvvmfx.ScopeProvider;
import de.saxsys.mvvmfx.ViewModel;
import de.saxsys.mvvmfx.examples.contacts.model.Address;
import de.saxsys.mvvmfx.examples.contacts.model.Contact;
import de.saxsys.mvvmfx.examples.contacts.model.Repository;
import de.saxsys.mvvmfx.examples.contacts.ui.scopes.ContactDialogScope;
import de.saxsys.mvvmfx.examples.contacts.ui.scopes.MasterDetailScope;
import de.saxsys.mvvmfx.utils.commands.Action;
import de.saxsys.mvvmfx.utils.commands.Command;
import de.saxsys.mvvmfx.utils.commands.DelegateCommand;
import javafx.application.HostServices;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;

@ScopeProvider(scopes = ContactDialogScope.class)
public class DetailViewModel implements ViewModel {

	public static final String OPEN_EDIT_CONTACT_DIALOG = "open_edit_contact";

	private static final DateTimeFormatter BIRTHDAY_FORMATTER = DateTimeFormatter.ISO_DATE;

	private final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();

	private final ReadOnlyStringWrapper birthday = new ReadOnlyStringWrapper();
	private final ReadOnlyStringWrapper roleDepartment = new ReadOnlyStringWrapper();
	private final ReadOnlyStringWrapper email = new ReadOnlyStringWrapper();
	private final ReadOnlyStringWrapper phone = new ReadOnlyStringWrapper();
	private final ReadOnlyStringWrapper mobile = new ReadOnlyStringWrapper();

	private final ReadOnlyStringWrapper cityPostalcode = new ReadOnlyStringWrapper();
	private final ReadOnlyStringWrapper street = new ReadOnlyStringWrapper();
	private final ReadOnlyStringWrapper countrySubdivision = new ReadOnlyStringWrapper();

	private DelegateCommand editCommand;
	private DelegateCommand removeCommand;
	private DelegateCommand emailLinkCommand;

	@Inject
	HostServices hostServices;

	@Inject
	Repository repository;

	@InjectScope
	MasterDetailScope mdScope;

	@InjectScope
	ContactDialogScope dialogscope;

	public void initialize() {
		ReadOnlyObjectProperty<Contact> contactProperty = getSelectedContactPropertyFromScope();

		createBindingsForLabels(contactProperty);

		editCommand = new DelegateCommand(() -> new Action() {
			@Override
			protected void action() throws Exception {
				Contact selectedContact = getSelectedContactFromScope();
				if (selectedContact != null) {
					dialogscope.setContactToEdit(selectedContact);
					publish(OPEN_EDIT_CONTACT_DIALOG);
				}
			}
		}, getSelectedContactPropertyFromScope().isNotNull());

		removeCommand = new DelegateCommand(() -> new Action() {
			@Override
			protected void action() throws Exception {
				Contact selectedContact = getSelectedContactFromScope();
				if (selectedContact != null) {
					repository.delete(getSelectedContactFromScope());
				}
			}

		}, getSelectedContactPropertyFromScope().isNotNull());

		emailLinkCommand = new DelegateCommand(() -> new Action() {
			@Override
			protected void action() throws Exception {
				if (email.get() != null && !email.get().trim().isEmpty()) {
					hostServices.showDocument("mailto:" + email.get());
				}
			}
		});
	}

	private void createBindingsForLabels(ReadOnlyObjectProperty<Contact> contactProperty) {
		name.bind(emptyStringOnNull(map(contactProperty, contact -> {
			StringJoiner joiner = new StringJoiner(" ");

			Optional.ofNullable(contact.getTitle())
					.filter(Objects::nonNull)
					.filter(s -> !s.trim().isEmpty())
					.ifPresent(joiner::add);

			joiner.add(contact.getFirstname());
			joiner.add(contact.getLastname());

			return joiner.toString();
		})));

		email.bind(emptyStringOnNull(map(contactProperty, Contact::getEmailAddress)));

		roleDepartment.bind(emptyStringOnNull(map(contactProperty, contact -> {
			StringBuilder result = new StringBuilder();
			if (contact.getRole() != null && !contact.getRole().trim().isEmpty()) {
				result.append(contact.getRole());

				if (contact.getDepartment() != null && !contact.getDepartment().trim().isEmpty()) {
					result.append(" / ");
					result.append(contact.getDepartment());
				}
			} else if (contact.getDepartment() != null) {
				result.append(contact.getDepartment());
			}

			return result.toString();
		})));

		birthday.bind(emptyStringOnNull(map(contactProperty, contact -> {
			LocalDate date = contact.getBirthday();
			if (date == null) {
				return "";
			} else {
				return BIRTHDAY_FORMATTER.format(date);
			}
		})));

		phone.bind(emptyStringOnNull(map(contactProperty, Contact::getPhoneNumber)));

		mobile.bind(emptyStringOnNull(map(contactProperty, Contact::getMobileNumber)));

		ObjectBinding<Address> addressBinding = map(contactProperty, Contact::getAddress);

		cityPostalcode.bind(emptyStringOnNull(map(addressBinding, address -> {
			StringBuilder result = new StringBuilder();
			if (address.getCity() != null) {
				result.append(address.getCity());
			}

			if (address.getPostalcode() != null) {
				result.append(" (");
				result.append(address.getPostalcode());
				result.append(")");
			}
			return result.toString();
		})));

		street.bind(emptyStringOnNull(map(addressBinding, Address::getStreet)));

		countrySubdivision.bind(emptyStringOnNull(map(addressBinding, address -> {
			StringBuilder result = new StringBuilder();
			if (address.getCountry() != null) {
				result.append(address.getCountry().getName());
			}

			if (address.getSubdivision() != null) {
				result.append(" / ");
				result.append(address.getSubdivision().getName());
			}
			return result.toString();
		})));
	}

	/**
	 * When the given source binding has a value of null an empty
	 * string is used for the returned binding. Otherwise the value of the
	 * source binding is used.
	 */
	private StringBinding emptyStringOnNull(ObservableValue<String> source) {
		return Bindings.createStringBinding(() -> {
			if (source.getValue() == null) {
				return "";
			} else {
				return source.getValue();
			}
		}, source);
	}

	public Command getEditCommand() {
		return editCommand;
	}

	public Command getRemoveCommand() {
		return removeCommand;
	}

	public Command getEmailLinkCommand() {
		return emailLinkCommand;
	}

	public ReadOnlyStringProperty nameLabelTextProperty() {
		return name.getReadOnlyProperty();
	}

	public ReadOnlyStringProperty birthdayLabelTextProperty() {
		return birthday.getReadOnlyProperty();
	}

	public ReadOnlyStringProperty roleDepartmentLabelTextProperty() {
		return roleDepartment.getReadOnlyProperty();
	}

	public ReadOnlyStringProperty emailLabelTextProperty() {
		return email.getReadOnlyProperty();
	}

	public ReadOnlyStringProperty phoneLabelTextProperty() {
		return phone.getReadOnlyProperty();
	}

	public ReadOnlyStringProperty mobileLabelTextProperty() {
		return mobile.getReadOnlyProperty();
	}

	public ReadOnlyStringProperty cityPostalcodeLabelTextProperty() {
		return cityPostalcode.getReadOnlyProperty();
	}

	public ReadOnlyStringProperty streetLabelTextProperty() {
		return street.getReadOnlyProperty();
	}

	public ReadOnlyStringProperty countrySubdivisionLabelTextProperty() {
		return countrySubdivision.getReadOnlyProperty();
	}

	private Contact getSelectedContactFromScope() {
		return getSelectedContactPropertyFromScope().get();
	}

	private ObjectProperty<Contact> getSelectedContactPropertyFromScope() {
		return mdScope.selectedContactProperty();
	}

}


/*******************************************************************************
 * Copyright 2015 Alexander Casall, Manuel Mauky, Sven Lechner
 *
 * 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 de.saxsys.mvvmfx.Context;
import de.saxsys.mvvmfx.Initialize;
import de.saxsys.mvvmfx.InjectContext;
import de.saxsys.mvvmfx.InjectScope;
import de.saxsys.mvvmfx.InjectViewModel;
import de.saxsys.mvvmfx.Scope;
import de.saxsys.mvvmfx.ScopeProvider;
import de.saxsys.mvvmfx.SceneLifecycle;
import de.saxsys.mvvmfx.ViewModel;
import de.saxsys.mvvmfx.internal.ContextImpl;
import javafx.beans.value.ObservableBooleanValue;
import net.jodah.typetools.TypeResolver;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * This class encapsulates reflection related utility operations specific for
 * loading of views.
 *
 * @author manuel.mauky
 */
public class ViewLoaderReflectionUtils {

    /**
     * Returns the {@link java.lang.reflect.Field} of the viewModel for a given
     * view type and viewModel type. If there is no annotated field for the
     * viewModel in the view the returned Optional will be empty.
     *
     * @param viewType
     *            the type of the view
     * @param viewModelType
     *            the type of the viewModel
     * @return an Optional that contains the Field when the field exists.
     */
    public static Optional<Field> getViewModelField(Class<? extends View> viewType, Class<?> viewModelType) {
        List<Field> allViewModelFields = getViewModelFields(viewType);

        if (allViewModelFields.isEmpty()) {
            return Optional.empty();
        }

        if (allViewModelFields.size() > 1) {
            throw new RuntimeException("The View <" + viewType + "> may only define one viewModel but there were <"
                    + allViewModelFields.size() + "> viewModel fields with the @InjectViewModel annotation!");
        }

        Field field = allViewModelFields.get(0);

        if (!ViewModel.class.isAssignableFrom(field.getType())) {
            throw new RuntimeException("The View <" + viewType
                    + "> has a field annotated with @InjectViewModel but the type of the field doesn't implement the 'ViewModel' interface!");
        }

        if (!field.getType().isAssignableFrom(viewModelType)) {
            throw new RuntimeException("The View <" + viewType
                    + "> has a field annotated with @InjectViewModel but the type of the field doesn't match the generic ViewModel type of the View class. "
                    + "The declared generic type is <" + viewModelType + "> but the actual type of the field is <"
                    + field.getType() + ">.");
        }

        return Optional.of(field);
    }

    public static List<Field> getScopeFields(Class<?> viewModelType) {
        final List<Field> allScopeFields = getFieldsWithAnnotation(viewModelType, InjectScope.class);

        allScopeFields.stream().forEach(field -> {
            if (!Scope.class.isAssignableFrom(field.getType())) {
                throw new RuntimeException("The ViewModel <" + viewModelType
                        + "> has a field annotated with @InjectScope but the type of the field doesn't implement the 'Scope' interface!");
            }
        });

        return allScopeFields;
    }

    private static Optional<Field> getContextField(Class<? extends View> viewType) {
        List<Field> allViewModelFields = getContextFields(viewType);

        if (allViewModelFields.isEmpty()) {
            return Optional.empty();
        }

        if (allViewModelFields.size() > 1) {
            throw new RuntimeException("The View <" + viewType + "> may only define one Context but there were <"
                    + allViewModelFields.size() + "> Context fields with the @InjectContext annotation!");
        }

        Field field = allViewModelFields.get(0);

        if (!field.getType().isAssignableFrom(Context.class)) {
            throw new RuntimeException("The View <" + viewType
                    + "> has a field annotated with @InjectContext but the type of the field doesn't match the type Context. "
                    + "The actual type of the field is <" + field.getType() + ">.");
        }

        return Optional.of(field);
    }

    private static List<Field> getContextFields(Class<? extends View> viewType) {
        return getFieldsWithAnnotation(viewType, InjectContext.class);
    }

    /**
     * Returns a list of all {@link Field}s of ViewModels for a given view type
     * that are annotated with {@link InjectViewModel}.
     *
     * @param viewType
     *            the type of the view.
     * @return a list of fields.
     */
    public static List<Field> getViewModelFields(Class<? extends View> viewType) {
        return getFieldsWithAnnotation(viewType, InjectViewModel.class);
    }

    private static <T, A extends Annotation> List<Field> getFieldsWithAnnotation(Class<T> classType,
            Class annotationType) {
        return ReflectionUtils.getFieldsFromClassHierarchy(classType)
                .stream()
                .filter(field -> field.isAnnotationPresent(annotationType))
                .collect(Collectors.toList());
    }

    /**
     * This method is used to get the ViewModel instance of a given
     * view/codeBehind.
     *
     * @param view
     *            the view instance where the viewModel will be looked for.
     * @param <ViewType>
     *            the generic type of the View
     * @param <ViewModelType>
     *            the generic type of the ViewModel
     * @return the ViewModel instance or null if no viewModel could be found.
     */
    @SuppressWarnings("unchecked")
    public static <ViewType extends View<? extends ViewModelType>, ViewModelType extends ViewModel> ViewModelType getExistingViewModel(
            ViewType view) {
        final Class<?> viewModelType = TypeResolver.resolveRawArgument(View.class, view.getClass());
        Optional<Field> fieldOptional = getViewModelField(view.getClass(), viewModelType);
        if (fieldOptional.isPresent()) {
            Field field = fieldOptional.get();
            return ReflectionUtils.accessMember(field, () -> (ViewModelType) field.get(view),
                    "Can't get the viewModel of type <" + viewModelType + ">");
        } else {
            return null;
        }
    }

    /**
     * Injects the given viewModel instance into the given view. The injection
     * will only happen when the class of the given view has a viewModel field
     * that fulfills all requirements for the viewModel injection (matching
     * types, no viewModel already existing ...).
     *
     * @param view
     * @param viewModel
     */
    public static void injectViewModel(final View view, ViewModel viewModel) {
        if (viewModel == null) {
            return;
        }
        final Optional<Field> fieldOptional = getViewModelField(view.getClass(), viewModel.getClass());
        if (fieldOptional.isPresent()) {
            Field field = fieldOptional.get();
            ReflectionUtils.accessMember(field, () -> {
                Object existingViewModel = field.get(view);
                if (existingViewModel == null) {
                    field.set(view, viewModel);
                }
            }, "Can't inject ViewModel of type <" + viewModel.getClass() + "> into the view <" + view + ">");
        }
    }

    /**
     * This method is used to create and inject the ViewModel for a given View
     * instance.
     *
     * The following checks are done:
     *

     *
Check whether the View class specifies a ViewModel type as generic
     * type.

     *
Check whether the View has a field with a matching ViewModel type and
     * the annotation {@link InjectViewModel}.

     *
     *
Check whether field in the view instance already contains a ViewModel
     * instance. In this case nothing will happen to the existing ViewModel
     * instance.

     *
     *

     *
     * If a suitable field was found a new ViewModel instance will be created
     * and injected into the field. After that the given Consumer function will
     * be applied with the injected ViewModel instance as argument.
     *
     * @param view
     *            the view instance.
     * @param <V>
     *            the generic type of the View.
     * @param <VM>
     *            the generic type of the ViewModel.
     * @param newVmConsumer
     *            a Consumer function that is applied when a new ViewModel
     *            instance is created.
     *
     * @throws RuntimeException
     *             if there is a ViewModel field in the View with the
     *             {@link InjectViewModel} annotation whose type doesn't match
     *             the generic ViewModel type from the View class.
     */
    @SuppressWarnings("unchecked")
    public static <V extends View<? extends VM>, VM extends ViewModel> void createAndInjectViewModel(final V view,
            Consumer<ViewModel> newVmConsumer) {
        final Class<?> viewModelType = TypeResolver.resolveRawArgument(View.class, view.getClass());

        if (viewModelType == ViewModel.class) {
            // if no viewModel can be created, we have to check if the user has
            // tried to inject a ViewModel
            final List<Field> viewModelFields = ViewLoaderReflectionUtils.getViewModelFields(view.getClass());

            if (!viewModelFields.isEmpty()) {
                throw new RuntimeException("The given view of type <" + view.getClass()
                        + "> has no generic viewModel type declared but tries to inject a viewModel.");
            }
            return;
        }
        if (viewModelType == TypeResolver.Unknown.class) {
            return;
        }

        final Optional<Field> fieldOptional = getViewModelField(view.getClass(), viewModelType);
        if (fieldOptional.isPresent()) {
            Field field = fieldOptional.get();

            ReflectionUtils.accessMember(field, () -> {
                Object existingViewModel = field.get(view);

                if (existingViewModel == null) {
                    final Object newViewModel = DependencyInjector.getInstance().getInstanceOf(viewModelType);

                    field.set(view, newViewModel);

                    newVmConsumer.accept((ViewModel) newViewModel);
                }
            }, "Can't inject ViewModel of type <" + viewModelType + "> into the view <" + view + ">");

        }
    }

    static void createAndInjectScopes(Object viewModel, ContextImpl context) {

        // FIXME CLEANUP!!!
        Class<? extends Object> viewModelClass = viewModel.getClass();

        for (Annotation annotation : viewModelClass.getDeclaredAnnotations()) {
            if (annotation.annotationType().isAssignableFrom(ScopeProvider.class)) {
                ScopeProvider provider = (ScopeProvider) annotation;
                Class<? extends Scope>[] scopes = getScopesFromProvider(provider, viewModelClass);
                for (int i = 0; i < scopes.length; i++) {
                    Class<? extends Scope> scopeType = scopes[i];
                    // Overrides existing scopes!!!!
                    context.addScopeToContext(DependencyInjector.getInstance().getInstanceOf(scopeType));
                }
            }
        }

        // Inject
        List<Field> scopeFields = getScopeFields(viewModel.getClass());

        scopeFields.forEach(scopeField -> {
            ReflectionUtils.accessMember(scopeField, () -> injectScopeIntoField(scopeField, viewModel, context),
                    "Can't inject Scope into ViewModel <" + viewModel.getClass() + ">");
        });
    }

    private static Class<? extends Scope>[] getScopesFromProvider(final ScopeProvider scopeProvider, final Class<?> aViewModelClass) {
        Class<? extends Scope>[] scopes = scopeProvider.value();

        if (scopes.length == 0) {
            scopes = scopeProvider.scopes();
        }

        if (scopes.length == 0) {
            final String message = String.format("The scope provider '%s' has to provide at least one scope.", aViewModelClass.getCanonicalName());
            throw new IllegalArgumentException(message);
        }

        return scopes;
    }

    public static void injectContext(View codeBehind, ContextImpl context) {

        Optional<Field> contextField = getContextField(codeBehind.getClass());

        if (contextField.isPresent()) {
            Field field = contextField.get();
            ReflectionUtils.accessMember(field, () -> {
                field.set(codeBehind, context);
            }, "Can't inject Context into the view <" + codeBehind + ">");
        }
    }

    static Object injectScopeIntoField(Field scopeField, Object viewModel, ContextImpl context)
            throws IllegalAccessException {
        Class<? extends Scope> scopeType = (Class<? extends Scope>) scopeField.getType();

        // FIXME

        final InjectScope[] annotations = scopeField.getAnnotationsByType(InjectScope.class);

        if (annotations.length != 1) {
            throw new RuntimeException("A field to inject a Scope into should have exactly one @InjectScope annotation "
                    + "but the viewModel <" + viewModel + "> has a field that violates this rule.");
        }

        Object newScope = context.getScope(scopeType);

        if (newScope == null) {
            // TODO Modify Stacktrace to get the Injectionpoint of the Scope
            throw new IllegalStateException(
                    "A scope was requested but no @ScopeProvider found in the hierarchy. Declare it like this: @ScopeProvider("
                            + scopeType.getName() + ".class )");
        }

        if (!newScope.getClass().equals(scopeType)) {
            throw new IllegalStateException("something went wrong...");
        }

        scopeField.set(viewModel, newScope);

        return newScope;
    }

    /**
     * Creates a viewModel instance for a View type. The type of the view is
     * determined by the given view instance.
     *
     * For the creation of the viewModel the {@link DependencyInjector} is used.
     *
     * @param view
     *            the view instance that is used to find out the type of the
     *            ViewModel
     * @param <ViewType>
     *            the generic view type
     * @param <ViewModelType>
     *            the generic viewModel type
     * @return the viewModel instance or null if the viewModel type
     *         can't be found or the viewModel can't be created.
     */
    @SuppressWarnings("unchecked")
    public static <ViewType extends View<? extends ViewModelType>, ViewModelType extends ViewModel> ViewModelType createViewModel(
            ViewType view) {
        final Class<?> viewModelType = TypeResolver.resolveRawArgument(View.class, view.getClass());
        if (viewModelType == ViewModel.class) {
            return null;
        }
        if (TypeResolver.Unknown.class == viewModelType) {
            return null;
        }
        return (ViewModelType) DependencyInjector.getInstance().getInstanceOf(viewModelType);
    }

    /**
     * If a ViewModel has a method annotated with {@link Initialize}
     * or method with the signature public void initialize()
     * it will be invoked. If no such method is available nothing happens.
     *
     * @param viewModel
     *            the viewModel that's initialize method (if available) will be
     *            invoked.
     * @param <ViewModelType>
     *            the generic type of the ViewModel.
     */
    public static <ViewModelType extends ViewModel> void initializeViewModel(ViewModelType viewModel) {
        if (viewModel == null) {
            return;
        }

        final Collection<Method> initializeMethods = getInitializeMethods(viewModel.getClass());

        initializeMethods.forEach(initMethod -> {
			// if there is a @PostConstruct annotation, throw an exception to prevent double injection
            final boolean postConstructPresent = Arrays.stream(initMethod.getAnnotations())
                    .map(Annotation::annotationType)
                    .map(Class::getName)
                    .anyMatch("javax.annotation.PostConstruct"::equals);

            if(postConstructPresent) {
				throw new IllegalStateException(String.format("initialize method of ViewModel [%s] is annotated with @PostConstruct. " +
						"This will lead to unexpected behaviour and duplicate initialization. " +
						"Please rename the method or remove the @PostConstruct annotation. " +
						"See mvvmFX wiki for more details: " +
						"https://github.com/sialcasa/mvvmFX/wiki/Dependency-Injection#lifecycle-postconstruct", viewModel));
			}

			ReflectionUtils.accessMember(initMethod, () -> initMethod.invoke(viewModel), "mvvmFX wasn't able to call the initialize method of ViewModel [" + viewModel + "].");
		});
    }

	/**
	 * Returns a collection of {@link Method}s that represent initializer methods.
	 * A method is an "initializer method" if it either:

	 *

	 *
has a signature of "public void initialize()"

	 *
is annotated with {@link Initialize}

	 *

	 */
	private static Collection<Method> getInitializeMethods(Class<?> classType) {
        final List<Method> initializeMethods = new ArrayList<>();

        Arrays.stream(classType.getMethods())
                .filter(method -> "initialize".equals(method.getName()))
                .filter(method -> void.class.equals(method.getReturnType()))
                .filter(method -> method.getParameterCount() == 0)
                .forEach(initializeMethods::add);

		Arrays.stream(classType.getDeclaredMethods())
				.filter(method -> method.isAnnotationPresent(Initialize.class))
				.forEach(initializeMethods::add);

		return initializeMethods;
	}

    /**
     * This method adds listeners for the {@link SceneLifecycle}.
     */
    static void addSceneLifecycleHooks(ViewModel viewModel, ObservableBooleanValue viewInSceneProperty) {
        if(viewModel != null) {

            if(viewModel instanceof SceneLifecycle) {
                SceneLifecycle lifecycleViewModel = (SceneLifecycle) viewModel;

                PreventGarbageCollectionStore.getInstance().put(viewInSceneProperty);

                viewInSceneProperty.addListener((observable, oldValue, newValue) -> {
					if(newValue) {
						lifecycleViewModel.onViewAdded();
					} else {
						lifecycleViewModel.onViewRemoved();
                        PreventGarbageCollectionStore.getInstance().remove(viewInSceneProperty);
					}
				});
            }
        }
    }

    /**
     * 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.");
        }

    }
}


package de.saxsys.mvvmfx.examples.contacts.model.countries;

import de.saxsys.mvvmfx.examples.contacts.model.Country;
import de.saxsys.mvvmfx.examples.contacts.model.Subdivision;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Worker;
import org.datafx.provider.ListDataProvider;
import org.datafx.reader.DataReader;
import org.datafx.reader.InputStreamDataReader;
import org.datafx.reader.converter.InputStreamConverter;
import org.datafx.reader.converter.XmlConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.enterprise.inject.Alternative;
import javax.inject.Singleton;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * This class is used to encapsulate the process of loading available countries
 * and their subdivisions (if available).
 *
 * This class is meant to be a stateful wrapper around the existing countries.
 * You should create an instance of this class, call the {@link #init()} method
 * and then bind the UI to the provided observable lists (
 * {@link #availableCountries()} and {@link #subdivisions()}).
 *
 * To choose a country use the {@link #setCountry(Country)} method. This
 * will lead to a change of the {@link #subdivisions()} list.
 *
 *
 * At the moment this class uses two XML files ({@link #ISO_3166_LOCATION} and
 * {@link #ISO_3166_2_LOCATION}) that contain information about countries,
 * country-codes and subdivisions according to ISO 3166 and ISO 3166-2.
 *
 * The loading process is implemented with the DataFX framework.
 */
@Singleton
@Alternative
public class DataFxCountrySelector implements CountrySelector {

	private static final Logger LOG = LoggerFactory.getLogger(DataFxCountrySelector.class);

	public static final String ISO_3166_LOCATION = "/countries/iso_3166.xml";
	public static final String ISO_3166_2_LOCATION = "/countries/iso_3166_2.xml";
	private ObservableList<Country> countries = FXCollections.observableArrayList();
	private ObservableList<Subdivision> subdivisions = FXCollections.observableArrayList();

	private ReadOnlyStringWrapper subdivisionLabel = new ReadOnlyStringWrapper();

	private ReadOnlyBooleanWrapper inProgress = new ReadOnlyBooleanWrapper(false);

	private Map<Country, List<Subdivision>> countryCodeSubdivisionMap = new HashMap<>();
	private Map<Country, String> countryCodeSubdivisionNameMap = new HashMap<>();

	/**
	 * This method triggers the loading of the available countries and
	 * subdivisions.
	 */
	@PostConstruct
	@Override
	public void init() {
		inProgress.set(true);
		loadCountries();
	}

	/**
	 * Set the currently selected country. This will lead to an update of the
	 * {@link #subdivisions()} observable list and the
	 * {@link #subdivisionLabel()}.
	 *
	 * @param country the country that will be selected or null if
	 * no country is selected.
	 */
	@Override
	public void setCountry(Country country) {
		if (country == null) {
			subdivisionLabel.set(null);
			subdivisions.clear();
			return;
		}

		subdivisionLabel.set(countryCodeSubdivisionNameMap.get(country));

		subdivisions.clear();
		if (countryCodeSubdivisionMap.containsKey(country)) {
			subdivisions.addAll(countryCodeSubdivisionMap.get(country));
		}
	}

	/**
	 * Load all countries from the XML file source with DataFX.
	 */
	void loadCountries() {
		InputStream iso3166Resource = this.getClass().getResourceAsStream(ISO_3166_LOCATION);
		if (iso3166Resource == null) {
			throw new IllegalStateException("Can't find the list of countries! Expected location was:"
					+ ISO_3166_LOCATION);
		}

		XmlConverter<Country> countryConverter = new XmlConverter<>("iso_3166_entry", Country.class);

		try {
			DataReader<Country> dataSource = new InputStreamSource<>( iso3166Resource, countryConverter );

			ListDataProvider<Country> listDataProvider = new ListDataProvider<>(dataSource);

			listDataProvider.setResultObservableList(countries);

			Worker<ObservableList<Country>> worker = listDataProvider.retrieve();
			// when the countries are loaded we start the loading of the subdivisions.
			worker.stateProperty().addListener(obs -> {
				if (worker.getState() == Worker.State.SUCCEEDED) {
					loadSubdivisions();
				}
			});
		} catch (IOException e) {
			LOG.error("A problem was detected while loading the XML file with the available countries.", e);
		}
	}

	static class InputStreamSource<T> extends InputStreamDataReader<T> {
		public InputStreamSource(InputStream is, InputStreamConverter converter) throws IOException {
			super(converter);
			setInputStream(is);
		}
	}

	/**
	 * Load all subdivisions from the XML file source with DataFX.
	 */
	void loadSubdivisions() {

		InputStream iso3166_2Resource = this.getClass().getResourceAsStream(ISO_3166_2_LOCATION);

		if (iso3166_2Resource == null) {
			throw new IllegalStateException("Can't find the list of subdivisions! Expected location was:"
					+ ISO_3166_2_LOCATION);
		}

		XmlConverter<ISO3166_2_CountryEntity> converter = new XmlConverter<>("iso_3166_country",
				ISO3166_2_CountryEntity.class);

		ObservableList<ISO3166_2_CountryEntity> subdivisionsEntities = FXCollections.observableArrayList();

		try {

			DataReader<ISO3166_2_CountryEntity> dataSource =
					new InputStreamSource<>( iso3166_2Resource, converter );

			ListDataProvider<ISO3166_2_CountryEntity> listDataProvider = new ListDataProvider<>(dataSource);

			listDataProvider.setResultObservableList(subdivisionsEntities);

			Worker<ObservableList<ISO3166_2_CountryEntity>> worker = listDataProvider.retrieve();
			worker.stateProperty().addListener(obs -> {
				if (worker.getState() == Worker.State.SUCCEEDED) {

					subdivisionsEntities.forEach(entity -> {
						if (entity.subsets != null && !entity.subsets.isEmpty()) {

							Country country = findCountryByCode(entity.code);

							if (!countryCodeSubdivisionMap.containsKey(country)) {
								countryCodeSubdivisionMap.put(country, new ArrayList<>());
							}

							List<Subdivision> subdivisionList = countryCodeSubdivisionMap.get(country);

							entity.subsets.forEach(subset -> {
								subset.entryList.forEach(entry -> {
									subdivisionList.add(new Subdivision(entry.name, entry.code, country));
								});
							});

							String subdivisionName = entity.subsets.stream()
									.map(subset -> subset.subdivisionType)
									.collect(Collectors.joining("/"));

							countryCodeSubdivisionNameMap.put(country, subdivisionName);
						}
					});

					inProgress.set(false);
				}
			});
		} catch (IOException e) {
			LOG.error("A problem was detected while loading the XML file with the available subdivisions.", e);
		}

	}

	private Country findCountryByCode(String code) {
		return countries.stream().filter(country -> country.getCountryCode().equals(code)).findFirst().orElse(null);
	}

	@Override
	public ObservableList<Country> availableCountries() {
		return countries;
	}

	@Override
	public ReadOnlyStringProperty subdivisionLabel() {
		return subdivisionLabel.getReadOnlyProperty();
	}

	@Override
	public ObservableList<Subdivision> subdivisions() {
		return FXCollections.unmodifiableObservableList(subdivisions);
	}

	@Override
	public ReadOnlyBooleanProperty inProgressProperty() {
		return inProgress.getReadOnlyProperty();
	}
}