org.wicketopia.Wicketopia.java Source code

Java tutorial

Introduction

Here is the source code for org.wicketopia.Wicketopia.java

Source

/*
 * Copyright (c) 2011 Carman Consulting, Inc.
 *
 * 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 org.wicketopia;

import org.apache.commons.lang.StringUtils;
import org.apache.wicket.Component;
import org.apache.wicket.MetaDataKey;
import org.apache.wicket.RuntimeConfigurationType;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.model.IModel;
import org.apache.wicket.protocol.http.WebApplication;
import org.metastopheles.BeanMetaData;
import org.metastopheles.BeanMetaDataFactory;
import org.metastopheles.MetaDataDecorator;
import org.metastopheles.MethodMetaData;
import org.metastopheles.PropertyMetaData;
import org.metastopheles.annotation.AnnotationBeanMetaDataFactory;
import org.scannotation.ClasspathUrlFinder;
import org.scannotation.WarUrlFinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wicketopia.builder.EditorBuilder;
import org.wicketopia.builder.ViewerBuilder;
import org.wicketopia.context.Context;
import org.wicketopia.editor.PropertyEditorProvider;
import org.wicketopia.editor.component.property.CheckBoxPropertyEditor;
import org.wicketopia.editor.component.property.PasswordFieldPropertyEditor;
import org.wicketopia.editor.component.property.TextAreaPropertyEditor;
import org.wicketopia.editor.component.property.TextFieldPropertyEditor;
import org.wicketopia.editor.provider.EnumDropDownChoicePropertyEditorProvider;
import org.wicketopia.factory.PropertyComponentFactory;
import org.wicketopia.factory.PropertyEditorComponentFactory;
import org.wicketopia.factory.PropertyViewerComponentFactory;
import org.wicketopia.mapping.TypeMapping;
import org.wicketopia.mapping.editor.DefaultEditorTypeMapping;
import org.wicketopia.mapping.viewer.DefaultViewerTypeMapping;
import org.wicketopia.metadata.WicketopiaPropertyFacet;
import org.wicketopia.model.column.BeanPropertyColumn;
import org.wicketopia.util.ServiceLocator;
import org.wicketopia.viewer.PropertyViewerProvider;
import org.wicketopia.viewer.component.LabelPropertyViewer;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Wicketopia {
    //----------------------------------------------------------------------------------------------------------------------
    // Fields
    //----------------------------------------------------------------------------------------------------------------------

    private static final Logger LOGGER = LoggerFactory.getLogger(Wicketopia.class);

    private static final MetaDataKey<Wicketopia> META_KEY = new WicketopiaPluginKey();

    private BeanMetaDataFactory beanMetaDataFactory = new AnnotationBeanMetaDataFactory(findClasspathUrls());
    private TypeMapping editorTypeMapping = new DefaultEditorTypeMapping();
    private TypeMapping viewerTypeMapping = new DefaultViewerTypeMapping();
    private final Map<String, PropertyEditorProvider> editorProviders = new HashMap<String, PropertyEditorProvider>();
    private final Map<String, PropertyViewerProvider> viewerProviders = new HashMap<String, PropertyViewerProvider>();
    private final List<WicketopiaPlugin> plugins;
    private WebApplication application;

    //----------------------------------------------------------------------------------------------------------------------
    // Static Methods
    //----------------------------------------------------------------------------------------------------------------------

    private static URL[] findClasspathUrls() {
        final UrlList urls = new UrlList();
        urls.addClassLoader(Wicketopia.class.getClassLoader());
        urls.addClassLoader(Thread.currentThread().getContextClassLoader());
        WebApplication webApplication = WebApplication.get();
        if (webApplication != null) {
            urls.addUrl(WarUrlFinder.findWebInfClassesPath(webApplication.getServletContext()));
            urls.addAll(WarUrlFinder.findWebInfLibClasspaths(webApplication.getServletContext()));
        }
        urls.addAll(ClasspathUrlFinder.findClassPaths());
        LOGGER.debug("Scanning classpath URLs\n{}", urls);
        return urls.toArray();
    }

    public static Wicketopia get() {
        return WebApplication.get().getMetaData(META_KEY);
    }

    /**
     * Installs Wicketopia into the currently-running web application using all default settings.
     */
    public static void install() {
        new Wicketopia().install(WebApplication.get());
    }

    //----------------------------------------------------------------------------------------------------------------------
    // Constructors
    //----------------------------------------------------------------------------------------------------------------------

    public Wicketopia() {
        this(findDefaultPlugins());
    }

    private static List<WicketopiaPlugin> findDefaultPlugins() {
        final List<WicketopiaPlugin> plugins = new LinkedList<WicketopiaPlugin>();
        for (WicketopiaPlugin wicketopiaPlugin : ServiceLocator.findAll(WicketopiaPlugin.class)) {
            plugins.add(wicketopiaPlugin);
        }
        return plugins;
    }

    public Wicketopia(List<WicketopiaPlugin> plugins) {
        this.plugins = plugins;
    }

    public Wicketopia(WicketopiaPlugin... plugins) {
        this(Arrays.asList(plugins));
    }

    //----------------------------------------------------------------------------------------------------------------------
    // Getter/Setter Methods
    //----------------------------------------------------------------------------------------------------------------------

    public WebApplication getApplication() {
        return application;
    }

    public TypeMapping getEditorTypeMapping() {
        return editorTypeMapping;
    }

    public void setEditorTypeMapping(TypeMapping editorTypeMapping) {
        this.editorTypeMapping = editorTypeMapping;
    }

    public TypeMapping getViewerTypeMapping() {
        return viewerTypeMapping;
    }

    public void setViewerTypeMapping(TypeMapping viewerTypeMapping) {
        this.viewerTypeMapping = viewerTypeMapping;
    }

    public void setBeanMetaDataFactory(BeanMetaDataFactory beanMetaDataFactory) {
        this.beanMetaDataFactory = beanMetaDataFactory;
    }

    //----------------------------------------------------------------------------------------------------------------------
    // Other Methods
    //----------------------------------------------------------------------------------------------------------------------

    public void addBeanMetaDataDecorator(MetaDataDecorator<BeanMetaData> decorator) {
        beanMetaDataFactory.getBeanMetaDataDecorators().add(decorator);
    }

    public void addEditorTypeOverride(Class<?> propertyType, String typeName) {
        editorTypeMapping.addTypeOverride(propertyType, typeName);
    }

    public void addMethodMetaDataDecorator(MetaDataDecorator<MethodMetaData> decorator) {
        beanMetaDataFactory.getMethodMetaDataDecorators().add(decorator);
    }

    public void addPlugin(WicketopiaPlugin plugin) {
        plugins.add(plugin);
    }

    public void addPropertyEditorProvider(String typeName, PropertyEditorProvider provider) {
        editorProviders.put(typeName, provider);
    }

    public void addPropertyMetaDataDecorator(MetaDataDecorator<PropertyMetaData> decorator) {
        beanMetaDataFactory.getPropertyMetaDataDecorators().add(decorator);
    }

    public void addPropertyViewerProvider(String typeName, PropertyViewerProvider provider) {
        viewerProviders.put(typeName, provider);
    }

    public void addViewerTypeOverride(Class<?> propertyType, String typeName) {
        viewerTypeMapping.addTypeOverride(propertyType, typeName);
    }

    public <T> List<IColumn<T, String>> createColumns(Class<T> beanType, PropertyComponentFactory<T> factory,
            Context context, String... properties) {
        final List<String> visible = getVisibleProperties(beanType, context, properties);
        final List<IColumn<T, String>> columns = new ArrayList<IColumn<T, String>>(visible.size());
        for (String propertyName : visible) {
            columns.add(new BeanPropertyColumn<T>(factory, propertyName, context));
        }
        return columns;
    }

    public <T> PropertyComponentFactory<T> createEditorFactory(Class<T> beanType) {
        return new PropertyEditorComponentFactory<T>(beanType);
    }

    public Component createPropertyEditor(String id, PropertyMetaData propertyMetadata, IModel<?> propertyModel,
            Context context) {
        final WicketopiaPropertyFacet facet = WicketopiaPropertyFacet.get(propertyMetadata);
        EditorBuilder builder = getEditorProvider(propertyMetadata).createPropertyEditor(id, propertyMetadata,
                propertyModel, context);
        facet.decorate(builder, context);
        return builder.build();
    }

    public Component createPropertyViewer(String id, PropertyMetaData propertyMetaData, IModel<?> propertyModel,
            Context context) {
        final WicketopiaPropertyFacet facet = WicketopiaPropertyFacet.get(propertyMetaData);
        ViewerBuilder builder = getViewerProvider(propertyMetaData).createPropertyViewer(id, propertyMetaData,
                propertyModel, context);
        facet.decorate(builder, context);
        return builder.build();
    }

    public <T> PropertyComponentFactory<T> createViewerFactory(Class<T> beanType) {
        return new PropertyViewerComponentFactory<T>(beanType);
    }

    public BeanMetaData getBeanMetaData(Class<?> beanClass) {
        if (WebApplication.get().getConfigurationType().equals(RuntimeConfigurationType.DEVELOPMENT)) {
            beanMetaDataFactory.clear();
        }
        return beanMetaDataFactory.getBeanMetaData(beanClass);
    }

    public PropertyEditorProvider getEditorProvider(PropertyMetaData propertyMetaData) {
        WicketopiaPropertyFacet facet = WicketopiaPropertyFacet.get(propertyMetaData);
        String editorType = facet.getEditorType();
        if (editorType == null) {
            editorType = editorTypeMapping.getTypeName(propertyMetaData);
            facet.setEditorType(editorType);
        }
        if (editorType == null) {
            throw new WicketRuntimeException("No editor type defined for property "
                    + propertyMetaData.getPropertyDescriptor().getName() + " of class "
                    + propertyMetaData.getBeanMetaData().getBeanDescriptor().getBeanClass().getName() + ".");
        }
        PropertyEditorProvider provider = editorProviders.get(editorType);
        if (provider == null) {
            throw new WicketRuntimeException("No property editor provider registered for editor type \""
                    + editorType + "\" for property " + propertyMetaData.getPropertyDescriptor().getName()
                    + " of class " + propertyMetaData.getBeanMetaData().getBeanDescriptor().getBeanClass().getName()
                    + ".");
        }
        return provider;
    }

    /**
     * Retrieves a plugin by type
     *
     * @param pluginType the plugin type
     * @param <P>        the plugin type
     * @return the plugin
     */
    public <P extends WicketopiaPlugin> P getPlugin(Class<P> pluginType) {
        for (WicketopiaPlugin plugin : plugins) {
            if (pluginType.isInstance(plugin)) {
                return pluginType.cast(plugin);
            }
        }
        throw new WicketRuntimeException("No plugin of type " + pluginType.getName() + " is installed.");
    }

    public PropertyViewerProvider getViewerProvider(PropertyMetaData propertyMetaData) {
        WicketopiaPropertyFacet facet = WicketopiaPropertyFacet.get(propertyMetaData);
        String viewerType = facet.getViewerType();
        if (viewerType == null) {
            viewerType = viewerTypeMapping.getTypeName(propertyMetaData);
            facet.setViewerType(viewerType);
        }
        if (viewerType == null) {
            throw new WicketRuntimeException("No viewer type defined for property "
                    + propertyMetaData.getPropertyDescriptor().getName() + " of class "
                    + propertyMetaData.getBeanMetaData().getBeanDescriptor().getBeanClass().getName() + ".");
        }
        PropertyViewerProvider provider = viewerProviders.get(viewerType);
        if (provider == null) {
            throw new WicketRuntimeException("No property viewer provider registered for viewer type \""
                    + viewerType + "\" for property " + propertyMetaData.getPropertyDescriptor().getName()
                    + " of class " + propertyMetaData.getBeanMetaData().getBeanDescriptor().getBeanClass().getName()
                    + ".");
        }
        return viewerProviders.get(viewerType);
    }

    public List<String> getVisibleProperties(Class<?> beanType, Context context, String... properties) {
        final List<String> names = new LinkedList<String>();
        final BeanMetaData beanMetaData = getBeanMetaData(beanType);
        if (properties == null || properties.length == 0) {
            for (String propertyName : beanMetaData.getPropertyNames()) {
                WicketopiaPropertyFacet facet = WicketopiaPropertyFacet
                        .get(beanMetaData.getPropertyMetaData(propertyName));
                if (!facet.isIgnored() && facet.isVisible(context)) {
                    names.add(propertyName);
                }
            }
            Collections.sort(names, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    WicketopiaPropertyFacet left = WicketopiaPropertyFacet
                            .get(beanMetaData.getPropertyMetaData(o1));
                    WicketopiaPropertyFacet right = WicketopiaPropertyFacet
                            .get(beanMetaData.getPropertyMetaData(o2));
                    return WicketopiaPropertyFacet.compare(left, right);
                }
            });
        } else {
            for (String propertyName : properties) {
                WicketopiaPropertyFacet facet = WicketopiaPropertyFacet
                        .get(beanMetaData.getPropertyMetaData(propertyName));
                if (!facet.isIgnored() && facet.isVisible(context)) {
                    names.add(propertyName);
                }
            }
        }
        return names;
    }

    public void install(WebApplication application) {
        this.application = application;
        application.setMetaData(META_KEY, this);
        addDefaultEditorProviders();
        adDefaultViewerProviders();
        for (WicketopiaPlugin plugin : plugins) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Initializing {} plugin...", plugin.getClass().getName());
            }
            plugin.initialize(this);
        }
    }

    private void addDefaultEditorProviders() {
        addPropertyEditorProvider(TextFieldPropertyEditor.TYPE_NAME, TextFieldPropertyEditor.getProvider());
        addPropertyEditorProvider(TextAreaPropertyEditor.TYPE_NAME, TextAreaPropertyEditor.getProvider());
        addPropertyEditorProvider(EnumDropDownChoicePropertyEditorProvider.TYPE_NAME,
                new EnumDropDownChoicePropertyEditorProvider());
        addPropertyEditorProvider(PasswordFieldPropertyEditor.TYPE_NAME, PasswordFieldPropertyEditor.getProvider());
        addPropertyEditorProvider(CheckBoxPropertyEditor.TYPE_NAME, CheckBoxPropertyEditor.getProvider());
    }

    private void adDefaultViewerProviders() {
        addPropertyViewerProvider(LabelPropertyViewer.TYPE_NAME, LabelPropertyViewer.getProvider());
    }

    //----------------------------------------------------------------------------------------------------------------------
    // Inner Classes
    //----------------------------------------------------------------------------------------------------------------------

    private static class UrlList {
        private final List<URL> urls = new LinkedList<URL>();
        private final Set<String> urlStrings = new HashSet<String>();

        public void addClassLoader(ClassLoader classLoader) {
            if (classLoader instanceof URLClassLoader) {
                addAll(((URLClassLoader) classLoader).getURLs());
            } else {
                LOGGER.warn("ClassLoader {} is not a URLClassLoader!", classLoader);
            }
        }

        public void addUrl(URL url) {
            if (url != null && urlStrings.add(url.toString())) {
                urls.add(url);
            }
        }

        public void addAll(URL[] urls) {
            for (URL url : urls) {
                addUrl(url);
            }
        }

        public URL[] toArray() {
            return urls.toArray(new URL[urls.size()]);
        }

        public String toString() {
            return StringUtils.join(urls, "\n");
        }
    }

    private static final class WicketopiaPluginKey extends MetaDataKey<Wicketopia> {
    }
}