Commits

Kevin Poalses  committed 74f5082

RENO: Moved some useful classes to dossier from dossier-contacts

  • Participants
  • Parent commits 0b5890f
  • Branches editing

Comments (0)

Files changed (41)

File .idea/compiler.xml

       <entry name="?*.dtd" />
       <entry name="?*.tld" />
       <entry name="?*.ftl" />
+      <entry name="/**/?*.css" />
     </wildcardResourcePatterns>
     <annotationProcessing>
       <profile default="true" name="Default" enabled="false">

File src/main/java/net/poalsoft/dossier/base/BuildException.java

+/*
+ * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1) Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2) Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.poalsoft.dossier.base;
+
+/**
+ * A build exception.
+ * 
+ * @author Kevin Poalses
+ */
+public class BuildException
+        extends RuntimeException {
+
+    /**
+     * Constructs a new runtime exception with the specified detail message and
+     * cause.  <p>Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this runtime exception's detail message.
+     *
+     * @param  message the detail message
+     * @param  cause the cause
+     */
+    public BuildException(String message,
+                          Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new runtime exception with the specified detail message.
+     *
+     * @param  message the detail message
+     */
+    public BuildException(String message) {
+        super(message);
+    }
+
+    /**
+     * Serialization UUID
+     */
+    private static final long serialVersionUID = 4482307608733560451L;
+}

File src/main/java/net/poalsoft/dossier/contacts/base/BuildException.java

-/*
- * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * 1) Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2) Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package net.poalsoft.dossier.contacts.base;
-
-/**
- * A build exception.
- * 
- * @author Kevin Poalses
- */
-public class BuildException
-        extends RuntimeException {
-
-    /**
-     * Constructs a new runtime exception with the specified detail message and
-     * cause.  <p>Note that the detail message associated with
-     * {@code cause} is <i>not</i> automatically incorporated in
-     * this runtime exception's detail message.
-     *
-     * @param  message the detail message
-     * @param  cause the cause
-     */
-    public BuildException(String message,
-                          Throwable cause) {
-        super(message, cause);
-    }
-
-    /**
-     * Constructs a new runtime exception with the specified detail message.
-     *
-     * @param  message the detail message
-     */
-    public BuildException(String message) {
-        super(message);
-    }
-
-    /**
-     * Serialization UUID
-     */
-    private static final long serialVersionUID = 4482307608733560451L;
-}

File src/main/java/net/poalsoft/dossier/contacts/tool/internal/ViewPersonsApplication.java

 import javafx.scene.control.Label;
 import javafx.scene.control.TableView;
 import javafx.scene.layout.VBox;
-import javafx.scene.text.Font;
 import javafx.stage.Stage;
 import net.poalsoft.dossier.contacts.ui.model.VisualPerson;
-import net.poalsoft.dossier.contacts.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.VisualEntityTableViewBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ApplicationContext;
         stage.setHeight(600);
 
         final Label label = new Label("Persons");
-        label.setFont(new Font("Arial", 20));
-
 
         final VBox vbox = new VBox();
         vbox.setSpacing(5);
-        vbox.setPadding(new Insets(10, 0, 0, 10));
+        vbox.setPadding(new Insets(1, 0, 0, 1));
         vbox.getChildren()
             .addAll(label, tableView);
 

File src/main/java/net/poalsoft/dossier/contacts/tool/internal/ViewPersonsModel.java

 import net.poalsoft.common.persistence.HibernateUtils;
 import net.poalsoft.dossier.contacts.base.Person;
 import net.poalsoft.dossier.contacts.persistence.PersonRepository;
-import net.poalsoft.dossier.contacts.ui.model.VisualEntityProxyBuilder;
+import net.poalsoft.dossier.ui.model.VisualEntityProxyBuilder;
 import net.poalsoft.dossier.contacts.ui.model.VisualPerson;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

File src/main/java/net/poalsoft/dossier/contacts/ui/ContactsUiConfig.java

 
 package net.poalsoft.dossier.contacts.ui;
 
+import net.poalsoft.dossier.ui.DossierUiConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
 
 /**
  * Dossier contacts user interface configuration.
  * @author Kevin Poalses
  */
 @Configuration
+@Import(DossierUiConfig.class)
 @ComponentScan("net.poalsoft.dossier.contacts.ui")
 public class ContactsUiConfig {
 

File src/main/java/net/poalsoft/dossier/contacts/ui/model/VisualEntityProxyBuilder.java

-/*
- * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * 1) Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2) Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.poalsoft.dossier.contacts.ui.model;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * Visual entity proxy builder.
- *
- * <p>Implementations <em>must</em> be {@link javax.annotation.concurrent.ThreadSafe}</p>
- *
- * @author Kevin Poalses
- */
-@ThreadSafe
-public interface VisualEntityProxyBuilder<VisualType extends WrappedType, WrappedType> {
-
-    /**
-     * Builds a visual entity proxy.
-     *
-     * @return Visual entity proxy
-     */
-    <VisualType> VisualType build();
-
-    /**
-     * Resets the builder values.
-     *
-     * @return Reference to self for fluent programming
-     */
-    VisualEntityProxyBuilder<VisualType, WrappedType> reset();
-
-    /**
-     * Sets the visual type class.
-     *
-     * @param visualType Visual type class
-     * @return Reference to self for fluent programming
-     */
-    VisualEntityProxyBuilder<VisualType, WrappedType> withVisualType(Class<VisualType> visualType);
-
-    /**
-     * Sets the wrapped entity.
-     *
-     * @param wrappedEntity Wrapped entity
-     * @return Reference to self for fluent programming
-     */
-    VisualEntityProxyBuilder<VisualType, WrappedType> withWrappedEntity(WrappedType wrappedEntity);
-
-}

File src/main/java/net/poalsoft/dossier/contacts/ui/model/internal/VisualEntityProxyBuilderImpl.java

-/*
- * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * 1) Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2) Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.poalsoft.dossier.contacts.ui.model.internal;
-
-import net.poalsoft.dossier.contacts.ui.model.VisualEntityProxyBuilder;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.context.annotation.Scope;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.concurrent.ThreadSafe;
-import java.lang.reflect.Proxy;
-
-/**
- * Visual entity proxy builder.
- *
- * @author Kevin Poalses
- */
-@ThreadSafe
-@Component("visualEntityProxyBuilder")
-@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
-public class VisualEntityProxyBuilderImpl<VisualType extends WrappedType, WrappedType> implements VisualEntityProxyBuilder<VisualType, WrappedType> {
-
-    public VisualEntityProxyBuilderImpl() {
-        this.visualType = new ThreadLocal<>();
-        this.wrappedEntity = new ThreadLocal<>();
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public VisualType build() {
-        if (this.visualType
-                .get() == null) {
-            throw new IllegalStateException("Visual entity proxy builder not ready: Visual type not set");
-        } else if (this.wrappedEntity
-                .get() == null) {
-            throw new IllegalStateException("Visual entity proxy builder not ready: Wrapped entity not set");
-        }
-
-        return (VisualType) Proxy.newProxyInstance(getClass().getClassLoader(),
-                                                   new Class[]{this.visualType
-                                                                       .get()},
-                                                   new VisualEntityProxyInvocationHandler<>(
-                                                           this.visualType
-                                                                   .get(),
-                                                           this.wrappedEntity
-                                                                   .get()));
-    }
-
-    @Override
-    public VisualEntityProxyBuilder<VisualType, WrappedType> reset() {
-        this.visualType
-                .set(null);
-        this.wrappedEntity
-                .set(null);
-        return this;
-    }
-
-    @Override
-    public VisualEntityProxyBuilder<VisualType, WrappedType> withVisualType(Class<VisualType> visualType) {
-        if (visualType == null) {
-            throw new IllegalArgumentException("Visual type may not be null");
-        }
-        this.visualType
-                .set(visualType);
-        return this;
-    }
-
-    @Override
-    public VisualEntityProxyBuilder<VisualType, WrappedType> withWrappedEntity(WrappedType wrappedEntity) {
-        if (wrappedEntity == null) {
-            throw new IllegalArgumentException("Wrapped entity may not be null");
-        }
-        this.wrappedEntity
-                .set(wrappedEntity);
-        return this;
-    }
-
-    private final ThreadLocal<Class<VisualType>> visualType;
-
-    private final ThreadLocal<WrappedType> wrappedEntity;
-}

File src/main/java/net/poalsoft/dossier/contacts/ui/model/internal/VisualEntityProxyInvocationHandler.java

-/*
- * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * 1) Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2) Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.poalsoft.dossier.contacts.ui.model.internal;
-
-import javafx.beans.property.*;
-import javafx.beans.property.adapter.*;
-import javafx.collections.FXCollections;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeanUtils;
-
-import java.beans.IntrospectionException;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * An invocation handler to perform visually wrapped method calls.
- *
- * @author Kevin Poalses
- */
-public class VisualEntityProxyInvocationHandler<VisualType, WrappedType> implements InvocationHandler {
-
-    public VisualEntityProxyInvocationHandler(Class<? extends VisualType> visualType,
-                                              WrappedType wrappedEntity) {
-        logger.trace("Creating visual entity proxy invocation handler for visual type [{}] with wrapped entity [{}]",
-                     visualType,
-                     wrappedEntity);
-
-        this.visualType = visualType;
-        this.wrappedEntity = wrappedEntity;
-
-        this.readOnlyPropertyAccessorMap = new HashMap<>();
-        this.propertyValueGetterMap = new HashMap<>();
-        this.writablePropertyAccessorMap = new HashMap<>();
-        this.propertyValueSetterMap = new HashMap<>();
-
-        initializePropertyMaps();
-
-    }
-
-    @Override
-    public Object invoke(Object proxy,
-                         Method method,
-                         Object[] args) throws Throwable {
-
-        // Use property accessor maps if possible
-        if (this.propertyValueGetterMap
-                .containsKey(method)) {
-            return this.propertyValueGetterMap
-                    .get(method)
-                    .getValue();
-        } else if (this.propertyValueSetterMap
-                .containsKey(method)) {
-            //noinspection unchecked
-            this.propertyValueSetterMap
-                    .get(method)
-                    .setValue(args[0]);
-        } else if (this.readOnlyPropertyAccessorMap
-                .containsKey(method)) {
-            return this.readOnlyPropertyAccessorMap
-                    .get(method);
-        } else if (this.writablePropertyAccessorMap
-                .containsKey(method)) {
-            return this.writablePropertyAccessorMap
-                    .get(method);
-        }
-
-        // Invoke method on wrapped entity instead
-        return method.invoke(this.wrappedEntity, args);
-    }
-
-    @Override
-    public int hashCode() {
-        return this.wrappedEntity
-                .hashCode();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof VisualEntityProxyInvocationHandler)) {
-            return false;
-        }
-
-        VisualEntityProxyInvocationHandler that = (VisualEntityProxyInvocationHandler) o;
-
-        return this.wrappedEntity
-                .equals(that.wrappedEntity);
-    }
-
-    @Override
-    public String toString() {
-        return "VisualEntityProxyInvocationHandler{" +
-                "wrappedEntity=" + wrappedEntity +
-                '}';
-    }
-
-    private void initializePropertyMaps() {
-
-        logger.trace("Initializing property maps for visual type [{}] with wrapped entity [{}]",
-                     visualType,
-                     wrappedEntity);
-
-        // Find JavaBean properties
-        Map<String, PropertyDescriptor> javaBeanProperties =
-                new HashMap<>();
-        for (Method visualMethod : visualType.getMethods()) {
-            PropertyDescriptor propertyForMethod = BeanUtils.findPropertyForMethod(visualMethod);
-            if (propertyForMethod != null) {
-                String propertyName = propertyForMethod.getName();
-                if (javaBeanProperties.containsKey(propertyName)) {
-                    PropertyDescriptor oldPropertyDescriptor = javaBeanProperties.get(propertyName);
-                    if (oldPropertyDescriptor.getReadMethod() == null) {
-                        if (propertyForMethod.getReadMethod() != null) {
-                            try {
-                                propertyForMethod = new PropertyDescriptor(propertyName,
-                                                                           propertyForMethod.getReadMethod(),
-                                                                           oldPropertyDescriptor.getWriteMethod());
-                            } catch (IntrospectionException e) {
-                                throw new RuntimeException(
-                                        "Failed to set read method of property [" + propertyName + "]");
-                            }
-                        }
-                    } else if (propertyForMethod.getWriteMethod() == null) {
-                        if (propertyForMethod.getWriteMethod() != null) {
-                            try {
-                                propertyForMethod = new PropertyDescriptor(propertyName,
-                                                                           oldPropertyDescriptor.getReadMethod(),
-                                                                           propertyForMethod.getWriteMethod());
-                            } catch (IntrospectionException e) {
-                                throw new RuntimeException(
-                                        "Failed to set read method of property [" + propertyName + "]");
-                            }
-                        }
-                    }
-                }
-                javaBeanProperties.put(propertyName,
-                                       propertyForMethod);
-            }
-        }
-
-        // Find visual properties
-        Map<PropertyDescriptor, Method> visualProperties =
-                new HashMap<>();
-        for (Method visualMethod : visualType.getMethods()) {
-            if (visualMethod.getName()
-                            .endsWith("Property")) {
-                String propertyName = visualMethod.getName()
-                                                  .substring(0, visualMethod.getName()
-                                                                            .length() - 8);
-                if (javaBeanProperties.containsKey(propertyName)
-                        && ReadOnlyProperty.class.isAssignableFrom(visualMethod.getReturnType())) {
-                    visualProperties.put(javaBeanProperties.get(propertyName),
-                                         visualMethod);
-                }
-            }
-        }
-
-        // Prepare visual property maps
-        for (Map.Entry<PropertyDescriptor, Method> visualPropertyDescriptorEntry : visualProperties.entrySet()) {
-
-            String propertyName = visualPropertyDescriptorEntry.getKey()
-                                                               .getName();
-
-            Class<?> propertyType = visualPropertyDescriptorEntry.getKey()
-                                                                 .getPropertyType();
-
-            boolean readOnlyProperty = visualPropertyDescriptorEntry.getKey()
-                                                                    .getWriteMethod() == null;
-
-            Method readMethod = visualPropertyDescriptorEntry.getKey()
-                                                             .getReadMethod();
-
-            if (readOnlyProperty) {
-                ReadOnlyProperty<?> propertyAdapter = createReadOnlyPropertyAdapter(propertyName,
-                                                                                    propertyType,
-                                                                                    readMethod);
-                this.readOnlyPropertyAccessorMap
-                        .put(visualPropertyDescriptorEntry.getValue(),
-                             propertyAdapter);
-                this.propertyValueGetterMap
-                        .put(readMethod,
-                             propertyAdapter);
-            } else {
-                Property<?> propertyAdapter = createPropertyAdapter(propertyName,
-                                                                    propertyType,
-                                                                    readMethod);
-                this.writablePropertyAccessorMap
-                        .put(visualPropertyDescriptorEntry.getValue(),
-                             propertyAdapter);
-                this.propertyValueGetterMap
-                        .put(readMethod,
-                             propertyAdapter);
-                this.propertyValueSetterMap
-                        .put(visualPropertyDescriptorEntry.getKey()
-                                                          .getWriteMethod(),
-                             propertyAdapter);
-            }
-
-        }
-    }
-
-    /**
-     * Creates a visual property adapter around the given JavaBeans property.
-     *
-     * @param propertyName Property name
-     * @param propertyType Property type
-     * @param readMethod   Property read method
-     * @return Visual property adapter
-     */
-    private Property<?> createPropertyAdapter(String propertyName,
-                                              Class<?> propertyType,
-                                              Method readMethod) {
-
-        logger.trace("Creating visual property adapter for JavaBean property [{}] of type [{}]",
-                     propertyName,
-                     propertyType);
-
-        try {
-
-            if (String.class.isAssignableFrom(propertyType)) {
-                return JavaBeanStringPropertyBuilder.create()
-                                                    .name(propertyName)
-                                                    .bean(this.wrappedEntity)
-                                                    .build();
-            } else if (Boolean.class.isAssignableFrom(propertyType)) {
-                return JavaBeanBooleanPropertyBuilder.create()
-                                                     .name(propertyName)
-                                                     .bean(this.wrappedEntity)
-                                                     .build();
-            } else if (Integer.class.isAssignableFrom(propertyType)) {
-                return JavaBeanIntegerPropertyBuilder.create()
-                                                     .name(propertyName)
-                                                     .bean(this.wrappedEntity)
-                                                     .build();
-            } else if (Long.class.isAssignableFrom(propertyType)) {
-                return JavaBeanLongPropertyBuilder.create()
-                                                  .name(propertyName)
-                                                  .bean(this.wrappedEntity)
-                                                  .build();
-            } else if (Float.class.isAssignableFrom(propertyType)) {
-                return JavaBeanFloatPropertyBuilder.create()
-                                                   .name(propertyName)
-                                                   .bean(this.wrappedEntity)
-                                                   .build();
-            } else if (Double.class.isAssignableFrom(propertyType)) {
-                return JavaBeanDoublePropertyBuilder.create()
-                                                    .name(propertyName)
-                                                    .bean(this.wrappedEntity)
-                                                    .build();
-            } else if (List.class.isAssignableFrom(propertyType)) {
-                ListProperty property =
-                        new SimpleListProperty<>(this, propertyName);
-                //noinspection unchecked
-                property.setValue(FXCollections.observableList(
-                        (List<Object>) readMethod.invoke(this.wrappedEntity)));
-                return property;
-            } else if (Set.class.isAssignableFrom(propertyType)) {
-                SetProperty property =
-                        new SimpleSetProperty<>(this, propertyName);
-                //noinspection unchecked
-                property.setValue(FXCollections.observableSet((Set<Object>) readMethod.invoke(this.wrappedEntity)));
-                return property;
-            } else {
-                return JavaBeanObjectPropertyBuilder.create()
-                                                    .name(propertyName)
-                                                    .bean(this.wrappedEntity)
-                                                    .build();
-            }
-        } catch (NoSuchMethodException | IllegalAccessException e) {
-            logger.error("Failed to create visual JavaBean property wrapper for [{}]", propertyName, e);
-            throw new RuntimeException(
-                    "Failed to create visual JavaBean property wrapper for [" + propertyName + "]", e);
-        } catch (InvocationTargetException e) {
-            logger.error("Failed to create visual JavaBean property wrapper for [{}]", propertyName, e.getCause());
-            throw new RuntimeException(
-                    "Failed to create visual JavaBean property wrapper for [" + propertyName + "]", e.getCause());
-        }
-    }
-
-    /**
-     * Creates a read-only visual property adapter around the given JavaBeans property.
-     *
-     * @param propertyName Property name
-     * @param propertyType Property type
-     * @param readMethod   Property read method
-     * @return Read-only visual property adapter
-     */
-    private ReadOnlyProperty<?> createReadOnlyPropertyAdapter(String propertyName,
-                                                              Class<?> propertyType,
-                                                              Method readMethod) {
-
-        logger.trace("Creating read-only visual property adapter for JavaBean property [{}] of type [{}]",
-                     propertyName,
-                     propertyType);
-
-        try {
-
-            if (String.class.isAssignableFrom(propertyType)) {
-                return ReadOnlyJavaBeanStringPropertyBuilder.create()
-                                                            .name(propertyName)
-                                                            .bean(this.wrappedEntity)
-                                                            .build();
-            } else if (Boolean.class.isAssignableFrom(propertyType)) {
-                return ReadOnlyJavaBeanBooleanPropertyBuilder.create()
-                                                             .name(propertyName)
-                                                             .bean(this.wrappedEntity)
-                                                             .build();
-            } else if (Integer.class.isAssignableFrom(propertyType)) {
-                return ReadOnlyJavaBeanIntegerPropertyBuilder.create()
-                                                             .name(propertyName)
-                                                             .bean(this.wrappedEntity)
-                                                             .build();
-            } else if (Long.class.isAssignableFrom(propertyType)) {
-                return ReadOnlyJavaBeanLongPropertyBuilder.create()
-                                                          .name(propertyName)
-                                                          .bean(this.wrappedEntity)
-                                                          .build();
-            } else if (Float.class.isAssignableFrom(propertyType)) {
-                return ReadOnlyJavaBeanFloatPropertyBuilder.create()
-                                                           .name(propertyName)
-                                                           .bean(this.wrappedEntity)
-                                                           .build();
-            } else if (Double.class.isAssignableFrom(propertyType)) {
-                return ReadOnlyJavaBeanDoublePropertyBuilder.create()
-                                                            .name(propertyName)
-                                                            .bean(this.wrappedEntity)
-                                                            .build();
-            } else if (List.class.isAssignableFrom(propertyType)) {
-                //noinspection unchecked
-                ReadOnlyListProperty property =
-                        new ReadOnlyListWrapper(this.wrappedEntity,
-                                                propertyName,
-                                                FXCollections.observableList(
-                                                        (List<Object>) readMethod.invoke(this.wrappedEntity)));
-                return property;
-            } else if (Set.class.isAssignableFrom(propertyType)) {
-                @SuppressWarnings("unchecked") SetProperty property =
-                        new ReadOnlySetWrapper(this.wrappedEntity,
-                                               propertyName,
-                                               FXCollections.observableSet(
-                                                       (Set<Object>) readMethod.invoke(this.wrappedEntity)));
-                return property;
-            } else {
-                return ReadOnlyJavaBeanObjectPropertyBuilder.create()
-                                                            .name(propertyName)
-                                                            .bean(this.wrappedEntity)
-                                                            .getter(readMethod)
-                                                            .build();
-            }
-        } catch (NoSuchMethodException | IllegalAccessException e) {
-            logger.error("Failed to create read-only visual JavaBean property wrapper for [{}]", propertyName, e);
-            throw new RuntimeException(
-                    "Failed to create read-only visual JavaBean property wrapper for [" + propertyName + "]", e);
-        } catch (InvocationTargetException e) {
-            logger.error("Failed to create read-only visual JavaBean property wrapper for [{}]", propertyName,
-                         e.getCause());
-            throw new RuntimeException(
-                    "Failed to create read-only visual JavaBean property wrapper for [" + propertyName + "]",
-                    e.getCause());
-        }
-    }
-
-    private final Map<Method, ReadOnlyProperty> readOnlyPropertyAccessorMap;
-
-    private final Map<Method, ReadOnlyProperty> propertyValueGetterMap;
-
-    private final Map<Method, Property> writablePropertyAccessorMap;
-
-    private final Map<Method, Property> propertyValueSetterMap;
-
-    private final Class<? extends VisualType> visualType;
-
-    private final WrappedType wrappedEntity;
-
-    private static final Logger logger = LoggerFactory.getLogger(VisualEntityProxyInvocationHandler.class);
-}

File src/main/java/net/poalsoft/dossier/contacts/ui/view/VisualEntityTableViewBuilder.java

-/*
- * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * 1) Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2) Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.poalsoft.dossier.contacts.ui.view;
-
-import javafx.collections.ObservableList;
-import javafx.scene.control.TableView;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * A builder for creating visual entity table views.
- * <p/>
- * <p>Implementations <em>must</em> be {@link javax.annotation.concurrent.ThreadSafe}</p>
- *
- * @author Kevin Poalses
- */
-@ThreadSafe
-public interface VisualEntityTableViewBuilder<VisualEntity> {
-
-    /**
-     * Builds a table view of the visual entities.
-     *
-     * @return Table view
-     */
-    TableView<VisualEntity> build();
-
-    /**
-     * Resets the table view builder.
-     *
-     * @return Reference to self for fluent programming
-     */
-    VisualEntityTableViewBuilder<VisualEntity> reset();
-
-    /**
-     * Sets the initial table items.
-     *
-     * @param items Table items
-     * @return Reference to self for fluent programming
-     */
-    VisualEntityTableViewBuilder<VisualEntity> withItems(ObservableList<VisualEntity> items);
-
-    /**
-     * Creates the default columns.
-     *
-     * @return Reference to self for fluent programming
-     */
-    VisualEntityTableViewBuilder<VisualEntity> withDefaultColumns();
-
-    /**
-     * Creates any non-essential columns.
-     *
-     * @return Reference to self for fluent programming
-     */
-    VisualEntityTableViewBuilder<VisualEntity> withExtraColumns();
-
-    /**
-     * Sets the default preferred width.
-     *
-     * @return Reference to self for fluent programming
-     */
-    VisualEntityTableViewBuilder<VisualEntity> withDefaultPreferredWidth();
-
-    /**
-     * Sets the preferred width.
-     *
-     * @param preferredWidth Preferred table width
-     * @return Reference to self for fluent programming
-     */
-    VisualEntityTableViewBuilder<VisualEntity> withPreferredWidth(double preferredWidth);
-}

File src/main/java/net/poalsoft/dossier/contacts/ui/view/internal/AbstractVisualContactTableViewBuilder.java

 import net.poalsoft.dossier.contacts.base.EmailAddress;
 import net.poalsoft.dossier.contacts.base.Geolocation;
 import net.poalsoft.dossier.contacts.ui.model.VisualContact;
-import net.poalsoft.dossier.contacts.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.internal.AbstractVisualEntityTableViewBuilder;
 
 import javax.annotation.concurrent.ThreadSafe;
 import java.util.Iterator;

File src/main/java/net/poalsoft/dossier/contacts/ui/view/internal/AbstractVisualEntityTableViewBuilder.java

-/*
- * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * 1) Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2) Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.poalsoft.dossier.contacts.ui.view.internal;
-
-import javafx.collections.ObservableList;
-import javafx.scene.control.TableColumn;
-import javafx.scene.control.TableView;
-import javafx.scene.control.TableViewBuilder;
-import net.poalsoft.dossier.contacts.ui.view.VisualEntityTableViewBuilder;
-
-import javax.annotation.concurrent.ThreadSafe;
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * An abstract visual entity table view builder.
- * <p/>
- * <p>Derived classes <em>must</em> be {@link javax.annotation.concurrent.ThreadSafe}</p>
- *
- * @author Kevin Poalses
- */
-@ThreadSafe
-public abstract class AbstractVisualEntityTableViewBuilder<VisualEntity> implements
-                                                                         VisualEntityTableViewBuilder<VisualEntity> {
-
-    public AbstractVisualEntityTableViewBuilder() {
-
-        this.tableViewBuilder = new ThreadLocal<TableViewBuilder<VisualEntity, ?>>() {
-            @Override
-            protected TableViewBuilder<VisualEntity, ?> initialValue() {
-                return TableViewBuilder.create();
-            }
-        };
-        this.defaultTableColumns = new
-                ThreadLocal<Collection<TableColumn<VisualEntity, ?>>>() {
-                    @Override
-                    protected Collection<TableColumn<VisualEntity, ?>> initialValue() {
-                        return new ArrayList<>(INITIAL_TABLE_COLUMNS_CAPACITY);
-                    }
-                };
-        this.extraTableColumns = new
-                ThreadLocal<Collection<TableColumn<VisualEntity, ?>>>() {
-                    @Override
-                    protected Collection<TableColumn<VisualEntity, ?>> initialValue() {
-                        return new ArrayList<>(INITIAL_TABLE_COLUMNS_CAPACITY);
-                    }
-                };
-
-    }
-
-    @Override
-    public TableView<VisualEntity> build() {
-        return this.tableViewBuilder
-                .get()
-                .columns(this.defaultTableColumns
-                                 .get())
-                .build();
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p/>
-     * <p>
-     * Subclasses <em>must</em> super call this if they override {@link #reset()}
-     * </p>
-     *
-     * @return Reference to self for fluent programming
-     */
-    @Override
-    public VisualEntityTableViewBuilder<VisualEntity> reset() {
-        this.tableViewBuilder
-                .set(TableViewBuilder.<VisualEntity>create());
-        this.defaultTableColumns
-                .set(new ArrayList<TableColumn<VisualEntity, ?>>(INITIAL_TABLE_COLUMNS_CAPACITY));
-        return this;
-    }
-
-    @Override
-    public VisualEntityTableViewBuilder<VisualEntity> withItems(ObservableList<VisualEntity> items) {
-        this.tableViewBuilder
-                .get()
-                .items(items);
-        return this;
-    }
-
-    @Override
-    public VisualEntityTableViewBuilder<VisualEntity> withPreferredWidth(double preferredWidth) {
-        this.tableViewBuilder
-                .get()
-                .prefWidth(preferredWidth);
-
-        return this;
-    }
-
-    /**
-     * Add the given table column to the list of default columns.
-     *
-     * @param tableColumn Table column
-     */
-    protected void withDefaultTableColumn(TableColumn<VisualEntity, ?> tableColumn) {
-        this.defaultTableColumns
-                .get()
-                .add(tableColumn);
-    }
-
-    /**
-     * Add the given table column to the list of extra columns.
-     *
-     * @param tableColumn Table column
-     */
-    protected void withExtraTableColumn(TableColumn<VisualEntity, ?> tableColumn) {
-        this.extraTableColumns
-                .get()
-                .add(tableColumn);
-    }
-
-    private final ThreadLocal<Collection<TableColumn<VisualEntity, ?>>> defaultTableColumns;
-
-    private final ThreadLocal<Collection<TableColumn<VisualEntity, ?>>> extraTableColumns;
-
-    private final ThreadLocal<TableViewBuilder<VisualEntity, ?>> tableViewBuilder;
-
-    private static final int INITIAL_TABLE_COLUMNS_CAPACITY = 20;
-}

File src/main/java/net/poalsoft/dossier/contacts/ui/view/internal/VisualAddressTableViewBuilderImpl.java

 import javafx.scene.control.cell.PropertyValueFactoryBuilder;
 import net.poalsoft.dossier.contacts.base.AddressType;
 import net.poalsoft.dossier.contacts.ui.model.VisualAddress;
-import net.poalsoft.dossier.contacts.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.internal.AbstractVisualEntityTableViewBuilder;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;

File src/main/java/net/poalsoft/dossier/contacts/ui/view/internal/VisualEmailAddressTableViewBuilderImpl.java

 import javafx.util.Callback;
 import net.poalsoft.dossier.contacts.base.EmailContentFormat;
 import net.poalsoft.dossier.contacts.ui.model.VisualEmailAddress;
-import net.poalsoft.dossier.contacts.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.internal.AbstractVisualEntityTableViewBuilder;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;

File src/main/java/net/poalsoft/dossier/contacts/ui/view/internal/VisualOrganizationTableViewBuilderImpl.java

 import javafx.scene.control.TableColumnBuilder;
 import javafx.scene.control.cell.PropertyValueFactoryBuilder;
 import net.poalsoft.dossier.contacts.ui.model.VisualOrganization;
-import net.poalsoft.dossier.contacts.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.VisualEntityTableViewBuilder;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;

File src/main/java/net/poalsoft/dossier/contacts/ui/view/internal/VisualPersonTableViewBuilderImpl.java

 import javafx.scene.control.cell.PropertyValueFactoryBuilder;
 import net.poalsoft.dossier.contacts.base.PersonGender;
 import net.poalsoft.dossier.contacts.ui.model.VisualPerson;
-import net.poalsoft.dossier.contacts.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.VisualEntityTableViewBuilder;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;

File src/main/java/net/poalsoft/dossier/contacts/ui/view/internal/VisualTelephoneNumberTableViewBuilderImpl.java

 import javafx.scene.control.cell.PropertyValueFactoryBuilder;
 import net.poalsoft.dossier.contacts.base.TelephoneNumberType;
 import net.poalsoft.dossier.contacts.ui.model.VisualTelephoneNumber;
-import net.poalsoft.dossier.contacts.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.VisualEntityTableViewBuilder;
+import net.poalsoft.dossier.ui.view.internal.AbstractVisualEntityTableViewBuilder;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;

File src/main/java/net/poalsoft/dossier/ui/DossierUiConfig.java

+/*
+ * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1) Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2) Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.poalsoft.dossier.ui;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Dossier user interface configuration.
+ *
+ * @author Kevin Poalses
+ */
+@Configuration
+@ComponentScan("net.poalsoft.dossier.ui")
+public class DossierUiConfig {
+
+    /**
+     * Logger
+     */
+    private static final Logger logger = LoggerFactory.getLogger(DossierUiConfig.class);
+}

File src/main/java/net/poalsoft/dossier/ui/model/VisualEntityProxyBuilder.java

+/*
+ * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1) Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2) Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.poalsoft.dossier.ui.model;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Visual entity proxy builder.
+ *
+ * <p>Implementations <em>must</em> be {@link javax.annotation.concurrent.ThreadSafe}</p>
+ *
+ * @author Kevin Poalses
+ */
+@ThreadSafe
+public interface VisualEntityProxyBuilder<VisualType extends WrappedType, WrappedType> {
+
+    /**
+     * Builds a visual entity proxy.
+     *
+     * @return Visual entity proxy
+     */
+    <VisualType> VisualType build();
+
+    /**
+     * Resets the builder values.
+     *
+     * @return Reference to self for fluent programming
+     */
+    VisualEntityProxyBuilder<VisualType, WrappedType> reset();
+
+    /**
+     * Sets the visual type class.
+     *
+     * @param visualType Visual type class
+     * @return Reference to self for fluent programming
+     */
+    VisualEntityProxyBuilder<VisualType, WrappedType> withVisualType(Class<VisualType> visualType);
+
+    /**
+     * Sets the wrapped entity.
+     *
+     * @param wrappedEntity Wrapped entity
+     * @return Reference to self for fluent programming
+     */
+    VisualEntityProxyBuilder<VisualType, WrappedType> withWrappedEntity(WrappedType wrappedEntity);
+
+}

File src/main/java/net/poalsoft/dossier/ui/model/internal/VisualEntityProxyBuilderImpl.java

+/*
+ * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1) Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2) Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.poalsoft.dossier.ui.model.internal;
+
+import net.poalsoft.dossier.ui.model.VisualEntityProxyBuilder;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.lang.reflect.Proxy;
+
+/**
+ * Visual entity proxy builder.
+ *
+ * @author Kevin Poalses
+ */
+@ThreadSafe
+@Component("visualEntityProxyBuilder")
+@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
+public class VisualEntityProxyBuilderImpl<VisualType extends WrappedType, WrappedType> implements VisualEntityProxyBuilder<VisualType, WrappedType> {
+
+    public VisualEntityProxyBuilderImpl() {
+        this.visualType = new ThreadLocal<>();
+        this.wrappedEntity = new ThreadLocal<>();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public VisualType build() {
+        if (this.visualType
+                .get() == null) {
+            throw new IllegalStateException("Visual entity proxy builder not ready: Visual type not set");
+        } else if (this.wrappedEntity
+                .get() == null) {
+            throw new IllegalStateException("Visual entity proxy builder not ready: Wrapped entity not set");
+        }
+
+        return (VisualType) Proxy.newProxyInstance(getClass().getClassLoader(),
+                                                   new Class[]{this.visualType
+                                                                       .get()},
+                                                   new VisualEntityProxyInvocationHandler<>(
+                                                           this.visualType
+                                                                   .get(),
+                                                           this.wrappedEntity
+                                                                   .get()));
+    }
+
+    @Override
+    public VisualEntityProxyBuilder<VisualType, WrappedType> reset() {
+        this.visualType
+                .set(null);
+        this.wrappedEntity
+                .set(null);
+        return this;
+    }
+
+    @Override
+    public VisualEntityProxyBuilder<VisualType, WrappedType> withVisualType(Class<VisualType> visualType) {
+        if (visualType == null) {
+            throw new IllegalArgumentException("Visual type may not be null");
+        }
+        this.visualType
+                .set(visualType);
+        return this;
+    }
+
+    @Override
+    public VisualEntityProxyBuilder<VisualType, WrappedType> withWrappedEntity(WrappedType wrappedEntity) {
+        if (wrappedEntity == null) {
+            throw new IllegalArgumentException("Wrapped entity may not be null");
+        }
+        this.wrappedEntity
+                .set(wrappedEntity);
+        return this;
+    }
+
+    private final ThreadLocal<Class<VisualType>> visualType;
+
+    private final ThreadLocal<WrappedType> wrappedEntity;
+}

File src/main/java/net/poalsoft/dossier/ui/model/internal/VisualEntityProxyInvocationHandler.java

+/*
+ * Copyright (c) 2010-2013. Kevin Poalses <kevinp@poalsoft.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1) Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2) Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.poalsoft.dossier.ui.model.internal;
+
+import javafx.beans.property.*;
+import javafx.beans.property.adapter.*;
+import javafx.collections.FXCollections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An invocation handler to perform visually wrapped method calls.
+ *
+ * @author Kevin Poalses
+ */
+public class VisualEntityProxyInvocationHandler<VisualType, WrappedType> implements InvocationHandler {
+
+    public VisualEntityProxyInvocationHandler(Class<? extends VisualType> visualType,
+                                              WrappedType wrappedEntity) {
+        logger.trace("Creating visual entity proxy invocation handler for visual type [{}] with wrapped entity [{}]",
+                     visualType,
+                     wrappedEntity);
+
+        this.visualType = visualType;
+        this.wrappedEntity = wrappedEntity;
+
+        this.readOnlyPropertyAccessorMap = new HashMap<>();
+        this.propertyValueGetterMap = new HashMap<>();
+        this.writablePropertyAccessorMap = new HashMap<>();
+        this.propertyValueSetterMap = new HashMap<>();
+
+        initializePropertyMaps();
+
+    }
+
+    @Override
+    public Object invoke(Object proxy,
+                         Method method,
+                         Object[] args) throws Throwable {
+
+        // Use property accessor maps if possible
+        if (this.propertyValueGetterMap
+                .containsKey(method)) {
+            return this.propertyValueGetterMap
+                    .get(method)
+                    .getValue();
+        } else if (this.propertyValueSetterMap
+                .containsKey(method)) {
+            //noinspection unchecked
+            this.propertyValueSetterMap
+                    .get(method)
+                    .setValue(args[0]);
+        } else if (this.readOnlyPropertyAccessorMap
+                .containsKey(method)) {
+            return this.readOnlyPropertyAccessorMap
+                    .get(method);
+        } else if (this.writablePropertyAccessorMap
+                .containsKey(method)) {
+            return this.writablePropertyAccessorMap
+                    .get(method);
+        }
+
+        // Invoke method on wrapped entity instead
+        try {
+            return method.invoke(this.wrappedEntity, args);
+        } catch (Exception e) {
+            logger.error("Error while invoking [{}] on wrapped entity [{}]",
+                         method,
+                         this.wrappedEntity,
+                         e);
+            throw e;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return this.wrappedEntity
+                .hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof VisualEntityProxyInvocationHandler)) {
+            return false;
+        }
+
+        VisualEntityProxyInvocationHandler that = (VisualEntityProxyInvocationHandler) o;
+
+        return this.wrappedEntity
+                .equals(that.wrappedEntity);
+    }
+
+    @Override
+    public String toString() {
+        return "VisualEntityProxyInvocationHandler{" +
+                "wrappedEntity=" + wrappedEntity +
+                '}';
+    }
+
+    private void initializePropertyMaps() {
+
+        logger.trace("Initializing property maps for visual type [{}] with wrapped entity [{}]",
+                     visualType,
+                     wrappedEntity);
+
+        // Find JavaBean properties
+        Map<String, PropertyDescriptor> javaBeanProperties =
+                new HashMap<>();
+        for (Method visualMethod : visualType.getMethods()) {
+            PropertyDescriptor propertyForMethod = BeanUtils.findPropertyForMethod(visualMethod);
+            if (propertyForMethod != null) {
+                String propertyName = propertyForMethod.getName();
+                if (javaBeanProperties.containsKey(propertyName)) {
+                    PropertyDescriptor oldPropertyDescriptor = javaBeanProperties.get(propertyName);
+                    if (oldPropertyDescriptor.getReadMethod() == null) {
+                        if (propertyForMethod.getReadMethod() != null) {
+                            try {
+                                propertyForMethod = new PropertyDescriptor(propertyName,
+                                                                           propertyForMethod.getReadMethod(),
+                                                                           oldPropertyDescriptor.getWriteMethod());
+                            } catch (IntrospectionException e) {
+                                throw new RuntimeException(
+                                        "Failed to set read method of property [" + propertyName + "]");
+                            }
+                        }
+                    } else if (propertyForMethod.getWriteMethod() == null) {
+                        if (propertyForMethod.getWriteMethod() != null) {
+                            try {
+                                propertyForMethod = new PropertyDescriptor(propertyName,
+                                                                           oldPropertyDescriptor.getReadMethod(),
+                                                                           propertyForMethod.getWriteMethod());
+                            } catch (IntrospectionException e) {
+                                throw new RuntimeException(
+                                        "Failed to set read method of property [" + propertyName + "]");
+                            }
+                        }
+                    }
+                }
+                javaBeanProperties.put(propertyName,
+                                       propertyForMethod);
+            }
+        }
+
+        // Find visual properties
+        Map<PropertyDescriptor, Method> visualProperties =
+                new HashMap<>();
+        for (Method visualMethod : visualType.getMethods()) {
+            if (visualMethod.getName()
+                            .endsWith("Property")) {
+                String propertyName = visualMethod.getName()
+                                                  .substring(0, visualMethod.getName()
+                                                                            .length() - 8);
+                if (javaBeanProperties.containsKey(propertyName)
+                        && ReadOnlyProperty.class.isAssignableFrom(visualMethod.getReturnType())) {
+                    visualProperties.put(javaBeanProperties.get(propertyName),
+                                         visualMethod);
+                }
+            }
+        }
+
+        // Prepare visual property maps
+        for (Map.Entry<PropertyDescriptor, Method> visualPropertyDescriptorEntry : visualProperties.entrySet()) {
+
+            String propertyName = visualPropertyDescriptorEntry.getKey()