com.isotrol.impe3.core.component.ComponentDefinition.java Source code

Java tutorial

Introduction

Here is the source code for com.isotrol.impe3.core.component.ComponentDefinition.java

Source

/**
 * This file is part of Port@l
 * Port@l 3.0 - Portal Engine and Management System
 * Copyright (C) 2010  Isotrol, SA.  http://www.isotrol.com
 *
 * Port@l is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Port@l is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Port@l.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.isotrol.impe3.core.component;

import static com.google.common.collect.Iterables.filter;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.isotrol.impe3.api.Configuration;
import com.isotrol.impe3.api.Cookies;
import com.isotrol.impe3.api.Headers;
import com.isotrol.impe3.api.LocalParams;
import com.isotrol.impe3.api.RequestParams;
import com.isotrol.impe3.api.SessionParams;
import com.isotrol.impe3.api.component.CacheMode;
import com.isotrol.impe3.api.component.CacheScope;
import com.isotrol.impe3.api.component.Component;
import com.isotrol.impe3.api.component.ComponentCacheMode;
import com.isotrol.impe3.api.component.ComponentCacheScope;
import com.isotrol.impe3.api.component.ComponentETag;
import com.isotrol.impe3.api.component.ComponentETagMode;
import com.isotrol.impe3.api.component.ComponentRenderer;
import com.isotrol.impe3.api.component.ETag;
import com.isotrol.impe3.api.component.Extract;
import com.isotrol.impe3.api.component.ExtractAction;
import com.isotrol.impe3.api.component.ExtractCookie;
import com.isotrol.impe3.api.component.ExtractDynHeader;
import com.isotrol.impe3.api.component.ExtractDynLocal;
import com.isotrol.impe3.api.component.ExtractDynQuery;
import com.isotrol.impe3.api.component.ExtractDynSession;
import com.isotrol.impe3.api.component.ExtractHeader;
import com.isotrol.impe3.api.component.ExtractLocal;
import com.isotrol.impe3.api.component.ExtractQuery;
import com.isotrol.impe3.api.component.ExtractSession;
import com.isotrol.impe3.api.component.Inject;
import com.isotrol.impe3.api.component.InjectBase;
import com.isotrol.impe3.api.component.InjectBindingErrors;
import com.isotrol.impe3.api.component.InjectCookie;
import com.isotrol.impe3.api.component.InjectHeader;
import com.isotrol.impe3.api.component.InjectLocal;
import com.isotrol.impe3.api.component.InjectProperty;
import com.isotrol.impe3.api.component.InjectRequest;
import com.isotrol.impe3.api.component.InjectSession;
import com.isotrol.impe3.api.component.Paginated;
import com.isotrol.impe3.api.component.Renderer;
import com.isotrol.impe3.api.component.RequiresLink;
import com.isotrol.impe3.api.component.VisualComponent;
import com.isotrol.impe3.core.config.ConfigurationDefinition;
import com.isotrol.impe3.core.config.ConfigurationException;
import com.isotrol.impe3.core.support.Definition;
import com.isotrol.impe3.core.support.SingleValueSupport;

/**
 * Object that encapsulates the definition of a component.
 * @author Andres Rodriguez.
 * @param <T> Component class.
 */
public final class ComponentDefinition<T extends Component> extends Definition<T> {
    @SuppressWarnings("unchecked")
    private static final ImmutableSet<Class<? extends Annotation>> INJECT_ANNOTATIONS = ImmutableSet.of(
            Inject.class, InjectCookie.class, InjectHeader.class, InjectRequest.class, InjectSession.class,
            InjectLocal.class, InjectBindingErrors.class, InjectProperty.class, InjectBase.class);
    @SuppressWarnings("unchecked")
    private static final ImmutableSet<Class<? extends Annotation>> EXTRACT_ANNOTATIONS = ImmutableSet.of(
            Extract.class, ExtractSession.class, ExtractLocal.class, ExtractQuery.class, ExtractHeader.class,
            ExtractDynHeader.class, ExtractDynLocal.class, ExtractDynSession.class, ExtractDynQuery.class,
            ExtractAction.class, ExtractCookie.class);
    private static final ImmutableSet<Class<? extends Annotation>> ANNOTATIONS = ImmutableSet
            .copyOf(Iterables.concat(INJECT_ANNOTATIONS, EXTRACT_ANNOTATIONS, ImmutableSet.of(Renderer.class)));

    /** Cache. */
    private static final SingleValueSupport<Class<?>, Object> CACHE = SingleValueSupport.create();

    /**
     * Returns the component definition for the specified component class.
     * @param componentClass Component class.
     * @return The component definition.
     * @throws ComponentException if the configuration class is invalid.
     */
    public static <T extends Component> ComponentDefinition<T> of(Class<T> componentClass)
            throws ComponentException {
        Preconditions.checkNotNull(componentClass, "A component class must be provided");
        @SuppressWarnings("unchecked")
        final ComponentDefinition<T> d1 = (ComponentDefinition<T>) CACHE.get(componentClass);
        if (d1 != null) {
            return d1;
        }

        @SuppressWarnings("unchecked")
        final ComponentDefinition<T> d2 = (ComponentDefinition<T>) CACHE.put(componentClass,
                new ComponentDefinition<T>(componentClass));
        return d2;

    }

    /** Component configuration. */
    private final ConfigurationDefinition<?> configuration;
    private final DirectInjectors<T> directInjectors;
    private final DirectInjectors<T> bindingErrorsInjectors;
    private final ParameterInjectors<T, Headers> headerInjectors;
    private final ParameterInjectors<T, Cookies> cookieInjectors;
    private final ParameterInjectors<T, RequestParams> requestInjectors;
    private final ParameterInjectors<T, SessionParams> sessionInjectors;
    private final ParameterInjectors<T, LocalParams> localInjectors;
    private final PropertyInjectors<T> propertyInjectors;
    private final BaseInjectors<T> baseInjectors;
    private final Renderers<T> renderers;
    private final DirectExtractors<T> extractors;
    private final LocalExtractors<T> localExtractors;
    private final SessionExtractors<T> sessionExtractors;
    private final QueryExtractors<T> queryExtractors;
    private final HeaderExtractors<T> headerExtractors;
    private final ActionExtractors<T> actionExtractors;
    private final CookieExtractors<T> cookieExtractors;
    private boolean paginated;
    private final ImmutableSet<Class<?>> requiredLinks;
    private final ImmutableSet<Class<?>> providedLinks;
    /** Component type. */
    private ComponentType componentType;
    /** Cache mode. */
    private final CacheMode cacheMode;
    /** Cache scope. */
    private final CacheScope cacheScope;
    /** ETag mode. */
    private final ComponentETagMode eTagMode;

    private ComponentDefinition(Class<T> type) {
        super(type);
        final List<Method> methods = getComponentMethods();
        this.directInjectors = DirectInjectors.direct(type, methods);
        this.configuration = loadConfiguration();
        this.bindingErrorsInjectors = DirectInjectors.bindingErrors(type, methods);
        this.headerInjectors = ParameterInjectors.headers(type, methods);
        this.cookieInjectors = ParameterInjectors.cookies(type, methods);
        this.requestInjectors = ParameterInjectors.request(type, methods);
        this.sessionInjectors = ParameterInjectors.session(type, methods);
        this.localInjectors = ParameterInjectors.local(type, methods);
        this.propertyInjectors = PropertyInjectors.of(type, methods);
        this.baseInjectors = BaseInjectors.of(type, methods);
        this.renderers = Renderers.of(type, methods);
        this.extractors = DirectExtractors.of(type, methods);
        this.localExtractors = LocalExtractors.of(type, methods);
        this.sessionExtractors = SessionExtractors.of(type, methods);
        this.queryExtractors = QueryExtractors.of(type, methods);
        this.headerExtractors = HeaderExtractors.of(type, methods);
        this.actionExtractors = ActionExtractors.of(type, methods);
        this.cookieExtractors = CookieExtractors.of(type, methods);
        this.paginated = type.isAnnotationPresent(Paginated.class);
        final RequiresLink links = type.getAnnotation(RequiresLink.class);
        if (links == null) {
            this.requiredLinks = ImmutableSet.of();
        } else {
            this.requiredLinks = ImmutableSet.copyOf(links.value());
        }
        this.providedLinks = ImmutableSet.copyOf(Sets.union(this.requiredLinks, extractors.typeSet()));
        this.componentType = getComponentType(!renderers.isEmpty());
        // Cache parameters
        this.cacheMode = type.isAnnotationPresent(ComponentCacheMode.class)
                ? type.getAnnotation(ComponentCacheMode.class).value()
                : CacheMode.ON;
        this.cacheScope = type.isAnnotationPresent(ComponentCacheScope.class)
                ? type.getAnnotation(ComponentCacheScope.class).value()
                : CacheScope.PUBLIC;
        if (type.isAnnotationPresent(ComponentETag.class)) {
            this.eTagMode = type.getAnnotation(ComponentETag.class).value();
        } else {
            this.eTagMode = this.extractors.contains(ETag.class) ? ComponentETagMode.DEFAULT
                    : ComponentETagMode.DISABLED;
        }
    }

    public ConfigurationDefinition<?> getConfiguration() {
        return configuration;
    }

    public DirectInjectors<T> getDirectInjectors() {
        return directInjectors;
    }

    public DirectInjectors<T> getBindingErrorsInjectors() {
        return bindingErrorsInjectors;
    }

    public ParameterInjectors<T, Headers> getHeaderInjectors() {
        return headerInjectors;
    }

    public ParameterInjectors<T, Cookies> getCookieInjectors() {
        return cookieInjectors;
    }

    public ParameterInjectors<T, RequestParams> getRequestInjectors() {
        return requestInjectors;
    }

    public ParameterInjectors<T, SessionParams> getSessionInjectors() {
        return sessionInjectors;
    }

    public ParameterInjectors<T, LocalParams> getLocalInjectors() {
        return localInjectors;
    }

    public PropertyInjectors<T> getPropertyInjectors() {
        return propertyInjectors;
    }

    public BaseInjectors<T> getBaseInjectors() {
        return baseInjectors;
    }

    public Renderers<T> getRenderers() {
        return renderers;
    }

    public DirectExtractors<T> getExtractors() {
        return extractors;
    }

    public LocalExtractors<T> getLocalExtractors() {
        return localExtractors;
    }

    public SessionExtractors<T> getSessionExtractors() {
        return sessionExtractors;
    }

    public QueryExtractors<T> getQueryExtractors() {
        return queryExtractors;
    }

    public HeaderExtractors<T> getHeaderExtractors() {
        return headerExtractors;
    }

    public ActionExtractors<T> getActionExtractors() {
        return actionExtractors;
    }

    public CookieExtractors<T> getCookieExtractors() {
        return cookieExtractors;
    }

    public boolean isPaginated() {
        return paginated;
    }

    public ImmutableSet<Class<?>> getRequiredLinks() {
        return requiredLinks;
    }

    public ImmutableSet<Class<?>> getProvidedLinks() {
        return providedLinks;
    }

    public ComponentType getComponentType() {
        return componentType;
    }

    public CacheMode getCacheMode() {
        return cacheMode;
    }

    public CacheScope getCacheScope() {
        return cacheScope;
    }

    public ComponentETagMode getETagMode() {
        return eTagMode;
    }

    /**
     * Returns the component type in relation with a provided renderer type.
     * @param rendererType Renderer type.
     * @return The component type.
     */
    public <R extends ComponentRenderer> ComponentType getComponentType(Class<R> rendererType) {
        return getComponentType(renderers.contains(rendererType));
    }

    /* LOADING METHODS. */

    private List<Method> getComponentMethods() {
        final Predicate<Method> valid = new Predicate<Method>() {
            public boolean apply(Method input) {
                boolean added = false;
                for (Class<? extends Annotation> a : ANNOTATIONS) {
                    if (input.getAnnotation(a) != null) {
                        if (added) {
                            throw new IllegalMethodComponentException(getType(), input);
                        } else {
                            added = true;
                        }
                    }
                }
                return added;
            }
        };
        return Lists.newLinkedList(filter(getMethods(), valid));
    }

    private ConfigurationDefinition<?> loadConfiguration() {
        ConfigurationDefinition<?> cd = null;
        for (final Class<?> type : directInjectors.getInjectedTypes()) {
            if (ConfigurationDefinition.IS_CONFIGURATION.apply(type)) {
                if (cd != null) {
                    throw new DuplicateComponentConfigurationException(getType());
                }
                try {
                    cd = ConfigurationDefinition.of(type.asSubclass(Configuration.class));
                } catch (ConfigurationException e) {
                    throw new ComponentConfigurationException(getType(), e);
                }
            }
        }
        return cd;
    }

    private ComponentType getComponentType(boolean hasRenderers) {
        final boolean visual = VisualComponent.class.isAssignableFrom(getType());
        if (!visual) {
            if (hasRenderers) {
                return ComponentType.DECORATOR;
            }
            return ComponentType.FUNCTIONAL;
        }
        if (hasRenderers) {
            return ComponentType.VISUAL;
        }
        return ComponentType.FUNCTIONAL;
    }
}