org.shredzone.commons.view.manager.ViewManager.java Source code

Java tutorial

Introduction

Here is the source code for org.shredzone.commons.view.manager.ViewManager.java

Source

/*
 * Shredzone Commons
 *
 * Copyright (C) 2012 Richard "Shred" Krber
 *   http://commons.shredzone.org
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Library General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.shredzone.commons.view.manager;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.shredzone.commons.view.Signature;
import org.shredzone.commons.view.annotation.View;
import org.shredzone.commons.view.annotation.ViewGroup;
import org.shredzone.commons.view.annotation.ViewHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * Manages the view handlers.
 *
 * @author Richard "Shred" Krber
 */
@Component
public class ViewManager {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    private @Resource ApplicationContext applicationContext;
    private @Resource ConversionService conversionService;

    private Map<String, Map<String, List<ViewPattern>>> patternMap = new HashMap<>();
    private Map<String, Map<Signature, ViewPattern>> signatureMap = new HashMap<>();
    private List<ViewPattern> patternOrder = new ArrayList<>();

    /**
     * Returns a collection of all defined {@link ViewPattern}.
     *
     * @return Collection of matching {@link ViewPattern}
     */
    public Collection<ViewPattern> getViewPatterns() {
        return Collections.unmodifiableCollection(patternOrder);
    }

    /**
     * Returns a collection of {@link ViewPattern} that were defined for the given view.
     *
     * @param view
     *            View name
     * @param qualifier
     *            Qualifier name, or {@code null}
     * @return Collection of matching {@link ViewPattern}, or {@code null} if there is no
     *         such view
     */
    public Collection<ViewPattern> getViewPatternsForView(String view, String qualifier) {
        Map<String, List<ViewPattern>> viewMap = patternMap.get(view);
        if (viewMap != null) {
            List<ViewPattern> result = viewMap.get(qualifier);
            if (result != null) {
                return Collections.unmodifiableCollection(result);
            }
        }
        return null;
    }

    /**
     * Returns the {@link ViewPattern} that handles the given {@link Signature}.
     *
     * @param signature
     *            {@link Signature} to find a {@link ViewPattern} for
     * @param qualifier
     *            Qualifier name, or {@code null}
     * @return {@link ViewPattern} found, or {@code null} if there is no such
     *         {@link ViewPattern}
     */
    public ViewPattern getViewPatternForSignature(Signature signature, String qualifier) {
        Map<Signature, ViewPattern> sigMap = signatureMap.get(qualifier);
        if (sigMap != null) {
            return sigMap.get(signature);
        }
        return null;
    }

    /**
     * Sets up the view manager. All Spring beans are searched for {@link ViewHandler}
     * annotations.
     */
    @PostConstruct
    protected void setup() {
        Collection<Object> beans = applicationContext.getBeansWithAnnotation(ViewHandler.class).values();
        for (Object bean : beans) {
            ViewHandler vhAnno = bean.getClass().getAnnotation(ViewHandler.class);
            if (vhAnno != null) {
                for (Method method : bean.getClass().getMethods()) {
                    ViewGroup groupAnno = AnnotationUtils.findAnnotation(method, ViewGroup.class);
                    if (groupAnno != null) {
                        for (View viewAnno : groupAnno.value()) {
                            processView(bean, method, viewAnno);
                        }
                    }

                    View viewAnno = AnnotationUtils.findAnnotation(method, View.class);
                    if (viewAnno != null) {
                        processView(bean, method, viewAnno);
                    }
                }
            }
        }

        patternMap.values().forEach(pm -> pm.values().forEach(Collections::sort));
        Collections.sort(patternOrder);
    }

    /**
     * Processes a {@link View}. A view name and view pattern is generated, and a
     * {@link ViewInvoker} is built.
     *
     * @param bean
     *            Spring bean to be used
     * @param method
     *            View handler method to be invoked
     * @param anno
     *            {@link View} annotation
     */
    private void processView(Object bean, Method method, View anno) {
        String name = computeViewName(method, anno);

        Map<String, List<ViewPattern>> vpMap = patternMap.computeIfAbsent(name, it -> new HashMap<>());

        ViewInvoker invoker = new ViewInvoker(bean, method, conversionService);
        ViewPattern vp = new ViewPattern(anno, invoker);

        List<ViewPattern> vpList = vpMap.computeIfAbsent(vp.getQualifier(), it -> new ArrayList<>());
        vpList.add(vp);

        Signature sig = vp.getSignature();
        if (sig != null) {
            Map<Signature, ViewPattern> sigMap = signatureMap.computeIfAbsent(vp.getQualifier(),
                    it -> new HashMap<>());
            if (sigMap.putIfAbsent(sig, vp) != null) {
                throw new IllegalStateException("Signature '" + sig + "' defined twice");
            }
        }

        patternOrder.add(vp);

        log.info("Found view '{}' with pattern '{}'", name, anno.pattern());
    }

    /**
     * Computes a view name. If the {@link View} annotation contains a name, it is used.
     * If no name is given, it is guessed by the method name. If the method name ends with
     * "View", it is removed.
     *
     * @param method
     *            {@link Method} of the view handler
     * @param anno
     *            {@link View} annotation
     * @return view name to be used for this view
     */
    private String computeViewName(Method method, View anno) {
        if (StringUtils.hasText(anno.name())) {
            return anno.name();
        }

        String name = method.getName();
        if (name.length() > 4 && name.endsWith("View")) {
            name = name.substring(0, name.length() - "View".length());
        }

        return name;
    }

}